mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 05:43:36 +02:00
Add stress test for QML web content
This commit is contained in:
parent
e892694bf5
commit
f2172d84a0
2 changed files with 187 additions and 43 deletions
32
tests/qml/qml/controls/WebEntityView.qml
Normal file
32
tests/qml/qml/controls/WebEntityView.qml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// WebEntityView.qml
|
||||||
|
//
|
||||||
|
// Created by Kunal Gosar on 16 March 2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtWebEngine 1.5
|
||||||
|
|
||||||
|
WebEngineView {
|
||||||
|
id: webViewCore
|
||||||
|
objectName: "webEngineView"
|
||||||
|
width: parent !== null ? parent.width : undefined
|
||||||
|
height: parent !== null ? parent.height : undefined
|
||||||
|
|
||||||
|
onFeaturePermissionRequested: {
|
||||||
|
grantFeaturePermission(securityOrigin, feature, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//disable popup
|
||||||
|
onContextMenuRequested: {
|
||||||
|
request.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewViewRequested: {
|
||||||
|
newViewRequestedCallback(request)
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,52 +28,82 @@
|
||||||
#include <QtGui/QImage>
|
#include <QtGui/QImage>
|
||||||
#include <QtGui/QOpenGLFunctions_4_5_Core>
|
#include <QtGui/QOpenGLFunctions_4_5_Core>
|
||||||
#include <QtGui/QOpenGLContext>
|
#include <QtGui/QOpenGLContext>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
#include <QtQml/QQmlContext>
|
#include <QtQml/QQmlContext>
|
||||||
#include <QtQml/QQmlComponent>
|
#include <QtQml/QQmlComponent>
|
||||||
|
|
||||||
|
#include <SettingInterface.h>
|
||||||
|
|
||||||
#include <gl/OffscreenGLCanvas.h>
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
#include <GLMHelpers.h>
|
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <qml/OffscreenSurface.h>
|
#include <qml/OffscreenSurface.h>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#pragma optimize("", off)
|
||||||
|
|
||||||
|
namespace gl {
|
||||||
|
extern void initModuleGl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
|
QUrl getTestResource(const QString& relativePath) {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
QDir path(__FILE__);
|
||||||
|
path.cdUp();
|
||||||
|
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
||||||
|
qDebug() << "Resources Path: " << dir;
|
||||||
|
}
|
||||||
|
return QUrl::fromLocalFile(dir + relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define DIVISIONS_X 5
|
||||||
|
#define DIVISIONS_Y 5
|
||||||
|
|
||||||
|
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
|
||||||
|
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
||||||
|
|
||||||
|
struct QmlInfo {
|
||||||
|
QmlPtr surface;
|
||||||
|
GLuint texture{ 0 };
|
||||||
|
uint64_t lifetime{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class TestWindow : public QWindow {
|
class TestWindow : public QWindow {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TestWindow();
|
TestWindow();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
|
||||||
QOpenGLContext _glContext;
|
QOpenGLContext _glContext;
|
||||||
OffscreenGLCanvas _sharedContext;
|
OffscreenGLCanvas _sharedContext;
|
||||||
OffscreenQmlSurface _offscreenQml;
|
std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
|
||||||
|
|
||||||
QOpenGLFunctions_4_5_Core _glf;
|
QOpenGLFunctions_4_5_Core _glf;
|
||||||
uint32_t _currentTexture{ 0 };
|
|
||||||
GLsync _readFence{ 0 };
|
|
||||||
std::function<void(uint32_t, void*)> _discardLamdba;
|
std::function<void(uint32_t, void*)> _discardLamdba;
|
||||||
QSize _size;
|
QSize _size;
|
||||||
|
size_t _surfaceCount{ 0 };
|
||||||
GLuint _fbo{ 0 };
|
GLuint _fbo{ 0 };
|
||||||
const QSize _qmlSize{ 640, 480 };
|
const QSize _qmlSize{ 640, 480 };
|
||||||
bool _aboutToQuit{ false };
|
bool _aboutToQuit{ false };
|
||||||
void initGl();
|
void initGl();
|
||||||
|
void updateSurfaces();
|
||||||
|
void buildSurface(QmlInfo& qmlInfo);
|
||||||
|
void destroySurface(QmlInfo& qmlInfo);
|
||||||
void resizeWindow(const QSize& size);
|
void resizeWindow(const QSize& size);
|
||||||
void draw();
|
void draw();
|
||||||
void resizeEvent(QResizeEvent* ev) override;
|
void resizeEvent(QResizeEvent* ev) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
TestWindow::TestWindow() {
|
TestWindow::TestWindow() {
|
||||||
setSurfaceType(QSurface::OpenGLSurface);
|
Setting::init();
|
||||||
|
|
||||||
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setDepthBufferSize(24);
|
format.setDepthBufferSize(24);
|
||||||
format.setStencilBufferSize(8);
|
format.setStencilBufferSize(8);
|
||||||
|
@ -85,11 +115,12 @@ TestWindow::TestWindow() {
|
||||||
|
|
||||||
show();
|
show();
|
||||||
|
|
||||||
|
|
||||||
resize(QSize(800, 600));
|
resize(QSize(800, 600));
|
||||||
|
|
||||||
auto timer = new QTimer(this);
|
auto timer = new QTimer(this);
|
||||||
timer->setTimerType(Qt::PreciseTimer);
|
timer->setTimerType(Qt::PreciseTimer);
|
||||||
timer->setInterval(5);
|
timer->setInterval(30);
|
||||||
connect(timer, &QTimer::timeout, [&] { draw(); });
|
connect(timer, &QTimer::timeout, [&] { draw(); });
|
||||||
timer->start();
|
timer->start();
|
||||||
|
|
||||||
|
@ -97,7 +128,6 @@ TestWindow::TestWindow() {
|
||||||
timer->stop();
|
timer->stop();
|
||||||
_aboutToQuit = true;
|
_aboutToQuit = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWindow::initGl() {
|
void TestWindow::initGl() {
|
||||||
|
@ -105,6 +135,7 @@ void TestWindow::initGl() {
|
||||||
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
|
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
|
||||||
qFatal("Unable to intialize Window GL context");
|
qFatal("Unable to intialize Window GL context");
|
||||||
}
|
}
|
||||||
|
gl::initModuleGl();
|
||||||
|
|
||||||
_glf.initializeOpenGLFunctions();
|
_glf.initializeOpenGLFunctions();
|
||||||
_glf.glCreateFramebuffers(1, &_fbo);
|
_glf.glCreateFramebuffers(1, &_fbo);
|
||||||
|
@ -113,15 +144,104 @@ void TestWindow::initGl() {
|
||||||
qFatal("Unable to intialize Shared GL context");
|
qFatal("Unable to intialize Shared GL context");
|
||||||
}
|
}
|
||||||
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
|
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
|
||||||
_discardLamdba = _offscreenQml.getDiscardLambda();
|
_discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
|
||||||
_offscreenQml.resize({ 640, 480 });
|
|
||||||
_offscreenQml.load(QUrl::fromLocalFile("C:/Users/bdavi/Git/hifi/tests/qml/qml/main.qml"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWindow::resizeWindow(const QSize& size) {
|
void TestWindow::resizeWindow(const QSize& size) {
|
||||||
_size = size;
|
_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int DEFAULT_MAX_FPS = 10;
|
||||||
|
static const int YOUTUBE_MAX_FPS = 30;
|
||||||
|
static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
|
||||||
|
static const char* URL_PROPERTY{ "url" };
|
||||||
|
|
||||||
|
QString getSourceUrl() {
|
||||||
|
static const std::vector<QString> SOURCE_URLS{
|
||||||
|
"https://www.reddit.com/wiki/random",
|
||||||
|
"https://en.wikipedia.org/wiki/Wikipedia:Random",
|
||||||
|
"https://slashdot.org/",
|
||||||
|
//"https://www.youtube.com/watch?v=gDXwhHm4GhM",
|
||||||
|
//"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
|
||||||
|
};
|
||||||
|
|
||||||
|
auto index = rand() % SOURCE_URLS.size();
|
||||||
|
return SOURCE_URLS[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void TestWindow::buildSurface(QmlInfo& qmlInfo) {
|
||||||
|
++_surfaceCount;
|
||||||
|
auto lifetimeSecs = (uint32_t)(2.0f + (randFloat() * 10.0f));
|
||||||
|
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
|
||||||
|
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
|
||||||
|
qmlInfo.texture = 0;
|
||||||
|
auto& surface = qmlInfo.surface;
|
||||||
|
surface.reset(new hifi::qml::OffscreenSurface());
|
||||||
|
surface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
|
surface->resize(_qmlSize);
|
||||||
|
surface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
|
hifi::qml::QmlContextObjectCallback callback = [](QQmlContext* context, QQuickItem* item) {
|
||||||
|
item->setProperty(URL_PROPERTY, getSourceUrl());
|
||||||
|
};
|
||||||
|
surface->load(getTestResource(CONTROL_URL), callback);
|
||||||
|
surface->resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::destroySurface(QmlInfo& qmlInfo) {
|
||||||
|
auto& surface = qmlInfo.surface;
|
||||||
|
QQuickItem* rootItem = surface->getRootItem();
|
||||||
|
if (rootItem) {
|
||||||
|
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
|
||||||
|
if (!obj && rootItem->objectName() == "webEngineView") {
|
||||||
|
obj = rootItem;
|
||||||
|
}
|
||||||
|
if (obj) {
|
||||||
|
// stop loading
|
||||||
|
QMetaObject::invokeMethod(obj, "stop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
surface->pause();
|
||||||
|
surface.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::updateSurfaces() {
|
||||||
|
auto now = usecTimestampNow();
|
||||||
|
// Fetch any new textures
|
||||||
|
for (size_t x = 0; x < DIVISIONS_X; ++x) {
|
||||||
|
for (size_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||||
|
auto& qmlInfo = _surfaces[x][y];
|
||||||
|
if (!qmlInfo.surface) {
|
||||||
|
if (randFloat() > 0.99f) {
|
||||||
|
buildSurface(qmlInfo);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now > qmlInfo.lifetime) {
|
||||||
|
destroySurface(qmlInfo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& surface = qmlInfo.surface;
|
||||||
|
auto& currentTexture = qmlInfo.texture;
|
||||||
|
|
||||||
|
TextureAndFence newTextureAndFence;
|
||||||
|
if (surface->fetchTexture(newTextureAndFence)) {
|
||||||
|
if (currentTexture != 0) {
|
||||||
|
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
glFlush();
|
||||||
|
_discardLamdba(currentTexture, readFence);
|
||||||
|
}
|
||||||
|
currentTexture = newTextureAndFence.first;
|
||||||
|
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestWindow::draw() {
|
void TestWindow::draw() {
|
||||||
if (_aboutToQuit) {
|
if (_aboutToQuit) {
|
||||||
return;
|
return;
|
||||||
|
@ -140,36 +260,31 @@ void TestWindow::draw() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSurfaces();
|
||||||
|
|
||||||
|
auto size = this->geometry().size();
|
||||||
|
auto incrementX = size.width() / DIVISIONS_X;
|
||||||
|
auto incrementY = size.height() / DIVISIONS_Y;
|
||||||
|
_glf.glViewport(0, 0, size.width(), size.height());
|
||||||
_glf.glClearColor(1, 0, 0, 1);
|
_glf.glClearColor(1, 0, 0, 1);
|
||||||
_glf.glClear(GL_COLOR_BUFFER_BIT);
|
_glf.glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
TextureAndFence newTextureAndFence;
|
for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
|
||||||
if (_offscreenQml.fetchTexture(newTextureAndFence)) {
|
for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||||
if (_currentTexture) {
|
auto& qmlInfo = _surfaces[x][y];
|
||||||
_discardLamdba(_currentTexture, _readFence);
|
if (!qmlInfo.surface || !qmlInfo.texture) {
|
||||||
_readFence = 0;
|
continue;
|
||||||
|
}
|
||||||
|
_glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, qmlInfo.texture, 0);
|
||||||
|
_glf.glBlitNamedFramebuffer(_fbo, 0,
|
||||||
|
// src coordinates
|
||||||
|
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
||||||
|
// dst coordinates
|
||||||
|
incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
|
||||||
|
// blit mask and filter
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentTexture = newTextureAndFence.first;
|
|
||||||
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
|
||||||
_glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, _currentTexture, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto diff = _size - _qmlSize;
|
|
||||||
diff /= 2;
|
|
||||||
auto qmlExtent = diff + _qmlSize;
|
|
||||||
|
|
||||||
if (_currentTexture) {
|
|
||||||
_glf.glBlitNamedFramebuffer(_fbo, 0,
|
|
||||||
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
|
||||||
diff.width(), diff.height(), qmlExtent.width() - 1, qmlExtent.height() - 2,
|
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_readFence) {
|
|
||||||
_glf.glDeleteSync(_readFence);
|
|
||||||
}
|
|
||||||
_readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
||||||
_glf.glFlush();
|
_glf.glFlush();
|
||||||
|
|
||||||
_glContext.swapBuffers(this);
|
_glContext.swapBuffers(this);
|
||||||
|
@ -180,11 +295,8 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
setupHifiApplication("QML Test");
|
|
||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
TestWindow window;
|
TestWindow window;
|
||||||
app.exec();
|
app.exec();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue