diff options
Diffstat (limited to 'modules/gdscript/gdscript_vm.cpp')
-rw-r--r-- | modules/gdscript/gdscript_vm.cpp | 94 |
1 files changed, 70 insertions, 24 deletions
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index a8c9cfa579..1ddd54b323 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -663,8 +663,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (GDScriptLanguage::get_singleton()->profiling) { function_start_time = OS::get_singleton()->get_ticks_usec(); function_call_time = 0; - profile.call_count++; - profile.frame_call_count++; + profile.call_count.increment(); + profile.frame_call_count.increment(); } bool exit_ok = false; bool awaited = false; @@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_SWITCH(_code_ptr[ip]) { OPCODE(OPCODE_OPERATOR) { - CHECK_SPACE(5); + constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr); + CHECK_SPACE(7 + _pointer_size); bool valid; Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4]; @@ -694,28 +695,71 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(a, 0); GET_VARIANT_PTR(b, 1); GET_VARIANT_PTR(dst, 2); + // Compute signatures (types of operands) so it can be optimized when matching. + uint32_t op_signature = _code_ptr[ip + 5]; + uint32_t actual_signature = (a->get_type() << 8) | (b->get_type()); + + // Check if this is the first run. If so, store the current signature for the optimized path. + if (unlikely(op_signature == 0)) { + static Mutex initializer_mutex; + initializer_mutex.lock(); + Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF); + Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF); + Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type); + + if (unlikely(!op_func)) { +#ifdef DEBUG_ENABLED + err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; +#endif + initializer_mutex.unlock(); + OPCODE_BREAK; + } else { + Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type); + VariantInternal::initialize(dst, ret_type); + op_func(a, b, dst); + + // Check again in case another thread already set it. + if (_code_ptr[ip + 5] == 0) { + _code_ptr[ip + 5] = actual_signature; + _code_ptr[ip + 6] = static_cast<int>(ret_type); + Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); + *tmp = op_func; + } + } + initializer_mutex.unlock(); + } else if (likely(op_signature == actual_signature)) { + // If the signature matches, we can use the optimized path. + Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]); + Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); + + // Make sure the return value has the correct type. + VariantInternal::initialize(dst, ret_type); + op_func(a, b, dst); + } else { + // If the signature doesn't match, we have to use the slow path. #ifdef DEBUG_ENABLED - Variant ret; - Variant::evaluate(op, *a, *b, ret, valid); + Variant ret; + Variant::evaluate(op, *a, *b, ret, valid); #else - Variant::evaluate(op, *a, *b, *dst, valid); + Variant::evaluate(op, *a, *b, *dst, valid); #endif #ifdef DEBUG_ENABLED - if (!valid) { - if (ret.get_type() == Variant::STRING) { - //return a string when invalid with the error - err_text = ret; - err_text += " in operator '" + Variant::get_operator_name(op) + "'."; - } else { - err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; + if (!valid) { + if (ret.get_type() == Variant::STRING) { + //return a string when invalid with the error + err_text = ret; + err_text += " in operator '" + Variant::get_operator_name(op) + "'."; + } else { + err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; + } + OPCODE_BREAK; } - OPCODE_BREAK; - } - *dst = ret; + *dst = ret; #endif - ip += 5; + } + ip += 7 + _pointer_size; } DISPATCH_OPCODE; @@ -3550,7 +3594,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a // line bool do_break = false; - if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) { + if (unlikely(EngineDebugger::get_script_debugger()->get_lines_left() > 0)) { if (EngineDebugger::get_script_debugger()->get_depth() <= 0) { EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1); } @@ -3563,7 +3607,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a do_break = true; } - if (do_break) { + if (unlikely(do_break)) { GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true); } @@ -3630,11 +3674,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time; - profile.total_time += time_taken; - profile.self_time += time_taken - function_call_time; - profile.frame_total_time += time_taken; - profile.frame_self_time += time_taken - function_call_time; - GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; + profile.total_time.add(time_taken); + profile.self_time.add(time_taken - function_call_time); + profile.frame_total_time.add(time_taken); + profile.frame_self_time.add(time_taken - function_call_time); + if (Thread::get_caller_id() == Thread::get_main_id()) { + GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; + } } // Check if this is not the last time it was interrupted by `await` or if it's the first time executing. |