Merge remote-tracking branch 'upstream/master' into etc2comp

This commit is contained in:
SamGondelman 2018-05-11 11:23:33 -07:00
commit eede3ff1ba
123 changed files with 1555 additions and 537 deletions

View file

@ -180,6 +180,7 @@ add_subdirectory(tools)
if (BUILD_TESTS)
add_subdirectory(tests)
add_subdirectory(tests-manual)
endif()
if (BUILD_INSTALLER)

View file

@ -124,15 +124,14 @@ macro(SETUP_HIFI_TESTCASE)
# This target will also build + run the other test targets using ctest when built.
add_custom_target(${TEST_TARGET}
COMMAND ctest .
SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target
DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
set_target_properties(${TEST_TARGET} PROPERTIES
FOLDER "Tests"
EXCLUDE_FROM_DEFAULT_BUILD TRUE
EXCLUDE_FROM_ALL TRUE)
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests")
list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
else ()

View file

@ -20,15 +20,9 @@ import "../fileDialog"
Item {
// Set from OffscreenUi::assetDialog()
property alias dir: assetTableModel.folder
property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
property int options // Not used.
property alias filter: selectionType.filtersString
property bool selectDirectory: false
// Not implemented.
//property bool saveDialog: false;
//property bool multiSelect: false;
property bool singleClickNavigate: false
HifiConstants { id: hifi }
@ -85,7 +79,6 @@ Item {
size: 28
width: height
enabled: destination !== ""
//onClicked: d.navigateHome();
onClicked: assetTableModel.folder = destination;
}
}
@ -228,7 +221,9 @@ Item {
function onGetAllMappings(error, map) {
var mappings,
fileTypeFilter,
fileTypeFilters = [],
filterListStart,
filterListEnd,
index,
path,
fileName,
@ -249,7 +244,16 @@ Item {
if (error === "") {
mappings = Object.keys(map);
fileTypeFilter = filter.replace("*", "").toLowerCase();
filter = filter.replace(/\s/g, '');
filterListStart = filter.indexOf("(");
filterListEnd = filter.indexOf(")");
if (filterListStart !== -1 && filterListEnd !== -1) {
var FIRST_EXTENSION_OFFSET = 2;
fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET
, filterListEnd).toLowerCase().split("*");
} else if (filter !== "") {
fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
}
for (i = 0, length = mappings.length; i < length; i++) {
index = mappings[i].lastIndexOf("/");
@ -260,7 +264,24 @@ Item {
fileIsDir = false;
isValid = false;
if (fileType.toLowerCase() === fileTypeFilter) {
if (fileTypeFilters.length > 1) {
if (fileTypeFilters.indexOf(fileType.toLowerCase()) !== -1) {
if (path === folder) {
isValid = !selectDirectory;
} else if (path.length > folder.length) {
subDirectory = path.slice(folder.length);
index = subDirectory.indexOf("/");
if (index === subDirectory.lastIndexOf("/")) {
fileName = subDirectory.slice(0, index);
if (subDirectories.indexOf(fileName) === -1) {
fileIsDir = true;
isValid = true;
subDirectories.push(fileName);
}
}
}
}
} else if (fileType.toLowerCase() === fileTypeFilters[0] || fileTypeFilters.length === 0) {
if (path === folder) {
isValid = !selectDirectory;
} else if (path.length > folder.length) {

View file

@ -145,16 +145,6 @@
#include <avatars-renderer/ScriptAvatar.h>
#include <RenderableEntityItem.h>
#include <AnimationLogging.h>
#include <AvatarLogging.h>
#include <ScriptEngineLogging.h>
#include <ModelFormatLogging.h>
#include <controllers/Logging.h>
#include <NetworkLogging.h>
#include <shared/StorageLogging.h>
#include <ScriptEngineLogging.h>
#include <ui/Logging.h>
#include "AudioClient.h"
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
@ -1372,8 +1362,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Needs to happen AFTER the render engine initialization to access its configuration
initializeUi();
updateVerboseLogging();
init();
qCDebug(interfaceapp, "init() complete.");
@ -1713,6 +1701,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
loadSettings();
updateVerboseLogging();
// Now that we've loaded the menu and thus switched to the previous display plugin
// we can unlock the desktop repositioning code, since all the positions will be
// relative to the desktop size for this plugin
@ -2257,43 +2247,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
void Application::updateVerboseLogging() {
bool enable = Menu::getInstance()->isOptionChecked(MenuOption::VerboseLogging);
auto menu = Menu::getInstance();
if (!menu) {
return;
}
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtInfoMsg, enable);
QString rules =
"hifi.*.debug=%1\n"
"hifi.*.info=%1\n"
"hifi.audio-stream.debug=false\n"
"hifi.audio-stream.info=false";
rules = rules.arg(enable ? "true" : "false");
QLoggingCategory::setFilterRules(rules);
}
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {

View file

@ -12,5 +12,4 @@
#include "AudioLogging.h"
Q_LOGGING_CATEGORY(audio, "hifi.audio")
Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream", QtWarningMsg)
Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream")

View file

@ -77,7 +77,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
_myAvatar->updateAvatarEntity(entityItemID, binaryProperties);
entity->setLastBroadcast(usecTimestampNow());
return;
}
@ -85,10 +84,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
EntityTreePointer entityTree,
EntityItemID entityItemID,
const EntityItemProperties& properties) {
if (!_shouldSend) {
return; // bail early
}
if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) {
// this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties);
@ -147,9 +142,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
}
void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) {
if (!_shouldSend) {
return; // bail early
}
// in case this was a clientOnly entity:
if(_myAvatar) {

View file

@ -1070,7 +1070,7 @@ bool EntityItem::stepKinematicMotion(float timeElapsed) {
const float MAX_TIME_ELAPSED = 1.0f; // seconds
if (timeElapsed > MAX_TIME_ELAPSED) {
qCWarning(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
qCDebug(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
}
timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED);

View file

@ -103,12 +103,15 @@ void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage)
bool OffscreenGLCanvas::makeCurrent() {
bool result = _context->makeCurrent(_offscreenSurface);
std::call_once(_reportOnce, []{
qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
});
if (glGetString) {
std::call_once(_reportOnce, [] {
qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
qCDebug(glLogging) << "GL Shader Language Version: "
<< QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
});
}
return result;
}

View file

@ -17,7 +17,6 @@ std::string GL41Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 410 core
#define GPU_GL410
#define PRECISIONQ
#define BITFIELD int
)SHADER");
return header;

View file

@ -18,7 +18,6 @@ std::string GL45Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 450 core
#define GPU_GL450
#define PRECISIONQ
#define BITFIELD int
)SHADER"
#ifdef GPU_SSBO_TRANSFORM_OBJECT

View file

@ -17,10 +17,9 @@ std::string GLESBackend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 310 es
#extension GL_EXT_texture_buffer : enable
precision lowp float; // check precision 2
precision lowp samplerBuffer;
precision lowp sampler2DShadow;
#define PRECISIONQ highp
precision highp float;
precision highp samplerBuffer;
precision highp sampler2DShadow;
#define BITFIELD highp int
)SHADER");
return header;

View file

@ -34,7 +34,7 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi
// Light Ambient
struct LightAmbient {
PRECISIONQ vec4 _ambient;
vec4 _ambient;
SphericalHarmonics _ambientSphere;
mat4 transform;
};

View file

@ -14,9 +14,9 @@
#define LightIrradianceConstRef LightIrradiance
struct LightIrradiance {
PRECISIONQ vec4 colorIntensity;
vec4 colorIntensity;
// falloffRadius, cutoffRadius, falloffSpot, spare
PRECISIONQ vec4 attenuation;
vec4 attenuation;
};

View file

@ -16,8 +16,8 @@
#define LightVolumeConstRef LightVolume
struct LightVolume {
PRECISIONQ vec4 positionRadius;
PRECISIONQ vec4 directionSpotCos;
vec4 positionRadius;
vec4 directionSpotCos;
};
bool lightVolume_isPoint(LightVolume lv) { return bool(lv.directionSpotCos.w < 0.f); }

View file

@ -15,10 +15,10 @@
// to what is provided by the uniform buffer, or the material key has the wrong bits
struct Material {
PRECISIONQ vec4 _emissiveOpacity;
PRECISIONQ vec4 _albedoRoughness;
PRECISIONQ vec4 _fresnelMetallic;
PRECISIONQ vec4 _scatteringSpare2Key;
vec4 _emissiveOpacity;
vec4 _albedoRoughness;
vec4 _fresnelMetallic;
vec4 _scatteringSpare2Key;
};
uniform materialBuffer {
@ -64,7 +64,4 @@ const BITFIELD OCCLUSION_MAP_BIT = 0x00004000;
const BITFIELD LIGHTMAP_MAP_BIT = 0x00008000;
const BITFIELD SCATTERING_MAP_BIT = 0x00010000;
#ifdef GL_ES
precision lowp float;
#endif
<@endif@>

View file

@ -16,15 +16,15 @@
#define SphericalHarmonicsConstRef SphericalHarmonics
struct SphericalHarmonics {
PRECISIONQ vec4 L00;
PRECISIONQ vec4 L1m1;
PRECISIONQ vec4 L10;
PRECISIONQ vec4 L11;
PRECISIONQ vec4 L2m2;
PRECISIONQ vec4 L2m1;
PRECISIONQ vec4 L20;
PRECISIONQ vec4 L21;
PRECISIONQ vec4 L22;
vec4 L00;
vec4 L1m1;
vec4 L10;
vec4 L11;
vec4 L2m2;
vec4 L2m1;
vec4 L20;
vec4 L21;
vec4 L22;
};
vec4 sphericalHarmonics_evalSphericalLight(SphericalHarmonicsConstRef sh, vec3 direction) {

View file

@ -0,0 +1,117 @@
//
// HMACAuth.cpp
// libraries/networking/src
//
// Created by Simon Walton on 3/19/2018.
// Copyright 2018 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
//
#include "HMACAuth.h"
#include <openssl/opensslv.h>
#include <openssl/hmac.h>
#include <QUuid>
#include "NetworkLogging.h"
#include <cassert>
#if OPENSSL_VERSION_NUMBER >= 0x10100000
HMACAuth::HMACAuth(AuthMethod authMethod)
: _hmacContext(HMAC_CTX_new())
, _authMethod(authMethod) { }
HMACAuth::~HMACAuth()
{
HMAC_CTX_free(_hmacContext);
}
#else
HMACAuth::HMACAuth(AuthMethod authMethod)
: _hmacContext(new HMAC_CTX())
, _authMethod(authMethod) {
HMAC_CTX_init(_hmacContext);
}
HMACAuth::~HMACAuth() {
HMAC_CTX_cleanup(_hmacContext);
delete _hmacContext;
}
#endif
bool HMACAuth::setKey(const char* keyValue, int keyLen) {
const EVP_MD* sslStruct = nullptr;
switch (_authMethod) {
case MD5:
sslStruct = EVP_md5();
break;
case SHA1:
sslStruct = EVP_sha1();
break;
case SHA224:
sslStruct = EVP_sha224();
break;
case SHA256:
sslStruct = EVP_sha256();
break;
case RIPEMD160:
sslStruct = EVP_ripemd160();
break;
default:
return false;
}
QMutexLocker lock(&_lock);
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
}
bool HMACAuth::setKey(const QUuid& uidKey) {
const QByteArray rfcBytes(uidKey.toRfc4122());
return setKey(rfcBytes.constData(), rfcBytes.length());
}
bool HMACAuth::addData(const char* data, int dataLen) {
QMutexLocker lock(&_lock);
return (bool) HMAC_Update(_hmacContext, reinterpret_cast<const unsigned char*>(data), dataLen);
}
HMACAuth::HMACHash HMACAuth::result() {
HMACHash hashValue(EVP_MAX_MD_SIZE);
unsigned int hashLen;
QMutexLocker lock(&_lock);
auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
if (hmacResult) {
hashValue.resize((size_t)hashLen);
} else {
// the HMAC_FINAL call failed - should not be possible to get into this state
qCWarning(networking) << "Error occured calling HMAC_Final";
assert(hmacResult);
}
// Clear state for possible reuse.
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return hashValue;
}
bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
QMutexLocker lock(&_lock);
if (!addData(data, dataLen)) {
qCWarning(networking) << "Error occured calling HMACAuth::addData()";
assert(false);
return false;
}
hashResult = result();
return true;
}

View file

@ -0,0 +1,47 @@
//
// HMACAuth.h
// libraries/networking/src
//
// Created by Simon Walton on 3/19/2018.
// Copyright 2018 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
//
#ifndef hifi_HMACAuth_h
#define hifi_HMACAuth_h
#include <vector>
#include <memory>
#include <QtCore/QMutex>
class QUuid;
class HMACAuth {
public:
enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
using HMACHash = std::vector<unsigned char>;
explicit HMACAuth(AuthMethod authMethod = MD5);
~HMACAuth();
bool setKey(const char* keyValue, int keyLen);
bool setKey(const QUuid& uidKey);
// Calculate complete hash in one.
bool calculateHash(HMACHash& hashResult, const char* data, int dataLen);
// Append to data to be hashed.
bool addData(const char* data, int dataLen);
// Get the resulting hash from calls to addData().
// Note that only one hash may be calculated at a time for each
// HMACAuth instance if this interface is used.
HMACHash result();
private:
QMutex _lock { QMutex::Recursive };
struct hmac_ctx_st* _hmacContext;
AuthMethod _authMethod;
};
#endif // hifi_HMACAuth_h

View file

@ -36,6 +36,7 @@
#include "HifiSockAddr.h"
#include "NetworkLogging.h"
#include "udt/Packet.h"
#include "HMACAuth.h"
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
@ -332,15 +333,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (verifiedPacket && !ignoreVerification) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
QByteArray expectedHash;
auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash();
if (sourceNode->getAuthenticateHash()) {
expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth);
}
// check if the md5 hash in the header matches the hash we would expect
if (packetHeaderHash != expectedHash) {
// check if the HMAC-md5 hash in the header matches the hash we would expect
if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) {
static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
qCDebug(networking) << packetHeaderHash << expectedHash;
qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" <<
expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex();
hashDebugSuppressMap.insert(sourceID, headerType);
}
@ -372,15 +378,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
_numCollectedBytes += packet.getDataSize();
}
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
packet.writeSourceID(getSessionLocalID());
}
if (!connectionSecret.isNull()
if (hmacAuth
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
packet.writeVerificationHashGivenSecret(connectionSecret);
packet.writeVerificationHash(*hmacAuth);
}
}
@ -396,17 +402,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
emit dataSent(destinationNode.getType(), packet.getDataSize());
destinationNode.recordBytesSent(packet.getDataSize());
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash());
}
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret) {
HMACAuth* hmacAuth) {
Q_ASSERT(!packet.isPartOfMessage());
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
"Trying to send a reliable packet unreliably.");
collectPacketStats(packet);
fillPacketHeader(packet, connectionSecret);
fillPacketHeader(packet, hmacAuth);
return _nodeSocket.writePacket(packet, sockAddr);
}
@ -419,7 +425,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
emit dataSent(destinationNode.getType(), packet->getDataSize());
destinationNode.recordBytesSent(packet->getDataSize());
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return ERROR_SENDING_PACKET_BYTES;
@ -427,18 +433,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
}
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret) {
HMACAuth* hmacAuth) {
Q_ASSERT(!packet->isPartOfMessage());
if (packet->isReliable()) {
collectPacketStats(*packet);
fillPacketHeader(*packet, connectionSecret);
fillPacketHeader(*packet, hmacAuth);
auto size = packet->getDataSize();
_nodeSocket.writePacket(std::move(packet), sockAddr);
return size;
} else {
return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
}
}
@ -447,13 +453,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
if (activeSocket) {
qint64 bytesSent = 0;
auto connectionSecret = destinationNode.getConnectionSecret();
auto connectionHash = destinationNode.getAuthenticateHash();
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket, connectionSecret);
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket,
connectionHash);
}
emit dataSent(destinationNode.getType(), bytesSent);
@ -466,14 +473,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
}
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret) {
HMACAuth* hmacAuth) {
qint64 bytesSent = 0;
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, connectionSecret);
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, hmacAuth);
}
return bytesSent;
@ -501,7 +508,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
collectPacketStats(*nlPacket);
fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash());
}
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@ -524,7 +531,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
: overridenSockAddr;
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash());
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {

View file

@ -138,19 +138,17 @@ public:
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
// either to a node (via its active socket) or to a manual sockaddr
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret = QUuid());
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret = QUuid());
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
// either to a node's active socket or to a manual sockaddr
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret = QUuid());
HMACAuth* hmacAuth = nullptr);
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
// or to a manual sock addr
@ -372,7 +370,7 @@ protected:
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
const QUuid& connectionSecret = QUuid());
void collectPacketStats(const NLPacket& packet);
void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
void setLocalSocket(const HifiSockAddr& sockAddr);

