From 0c852542e74f1a189a10d81c4f82809ffd2fa57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Sat, 5 Nov 2016 17:56:11 +0200 Subject: [PATCH] Performance|Bindings|Client: Minor optimizations in input bindings Using compiled records for storing the bindings to avoid repeated Record lookups. --- doomsday/apps/client/include/ui/b_util.h | 4 +- doomsday/apps/client/include/ui/bindcontext.h | 6 +- doomsday/apps/client/include/ui/binding.h | 25 +++++- .../apps/client/include/ui/impulsebinding.h | 22 +++++ .../client/src/client/clientsubsector.cpp | 6 +- doomsday/apps/client/src/con_config.cpp | 4 +- doomsday/apps/client/src/ui/b_util.cpp | 83 +++++++++---------- doomsday/apps/client/src/ui/bindcontext.cpp | 69 +++++++-------- doomsday/apps/client/src/ui/binding.cpp | 56 +++++++++---- .../apps/client/src/ui/commandbinding.cpp | 3 +- .../apps/client/src/ui/impulsebinding.cpp | 25 +++++- doomsday/apps/client/src/ui/inputsystem.cpp | 32 +++---- .../apps/client/src/world/base/p_players.cpp | 9 +- 13 files changed, 212 insertions(+), 132 deletions(-) diff --git a/doomsday/apps/client/include/ui/b_util.h b/doomsday/apps/client/include/ui/b_util.h index a99c940bfa..31e2f6392e 100644 --- a/doomsday/apps/client/include/ui/b_util.h +++ b/doomsday/apps/client/include/ui/b_util.h @@ -56,9 +56,7 @@ bool B_CheckAxisPosition(Binding::ControlTest test, float testPos, float pos); * @param localNum Local player number. * @param context Relevant binding context, if any (may be @c nullptr). */ -bool B_CheckCondition(de::Record const *cond, int localNum, BindContext const *context); - -bool B_EqualConditions(de::Record const &a, de::Record const &b); +bool B_CheckCondition(Binding::CompiledConditionRecord const *cond, int localNum, BindContext const *context); // --------------------------------------------------------------------------------- diff --git a/doomsday/apps/client/include/ui/bindcontext.h b/doomsday/apps/client/include/ui/bindcontext.h index 9ca92affb6..b41fbe0a17 100644 --- a/doomsday/apps/client/include/ui/bindcontext.h +++ b/doomsday/apps/client/include/ui/bindcontext.h @@ -161,9 +161,11 @@ class BindContext * * @param localPlayer (@c < 0 || >= DDMAXPLAYERS) for all local players. */ - de::LoopResult forAllImpulseBindings(int localPlayer, std::function func) const; + de::LoopResult forAllImpulseBindings(int localPlayer, + std::function func) const; - inline de::LoopResult forAllImpulseBindings(std::function func) const { + inline de::LoopResult forAllImpulseBindings( + std::function func) const { return forAllImpulseBindings(-1/*all local players*/, func); } diff --git a/doomsday/apps/client/include/ui/binding.h b/doomsday/apps/client/include/ui/binding.h index b799bd8d77..4ce13ecad5 100644 --- a/doomsday/apps/client/include/ui/binding.h +++ b/doomsday/apps/client/include/ui/binding.h @@ -21,7 +21,7 @@ #define CLIENT_INPUTSYSTEM_BINDING_H #include -#include +#include /** * Base class for binding record accessors. @@ -62,6 +62,23 @@ class Binding : public de::RecordAccessor ButtonStateUp }; + struct CompiledCondition + { + ConditionType type = Invalid; + ControlTest test = None; + int device = -1; + int id = -1; + float pos = 0; + bool negate = false; + bool multiplayer = false; + + CompiledCondition() {} + CompiledCondition(de::Record const &rec); + bool operator == (CompiledCondition const &other) const; + }; + + typedef de::CompiledRecordT CompiledConditionRecord; + public: Binding() : RecordAccessor(0) {} Binding(Binding const &other) : RecordAccessor(other) {} @@ -97,11 +114,11 @@ class Binding : public de::RecordAccessor */ virtual de::String composeDescriptor() = 0; - de::Record &addCondition(); + CompiledConditionRecord &addCondition(); int conditionCount() const; bool hasCondition(int index) const; - de::Record &condition(int index); - de::Record const &condition(int index) const; + CompiledConditionRecord &condition(int index); + CompiledConditionRecord const &condition(int index) const; /** * Compare the binding conditions with @a other and return @c true if equivalent. diff --git a/doomsday/apps/client/include/ui/impulsebinding.h b/doomsday/apps/client/include/ui/impulsebinding.h index f986f9e40f..adbcb0698d 100644 --- a/doomsday/apps/client/include/ui/impulsebinding.h +++ b/doomsday/apps/client/include/ui/impulsebinding.h @@ -21,6 +21,7 @@ #define CLIENT_INPUTSYSTEM_IMPULSEBINDING_H #include +#include #include "Binding" #include "ddevent.h" @@ -39,6 +40,23 @@ enum ibcontroltype_t #define IBDF_INVERSE 0x1 #define IBDF_TIME_STAGED 0x2 +struct CompiledImpulseBinding +{ + int id = -1; + int deviceId = -1; + int controlId = -1; + ibcontroltype_t type = IBD_TOGGLE; ///< Type of event. + float angle = 0; + int flags = 0; + int impulseId = 0; ///< Identifier of the bound player impulse. + int localPlayer = -1; ///< Local player number. + + CompiledImpulseBinding() {} + CompiledImpulseBinding(de::Record const &bind); +}; + +typedef de::CompiledRecordT CompiledImpulseBindingRecord; + /** * Utility for handling input-device-control => impulse binding records. * @@ -54,9 +72,13 @@ class ImpulseBinding : public Binding ImpulseBinding &operator = (de::Record const *d) { *static_cast(this) = d; + def().resetCompiled(); return *this; } + CompiledImpulseBindingRecord &def(); + CompiledImpulseBindingRecord const &def() const; + void resetToDefaults(); de::String composeDescriptor(); diff --git a/doomsday/apps/client/src/client/clientsubsector.cpp b/doomsday/apps/client/src/client/clientsubsector.cpp index a6502c895b..d3555bbac5 100644 --- a/doomsday/apps/client/src/client/clientsubsector.cpp +++ b/doomsday/apps/client/src/client/clientsubsector.cpp @@ -175,13 +175,11 @@ DENG2_PIMPL(ClientSubsector) DecoratedSurface() {} - ~DecoratedSurface() - { + ~DecoratedSurface() { qDeleteAll(decorations); } - void markForUpdate(bool yes = true) - { + void markForUpdate(bool yes = true) { if (::ddMapSetup) return; needUpdate = yes; } diff --git a/doomsday/apps/client/src/con_config.cpp b/doomsday/apps/client/src/con_config.cpp index 16338c04bd..e53f1ce76e 100644 --- a/doomsday/apps/client/src/con_config.cpp +++ b/doomsday/apps/client/src/con_config.cpp @@ -233,10 +233,10 @@ static bool writeBindingsState(Path const &filePath) }); // Impulses. - context.forAllImpulseBindings([&out, &context] (Record &rec) + context.forAllImpulseBindings([&out, &context] (CompiledImpulseBindingRecord &rec) { ImpulseBinding bind(rec); - PlayerImpulse const *impulse = P_PlayerImpulsePtr(bind.geti("impulseId")); + PlayerImpulse const *impulse = P_PlayerImpulsePtr(rec.compiled().impulseId); DENG2_ASSERT(impulse); out.writeText(String::format("bindcontrol local%i-%s \"%s\"\n", diff --git a/doomsday/apps/client/src/ui/b_util.cpp b/doomsday/apps/client/src/ui/b_util.cpp index a883c2a3ba..77000d495d 100644 --- a/doomsday/apps/client/src/ui/b_util.cpp +++ b/doomsday/apps/client/src/ui/b_util.cpp @@ -426,39 +426,42 @@ bool B_CheckAxisPosition(Binding::ControlTest test, float testPos, float pos) return false; } -bool B_CheckCondition(Record const *cond, int localNum, BindContext const *context) +bool B_CheckCondition(Binding::CompiledConditionRecord const *condRec, int localNum, + BindContext const *context) { - DENG2_ASSERT(cond); - bool const fulfilled = !cond->getb("negate"); + DENG2_ASSERT(condRec); - switch (cond->geti("type")) + auto const &cond = condRec->compiled(); + bool const fulfilled = !cond.negate; + + switch (cond.type) { case Binding::GlobalState: - if (cond->getb("multiplayer") && netGame) + if (cond.multiplayer && netGame) return fulfilled; break; case Binding::AxisState: { - AxisInputControl const &axis = InputSystem::get().device(cond->geti("device")).axis(cond->geti("id")); - if (B_CheckAxisPosition(Binding::ControlTest(cond->geti("test")), cond->getf("pos"), axis.position())) + AxisInputControl const &axis = InputSystem::get().device(cond.device).axis(cond.id); + if (B_CheckAxisPosition(cond.test, cond.pos, axis.position())) { return fulfilled; } break; } case Binding::ButtonState: { - ButtonInputControl const &button = InputSystem::get().device(cond->geti("device")).button(cond->geti("id")); + ButtonInputControl const &button = InputSystem::get().device(cond.device).button(cond.id); bool isDown = button.isDown(); - if (( isDown && cond->geti("test") == Binding::ButtonStateDown) || - (!isDown && cond->geti("test") == Binding::ButtonStateUp)) + if (( isDown && cond.test == Binding::ButtonStateDown) || + (!isDown && cond.test == Binding::ButtonStateUp)) { return fulfilled; } break; } case Binding::HatState: { - HatInputControl const &hat = InputSystem::get().device(cond->geti("device")).hat(cond->geti("id")); - if (hat.position() == cond->getf("pos")) + HatInputControl const &hat = InputSystem::get().device(cond.device).hat(cond.id); + if (hat.position() == cond.pos) { return fulfilled; } @@ -469,32 +472,21 @@ bool B_CheckCondition(Record const *cond, int localNum, BindContext const *conte { // Evaluate the current state of the modifier (in this context). float pos = 0, relative = 0; - B_EvaluateImpulseBindings(context, localNum, cond->geti("id"), &pos, &relative, false /*no triggered*/); - if ((cond->geti("test") == Binding::ButtonStateDown && fabs(pos) > .5) || - (cond->geti("test") == Binding::ButtonStateUp && fabs(pos) < .5)) + B_EvaluateImpulseBindings(context, localNum, cond.id, &pos, &relative, false /*no triggered*/); + if ((cond.test == Binding::ButtonStateDown && fabs(pos) > .5) || + (cond.test == Binding::ButtonStateUp && fabs(pos) < .5)) { return fulfilled; } } break; - default: DENG2_ASSERT(!"B_CheckCondition: Unknown cond->type"); break; + default: DENG2_ASSERT(!"B_CheckCondition: Unknown cond.type"); break; } return !fulfilled; } -bool B_EqualConditions(Record const &a, Record const &b) -{ - return (a.geti("type") == b.geti("type") && - a.geti("test") == b.geti("test") && - a.geti("device") == b.geti("device") && - a.geti("id") == b.geti("id") && - de::fequal(a.getf("pos"), b.getf("pos")) && - a.getb("negate") == b.getb("negate") && - a.getb("multiplayer") == b.getb("multiplayer")); -} - /// @todo: Belongs in BindContext? -ds void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int impulseId, float *pos, float *relativeOffset, bool allowTriggered) @@ -511,18 +503,20 @@ void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int imp bool conflicted[NUM_IBD_TYPES]; de::zap(conflicted); bool appliedState[NUM_IBD_TYPES]; de::zap(appliedState); - context->forAllImpulseBindings(localNum, [&] (Record &rec) + context->forAllImpulseBindings(localNum, [&] (CompiledImpulseBindingRecord &rec) { // Wrong impulse? - ImpulseBinding bind(rec); - if (bind.geti(QStringLiteral("impulseId")) != impulseId) return LoopContinue; + //ImpulseBinding bind(rec); + auto const &bind = rec.compiled(); + if (bind.impulseId != impulseId) return LoopContinue; // If the binding has conditions, they may prevent using it. bool skip = false; - ArrayValue const &conds = bind.geta(QStringLiteral("condition")); + ArrayValue const &conds = rec.geta(QStringLiteral("condition")); DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, conds.elements()) { - if (!B_CheckCondition((*i)->as().record(), localNum, context)) + if (!B_CheckCondition(static_cast + ((*i)->as().record()), localNum, context)) { skip = true; break; @@ -531,17 +525,17 @@ void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int imp if (skip) return LoopContinue; // Get the device. - InputDevice const *device = InputSystem::get().devicePtr(bind.geti(QStringLiteral("deviceId"))); + InputDevice const *device = InputSystem::get().devicePtr(bind.deviceId); if (!device || !device->isActive()) return LoopContinue; // Not available. // Get the control. InputControl *ctrl = nullptr; - switch (bind.geti("type")) + switch (bind.type) { - case IBD_AXIS: ctrl = &device->axis (bind.geti(QStringLiteral("controlId"))); break; - case IBD_TOGGLE: ctrl = &device->button(bind.geti(QStringLiteral("controlId"))); break; - case IBD_ANGLE: ctrl = &device->hat (bind.geti(QStringLiteral("controlId"))); break; + case IBD_AXIS: ctrl = &device->axis (bind.controlId); break; + case IBD_TOGGLE: ctrl = &device->button(bind.controlId); break; + case IBD_ANGLE: ctrl = &device->hat (bind.controlId); break; default: DENG2_ASSERT(!"B_EvaluateImpulseBindings: Invalid bind.type"); break; } @@ -554,7 +548,8 @@ void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int imp { if (context && axis->bindContext() != context) { - if (axis->hasBindContext() && !axis->bindContext()->findImpulseBinding(bind.geti(QStringLiteral("deviceId")), IBD_AXIS, bind.geti(QStringLiteral("controlId")))) + if (axis->hasBindContext() && !axis->bindContext()-> + findImpulseBinding(bind.deviceId, IBD_AXIS, bind.controlId)) { // The overriding context doesn't bind to the axis, though. if (axis->type() == AxisInputControl::Pointer) @@ -605,12 +600,12 @@ void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int imp // Expired? if (!(hat->bindContextAssociation() & InputControl::Expired)) { - devicePos = (hat->position() == bind.getf(QStringLiteral("angle"))? 1.0f : 0.0f); + devicePos = (fequal(hat->position(), bind.angle)? 1.0f : 0.0f); deviceTime = hat->time(); } } - int const bflags = bind.geti(QStringLiteral("flags")); + int const bflags = bind.flags; // Apply further modifications based on flags. if (bflags & IBDF_INVERSE) @@ -632,16 +627,14 @@ void B_EvaluateImpulseBindings(BindContext const *context, int localNum, int imp // Is this state contributing to the outcome? if (!de::fequal(devicePos, 0.f)) { - int const btype = bind.geti(QStringLiteral("type")); - - if (appliedState[btype]) + if (appliedState[bind.type]) { // Another binding already influenced this; we have a conflict. - conflicted[btype] = true; + conflicted[bind.type] = true; } // We've found one effective binding that influences this control. - appliedState[btype] = true; + appliedState[bind.type] = true; } return LoopContinue; }); diff --git a/doomsday/apps/client/src/ui/bindcontext.cpp b/doomsday/apps/client/src/ui/bindcontext.cpp index 394a487ead..062bada252 100644 --- a/doomsday/apps/client/src/ui/bindcontext.cpp +++ b/doomsday/apps/client/src/ui/bindcontext.cpp @@ -54,7 +54,7 @@ DENG2_PIMPL(BindContext) typedef QList CommandBindings; CommandBindings commandBinds; - typedef QList ImpulseBindings; + typedef QList ImpulseBindings; ImpulseBindings impulseBinds[DDMAXPLAYERS]; ///< Group bindings for each local player. DDFallbackResponderFunc ddFallbackResponder = nullptr; @@ -123,30 +123,30 @@ DENG2_PIMPL(BindContext) } for (int i = 0; i < DDMAXPLAYERS; ++i) - for (Record const *rec : impulseBinds[i]) + for (auto const *rec : impulseBinds[i]) { - ImpulseBinding bind(*rec); + auto const &bind = rec->compiled(); if (matchCmd) { - if (matchCmd.geti(VAR_TYPE) == bind.geti(VAR_TYPE) && - matchCmd.geti(VAR_DEVICE_ID) == bind.geti(VAR_DEVICE_ID) && - matchCmd.geti(VAR_CONTROL_ID) == bind.geti(VAR_CONTROL_ID) && - matchCmd.equalConditions(bind)) + if (matchCmd.geti(VAR_TYPE) == int(bind.type) && + matchCmd.geti(VAR_DEVICE_ID) == bind.deviceId && + matchCmd.geti(VAR_CONTROL_ID) == bind.controlId && + matchCmd.equalConditions(ImpulseBinding(*rec))) { - *impResult = const_cast(rec); + *impResult = const_cast(rec); return true; } } - if (matchImp && matchImp.geti(VAR_ID) != bind.geti(VAR_ID)) + if (matchImp && matchImp.geti(VAR_ID) != bind.id) { - if (matchImp.geti(VAR_TYPE) == bind.geti(VAR_TYPE) && - matchImp.geti(VAR_DEVICE_ID) == bind.geti(VAR_DEVICE_ID) && - matchImp.geti(VAR_CONTROL_ID) == bind.geti(VAR_CONTROL_ID) && - matchImp.equalConditions(bind)) + if (matchImp.geti(VAR_TYPE) == int(bind.type) && + matchImp.geti(VAR_DEVICE_ID) == bind.deviceId && + matchImp.geti(VAR_CONTROL_ID) == bind.controlId && + matchImp.equalConditions(ImpulseBinding(*rec))) { - *impResult = const_cast(rec); + *impResult = const_cast(rec); return true; } } @@ -239,7 +239,7 @@ void BindContext::acquire(int deviceId, bool yes) int const countBefore = d->acquireDevices.count(); if (yes) d->acquireDevices.insert(deviceId); - else d->acquireDevices.remove(deviceId); + else d->acquireDevices.remove(deviceId); if (countBefore != d->acquireDevices.count()) { @@ -305,11 +305,11 @@ void BindContext::clearBindingsForDevice(int deviceId) } return LoopContinue; }); - forAllImpulseBindings([&ids, &deviceId] (Record const &bind) + forAllImpulseBindings([&ids, &deviceId] (CompiledImpulseBindingRecord const &bind) { - if (bind.geti(VAR_DEVICE_ID) == deviceId) + if (bind.compiled().deviceId == deviceId) { - ids.insert(bind.geti(VAR_ID)); + ids.insert(deviceId); } return LoopContinue; }); @@ -357,15 +357,16 @@ Record *BindContext::bindImpulse(char const *ctrlDesc, PlayerImpulse const &impu LOG_AS("BindContext"); try { - std::unique_ptr newBind(new Record); + std::unique_ptr newBind(new CompiledImpulseBindingRecord); ImpulseBinding bind(*newBind.get()); bind.configure(ctrlDesc, impulse.id, localPlayer); // Assign a new unique identifier. d->impulseBinds[localPlayer].append(newBind.release()); - LOG_INPUT_VERBOSE("Impulse " _E(b) "'%s'" _E(.) " of player%i now bound to \"%s\" in " _E(b) "'%s'" _E(.) - " (id %i)") - << impulse.name << (localPlayer + 1) << bind.composeDescriptor() << d->name << bind.geti(VAR_ID); + LOG_INPUT_VERBOSE("Impulse " _E(b) "'%s'" _E(.) " of player%i now bound to \"%s\" in " + _E(b) "'%s'" _E(.) " (id %i)") + << impulse.name << (localPlayer + 1) << bind.composeDescriptor() + << d->name << bind.def().compiled().id; /// @todo: In interactive binding mode, should ask the user if the /// replacement is ok. For now, just delete the other bindings. @@ -402,14 +403,14 @@ Record *BindContext::findCommandBinding(char const *command, int deviceId) const Record *BindContext::findImpulseBinding(int deviceId, ibcontroltype_t bindType, int controlId) const { for (int i = 0; i < DDMAXPLAYERS; ++i) - for (Record const *rec : d->impulseBinds[i]) + for (auto const *rec : d->impulseBinds[i]) { - ImpulseBinding bind(*rec); - if (bind.geti(VAR_TYPE) == bindType && - bind.geti(VAR_DEVICE_ID) == deviceId && - bind.geti(VAR_CONTROL_ID) == controlId) + auto const &bind = rec->compiled(); + if (bind.type == bindType && + bind.deviceId == deviceId && + bind.controlId == controlId) { - return const_cast(rec); + return const_cast(rec); } } return nullptr; @@ -433,8 +434,8 @@ bool BindContext::deleteBinding(int id) for (int i = 0; i < DDMAXPLAYERS; ++i) for (int k = 0; k < d->impulseBinds[i].count(); ++k) { - Record *rec = d->impulseBinds[i].at(k); - if (rec->geti(VAR_ID) == id) + auto *rec = d->impulseBinds[i].at(k); + if (rec->compiled().id == id) { d->impulseBinds[i].removeAt(k); delete rec; @@ -498,13 +499,13 @@ LoopResult BindContext::forAllCommandBindings( } LoopResult BindContext::forAllImpulseBindings(int localPlayer, - std::function func) const + std::function func) const { for (int i = 0; i < DDMAXPLAYERS; ++i) { if ((localPlayer < 0 || localPlayer >= DDMAXPLAYERS) || localPlayer == i) { - for (Record *rec : d->impulseBinds[i]) + for (auto *rec : d->impulseBinds[i]) { if (auto result = func(*rec)) return result; } @@ -515,7 +516,7 @@ LoopResult BindContext::forAllImpulseBindings(int localPlayer, int BindContext::commandBindingCount() const { - return d->commandBinds.count(); + return d.getConst()->commandBinds.count(); } int BindContext::impulseBindingCount(int localPlayer) const @@ -525,7 +526,7 @@ int BindContext::impulseBindingCount(int localPlayer) const { if ((localPlayer < 0 || localPlayer >= DDMAXPLAYERS) || localPlayer == i) { - count += d->impulseBinds[i].count(); + count += d.getConst()->impulseBinds[i].count(); } } return count; diff --git a/doomsday/apps/client/src/ui/binding.cpp b/doomsday/apps/client/src/ui/binding.cpp index 0b2f573835..4b94e9f239 100644 --- a/doomsday/apps/client/src/ui/binding.cpp +++ b/doomsday/apps/client/src/ui/binding.cpp @@ -20,12 +20,13 @@ #include "ui/binding.h" #include -#include "ui/b_util.h" // B_EqualConditions using namespace de; static int idCounter = 0; +static String const VAR_CONDITION("condition"); + Record &Binding::def() { return const_cast(accessedRecord()); @@ -44,13 +45,12 @@ Binding::operator bool() const void Binding::resetToDefaults() { def().addNumber("id", 0); ///< Unique identifier. - def().addArray("condition", new ArrayValue); + def().addArray(VAR_CONDITION, new ArrayValue); } -Record &Binding::addCondition() +Binding::CompiledConditionRecord &Binding::addCondition() { - Record *cond = new Record; - + auto *cond = new CompiledConditionRecord; cond->addNumber("type", Invalid); cond->addNumber("test", None); cond->addNumber("device", -1); @@ -58,15 +58,16 @@ Record &Binding::addCondition() cond->addNumber("pos", 0); cond->addBoolean("negate", false); cond->addBoolean("multiplayer", false); + def()[VAR_CONDITION].array().add(new RecordValue(cond, RecordValue::OwnsRecord)); - def()["condition"].array().add(new RecordValue(cond, RecordValue::OwnsRecord)); + cond->resetCompiled(); return *cond; } int Binding::conditionCount() const { - return int(geta("condition").size()); + return int(geta(VAR_CONDITION).size()); } bool Binding::hasCondition(int index) const @@ -74,35 +75,35 @@ bool Binding::hasCondition(int index) const return index >= 0 && index < conditionCount(); } -Record &Binding::condition(int index) +Binding::CompiledConditionRecord &Binding::condition(int index) { - return *def().geta("condition")[index].as().record(); + return *static_cast(def().geta(VAR_CONDITION)[index].as().record()); } -Record const &Binding::condition(int index) const +Binding::CompiledConditionRecord const &Binding::condition(int index) const { - return *geta("condition")[index].as().record(); + return *static_cast(geta(VAR_CONDITION)[index].as().record()); } bool Binding::equalConditions(Binding const &other) const { // Quick test (assumes there are no duplicated conditions). - if (def()["condition"].array().elements().count() != other.geta("condition").elements().count()) + if (def()[VAR_CONDITION].array().elements().count() != other.geta(VAR_CONDITION).elements().count()) { return false; } - ArrayValue const &conds = def().geta("condition"); + ArrayValue const &conds = def().geta(VAR_CONDITION); DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, conds.elements()) { - Record const &a = *(*i)->as().record(); + auto const &a = *static_cast((*i)->as().record()); bool found = false; - ArrayValue const &conds2 = other.geta("condition"); + ArrayValue const &conds2 = other.geta(VAR_CONDITION); DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, conds2.elements()) { - Record const &b = *(*i)->as().record(); - if (B_EqualConditions(a, b)) + auto const &b = *static_cast((*i)->as().record()); + if (a.compiled() == b.compiled()) { found = true; break; @@ -125,3 +126,24 @@ int Binding::newIdentifier() // static while (!id) { id = ++idCounter; } return id; } + +Binding::CompiledCondition::CompiledCondition(Record const &rec) + : type(ConditionType(rec.geti("type"))) + , test(ControlTest(rec.geti("test"))) + , device(rec.geti("device")) + , id(rec.geti("id")) + , pos(rec.getf("pos")) + , negate(rec.getb("negate")) + , multiplayer(rec.getb("multiplayer")) +{} + +bool Binding::CompiledCondition::operator == (CompiledCondition const &other) const +{ + return (type == other.type && + test == other.test && + device == other.device && + id == other.id && + de::fequal(pos, other.pos) && + negate == other.negate && + multiplayer == other.multiplayer); +} diff --git a/doomsday/apps/client/src/ui/commandbinding.cpp b/doomsday/apps/client/src/ui/commandbinding.cpp index f7fda8f635..8d5459ed4c 100644 --- a/doomsday/apps/client/src/ui/commandbinding.cpp +++ b/doomsday/apps/client/src/ui/commandbinding.cpp @@ -427,7 +427,8 @@ Action *CommandBinding::makeAction(ddevent_t const &event, BindContext const &co ArrayValue const &conds = def().geta("condition"); DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, conds.elements()) { - if (!B_CheckCondition((*i)->as().record(), 0, &context)) + if (!B_CheckCondition(static_cast + ((*i)->as().record()), 0, &context)) return nullptr; } diff --git a/doomsday/apps/client/src/ui/impulsebinding.cpp b/doomsday/apps/client/src/ui/impulsebinding.cpp index b56505ed8f..48ba289b74 100644 --- a/doomsday/apps/client/src/ui/impulsebinding.cpp +++ b/doomsday/apps/client/src/ui/impulsebinding.cpp @@ -27,10 +27,33 @@ using namespace de; +CompiledImpulseBinding::CompiledImpulseBinding(Record const &bind) + : id (bind.geti("id")) + , deviceId (bind.geti("deviceId")) + , controlId (bind.geti("controlId")) + , type (ibcontroltype_t(bind.geti("type"))) + , angle (bind.getf("angle")) + , flags (bind.geti("flags")) + , impulseId (bind.geti("impulseId")) + , localPlayer (bind.geti("localPlayer")) +{} + +CompiledImpulseBindingRecord &ImpulseBinding::def() +{ + return static_cast(Binding::def()); +} + +CompiledImpulseBindingRecord const &ImpulseBinding::def() const +{ + return static_cast(Binding::def()); +} + void ImpulseBinding::resetToDefaults() { Binding::resetToDefaults(); + def().resetCompiled(); + def().addNumber("deviceId", -1); def().addNumber("controlId", -1); def().addNumber("type", int(IBD_TOGGLE)); ///< Type of event. @@ -114,7 +137,7 @@ static bool doConfigure(ImpulseBinding &bind, char const *ctrlDesc, int impulseI bind.def().set("type", int(EVTYPE_TO_IBDTYPE(type))); } else if (!Str_CompareIgnoreCase(str, "joy") || - !Str_CompareIgnoreCase(str, "head")) + !Str_CompareIgnoreCase(str, "head")) { bind.def().set("deviceId", (!Str_CompareIgnoreCase(str, "joy")? IDEV_JOY1 : IDEV_HEAD_TRACKER)); diff --git a/doomsday/apps/client/src/ui/inputsystem.cpp b/doomsday/apps/client/src/ui/inputsystem.cpp index f1ac7f2f32..f1916fae65 100644 --- a/doomsday/apps/client/src/ui/inputsystem.cpp +++ b/doomsday/apps/client/src/ui/inputsystem.cpp @@ -856,17 +856,18 @@ DENG2_PIMPL(InputSystem) }); // Associate all the device bindings. - context->forAllImpulseBindings([this, &context] (Record &rec) + context->forAllImpulseBindings([this, &context] (CompiledImpulseBindingRecord &rec) { - ImpulseBinding bind(rec); - InputDevice &dev = self.device(bind.geti("deviceId")); + //ImpulseBinding bind(rec); + auto const &bind = rec.compiled(); + InputDevice &dev = self.device(bind.deviceId); InputControl *ctrl = nullptr; - switch (bind.geti("type")) + switch (bind.type) { - case IBD_AXIS: ctrl = &dev.axis (bind.geti("controlId")); break; - case IBD_TOGGLE: ctrl = &dev.button(bind.geti("controlId")); break; - case IBD_ANGLE: ctrl = &dev.hat (bind.geti("controlId")); break; + case IBD_AXIS: ctrl = &dev.axis (bind.controlId); break; + case IBD_TOGGLE: ctrl = &dev.button(bind.controlId); break; + case IBD_ANGLE: ctrl = &dev.hat (bind.controlId); break; default: DENG2_ASSERT(!"InputSystem::updateAllDeviceStateAssociations: Invalid bind.type"); @@ -1697,7 +1698,7 @@ D_CMD(ListBindings) LOG_INPUT_MSG(" " _E(b) "%i impulse bindings:") << impCount; } for (int pl = 0; pl < DDMAXPLAYERS; ++pl) - context.forAllImpulseBindings(pl, [&pl] (Record &rec) + context.forAllImpulseBindings(pl, [&pl] (CompiledImpulseBindingRecord &rec) { ImpulseBinding bind(rec); PlayerImpulse const *impulse = P_PlayerImpulsePtr(bind.geti("impulseId")); @@ -1850,23 +1851,24 @@ DENG_EXTERN_C int B_BindingsForControl(int localPlayer, char const *impulseNameC int numFound = 0; isys.forAllContexts([&] (BindContext &context) { - context.forAllImpulseBindings(localPlayer, [&] (Record &rec) + context.forAllImpulseBindings(localPlayer, [&] (CompiledImpulseBindingRecord &rec) { - ImpulseBinding bind(rec); - DENG2_ASSERT(bind.geti("localPlayer") == localPlayer); + auto const &bind = rec.compiled(); + DENG2_ASSERT(bind.localPlayer == localPlayer); - PlayerImpulse const *impulse = P_PlayerImpulsePtr(bind.geti("impulseId")); + PlayerImpulse const *impulse = P_PlayerImpulsePtr(bind.impulseId); DENG2_ASSERT(impulse); if (!impulse->name.compareWithoutCase(impulseName)) { if (inverse == BFCI_BOTH || - (inverse == BFCI_ONLY_NON_INVERSE && !(bind.geti("flags") & IBDF_INVERSE)) || - (inverse == BFCI_ONLY_INVERSE && (bind.geti("flags") & IBDF_INVERSE))) + (inverse == BFCI_ONLY_NON_INVERSE && !(bind.flags & IBDF_INVERSE)) || + (inverse == BFCI_ONLY_INVERSE && (bind.flags & IBDF_INVERSE))) { if (numFound) out += " "; - out += String::number(bind.geti("id")) + "@" + context.name() + ":" + bind.composeDescriptor(); + out += String::number(bind.id) + "@" + context.name() + ":" + + ImpulseBinding(rec).composeDescriptor(); numFound++; } } diff --git a/doomsday/apps/client/src/world/base/p_players.cpp b/doomsday/apps/client/src/world/base/p_players.cpp index fc49bcab22..7daf8a824b 100644 --- a/doomsday/apps/client/src/world/base/p_players.cpp +++ b/doomsday/apps/client/src/world/base/p_players.cpp @@ -392,13 +392,14 @@ DENG_EXTERN_C int P_IsControlBound(int playerNum, int impulseId) return false; } - int found = bindContext->forAllImpulseBindings(localPlayer, [&isys, &impulseId] (Record &rec) + int found = bindContext->forAllImpulseBindings(localPlayer, [&isys, &impulseId] (CompiledImpulseBindingRecord &rec) { - ImpulseBinding bind(rec); + auto const &bind = rec.compiled(); + // Wrong impulse? - if(bind.geti("impulseId") != impulseId) return LoopContinue; + if(bind.impulseId != impulseId) return LoopContinue; - if(InputDevice const *device = isys.devicePtr(bind.geti("deviceId"))) + if(InputDevice const *device = isys.devicePtr(bind.deviceId)) { if(device->isActive()) {