diff --git a/examples/tests/cube_texture.png b/examples/tests/cube_texture.png new file mode 100644 index 0000000000..ed65d8d6cb Binary files /dev/null and b/examples/tests/cube_texture.png differ diff --git a/examples/tests/textureStress.fs b/examples/tests/textureStress.fs new file mode 100644 index 0000000000..cb59c9984f --- /dev/null +++ b/examples/tests/textureStress.fs @@ -0,0 +1,41 @@ +float aspect(vec2 v) { + return v.x / v.y; +} + +vec3 aspectCorrectedTexture() { + vec2 uv = _position.xy; + uv += 0.5; + uv.y = 1.0 - uv.y; + + float targetAspect = iWorldScale.x / iWorldScale.y; + float sourceAspect = aspect(iChannelResolution[0].xy); + float aspectCorrection = sourceAspect / targetAspect; + if (aspectCorrection > 1.0) { + float offset = aspectCorrection - 1.0; + float halfOffset = offset / 2.0; + uv.y -= halfOffset; + uv.y *= aspectCorrection; + } else { + float offset = 1.0 - aspectCorrection; + float halfOffset = offset / 2.0; + uv.x -= halfOffset; + uv.x /= aspectCorrection; + } + + if (any(lessThan(uv, vec2(0.0)))) { + return vec3(0.0); + } + + if (any(greaterThan(uv, vec2(1.0)))) { + return vec3(0.0); + } + + vec4 color = texture(iChannel0, uv); + return color.rgb * max(0.5, sourceAspect) * max(0.9, fract(iWorldPosition.x)); +} + +float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { + specular = aspectCorrectedTexture(); + return 1.0; +} + diff --git a/examples/tests/textureStress.js b/examples/tests/textureStress.js new file mode 100644 index 0000000000..daf6eb41ec --- /dev/null +++ b/examples/tests/textureStress.js @@ -0,0 +1,67 @@ +Script.include("https://s3.amazonaws.com/DreamingContent/scripts/Austin.js"); + +var ENTITY_SPAWN_LIMIT = 500; +var ENTITY_LIFETIME = 600; +var RADIUS = 1.0; // Spawn within this radius (square) +var TEST_ENTITY_NAME = "EntitySpawnTest"; + +var entities = []; +var textureIndex = 0; +var texture = Script.resolvePath('cube_texture.png'); +var shader = Script.resolvePath('textureStress.fs'); +var qml = Script.resolvePath('textureStress.qml'); +qmlWindow = new OverlayWindow({ + title: 'Test Qml', + source: qml, + height: 240, + width: 320, + toolWindow: false, + visible: true +}); + +function deleteItems(count) { + if (!count) { + var ids = Entities.findEntities(MyAvatar.position, 50); + ids.forEach(function(id) { + var properties = Entities.getEntityProperties(id, ["name"]); + if (properties.name === TEST_ENTITY_NAME) { + Entities.deleteEntity(id); + } + }, this); + entities = []; + return; + } else { + // FIXME... implement + } +} + +function createItems(count) { + for (var i = 0; i < count; ++i) { + var newEntity = Entities.addEntity({ + type: "Box", + name: TEST_ENTITY_NAME, + position: AUSTIN.avatarRelativePosition(AUSTIN.randomPositionXZ({ x: 0, y: 0, z: -2 }, RADIUS)), + color: { r: 255, g: 255, b: 255 }, + dimensions: AUSTIN.randomDimensions(), + lifetime: ENTITY_LIFETIME, + userData: JSON.stringify({ + ProceduralEntity: { + version: 2, + shaderUrl: shader, + channels: [ texture + "?" + textureIndex++ ] + } + }) + }); + entities.push(newEntity); + } +} + +qmlWindow.fromQml.connect(function(message){ + print(message); + if (message[0] === "create") { + var count = message[1] || 1; + createItems(message[1] || 1); + } else if (message[0] === "delete") { + deleteItems(message[1]); + } +}); diff --git a/examples/tests/textureStress.qml b/examples/tests/textureStress.qml new file mode 100644 index 0000000000..1a8b994475 --- /dev/null +++ b/examples/tests/textureStress.qml @@ -0,0 +1,69 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Rectangle { + id: root + width: parent ? parent.width : 100 + height: parent ? parent.height : 100 + + signal sendToScript(var message); + + Text { + id: label + text: "GPU Texture Usage: " + } + Text { + id: usage + anchors.left: label.right + anchors.leftMargin: 8 + text: "N/A" + Timer { + repeat: true + running: true + interval: 500 + onTriggered: { + usage.text = Render.getConfig("Stats")["textureGPUMemoryUsage"]; + } + } + } + + Column { + + + anchors { left: parent.left; right: parent.right; top: label.bottom; topMargin: 8; bottom: parent.bottom } + spacing: 8 + + Button { + text: "Add 1" + onClicked: root.sendToScript(["create", 1]); + } + Button { + text: "Add 10" + onClicked: root.sendToScript(["create", 10]); + } + Button { + text: "Add 100" + onClicked: root.sendToScript(["create", 100]); + } + /* + Button { + text: "Delete 1" + onClicked: root.sendToScript(["delete", 1]); + } + Button { + text: "Delete 10" + onClicked: root.sendToScript(["delete", 10]); + } + Button { + text: "Delete 100" + onClicked: root.sendToScript(["delete", 100]); + } + */ + Button { + text: "Delete All" + onClicked: root.sendToScript(["delete", 0]); + } + } +} + + diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index d18ada1f5a..66667a0b2a 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -122,13 +122,18 @@ void QmlWindowClass::initQml(QVariantMap properties) { object->setProperty(SOURCE_PROPERTY, _source); // Forward messages received from QML on to the script - connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); + connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection); }); } Q_ASSERT(_qmlWindow); Q_ASSERT(dynamic_cast(_qmlWindow.data())); } +void QmlWindowClass::qmlToScript(const QVariant& message) { + QJSValue js = qvariant_cast(message); + emit fromQml(js.toVariant()); +} + void QmlWindowClass::sendToQml(const QVariant& message) { // Forward messages received from the script on to QML QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 242f9b0dd4..e1865b133a 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -63,6 +63,7 @@ signals: protected slots: void hasClosed(); + void qmlToScript(const QVariant& message); protected: static QVariantMap parseArguments(QScriptContext* context);