View file

@ -11,6 +11,8 @@
#include "NLPacket.h"
#include "HMACAuth.h"
int NLPacket::localHeaderSize(PacketType type) {
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
@ -150,18 +152,16 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
}
QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
QCryptographicHash hash(QCryptographicHash::Md5);
QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
hash.addData(connectionSecret.toRfc4122());
// return the hash
return hash.result();
HMACAuth::HMACHash hashResult;
if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) {
return QByteArray();
}
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
}
void NLPacket::writeTypeAndVersion() {
@ -214,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
_sourceID = sourceID;
}
void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID;
QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
}

View file

@ -18,6 +18,8 @@
#include "udt/Packet.h"
class HMACAuth;
class NLPacket : public udt::Packet {
Q_OBJECT
public:
@ -69,7 +71,7 @@ public:
static LocalID sourceIDInHeader(const udt::Packet& packet);
static QByteArray verificationHashInHeader(const udt::Packet& packet);
static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
PacketType getType() const { return _type; }
void setType(PacketType type);
@ -78,9 +80,9 @@ public:
void setVersion(PacketVersion version);
LocalID getSourceID() const { return _sourceID; }
void writeSourceID(LocalID sourceID) const;
void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
void writeVerificationHash(HMACAuth& hmacAuth) const;
protected:

View file

@ -86,7 +86,7 @@ NodeType_t NodeType::fromString(QString type) {
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
const HifiSockAddr& localSocket, QObject* parent) :
const HifiSockAddr& localSocket, QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type),
_pingMs(-1), // "Uninitialized"
@ -108,6 +108,7 @@ void Node::setType(char type) {
_symmetricSocket.setObjectName(typeString);
}
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
@ -194,3 +195,16 @@ QDebug operator<<(QDebug debug, const Node& node) {
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
return debug.nospace();
}
void Node::setConnectionSecret(const QUuid& connectionSecret) {
if (_connectionSecret == connectionSecret) {
return;
}
if (!_authenticateHash) {
_authenticateHash.reset(new HMACAuth());
}
_connectionSecret = connectionSecret;
_authenticateHash->setKey(_connectionSecret);
}

