diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index a7641b9b87..4929093821 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -34,6 +34,12 @@ #include "ScriptValueIterator.h" #include "ScriptEngineLogging.h" +#define CONVERSIONS_OPTIMIZED_FOR_V8 + +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 +#include "v8/FastScriptValueUtils.h" +#endif + bool isListOfStrings(const ScriptValue& arg) { if (!arg.isArray()) { return false; @@ -59,7 +65,11 @@ void registerMetaTypes(ScriptEngine* engine) { scriptRegisterMetaType(engine); scriptRegisterMetaType(engine); +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + scriptRegisterMetaType(engine); +#else scriptRegisterMetaType(engine); +#endif scriptRegisterMetaType(engine); scriptRegisterMetaType(engine); scriptRegisterMetaType(engine); @@ -117,7 +127,11 @@ bool qVector2DFromScriptValue(const ScriptValue& object, QVector2D& qVector2D) { ScriptValue qVector3DToScriptValue(ScriptEngine* engine, const QVector3D& qVector3D) { glm::vec3 vec3(qVector3D.x(), qVector3D.y(), qVector3D.z()); +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + return vec3ToScriptValueFast(engine, vec3); +#else return vec3ToScriptValue(engine, vec3); +#endif } bool qVector3DFromScriptValue(const ScriptValue& object, QVector3D& qVector3D) { @@ -456,7 +470,11 @@ ScriptValue qVectorVec3ColorToScriptValue(ScriptEngine* engine, const QVector& vector) { ScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + array.setProperty(i, vec3ToScriptValueFast(engine, vector.at(i))); +#else array.setProperty(i, vec3ToScriptValue(engine, vector.at(i))); +#endif } return array; } @@ -776,9 +794,17 @@ bool qURLFromScriptValue(const ScriptValue& object, QUrl& url) { ScriptValue pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay) { ScriptValue obj = engine->newObject(); +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + ScriptValue origin = vec3ToScriptValueFast(engine, pickRay.origin); +#else ScriptValue origin = vec3ToScriptValue(engine, pickRay.origin); +#endif obj.setProperty("origin", origin); +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + ScriptValue direction = vec3ToScriptValueFast(engine, pickRay.direction); +#else ScriptValue direction = vec3ToScriptValue(engine, pickRay.direction); +#endif obj.setProperty("direction", direction); return obj; } @@ -824,9 +850,15 @@ ScriptValue collisionToScriptValue(ScriptEngine* engine, const Collision& collis obj.setProperty("type", collision.type); obj.setProperty("idA", quuidToScriptValue(engine, collision.idA)); obj.setProperty("idB", quuidToScriptValue(engine, collision.idB)); +#ifdef CONVERSIONS_OPTIMIZED_FOR_V8 + obj.setProperty("penetration", vec3ToScriptValueFast(engine, collision.penetration)); + obj.setProperty("contactPoint", vec3ToScriptValueFast(engine, collision.contactPoint)); + obj.setProperty("velocityChange", vec3ToScriptValueFast(engine, collision.velocityChange)); +#else obj.setProperty("penetration", vec3ToScriptValue(engine, collision.penetration)); obj.setProperty("contactPoint", vec3ToScriptValue(engine, collision.contactPoint)); obj.setProperty("velocityChange", vec3ToScriptValue(engine, collision.velocityChange)); +#endif return obj; } diff --git a/libraries/script-engine/src/v8/FastScriptValueUtils.cpp b/libraries/script-engine/src/v8/FastScriptValueUtils.cpp new file mode 100644 index 0000000000..f5b5f44c37 --- /dev/null +++ b/libraries/script-engine/src/v8/FastScriptValueUtils.cpp @@ -0,0 +1,187 @@ +// +// FastScriptValueUtils.cpp +// libraries/script-engine/src/v8/FastScriptValueUtils.cpp +// +// Created by dr Karol Suprynowicz on 2023/03/30. +// Copyright 2023 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FastScriptValueUtils.h" + +#include + +#include "../ScriptEngine.h" +#include "V8Types.h" +#include "ScriptValueV8Wrapper.h" + +ScriptValue vec3ToScriptValueFast(ScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_vec3__"); + if (!prototype.hasProperty("defined") || !prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})"); + } + ScriptValue value = engine->newObject(); + + ScriptValueV8Wrapper *proxy = ScriptValueV8Wrapper::unwrap(value); + + auto engineV8 = proxy->getV8Engine(); + + auto isolate = engineV8->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto context = engineV8->getContext(); + v8::Context::Scope contextScope(engineV8->getContext()); + V8ScriptValue v8ScriptValue = proxy->toV8Value(); + v8::Local v8Object = v8::Local::Cast(v8ScriptValue.get()); + + if (!v8Object->Set(context, v8::String::NewFromUtf8(isolate, "x").ToLocalChecked(), v8::Number::New(isolate, vec3.x)).FromMaybe(false)) { + Q_ASSERT(false); + } + if (!v8Object->Set(context, v8::String::NewFromUtf8(isolate, "y").ToLocalChecked(), v8::Number::New(isolate, vec3.y)).FromMaybe(false)) { + Q_ASSERT(false); + } + if (!v8Object->Set(context, v8::String::NewFromUtf8(isolate, "z").ToLocalChecked(), v8::Number::New(isolate, vec3.z)).FromMaybe(false)) { + Q_ASSERT(false); + } + + auto v8Prototype = ScriptValueV8Wrapper::fullUnwrap(engineV8, prototype); + if (!v8Object->SetPrototype(context, v8Prototype.get()).FromMaybe(false)) { + Q_ASSERT(false); + } + value.setPrototype(prototype); + return value; +} + +bool vec3FromScriptValueFast(const ScriptValue& object, glm::vec3& vec3) { + ScriptValueV8Wrapper *proxy = ScriptValueV8Wrapper::unwrap(object); + + auto engineV8 = proxy->getV8Engine(); + + auto isolate = engineV8->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto context = engineV8->getContext(); + v8::Context::Scope contextScope(engineV8->getContext()); + V8ScriptValue v8ScriptValue = proxy->toV8Value(); + + v8::Local v8Value = v8ScriptValue.get(); + + + if (v8Value->IsNumber()) { + vec3 = glm::vec3(v8Value->NumberValue(context).ToChecked()); + } else if (v8Value->IsString()) { + QColor qColor(QString(*v8::String::Utf8Value(isolate, v8::Local::Cast(v8Value)))); + if (qColor.isValid()) { + vec3.x = qColor.red(); + vec3.y = qColor.green(); + vec3.z = qColor.blue(); + } else { + return false; + } + } else if (v8Value->IsArray()) { + auto array = v8::Local::Cast(v8Value); + //QVariantList list = object.toVariant().toList(); + if (array->Length() == 3) { + v8::Local xValue,yValue,zValue; + if (!array->Get(context, 0).ToLocal(&xValue)) { + return false; + } + if (!array->Get(context, 1).ToLocal(&yValue)) { + return false; + } + if (!array->Get(context, 2).ToLocal(&zValue)) { + return false; + } + //V8TODO: + if (xValue->IsNullOrUndefined() || yValue->IsNullOrUndefined() || zValue->IsNullOrUndefined()) { + return false; + } + double x,y,z; + if (!xValue->NumberValue(context).To(&x) + || !yValue->NumberValue(context).To(&y) + || !zValue->NumberValue(context).To(&z)) { + return false; + } + + vec3.x = xValue->NumberValue(context).FromMaybe(0.0); + vec3.y = yValue->NumberValue(context).FromMaybe(0.0); + vec3.z = zValue->NumberValue(context).FromMaybe(0.0); + } else { + return false; + } + } else if (v8Value->IsObject()) { + v8::Local v8Object = v8::Local::Cast(v8Value); + v8::Local xValue; + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "x").ToLocalChecked()).ToLocal(&xValue)) { + Q_ASSERT(false); + } + if (xValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "r").ToLocalChecked()).ToLocal(&xValue)) { + Q_ASSERT(false); + } + } + if (xValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "red").ToLocalChecked()).ToLocal(&xValue)) { + Q_ASSERT(false); + } + } + + v8::Local yValue; + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "y").ToLocalChecked()).ToLocal(&yValue)) { + Q_ASSERT(false); + } + if (yValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "g").ToLocalChecked()).ToLocal(&yValue)) { + Q_ASSERT(false); + } + } + if (yValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "green").ToLocalChecked()).ToLocal(&yValue)) { + Q_ASSERT(false); + } + } + + v8::Local zValue; + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "z").ToLocalChecked()).ToLocal(&zValue)) { + Q_ASSERT(false); + } + if (zValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "b").ToLocalChecked()).ToLocal(&zValue)) { + Q_ASSERT(false); + } + } + if (zValue->IsNullOrUndefined()) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "blue").ToLocalChecked()).ToLocal(&zValue)) { + Q_ASSERT(false); + } + } + + //f (!x.isValid() || !y.isValid() || !z.isValid()) { + // V8TODO: This breaks the sit script for some reason + //return false; + //} + + vec3.x = xValue->NumberValue(context).FromMaybe(0.0); + vec3.y = yValue->NumberValue(context).FromMaybe(0.0); + vec3.z = zValue->NumberValue(context).FromMaybe(0.0); + } else { + return false; + } + return true; +} diff --git a/libraries/script-engine/src/v8/FastScriptValueUtils.h b/libraries/script-engine/src/v8/FastScriptValueUtils.h new file mode 100644 index 0000000000..8401f9ab36 --- /dev/null +++ b/libraries/script-engine/src/v8/FastScriptValueUtils.h @@ -0,0 +1,26 @@ +// +// FastScriptValueUtils.cpp +// libraries/script-engine/src/v8/FastScriptValueUtils.cpp +// +// Created by dr Karol Suprynowicz on 2023/03/30. +// Copyright 2023 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Contains V8-specific implementations of th + +#ifndef overte_FastScriptValueUtils_h +#define overte_FastScriptValueUtils_h + +#include +#include + +#include "../ScriptValue.h" + +ScriptValue vec3ToScriptValueFast(ScriptEngine* engine, const glm::vec3& vec3); + +bool vec3FromScriptValueFast(const ScriptValue& object, glm::vec3& vec3); + +#endif // overte_FastScriptValueUtils_h diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index fe9581507b..40a36b2e66 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -423,7 +423,8 @@ void ScriptValueV8Wrapper::setPrototype(const ScriptValue& prototype) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); ScriptValueV8Wrapper* unwrappedPrototype = unwrap(prototype); if (unwrappedPrototype) { if(unwrappedPrototype->toV8Value().constGet()->IsNullOrUndefined() && _value.constGet()->IsNullOrUndefined()) { @@ -432,7 +433,7 @@ void ScriptValueV8Wrapper::setPrototype(const ScriptValue& prototype) { if(unwrappedPrototype->toV8Value().constGet()->IsObject() && _value.constGet()->IsObject()) { auto object = v8::Local::Cast(_value.get()); //V8TODO: I don't know which context to use here - v8::Maybe retVal = object->SetPrototype(_engine->getContext(), unwrappedPrototype->toV8Value().constGet()); + v8::Maybe retVal = object->SetPrototype(context, unwrappedPrototype->toV8Value().constGet()); //v8::Maybe retVal = object->SetPrototype(_value.getContext(), unwrappedPrototype->toV8Value().constGet()); if (retVal.IsJust() ? !retVal.FromJust() : true){ qCDebug(scriptengine_v8) << "Failed to assign prototype"; diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h index 305554e64d..d174d91703 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h @@ -36,6 +36,7 @@ public: // construction static ScriptValueV8Wrapper* unwrap(const ScriptValue& val); inline const V8ScriptValue& toV8Value() const { return _value; } static V8ScriptValue fullUnwrap(ScriptEngineV8* engine, const ScriptValue& value); + ScriptEngineV8* getV8Engine() {return _engine;} public: virtual void release() override;