View file

@ -33,6 +33,7 @@
#include "SimpleMovingAverage.h"
#include "MovingPercentile.h"
#include "NodePermissions.h"
#include "HMACAuth.h"
class Node : public NetworkPeer {
Q_OBJECT
@ -55,7 +56,8 @@ public:
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
const QUuid& getConnectionSecret() const { return _connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret);
HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); }
NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
@ -97,6 +99,7 @@ private:
NodeType_t _type;
QUuid _connectionSecret;
std::unique_ptr<HMACAuth> _authenticateHash { nullptr };
std::unique_ptr<NodeData> _linkedData;
bool _isReplicated { false };
int _pingMs;

View file

@ -92,7 +92,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarQuery:
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
default:
return 20;
return 21;
}
}

View file

@ -23,7 +23,6 @@ const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::D
OctreeEditPacketSender::OctreeEditPacketSender() :
_shouldSend(true),
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
_releaseQueuedMessagesPending(false)
{
@ -146,10 +145,6 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(std::unique_ptr<NLPacket>
}
void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet) {
if (!_shouldSend) {
return; // bail early
}
assert(serversExist()); // we must have servers to be here!!
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
@ -162,10 +157,6 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet
// NOTE: editMessage - is JUST the octcode/color and does not contain the packet header
void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) {
if (!_shouldSend) {
return; // bail early
}
// If we don't have servers, then we will simply queue up all of these packets and wait till we have
// servers for processing
if (!serversExist()) {

View file

@ -41,12 +41,10 @@ public:
/// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and
/// not queued and not sent
bool getShouldSend() const { return _shouldSend; }
/// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll
/// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used
/// in an application like interface when all octree features are disabled.
void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; }
/// if you're running in non-threaded mode, you must call this method regularly
virtual bool process() override;
@ -76,7 +74,6 @@ public slots:
protected:
using EditMessagePair = std::pair<PacketType, QByteArray>;
bool _shouldSend;
void queuePacketToNode(const QUuid& nodeID, std::unique_ptr<NLPacket> packet);
void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr<NLPacketList> packetList);

View file

@ -349,7 +349,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
if (_numInactiveUpdates > 0) {
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) {
// clear local ownership (stop sending updates) and let the server clear itself
_entity->clearSimulationOwnership();
return false;
@ -833,3 +833,9 @@ void EntityMotionState::clearObjectVelocities() const {
}
_entity->setAcceleration(glm::vec3(0.0f));
}
bool EntityMotionState::isServerlessMode() {
EntityTreeElementPointer element = _entity->getElement();
EntityTreePointer tree = element ? element->getTree() : nullptr;
return tree ? tree->isServerlessMode() : false;
}

View file

@ -156,6 +156,8 @@ protected:
uint8_t _numInactiveUpdates { 1 };
uint8_t _bidPriority { 0 };
bool _serverVariablesSet { false };
bool isServerlessMode();
};
#endif // hifi_EntityMotionState_h

View file

@ -328,10 +328,18 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
}
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
motionState->initForBid();
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
_bids.push_back(motionState);
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
if (getEntityTree()->isServerlessMode()) {
EntityItemPointer entity = motionState->getEntity();
auto nodeList = DependencyManager::get<NodeList>();
auto sessionID = nodeList->getSessionUUID();
entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY));
_owned.push_back(motionState);
} else {
motionState->initForBid();
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
_bids.push_back(motionState);
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
}
}
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {

View file

@ -6,8 +6,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Controllers_Logging_h
#define hifi_Controllers_Logging_h
#ifndef hifi_QML_Logging_h
#define hifi_QML_Logging_h
#include <QLoggingCategory>

View file

@ -14,10 +14,10 @@
<@func declareLightingModel()@>
struct LightingModel {
PRECISIONQ vec4 _UnlitEmissiveLightmapBackground;
PRECISIONQ vec4 _ScatteringDiffuseSpecularAlbedo;
PRECISIONQ vec4 _AmbientDirectionalPointSpot;
PRECISIONQ vec4 _ShowContourObscuranceWireframe;
vec4 _UnlitEmissiveLightmapBackground;
vec4 _ScatteringDiffuseSpecularAlbedo;
vec4 _AmbientDirectionalPointSpot;
vec4 _ShowContourObscuranceWireframe;
};
uniform lightingModelBuffer{

View file

@ -101,8 +101,6 @@ int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature
int scriptEnginePointerMetaID = qRegisterMetaType<ScriptEnginePointer>();
Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script")
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
QString message = "";
for (int i = 0; i < context->argumentCount(); i++) {
@ -115,9 +113,9 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine *scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->print(message);
// prefix the script engine name to help disambiguate messages in the main debug log
qCDebug(scriptengineScript, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
} else {
qCDebug(scriptengineScript, "%s", qUtf8Printable(message));
qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
}
return QScriptValue();

View file

@ -13,3 +13,4 @@
Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine")
Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module")
Q_LOGGING_CATEGORY(scriptengine_script, "hifi.scriptengine.script")

View file

@ -16,6 +16,7 @@
Q_DECLARE_LOGGING_CATEGORY(scriptengine)
Q_DECLARE_LOGGING_CATEGORY(scriptengine_module)
Q_DECLARE_LOGGING_CATEGORY(scriptengine_script)
#endif // hifi_ScriptEngineLogging_h

View file

@ -0,0 +1,3 @@
set(TARGET_NAME test-utils)
setup_hifi_library(Network Gui)

View file

@ -0,0 +1,21 @@
#include "FileDownloader.h"
#include <QtCore/QCoreApplication>
#include <QtNetwork/QNetworkReply>
FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) {
connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
_accessManager.get(QNetworkRequest(url));
}
void FileDownloader::waitForDownload() {
while (!_complete) {
QCoreApplication::processEvents();
}
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
_handler(pReply->readAll());
pReply->deleteLater();
_complete = true;
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <QtCore/QObject>
#include <QtNetwork/QNetworkAccessManager>
class FileDownloader : public QObject {
Q_OBJECT
public:
using Handler = std::function<void(const QByteArray& data)>;
FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0);
void waitForDownload();
private slots:
void fileDownloaded(QNetworkReply* pReply);
private:
QNetworkAccessManager _accessManager;
Handler _handler;
bool _complete { false };
};

View file

@ -21,7 +21,8 @@ function printd(str) {
}
var radar = false;
var radarHeight = 10; // camera position meters above the avatar
var RADAR_HEIGHT_INIT_DELTA = 10;
var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y)
var tablet;
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
@ -45,12 +46,12 @@ var uniqueColor;
function moveTo(position) {
if (radar) {
MyAvatar.position = position;
Camera.position = Vec3.sum(MyAvatar.position, {
x : 0,
MyAvatar.goToLocation(position, false);
Camera.position = {
x : position.x,
y : radarHeight,
z : 0
});
z : position.z
};
}
}
@ -89,46 +90,6 @@ function keyPressEvent(event) {
}
}
function actionOnObjectFromEvent(event) {
var rayIntersection = findRayIntersection(Camera.computePickRay(event.x,
event.y));
if (rayIntersection && rayIntersection.intersects
&& rayIntersection.overlayID) {
printd("found overlayID touched " + rayIntersection.overlayID);
if (entitiesByOverlayID[rayIntersection.overlayID]) {
var entity = Entities.getEntityProperties(
entitiesByOverlayID[rayIntersection.overlayID],
[ "sourceUrl" ]);
App.openUrl(entity.sourceUrl);
return true;
}
}
if (rayIntersection && rayIntersection.intersects
&& rayIntersection.entityID && rayIntersection.properties) {
printd("found " + rayIntersection.entityID + " of type "
+ rayIntersection.properties.type);
if (rayIntersection.properties.type == "Web") {
printd("found web element to "
+ rayIntersection.properties.sourceUrl);
App.openUrl(rayIntersection.properties.sourceUrl);
return true;
}
}
return false;
}
function mousePress(event) {
mousePressOrTouchEnd(event);
}
function mousePressOrTouchEnd(event) {
if (radar) {
if (actionOnObjectFromEvent(event)) {
return;
}
}
}
function toggleRadarMode() {
if (radar) {
endRadar();
@ -229,9 +190,6 @@ function touchEnd(event) {
if (analyzeDoubleTap(event))
return; // double tap detected, finish
if (radar) {
mousePressOrTouchEnd(event);
}
}
/**
@ -386,12 +344,13 @@ function pinchUpdate(event) {
radarHeight -= pinchIncrement;
}
}
var deltaHeight = avatarY + radarHeight - Camera.position.y;
Camera.position = Vec3.sum(Camera.position, {
x : 0,
y : deltaHeight,
z : 0
});
Camera.position = {
x : Camera.position.x,
y : radarHeight,
z : Camera.position.z
};
if (!draggingCamera) {
startedDraggingCamera = true;
draggingCamera = true;
@ -401,7 +360,8 @@ function pinchUpdate(event) {
}
function isInsideSquare(coords0, coords1, halfside) {
return Math.abs(coords0.x - coords1.x) <= halfside
return coords0 != undefined && coords1 != undefined &&
Math.abs(coords0.x - coords1.x) <= halfside
&& Math.abs(coords0.y - coords1.y) <= halfside;
}
@ -412,7 +372,7 @@ function dragScrollUpdate(event) {
// drag management
var pickRay = Camera.computePickRay(event.x, event.y);
var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
radarHeight));
radarHeight - MyAvatar.position.y));
if (lastDragAt === undefined || lastDragAt === null) {
lastDragAt = dragAt;
@ -654,6 +614,7 @@ function Teleporter() {
return;
}
Camera.position = Vec3.sum(Camera.position, {
x : xDelta,
y : 0,
@ -722,7 +683,7 @@ function Teleporter() {
return {
dragTeleportBegin : function(event) {
printd("[newTeleport] TELEPORT began");
var overlayDimensions = entityIconModelDimensions();
var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y);
// var destination = computeDestination(event, MyAvatar.position,
// Camera.position, radarHeight);
// Dimension teleport and cancel overlays (not show them yet)
@ -843,7 +804,7 @@ var avatarIconDimensionsVal = {
};
function avatarIconPlaneDimensions() {
// given the current height, give a size
var xy = -0.003531 * radarHeight + 0.1;
var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1;
avatarIconDimensionsVal.x = Math.abs(xy);
avatarIconDimensionsVal.y = Math.abs(xy);
// reuse object
@ -1121,172 +1082,20 @@ function renderAllOthersAvatarIcons() {
}
}
function entityAdded(entityID) {
printd("Entity added " + entityID);
var props = Entities.getEntityProperties(entityID, [ "type" ]);
printd("Entity added " + entityID + " PROPS " + JSON.stringify(props));
if (props && props.type == "Web") {
printd("Entity Web added " + entityID);
saveEntityData(entityID, true);
}
}
function entityRemoved(entityID) {
printd("Entity removed " + entityID);
var props = Entities.getEntityProperties(entityID, [ "type" ]);
if (props && props.type == "Web") {
print("Entity Web removed " + entityID);
removeEntityData(entityID);
}
}
/*******************************************************************************
* Entities (to remark) cache structure for showing entities markers
******************************************************************************/
var entitiesData = {}; // by entityID
var entitiesByOverlayID = {}; // by overlayID
var entitiesIcons = []; // a parallel list of icons (overlays) to easily run
// through
var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg");
var ICON_ENTITY_IMG_MODEL_URL = Script
.resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use
// correct
// model&texture
var ICON_ENTITY_DEFAULT_DIMENSIONS = {
x : 0.10,
y : 0.00001,
z : 0.10
};
var entityIconModelDimensionsVal = {
x : 0,
y : 0.00001,
z : 0
};
function entityIconModelDimensions() {
// given the current height, give a size
var xz = -0.002831 * radarHeight + 0.1;
entityIconModelDimensionsVal.x = xz;
entityIconModelDimensionsVal.z = xz;
function teleportIconModelDimensions(y) {
var teleportModelDimensions = ICON_ENTITY_DEFAULT_DIMENSIONS;
var xz = -0.002831 * (radarHeight - y) + 0.1;
teleportModelDimensions.x = xz;
teleportModelDimensions.z = xz;
// reuse object
return entityIconModelDimensionsVal;
}
/*
* entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
* plane
*/
function entityIconPlaneDimensions() {
var dim = entityIconModelDimensions();
var z = dim.z;
dim.z = dim.y;
dim.y = z;
return dim;
}
function currentOverlayForEntity(QUuid) {
if (entitiesData[QUuid] != undefined) {
return entitiesData[QUuid].icon;
} else {
return null;
}
}
function saveEntityData(QUuid, planar) {
if (QUuid == null)
return;
var entity = Entities.getEntityProperties(QUuid, [ "position" ]);
printd("entity added save entity " + QUuid);
if (entitiesData[QUuid] != undefined) {
entitiesData[QUuid].position = entity.position;
} else {
var entityIcon = Overlays.addOverlay("image3d", {
subImage : {
x : 0,
y : 0,
width : 150,
height : 150
},
url : ICON_ENTITY_WEB_MODEL_URL,
dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS,
visible : false,
ignoreRayIntersection : false,
orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0)
});
entitiesIcons.push(entityIcon);
entitiesData[QUuid] = {
position : entity.position,
icon : entityIcon
};
entitiesByOverlayID[entityIcon] = QUuid;
}
}
function removeEntityData(QUuid) {
if (QUuid == null)
return;
var itsOverlay = currentOverlayForEntity(QUuid);
if (itsOverlay != null) {
Overlays.deleteOverlay(itsOverlay);
delete entitiesByOverlayID[itsOverlay];
}
var idx = entitiesIcons.indexOf(itsOverlay);
entitiesIcons.splice(idx, 1);
delete entitiesData[QUuid];
}
/*******************************************************************************
* Entities to remark Icon/Markers rendering
******************************************************************************/
function hideAllEntitiesIcons() {
var len = entitiesIcons.length;
for (var i = 0; i < len; i++) {
Overlays.editOverlay(entitiesIcons[i], {
visible : false
});
}
}
function renderAllEntitiesIcons() {
var entityPos;
var entityProps;
var iconDimensions = entityIconModelDimensions();
var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses
// xy instead of xz
for ( var QUuid in entitiesData) {
if (entitiesData.hasOwnProperty(QUuid)) {
entityProps = Entities.getEntityProperties(QUuid, [ "position",
"visible" ]);
if (entityProps != null) {
entityPos = entityProps.position;
if (entitiesData[QUuid].icon != undefined && entityPos) {
var iconPos = findLineToHeightIntersectionCoords(
entityPos.x,
entityPos.y
+ RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE,
entityPos.z, Camera.position.x, Camera.position.y,
Camera.position.z, Camera.position.y
- RADAR_CAMERA_DISTANCE_TO_ICONS);
if (!iconPos) {
printd("entity icon pos bad for " + QUuid);
continue;
}
var dimensions = entitiesData[QUuid].planar ? planeDimensions
: iconDimensions;
Overlays.editOverlay(entitiesData[QUuid].icon, {
visible : entityProps.visible,
dimensions : dimensions,
position : iconPos
});
}
}
}
}
return teleportModelDimensions;
}
/*******************************************************************************
@ -1298,11 +1107,8 @@ function startRadar() {
saveAllOthersAvatarsData();
Camera.mode = "independent";
Camera.position = Vec3.sum(MyAvatar.position, {
x : 0,
y : radarHeight,
z : 0
});
initCameraOverMyAvatar();
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
radar = true;
@ -1319,7 +1125,6 @@ function endRadar() {
Controller.setVPadEnabled(true);
disconnectRadarModeEvents();
hideAllEntitiesIcons();
hideAllAvatarIcons();
}
@ -1353,12 +1158,10 @@ function updateRadar() {
// Update avatar icons
if (startedDraggingCamera) {
hideAllAvatarIcons();
hideAllEntitiesIcons();
startedDraggingCamera = false;
} else if (!draggingCamera) {
renderMyAvatarIcon();
renderAllOthersAvatarIcons();
renderAllEntitiesIcons();
}
}
@ -1366,48 +1169,41 @@ function valueIfDefined(value) {
return value !== undefined ? value : "";
}
function entitiesAnalysis() {
var ids = Entities.findEntitiesInFrustum(Camera.frustum);
var entities = [];
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
entities.push({
id : id,
name : properties.name,
type : properties.type,
url : properties.type == "Model" ? properties.modelURL : "",
sourceUrl : properties.sourceUrl,
locked : properties.locked,
visible : properties.visible,
drawCalls : valueIfDefined(properties.renderInfo.drawCalls),
hasScript : properties.script !== ""
});
}
}
function connectRadarModeEvents() {
Script.update.connect(updateRadar); // 60Hz loop
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePress); // single click/touch
Controller.touchUpdateEvent.connect(touchUpdate);
Window.domainChanged.connect(domainChanged);
MyAvatar.positionGoneTo.connect(positionGoneTo);
}
function positionGoneTo() {
Camera.position = Vec3.sum(MyAvatar.position, {
x : 0,
function initCameraOverMyAvatar() {
radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
Camera.position = {
x : MyAvatar.position.x,
y : radarHeight,
z : 0
});
z : MyAvatar.position.z
};
}
function domainChanged() {
initCameraOverMyAvatar();
}
function positionGoneTo() {
Camera.position = {
x : MyAvatar.position.x,
y : radarHeight,
z : MyAvatar.position.z
};
}
function disconnectRadarModeEvents() {
Script.update.disconnect(updateRadar);
Controller.keyPressEvent.disconnect(keyPressEvent);
Controller.mousePressEvent.disconnect(mousePress);
Controller.touchUpdateEvent.disconnect(touchUpdate);
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
Window.domainChanged.disconnect(domainChanged);
}
function init() {
@ -1418,7 +1214,4 @@ function init() {
AvatarList.avatarAddedEvent.connect(avatarAdded);
AvatarList.avatarRemovedEvent.connect(avatarRemoved);
Entities.addingEntity.connect(entityAdded);
Entities.deletingEntity.connect(entityRemoved);
}

View file

@ -18,7 +18,7 @@
var headset; // The preferred headset. Default to the first one found in the following list.
var displayMenuName = "Display";
var desktopMenuItemName = "Desktop";
['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) {
['HTC Vive', 'Oculus Rift', 'WindowMS'].forEach(function (name) {
if (!headset && Menu.menuItemExists(displayMenuName, name)) {
headset = name;
}

View file

@ -0,0 +1,8 @@
# add the manual test directories
file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
foreach(DIR ${TEST_SUBDIRS})
if((IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/CMakeLists.txt"))
set(TEST_PROJ_NAME ${DIR})
add_subdirectory(${DIR})
endif()
endforeach()

View file

@ -0,0 +1,16 @@
set(TARGET_NAME gpu-textures-tests)
AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils)
# This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Quick Gui Script)
setup_memory_debugger()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
link_hifi_libraries(
shared task networking gl
ktx gpu octree
${PLATFORM_GL_BACKEND}
)
set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/qml\"")
package_libraries_for_deployment()
target_nsight()

View file

@ -0,0 +1,52 @@
import QtQuick 2.5
import QtQuick.Controls 2.3
Item {
width: 400
height: 600
Column {
spacing: 10
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 10
Text { text: qsTr("Total") }
Text { text: Stats.total + " MB" }
Text { text: qsTr("Allocated") }
Text { text: Stats.allocated }
Text { text: qsTr("Populated") }
Text { text: Stats.populated }
Text { text: qsTr("Pending") }
Text { text: Stats.pending }
Text { text: qsTr("Current Index") }
Text { text: Stats.index }
Text { text: qsTr("Current Source") }
Text { text: Stats.source }
Text { text: qsTr("Current Rez") }
Text { text: Stats.rez.width + " x " + Stats.rez.height }
}
Row {
id: row1
spacing: 10
anchors.bottom: row2.top
anchors.left: parent.left
anchors.margins: 10
Button { text: "1024"; onClicked: Stats.maxTextureMemory(1024); }
Button { text: "256"; onClicked: Stats.maxTextureMemory(256); }
}
Row {
id: row2
spacing: 10
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 10
Button { text: "Change Textures"; onClicked: Stats.changeTextures(); }
Button { text: "Next"; onClicked: Stats.nextTexture(); }
Button { text: "Previous"; onClicked: Stats.prevTexture(); }
}
}

View file

@ -0,0 +1,26 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#include "TestHelpers.h"
#include <QtCore/QFileInfo>
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
auto shader = gpu::Shader::createProgram(vs, fs);
if (!gpu::Shader::makeProgram(*shader, bindings)) {
printf("Could not compile shader\n");
exit(-1);
}
return shader;
}
QString projectRootDir() {
static QString projectRootPath = QFileInfo(QFileInfo(__FILE__).absolutePath() + "/..").absoluteFilePath();
return projectRootPath;
}

View file

@ -0,0 +1,40 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#pragma once
#include <vector>
#include <GLMHelpers.h>
#include <Transform.h>
#include <NumericalConstants.h>
#include <gpu/Resource.h>
#include <gpu/Forward.h>
#include <gpu/Shader.h>
#include <gpu/Stream.h>
struct RenderArgs {
gpu::ContextPointer _context;
ivec4 _viewport;
gpu::Batch* _batch;
};
class GpuTestBase : public QObject {
public:
virtual ~GpuTestBase() {}
virtual bool isReady() const { return true; }
virtual size_t getTestCount() const { return 1; }
virtual void renderTest(size_t test, const RenderArgs& args) = 0;
virtual QObject * statsObject() { return nullptr; }
virtual QUrl statUrl() { return QUrl(); }
};
uint32_t toCompactColor(const glm::vec4& color);
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
QString projectRootDir();

View file

@ -0,0 +1,166 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#include "TestTextures.h"
#include <random>
#include <algorithm>
#include <QtCore/QDir>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlContext>
#include <gpu/Batch.h>
#include <gpu/Context.h>
#include "TestHelpers.h"
std::string vertexShaderSource = R"SHADER(
#line 14
layout(location = 0) out vec2 outTexCoord0;
const vec4 VERTICES[] = vec4[](
vec4(-1.0, -1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0)
);
void main() {
outTexCoord0 = VERTICES[gl_VertexID].xy;
outTexCoord0 += 1.0;
outTexCoord0 /= 2.0;
gl_Position = VERTICES[gl_VertexID];
}
)SHADER";
std::string fragmentShaderSource = R"SHADER(
#line 28
uniform sampler2D tex;
layout(location = 0) in vec2 inTexCoord0;
layout(location = 0) out vec4 outFragColor;
void main() {
outFragColor = texture(tex, inTexCoord0);
outFragColor.a = 1.0;
//outFragColor.rb = inTexCoord0;
}
)SHADER";
#define STAT_UPDATE(name, src) \
{ \
auto val = src; \
if (_##name != val) { \
_##name = val; \
emit name##Changed(); \
} \
}
void TextureTestStats::update(int curIndex, const gpu::TexturePointer& texture) {
STAT_UPDATE(total, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
STAT_UPDATE(allocated, (int)gpu::Context::getTextureResourceGPUMemSize());
STAT_UPDATE(pending, (int)gpu::Context::getTexturePendingGPUTransferMemSize());
STAT_UPDATE(populated, (int)gpu::Context::getTextureResourcePopulatedGPUMemSize());
STAT_UPDATE(source, texture->source().c_str());
STAT_UPDATE(index, curIndex);
auto dims = texture->getDimensions();
STAT_UPDATE(rez, QSize(dims.x, dims.y));
}
TexturesTest::TexturesTest() {
connect(&stats, &TextureTestStats::changeTextures, this, &TexturesTest::onChangeTextures);
connect(&stats, &TextureTestStats::nextTexture, this, &TexturesTest::onNextTexture);
connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture);
connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory);
{
auto VS = gpu::Shader::createVertex(vertexShaderSource);
auto PS = gpu::Shader::createPixel(fragmentShaderSource);
auto program = gpu::Shader::createProgram(VS, PS);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
// If the pipeline did not exist, make it
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthTest({});
state->setBlendFunction({ false });
pipeline = gpu::Pipeline::create(program, state);
}
onChangeTextures();
}
void TexturesTest::renderTest(size_t testId, const RenderArgs& args) {
stats.update((int)index, textures[index]);
gpu::Batch& batch = *(args._batch);
batch.setPipeline(pipeline);
batch.setInputFormat(vertexFormat);
for (const auto& texture : textures) {
batch.setResourceTexture(0, texture);
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
}
batch.setResourceTexture(0, textures[index]);
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
}
#define LOAD_TEXTURE_COUNT 64
void TexturesTest::onChangeTextures() {
static const QDir TEST_DIR("D:/ktx_texture_test");
static std::vector<std::string> ALL_TEXTURE_FILES;
if (ALL_TEXTURE_FILES.empty()) {
auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files);
ALL_TEXTURE_FILES.reserve(entryList.size());
for (auto entry : entryList) {
auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
ALL_TEXTURE_FILES.push_back(textureFile);
}
}
oldTextures.clear();
oldTextures.swap(textures);
#if 0
static const std::string bad = "D:/ktx_texture_test/b4beed38675dbc7a827ecd576399c1f4.ktx";
auto texture = gpu::Texture::unserialize(bad);
auto texelFormat = texture->getTexelFormat();
qDebug() << texture->getTexelFormat().getSemantic();
qDebug() << texture->getTexelFormat().getScalarCount();
textures.push_back(texture);
#else
std::shuffle(ALL_TEXTURE_FILES.begin(), ALL_TEXTURE_FILES.end(), std::default_random_engine());
size_t newTextureCount = std::min<size_t>(ALL_TEXTURE_FILES.size(), LOAD_TEXTURE_COUNT);
for (size_t i = 0; i < newTextureCount; ++i) {
const auto& textureFile = ALL_TEXTURE_FILES[i];
auto texture = gpu::Texture::unserialize(textureFile);
qDebug() << textureFile.c_str();
qDebug() << texture->getTexelFormat().getSemantic();
qDebug() << texture->getTexelFormat().getScalarCount();
textures.push_back(texture);
}
#endif
index = 0;
qDebug() << "Done";
}
void TexturesTest::onNextTexture() {
index += textures.size() + 1;
index %= textures.size();
}
void TexturesTest::onPrevTexture() {
index += textures.size() - 1;
index %= textures.size();
}
void TexturesTest::onMaxTextureMemory(int maxTextureMemory) {
gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(maxTextureMemory));
}

View file

@ -0,0 +1,74 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#pragma once
#include "TestHelpers.h"
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
public: \
type name() { return _##name; }; \
private: \
type _##name{ initialValue };
class TextureTestStats : public QObject {
Q_OBJECT;
STATS_PROPERTY(int, pending, 0)
STATS_PROPERTY(int, total, 0)
STATS_PROPERTY(int, populated, 0)
STATS_PROPERTY(int, allocated, 0)
STATS_PROPERTY(int, index, 0)
STATS_PROPERTY(QString, source, QString())
STATS_PROPERTY(QSize, rez, QSize(0, 0))
public:
void update(int index, const gpu::TexturePointer& texture);
signals:
void pendingChanged();
void totalChanged();
void populatedChanged();
void allocatedChanged();
void changeTextures();
void rezChanged();
void indexChanged();
void sourceChanged();
void maxTextureMemory(int);
void nextTexture();
void prevTexture();
};
class TexturesTest : public GpuTestBase {
Q_OBJECT
gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
std::vector<gpu::TexturePointer> textures;
std::vector<gpu::TexturePointer> oldTextures;
gpu::PipelinePointer pipeline;
TextureTestStats stats;
size_t index{ 0 };
public:
TexturesTest();
QObject* statsObject() override { return &stats; }
QUrl statUrl() override { return QUrl::fromLocalFile(projectRootDir() + "/qml/textureStats.qml"); }
void renderTest(size_t testId, const RenderArgs& args) override;
protected slots:
void onChangeTextures();
void onMaxTextureMemory(int newValue);
void onNextTexture();
void onPrevTexture();
};

View file

@ -0,0 +1,117 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#include "TestWindow.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <QtCore/QTimer>
#include <QtGui/QResizeEvent>
#include <gl/GLHelpers.h>
#include <gpu/gl/GLBackend.h>
TestWindow::TestWindow() {
auto timer = new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);
timer->setInterval(5);
connect(timer, &QTimer::timeout, [&] { draw(); });
timer->start();
connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
timer->stop();
_aboutToQuit = true;
});
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
format.setOption(QSurfaceFormat::DebugContext);
setFormat(format);
_glContext.setFormat(format);
_glContext.create();
_glContext.makeCurrent(this);
show();
}
void TestWindow::initGl() {
_glContext.makeCurrent(this);
gl::initModuleGl();
gpu::Context::init<gpu::gl::GLBackend>();
_renderArgs->_context = std::make_shared<gpu::Context>();
_glContext.makeCurrent(this);
resize(QSize(800, 600));
}
void TestWindow::resizeWindow(const QSize& size) {
_size = size;
_renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
}
void TestWindow::beginFrame() {
_renderArgs->_context->recycle();
_renderArgs->_context->beginFrame();
gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
batch.clearDepthFramebuffer(1e4);
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
});
gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
batch.setViewportTransform(_renderArgs->_viewport);
batch.setStateScissorRect(_renderArgs->_viewport);
batch.setProjectionTransform(_projectionMatrix);
});
}
void TestWindow::endFrame() {
gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) {
batch.resetStages();
});
auto framePointer = _renderArgs->_context->endFrame();
_renderArgs->_context->consumeFrameUpdates(framePointer);
_renderArgs->_context->executeFrame(framePointer);
_glContext.swapBuffers(this);
}
void TestWindow::draw() {
if (_aboutToQuit) {
return;
}
// Attempting to draw before we're visible and have a valid size will
// produce GL errors.
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
return;
}
if (!_glContext.makeCurrent(this)) {
return;
}
static std::once_flag once;
std::call_once(once, [&] { initGl(); });
beginFrame();
renderFrame();
endFrame();
}
void TestWindow::resizeEvent(QResizeEvent* ev) {
resizeWindow(ev->size());
float fov_degrees = 60.0f;
float aspect_ratio = (float)_size.width() / _size.height();
float near_clip = 0.1f;
float far_clip = 1000.0f;
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
}

View file

@ -0,0 +1,41 @@
//
// Created by Bradley Austin Davis on 2016/05/16
// Copyright 2014 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
//
#pragma once
#include <QtGui/QWindow>
#include <QtCore/QTime>
#include <GLMHelpers.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/Forward.h>
#include "TestHelpers.h"
#define DEFERRED_LIGHTING
class TestWindow : public QWindow {
protected:
QOpenGLContextWrapper _glContext;
QSize _size;
glm::mat4 _projectionMatrix;
bool _aboutToQuit { false };
std::shared_ptr<RenderArgs> _renderArgs{ std::make_shared<RenderArgs>() };
TestWindow();
virtual void initGl();
virtual void renderFrame() = 0;
private:
void resizeWindow(const QSize& size);
void beginFrame();
void endFrame();
void draw();
void resizeEvent(QResizeEvent* ev) override;
};

View file

@ -0,0 +1,170 @@
//
// main.cpp
// tests/gpu-test/src
//
// Copyright 2015 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
//
#include <unordered_map>
#include <memory>
#include <cstdio>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QDir>
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
#include <QtCore/QLoggingCategory>
#include <QtGui/QDesktopServices>
#include <QtGui/QResizeEvent>
#include <QtGui/QWindow>
#include <QtGui/QGuiApplication>
#include <QtGui/QImage>
#include <QtGui/QScreen>
#include <QtWidgets/QApplication>
#include <gl/Config.h>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlContext>
#include <gpu/Context.h>
#include <gpu/Batch.h>
#include <gpu/Stream.h>
#include <gpu/gl/GLBackend.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLHelpers.h>
#include <GLMHelpers.h>
#include <PathUtils.h>
#include <NumericalConstants.h>
#include <PerfStat.h>
#include <PathUtils.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <gpu/Pipeline.h>
#include <gpu/Context.h>
#include "TestWindow.h"
#include "TestTextures.h"
using TestBuilder = std::function<GpuTestBase*()>;
using TestBuilders = std::list<TestBuilder>;
#define INTERACTIVE
class MyTestWindow : public TestWindow {
using Parent = TestWindow;
TestBuilders _testBuilders;
GpuTestBase* _currentTest{ nullptr };
size_t _currentTestId{ 0 };
size_t _currentMaxTests{ 0 };
glm::mat4 _camera;
QTime _time;
void initGl() override {
Parent::initGl();
_time.start();
updateCamera();
_testBuilders = TestBuilders({
[] { return new TexturesTest(); },
});
}
void updateCamera() {
float t = _time.elapsed() * 1e-3f;
glm::vec3 unitscale{ 1.0f };
glm::vec3 up{ 0.0f, 1.0f, 0.0f };
float distance = 3.0f;
glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) };
static const vec3 camera_focus(0);
static const vec3 camera_up(0, 1, 0);
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
ViewFrustum frustum;
frustum.setPosition(camera_position);
frustum.setOrientation(glm::quat_cast(_camera));
frustum.setProjection(_projectionMatrix);
}
void renderFrame() override {
updateCamera();
while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) {
if (_currentTest) {
delete _currentTest;
_currentTest = nullptr;
}
_currentTest = _testBuilders.front()();
_testBuilders.pop_front();
if (_currentTest) {
auto statsObject = _currentTest->statsObject();
QUrl url = _currentTest->statUrl();
if (statsObject) {
auto screens = qApp->screens();
auto primaryScreen = qApp->primaryScreen();
auto targetScreen = primaryScreen;
for (const auto& screen : screens) {
if (screen == primaryScreen) {
continue;
}
targetScreen = screen;
break;
}
auto destPoint = targetScreen->availableGeometry().topLeft();
QQuickView* view = new QQuickView();
view->rootContext()->setContextProperty("Stats", statsObject);
view->setSource(url);
view->show();
view->setPosition({ destPoint.x() + 100, destPoint.y() + 100 });
}
_currentMaxTests = _currentTest->getTestCount();
_currentTestId = 0;
}
}
if (!_currentTest && _testBuilders.empty()) {
qApp->quit();
return;
}
// Tests might need to wait for resources to download
if (!_currentTest->isReady()) {
return;
}
gpu::doInBatch("main::renderFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
batch.setViewTransform(_camera);
_renderArgs->_batch = &batch;
_currentTest->renderTest(_currentTestId, *_renderArgs);
_renderArgs->_batch = nullptr;
});
}
};
int main(int argc, char** argv) {
setupHifiApplication("GPU Test");
qputenv("HIFI_DEBUG_OPENGL", QByteArray("1"));
QApplication app(argc, argv);
MyTestWindow window;
app.exec();
return 0;
}

Some files were not shown because too many files have changed in this diff Show more