Merge branch 'master' of github.com:highfidelity/hifi into grab-try-timer-instead-of-update

This commit is contained in:
Seth Alves 2017-04-26 10:23:27 -07:00
commit aeaa95b7a0
406 changed files with 94870 additions and 3268 deletions
BUILD_ANDROID.mdCMakeLists.txt
assignment-client/src
cmake
externals/nvtt
modules
domain-server/src
interface
libraries

View file

@ -5,7 +5,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
You will need the following tools to build our Android targets.
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.5.1
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1

View file

@ -210,6 +210,7 @@ endforeach()
file(GLOB_RECURSE JS_SRC scripts/*.js)
add_custom_target(js SOURCES ${JS_SRC})
GroupSources("scripts")
if (UNIX)
install(

View file

@ -31,6 +31,7 @@
#include <ScriptCache.h>
#include <ScriptEngines.h>
#include <SoundCache.h>
#include <UsersScriptingInterface.h>
#include <UUID.h>
#include <recording/ClipCache.h>
@ -75,6 +76,8 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
DependencyManager::set<RecordingScriptingInterface>();
DependencyManager::set<UsersScriptingInterface>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
@ -351,6 +354,10 @@ void Agent::executeScript() {
// give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
// give scripts access to the Users object
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
auto player = DependencyManager::get<recording::Deck>();
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
if (player->isPlaying()) {

View file

@ -1,91 +0,0 @@
//
// AssignmentAction.cpp
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
// 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 "EntitySimulation.h"
#include "AssignmentAction.h"
AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
EntityActionInterface(type, id),
_data(QByteArray()),
_active(false),
_ownerEntity(ownerEntity) {
}
AssignmentAction::~AssignmentAction() {
}
void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const {
withReadLock([&]{
simulation->removeAction(_id);
simulation->applyActionChanges();
});
}
QByteArray AssignmentAction::serialize() const {
QByteArray result;
withReadLock([&]{
result = _data;
});
return result;
}
void AssignmentAction::deserialize(QByteArray serializedArguments) {
withWriteLock([&]{
_data = serializedArguments;
});
}
bool AssignmentAction::updateArguments(QVariantMap arguments) {
qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client.";
return false;
}
QVariantMap AssignmentAction::getArguments() {
qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client.";
return QVariantMap();
}
glm::vec3 AssignmentAction::getPosition() {
qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setPosition(glm::vec3 position) {
qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client.";
}
glm::quat AssignmentAction::getRotation() {
qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client.";
return glm::quat();
}
void AssignmentAction::setRotation(glm::quat rotation) {
qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client.";
}
glm::vec3 AssignmentAction::getLinearVelocity() {
qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) {
qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client.";
}
glm::vec3 AssignmentAction::getAngularVelocity() {
qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) {
qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client.";
}

View file

@ -1,48 +0,0 @@
//
// AssignmentActionFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// 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 "AssignmentActionFactory.h"
EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
return EntityActionPointer(new AssignmentAction(type, id, ownerEntity));
}
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
if (action) {
bool ok = action->updateArguments(arguments);
if (ok) {
return action;
}
}
return nullptr;
}
EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedActionDataStream(data);
EntityActionType type;
QUuid id;
serializedActionDataStream >> type;
serializedActionDataStream >> id;
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
if (action) {
action->deserialize(data);
}
return action;
}

View file

@ -1,29 +0,0 @@
//
// AssignmentActionFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// 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
//
#ifndef hifi_AssignmentActionFactory_h
#define hifi_AssignmentActionFactory_h
#include "EntityActionFactoryInterface.h"
#include "AssignmentAction.h"
class AssignmentActionFactory : public EntityActionFactoryInterface {
public:
AssignmentActionFactory() : EntityActionFactoryInterface() { }
virtual ~AssignmentActionFactory() { }
virtual EntityActionPointer factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
};
#endif // hifi_AssignmentActionFactory_h

View file

@ -32,7 +32,7 @@
#include <ResourceScriptingInterface.h>
#include "AssignmentFactory.h"
#include "AssignmentActionFactory.h"
#include "AssignmentDynamicFactory.h"
#include "AssignmentClient.h"
#include "AssignmentClientLogging.h"
@ -63,8 +63,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
auto animationCache = DependencyManager::set<AnimationCache>();
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
auto dynamicFactory = DependencyManager::set<AssignmentDynamicFactory>();
DependencyManager::set<ResourceScriptingInterface>();
// setup a thread for the NodeList and its PacketReceiver

View file

@ -0,0 +1,83 @@
//
// AssignmentDynamic.cpp
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
// 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 "EntitySimulation.h"
#include "AssignmentDynamic.h"
AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
EntityDynamicInterface(type, id),
_data(QByteArray()),
_active(false),
_ownerEntity(ownerEntity) {
}
AssignmentDynamic::~AssignmentDynamic() {
}
void AssignmentDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
withReadLock([&]{
simulation->removeDynamic(_id);
simulation->applyDynamicChanges();
});
}
QByteArray AssignmentDynamic::serialize() const {
QByteArray result;
withReadLock([&]{
result = _data;
});
return result;
}
void AssignmentDynamic::deserialize(QByteArray serializedArguments) {
withWriteLock([&]{
_data = serializedArguments;
});
}
bool AssignmentDynamic::updateArguments(QVariantMap arguments) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::updateArguments called in assignment-client.";
return false;
}
QVariantMap AssignmentDynamic::getArguments() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client.";
return QVariantMap();
}
glm::vec3 AssignmentDynamic::getPosition() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client.";
return glm::vec3(0.0f);
}
glm::quat AssignmentDynamic::getRotation() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client.";
return glm::quat();
}
glm::vec3 AssignmentDynamic::getLinearVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client.";
}
glm::vec3 AssignmentDynamic::getAngularVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client.";
}

View file

@ -1,5 +1,5 @@
//
// AssignmentAction.h
// AssignmentDynamic.h
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
@ -8,21 +8,21 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
#ifndef hifi_AssignmentAction_h
#define hifi_AssignmentAction_h
#ifndef hifi_AssignmentDynamic_h
#define hifi_AssignmentDynamic_h
#include <QUuid>
#include <EntityItem.h>
#include "EntityActionInterface.h"
#include "EntityDynamicInterface.h"
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
class AssignmentDynamic : public EntityDynamicInterface, public ReadWriteLockable {
public:
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentAction();
AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentDynamic();
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
@ -38,9 +38,7 @@ private:
protected:
virtual glm::vec3 getPosition() override;
virtual void setPosition(glm::vec3 position) override;
virtual glm::quat getRotation() override;
virtual void setRotation(glm::quat rotation) override;
virtual glm::vec3 getLinearVelocity() override;
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
virtual glm::vec3 getAngularVelocity() override;
@ -50,4 +48,4 @@ protected:
EntityItemWeakPointer _ownerEntity;
};
#endif // hifi_AssignmentAction_h
#endif // hifi_AssignmentDynamic_h

View file

@ -0,0 +1,48 @@
//
// AssignmentDynamcFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// 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 "AssignmentDynamicFactory.h"
EntityDynamicPointer assignmentDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
return EntityDynamicPointer(new AssignmentDynamic(type, id, ownerEntity));
}
EntityDynamicPointer AssignmentDynamicFactory::factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
if (dynamic) {
bool ok = dynamic->updateArguments(arguments);
if (ok) {
return dynamic;
}
}
return nullptr;
}
EntityDynamicPointer AssignmentDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedDynamicDataStream(data);
EntityDynamicType type;
QUuid id;
serializedDynamicDataStream >> type;
serializedDynamicDataStream >> id;
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
if (dynamic) {
dynamic->deserialize(data);
}
return dynamic;
}

View file

@ -0,0 +1,29 @@
//
// AssignmentDynamicFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// 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
//
#ifndef hifi_AssignmentDynamicFactory_h
#define hifi_AssignmentDynamicFactory_h
#include "EntityDynamicFactoryInterface.h"
#include "AssignmentDynamic.h"
class AssignmentDynamicFactory : public EntityDynamicFactoryInterface {
public:
AssignmentDynamicFactory() : EntityDynamicFactoryInterface() { }
virtual ~AssignmentDynamicFactory() { }
virtual EntityDynamicPointer factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
};
#endif // hifi_AssignmentDynamicFactory_h

87
cmake/externals/nvtt/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,87 @@
include(ExternalProject)
include(SelectLibraryConfigurations)
set(EXTERNAL_NAME nvtt)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip
URL_MD5 3ea6eeadbcc69071acf9c49ba565760e
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "Location of NVTT include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Release/x64/nvtt.lib CACHE FILEPATH "Path to NVTT release library")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/Release>/x64" CACHE PATH "Location of NVTT release DLL")
else ()
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip
URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a57
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "Location of NVTT include directory")
if (APPLE)
set(_LIB_EXT "dylib")
else ()
set(_LIB_EXT "so")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libnvtt.${_LIB_EXT} CACHE FILEPATH "Path to NVTT library")
if (APPLE)
# on OS X we have to use install_name_tool to fix the paths found in the NVTT shared libraries
# so that they can be found and linked during the linking phase
set(_NVTT_LIB_DIR "${INSTALL_DIR}/lib")
# first fix the install names of all present libraries
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-install-name
COMMENT "Calling install_name_tool on NVTT libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_NVTT_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>
LOG 1
)
# then, for the main library (libnvtt) fix the paths to the dependency libraries (core, image, math)
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-dependency-paths
COMMENT "Calling install_name_tool on NVTT libraries to fix paths for dependency libraries"
COMMAND install_name_tool -change libnvimage.dylib ${INSTALL_DIR}/lib/libnvimage.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvimage.dylib
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvimage.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvmath.dylib
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>/lib
LOG 1
)
endif ()
endif ()
# Hide this external target (for IDE users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")

View file

@ -0,0 +1,37 @@
#
# FindNVTT.cmake
#
# Try to find NVIDIA texture tools library and include path.
# Once done this will define
#
# NVTT_FOUND
# NVTT_INCLUDE_DIRS
# NVTT_LIBRARIES
# NVTT_DLL_PATH
#
# Created on 4/14/2017 by Stephen Birarda
# 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
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("nvtt")
find_path(NVTT_INCLUDE_DIRS nvtt/nvtt.h PATH_SUFFIXES include HINTS ${NVTT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_library(NVTT_LIBRARY_RELEASE nvtt PATH_SUFFIXES "lib" "Release.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
find_library(NVTT_LIBRARY_DEBUG nvtt PATH_SUFFIXES "lib" "Debug.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(NVTT)
if (WIN32)
find_path(NVTT_DLL_PATH nvtt.dll PATH_SUFFIXES "Release.x64/bin" HINTS ${NVTT_SEARCH_DIRS})
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES NVTT_DLL_PATH)
else ()
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES)
endif ()

View file

@ -435,23 +435,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return SharedNodePointer();
}
QUuid hintNodeID;
// in case this is a node that's failing to connect
// double check we don't have a node whose sockets match exactly already in the list
limitedNodeList->eachNodeBreakable([&nodeConnection, &hintNodeID](const SharedNodePointer& node){
if (node->getPublicSocket() == nodeConnection.publicSockAddr
&& node->getLocalSocket() == nodeConnection.localSockAddr) {
// we have a node that already has these exact sockets - this occurs if a node
// is unable to connect to the domain
hintNodeID = node->getUUID();
return false;
}
return true;
});
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
// set the edit rights for this user
newNode->setPermissions(userPerms);
@ -479,11 +464,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return newNode;
}
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID) {
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
QUuid nodeID;
if (connectedPeer) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
nodeID = nodeConnection.connectUUID;
@ -493,10 +479,8 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
discoveredSocket = *connectedPeer->getActiveSocket();
}
} else {
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
if (nodeID.isNull()) {
nodeID = QUuid::createUuid();
}
// we got a connectUUID we didn't recognize, randomly generate a new one
nodeID = QUuid::createUuid();
}
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();

View file

@ -76,8 +76,7 @@ private:
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
const QString& username,
const QByteArray& usernameSignature);
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID = QUuid());
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr);

View file

@ -65,6 +65,7 @@ set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js)
add_custom_target(qml SOURCES ${QML_SRC})
GroupSources("resources/qml")
if (UNIX)
install(
@ -193,7 +194,7 @@ link_hifi_libraries(
recording fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater
controllers plugins
controllers plugins image
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}
)

View file

@ -211,6 +211,11 @@ Item {
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
", Pending: " + root.downloadsPending;
}
StatText {
visible: root.expanded;
text: "Processing: " + root.processing +
", Pending: " + root.processingPending;
}
StatText {
visible: root.expanded && root.downloadUrls.length > 0;
text: "Download URLs:"

View file

@ -23,6 +23,8 @@ Item {
property bool keyboardRaised: false
property bool punctuationMode: false
property bool isDesktop: false
property bool removingPage: false
property bool loadingPage: false
property int currentPage: -1 // used as a model for repeater
@ -94,10 +96,14 @@ Item {
}
function goBack() {
if (webview.canGoBack) {
pagesModel.remove(currentPage);
if (webview.canGoBack && !isUrlLoaded(webview.url)) {
if (currentPage > 0) {
removingPage = true;
pagesModel.remove(currentPage);
}
webview.goBack();
} else if (currentPage > 0) {
removingPage = true;
pagesModel.remove(currentPage);
}
}
@ -121,6 +127,10 @@ Item {
urlAppend(url)
}
function isUrlLoaded(url) {
return (pagesModel.get(currentPage).webUrl === url);
}
function reloadPage() {
view.reloadAndBypassCache()
view.setActiveFocusOnPress(true);
@ -128,6 +138,10 @@ Item {
}
function urlAppend(url) {
if (removingPage) {
removingPage = false;
return;
}
var lurl = decodeURIComponent(url)
if (lurl[lurl.length - 1] !== "/") {
lurl = lurl + "/"
@ -140,6 +154,7 @@ Item {
onCurrentPageChanged: {
if (currentPage >= 0 && currentPage < pagesModel.count) {
timer.start();
webview.url = pagesModel.get(currentPage).webUrl;
web.url = webview.url;
web.address = webview.url;
@ -158,7 +173,7 @@ Item {
Timer {
id: timer
interval: 100
interval: 200
running: false
repeat: false
onTriggered: timer.stop();
@ -230,10 +245,10 @@ Item {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
urlAppend(loadRequest.url.toString());
loadingPage = true;
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
@ -241,12 +256,27 @@ Item {
}
}
}
if (WebEngineView.LoadFailedStatus == loadRequest.status) {
console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString());
}
}
onNewViewRequested: {
request.openIn(webview);
}
}
HiFiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
Component.onCompleted: {
web.isDesktop = (typeof desktop !== "undefined");

View file

@ -65,6 +65,7 @@ StackView {
var card = tabletWebView.createObject();
card.url = addressBarDialog.metaverseServerUrl + targetString;
card.parentStackItem = root;
card.eventBridge = root.eventBridge;
root.push(card);
return;
}

Binary file not shown.

View file

@ -140,7 +140,7 @@
#include "devices/Leapmotion.h"
#include "DiscoverabilityManager.h"
#include "GLCanvas.h"
#include "InterfaceActionFactory.h"
#include "InterfaceDynamicFactory.h"
#include "InterfaceLogging.h"
#include "LODManager.h"
#include "ModelPackager.h"
@ -462,7 +462,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
// Set dependencies
@ -516,7 +516,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<OffscreenUi>();
DependencyManager::set<AutoUpdater>();
DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<InterfaceDynamicFactory>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>();
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
@ -594,7 +594,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false),
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
_lastFaceTrackerUpdate(0),
_snapshotSound(nullptr)
{
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
@ -1444,8 +1445,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" };
QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" };
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } });
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", false } });
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", true } });
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
@ -1454,6 +1455,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return entityServerNode && !isPhysicsEnabled();
});
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(QUrl::fromLocalFile(inf.absoluteFilePath()));
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
if (testProperty.isValid()) {
@ -1776,12 +1780,17 @@ void Application::cleanupBeforeQuit() {
// stop QML
DependencyManager::destroy<OffscreenUi>();
if (_snapshotSoundInjector != nullptr) {
_snapshotSoundInjector->stop();
}
// stop audio after QML, as there are unexplained audio crashes originating in qtwebengine
// stop the AudioClient, synchronously
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
// destroy Audio so it and its threads have a chance to go down safely
DependencyManager::destroy<AudioClient>();
DependencyManager::destroy<AudioInjectorManager>();
@ -4431,7 +4440,7 @@ void Application::update(float deltaTime) {
_entitySimulation->setObjectsToChange(stillNeedChange);
});
_entitySimulation->applyActionChanges();
_entitySimulation->applyDynamicChanges();
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
@ -4441,8 +4450,8 @@ void Application::update(float deltaTime) {
_physicsEngine->changeObjects(motionStates);
myAvatar->prepareForPhysicsSimulation();
_physicsEngine->forEachAction([&](EntityActionPointer action) {
action->prepareForPhysicsSimulation();
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
dynamic->prepareForPhysicsSimulation();
});
}
{
@ -6406,15 +6415,24 @@ void Application::loadAddAvatarBookmarkDialog() const {
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
QMediaPlayer* player = new QMediaPlayer();
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play();
//keep sound thread out of event loop scope
AudioInjectorOptions options;
options.localOnly = true;
options.stereo = true;
if (_snapshotSoundInjector) {
_snapshotSoundInjector->setOptions(options);
_snapshotSoundInjector->restart();
} else {
QByteArray samples = _snapshotSound->getByteArray();
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
}
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
// Get a screenshot and save it
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
// If we're not doing an animated snapshot as well...
if (!includeAnimated || !(SnapshotAnimated::alsoTakeAnimatedSnapshot.get())) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
@ -6425,6 +6443,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
}
});
}
void Application::shareSnapshot(const QString& path, const QUrl& href) {
postLambdaEvent([path, href] {
// not much to do here, everything is done in snapshot code...
@ -6740,11 +6759,6 @@ void Application::updateDisplayMode() {
return;
}
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
});
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Make the switch atomic from the perspective of other threads
@ -6799,13 +6813,16 @@ void Application::updateDisplayMode() {
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
}
bool isHmd = _displayPlugin->isHmd();
qCDebug(interfaceapp) << "Entering into" << (isHmd ? "HMD" : "Desktop") << "Mode";
// Only log/emit after a successful change
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" },
{ "hmd", isHmd }
});
emit activeDisplayPluginChanged();
if (_displayPlugin->isHmd()) {
qCDebug(interfaceapp) << "Entering into HMD Mode";
} else {
qCDebug(interfaceapp) << "Entering into Desktop Mode";
}
// reset the avatar, to set head and hand palms back to a reasonable default pose.
getMyAvatar()->reset(false);

View file

@ -73,6 +73,7 @@
#include <model/Skybox.h>
#include <ModelScriptingInterface.h>
#include "Sound.h"
class OffscreenGLCanvas;
class GLCanvas;
@ -80,6 +81,7 @@ class FaceTracker;
class MainWindow;
class AssetUpload;
class CompositorHelper;
class AudioInjector;
namespace controller {
class StateController;
@ -683,6 +685,8 @@ private:
QTimer _addAssetToWorldErrorTimer;
FileScriptingInterface* _fileDownload;
AudioInjector* _snapshotSoundInjector { nullptr };
SharedSoundPointer _snapshotSound;
};

View file

@ -1,82 +0,0 @@
//
// InterfaceActionFactory.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
// 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 <avatar/AvatarActionHold.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTravelOriented.h>
#include <LogHandler.h>
#include "InterfaceActionFactory.h"
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case ACTION_TYPE_NONE:
return EntityActionPointer();
case ACTION_TYPE_OFFSET:
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case ACTION_TYPE_SPRING:
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case ACTION_TYPE_HOLD:
return std::make_shared<AvatarActionHold>(id, ownerEntity);
case ACTION_TYPE_TRAVEL_ORIENTED:
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
return EntityActionPointer();
}
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
if (action) {
bool ok = action->updateArguments(arguments);
if (ok) {
if (action->lifetimeIsOver()) {
return nullptr;
}
return action;
}
}
return nullptr;
}
EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedArgumentStream(data);
EntityActionType type;
QUuid id;
serializedArgumentStream >> type;
serializedArgumentStream >> id;
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
if (action) {
action->deserialize(data);
if (action->lifetimeIsOver()) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during action creation.*");
qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --"
<< action->getExpires() << "<" << usecTimestampNow();
return nullptr;
}
}
return action;
}

View file

@ -0,0 +1,88 @@
//
// InterfaceDynamicFactory.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
// 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 <avatar/AvatarActionHold.h>
#include <avatar/AvatarActionFarGrab.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTravelOriented.h>
#include <ObjectConstraintHinge.h>
#include <LogHandler.h>
#include "InterfaceDynamicFactory.h"
EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case DYNAMIC_TYPE_NONE:
return EntityDynamicPointer();
case DYNAMIC_TYPE_OFFSET:
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case DYNAMIC_TYPE_SPRING:
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case DYNAMIC_TYPE_HOLD:
return std::make_shared<AvatarActionHold>(id, ownerEntity);
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
case DYNAMIC_TYPE_HINGE:
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
case DYNAMIC_TYPE_FAR_GRAB:
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type");
return EntityDynamicPointer();
}
EntityDynamicPointer InterfaceDynamicFactory::factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
if (dynamic) {
bool ok = dynamic->updateArguments(arguments);
if (ok) {
if (dynamic->lifetimeIsOver()) {
return nullptr;
}
return dynamic;
}
}
return nullptr;
}
EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedArgumentStream(data);
EntityDynamicType type;
QUuid id;
serializedArgumentStream >> type;
serializedArgumentStream >> id;
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
if (dynamic) {
dynamic->deserialize(data);
if (dynamic->lifetimeIsOver()) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*");
qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --"
<< dynamic->getExpires() << "<" << usecTimestampNow();
return nullptr;
}
}
return dynamic;
}

View file

@ -1,5 +1,5 @@
//
// InterfaceActionFactory.cpp
// InterfaceDynamicFactory.cpp
// interface/src/
//
// Created by Seth Alves on 2015-6-10
@ -9,21 +9,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_InterfaceActionFactory_h
#define hifi_InterfaceActionFactory_h
#ifndef hifi_InterfaceDynamicFactory_h
#define hifi_InterfaceDynamicFactory_h
#include "EntityActionFactoryInterface.h"
#include "EntityDynamicFactoryInterface.h"
class InterfaceActionFactory : public EntityActionFactoryInterface {
class InterfaceDynamicFactory : public EntityDynamicFactoryInterface {
public:
InterfaceActionFactory() : EntityActionFactoryInterface() { }
virtual ~InterfaceActionFactory() { }
virtual EntityActionPointer factory(EntityActionType type,
InterfaceDynamicFactory() : EntityDynamicFactoryInterface() { }
virtual ~InterfaceDynamicFactory() { }
virtual EntityDynamicPointer factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
QByteArray data) override;
};
#endif // hifi_InterfaceActionFactory_h
#endif // hifi_InterfaceDynamicFactory_h

View file

@ -0,0 +1,64 @@
//
// AvatarActionFarGrab.cpp
// interface/src/avatar/
//
// Created by Seth Alves 2017-4-14
// 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
//
#include "AvatarActionFarGrab.h"
AvatarActionFarGrab::AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity) {
_type = DYNAMIC_TYPE_FAR_GRAB;
#if WANT_DEBUG
qDebug() << "AvatarActionFarGrab::AvatarActionFarGrab";
#endif
}
AvatarActionFarGrab::~AvatarActionFarGrab() {
#if WANT_DEBUG
qDebug() << "AvatarActionFarGrab::~AvatarActionFarGrab";
#endif
}
QByteArray AvatarActionFarGrab::serialize() const {
QByteArray serializedActionArguments;
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
dataStream << DYNAMIC_TYPE_FAR_GRAB;
dataStream << getID();
dataStream << ObjectActionSpring::springVersion;
serializeParameters(dataStream);
return serializedActionArguments;
}
void AvatarActionFarGrab::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityDynamicType type;
dataStream >> type;
QUuid id;
dataStream >> id;
if (type != getType() || id != getID()) {
qDebug() << "AvatarActionFarGrab::deserialize type or ID don't match." << type << id << getID();
return;
}
uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionSpring::springVersion) {
assert(false);
return;
}
deserializeParameters(serializedArguments, dataStream);
}

View file

@ -0,0 +1,27 @@
//
// AvatarActionFarGrab.h
// interface/src/avatar/
//
// Created by Seth Alves 2017-4-14
// 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
//
#ifndef hifi_AvatarActionFarGrab_h
#define hifi_AvatarActionFarGrab_h
#include <EntityItem.h>
#include <ObjectActionSpring.h>
class AvatarActionFarGrab : public ObjectActionSpring {
public:
AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AvatarActionFarGrab();
QByteArray serialize() const override;
virtual void deserialize(QByteArray serializedArguments) override;
};
#endif // hifi_AvatarActionFarGrab_h

View file

@ -23,7 +23,7 @@ const int AvatarActionHold::velocitySmoothFrames = 6;
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity)
{
_type = ACTION_TYPE_HOLD;
_type = DYNAMIC_TYPE_HOLD;
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
@ -323,28 +323,28 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
bool ignoreIK;
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
withReadLock([&]{
bool ok = true;
relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
relativePosition = EntityDynamicInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
if (!ok) {
relativePosition = _relativePosition;
}
ok = true;
relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
relativeRotation = EntityDynamicInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
if (!ok) {
relativeRotation = _relativeRotation;
}
ok = true;
timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
timeScale = EntityDynamicInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
if (!ok) {
timeScale = _linearTimeScale;
}
ok = true;
hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
hand = EntityDynamicInterface::extractStringArgument("hold", arguments, "hand", ok, false);
if (!ok || !(hand == "left" || hand == "right")) {
hand = _hand;
}
@ -353,20 +353,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
holderID = myAvatar->getSessionUUID();
ok = true;
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
if (!ok) {
kinematic = _kinematic;
}
ok = true;
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
kinematicSetVelocity = EntityDynamicInterface::extractBooleanArgument("hold", arguments,
"kinematicSetVelocity", ok, false);
if (!ok) {
kinematicSetVelocity = _kinematicSetVelocity;
}
ok = true;
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
ignoreIK = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
if (!ok) {
ignoreIK = _ignoreIK;
}
@ -400,8 +400,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
ownerEntity->setActionDataNeedsTransmit(true);
ownerEntity->setDynamicDataDirty(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
}
});
}
@ -410,7 +410,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
}
QVariantMap AvatarActionHold::getArguments() {
QVariantMap arguments = ObjectAction::getArguments();
QVariantMap arguments = ObjectDynamic::getArguments();
withReadLock([&]{
arguments["holderID"] = _holderID;
arguments["relativePosition"] = glmToQMap(_relativePosition);
@ -429,7 +429,7 @@ QByteArray AvatarActionHold::serialize() const {
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
withReadLock([&]{
dataStream << ACTION_TYPE_HOLD;
dataStream << DYNAMIC_TYPE_HOLD;
dataStream << getID();
dataStream << AvatarActionHold::holdVersion;
@ -451,7 +451,7 @@ QByteArray AvatarActionHold::serialize() const {
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityActionType type;
EntityDynamicType type;
dataStream >> type;
assert(type == getType());

View file

@ -242,7 +242,7 @@ void SkeletonModel::updateAttitude() {
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude();
if (fullUpdate) {
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
Model::simulate(deltaTime, fullUpdate);

View file

@ -94,7 +94,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
PROFILE_RANGE(app, __FUNCTION__);
if (!_uiTexture) {
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
_uiTexture->setSource(__FUNCTION__);
}
// Once we move UI rendering and screen rendering to different
@ -207,13 +207,13 @@ void ApplicationOverlay::buildFramebufferObject() {
auto width = uiSize.x;
auto height = uiSize.y;
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER));
auto overlayDepthTexture = gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER);
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
}
if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER));
auto colorBuffer = gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER);
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
}
}

View file

@ -31,6 +31,7 @@
#include "Menu.h"
#include "Util.h"
#include "SequenceNumberStats.h"
#include "StatTracker.h"
HIFI_QML_DEF(Stats)
@ -250,6 +251,9 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(downloads, loadingRequests.size());
STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit())
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
STAT_UPDATE(processing, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
STAT_UPDATE(processingPending, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
// See if the active download urls have changed
bool shouldUpdateUrls = _downloads != _downloadUrls.size();

View file

@ -89,6 +89,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, downloadLimit, 0)
STATS_PROPERTY(int, downloadsPending, 0)
Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged)
STATS_PROPERTY(int, processing, 0)
STATS_PROPERTY(int, processingPending, 0)
STATS_PROPERTY(int, triangles, 0)
STATS_PROPERTY(int, quads, 0)
STATS_PROPERTY(int, materialSwitches, 0)
@ -214,6 +216,8 @@ signals:
void downloadLimitChanged();
void downloadsPendingChanged();
void downloadUrlsChanged();
void processingChanged();
void processingPendingChanged();
void trianglesChanged();
void quadsChanged();
void materialSwitchesChanged();

View file

@ -298,7 +298,7 @@ void Web3DOverlay::render(RenderArgs* args) {
if (!_texture) {
auto webSurface = _webSurface;
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
_texture->setSource(__FUNCTION__);
}
OffscreenQmlSurface::TextureAndFence newTextureAndFence;

View file

@ -1006,7 +1006,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
}
emit inputReceived({ audioBuffer.data(), numSamples });
emit inputReceived(audioBuffer);
if (_noiseGate.openedInLastBlock()) {
emit noiseGateOpened();

View file

@ -967,6 +967,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
const int coefficientsSize = sizeof(float) * numCoefficients;
PACKET_READ_CHECK(FaceTrackerCoefficients, coefficientsSize);
_headData->_blendshapeCoefficients.resize(numCoefficients); // make sure there's room for the copy!
_headData->_baseBlendshapeCoefficients.resize(numCoefficients);
memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, coefficientsSize);
sourceBuffer += coefficientsSize;
int numBytesRead = sourceBuffer - startSection;

View file

@ -15,6 +15,7 @@
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QVector>
#include <FaceshiftConstants.h>
#include <GLMHelpers.h>
@ -38,6 +39,8 @@ HeadData::HeadData(AvatarData* owningAvatar) :
_rightEyeBlink(0.0f),
_averageLoudness(0.0f),
_browAudioLift(0.0f),
_baseBlendshapeCoefficients(QVector<float>(0, 0.0f)),
_currBlendShapeCoefficients(QVector<float>(0, 0.0f)),
_owningAvatar(owningAvatar)
{
@ -86,6 +89,24 @@ static const QMap<QString, int>& getBlendshapesLookupMap() {
return blendshapeLookupMap;
}
const QVector<float>& HeadData::getSummedBlendshapeCoefficients() {
int maxSize = std::max(_baseBlendshapeCoefficients.size(), _blendshapeCoefficients.size());
if (_currBlendShapeCoefficients.size() != maxSize) {
_currBlendShapeCoefficients.resize(maxSize);
}
for (int i = 0; i < maxSize; i++) {
if (i >= _baseBlendshapeCoefficients.size()) {
_currBlendShapeCoefficients[i] = _blendshapeCoefficients[i];
} else if (i >= _blendshapeCoefficients.size()) {
_currBlendShapeCoefficients[i] = _baseBlendshapeCoefficients[i];
} else {
_currBlendShapeCoefficients[i] = _baseBlendshapeCoefficients[i] + _blendshapeCoefficients[i];
}
}
return _currBlendShapeCoefficients;
}
void HeadData::setBlendshape(QString name, float val) {
const auto& blendshapeLookupMap = getBlendshapesLookupMap();
@ -96,7 +117,10 @@ void HeadData::setBlendshape(QString name, float val) {
if (_blendshapeCoefficients.size() <= it.value()) {
_blendshapeCoefficients.resize(it.value() + 1);
}
_blendshapeCoefficients[it.value()] = val;
if (_baseBlendshapeCoefficients.size() <= it.value()) {
_baseBlendshapeCoefficients.resize(it.value() + 1);
}
_baseBlendshapeCoefficients[it.value()] = val;
}
}

View file

@ -59,6 +59,7 @@ public:
void setBlendshape(QString name, float val);
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
const QVector<float>& getSummedBlendshapeCoefficients();
void setBlendshapeCoefficients(const QVector<float>& blendshapeCoefficients) { _blendshapeCoefficients = blendshapeCoefficients; }
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
@ -92,6 +93,8 @@ protected:
float _browAudioLift;
QVector<float> _blendshapeCoefficients;
QVector<float> _baseBlendshapeCoefficients;
QVector<float> _currBlendShapeCoefficients;
AvatarData* _owningAvatar;
private:

View file

@ -354,12 +354,11 @@ void OpenGLDisplayPlugin::customizeContext() {
}
if ((image.width() > 0) && (image.height() > 0)) {
cursorData.texture.reset(
gpu::Texture::createStrict(
cursorData.texture = gpu::Texture::createStrict(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Texture::MAX_NUM_MIPS,
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
cursorData.texture->setSource("cursor texture");
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
cursorData.texture->setUsage(usage.build());

View file

@ -295,12 +295,11 @@ void HmdDisplayPlugin::internalPresent() {
image = image.mirrored();
image = image.convertToFormat(QImage::Format_RGBA8888);
if (!_previewTexture) {
_previewTexture.reset(
gpu::Texture::createStrict(
_previewTexture = gpu::Texture::createStrict(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Texture::MAX_NUM_MIPS,
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
_previewTexture->setSource("HMD Preview Texture");
_previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build());
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
@ -592,7 +591,7 @@ void HmdDisplayPlugin::OverlayRenderer::updatePipeline() {
auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString());
auto program = gpu::Shader::createProgram(vs, ps);
gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet());
this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer");
this->uniformsLocation = program->getUniformBuffers().findLocation("overlayBuffer");
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));

View file

@ -1,7 +1,7 @@
set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image)
target_bullet()

View file

@ -495,7 +495,7 @@ bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
bool isAmbientSet = false;
if (_pendingAmbientTexture && !_ambientTexture) {
_ambientTexture = textureCache->getTexture(_ambientTextureURL, NetworkTexture::CUBE_TEXTURE);
_ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
}
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
@ -512,7 +512,7 @@ bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
if (_pendingSkyboxTexture &&
(!_skyboxTexture || (_skyboxTexture->getURL() != _skyboxTextureURL))) {
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, NetworkTexture::CUBE_TEXTURE);
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
}
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;

View file

@ -216,7 +216,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
if (!_texture) {
auto webSurface = _webSurface;
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
_texture->setSource(__FUNCTION__);
}
OffscreenQmlSurface::TextureAndFence newTextureAndFence;

View file

@ -1,332 +0,0 @@
//
// EntityActionInterface.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-4
// 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
//
/*
+-----------------------+ +-------------------+ +------------------------------+
| | | | | |
| EntityActionInterface | | btActionInterface | | EntityActionFactoryInterface |
| (entities) | | (bullet) | | (entities) |
+-----------------------+ +-------------------+ +------------------------------+
| | | | |
+----+ +--+ +----------+ | |
| | | | |
+-------------------+ +--------------+ +------------------------+ +-------------------------+
| | | | | | | |
| AssignmentAction | | ObjectAction | | InterfaceActionFactory | | AssignmentActionFactory |
|(assignment client)| | (physics) | | (interface) | | (assignment client) |
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
|
|
+--------------------+
| |
| ObjectActionSpring |
| (physics) |
+--------------------+
An action is a callback which is registered with bullet. An action is called-back every physics
simulation step and can do whatever it wants with the various datastructures it has available. An
action, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
point by a spring.
In this system, an action is a property of an EntityItem (rather, an EntityItem has a property which
encodes a list of actions). Each action has a type and some arguments. Actions can be created by a
script or when receiving information via an EntityTree data-stream (either over the network or from an
svo file).
In the interface, if an EntityItem has actions, this EntityItem will have pointers to ObjectAction
subclass (like ObjectActionSpring) instantiations. Code in the entities library affects an action-object
via the EntityActionInterface (which knows nothing about bullet). When the ObjectAction subclass
instance is created, it is registered as an action with bullet. Bullet will call into code in this
instance with the btActionInterface every physics-simulation step.
Because the action can exist next to the interface's EntityTree or the entity-server's EntityTree,
parallel versions of the factories and actions are needed.
In an entity-server, any type of action is instantiated as an AssignmentAction. This action isn't called
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
right now the AssignmentAction class is a place-holder.
The action-objects are instantiated by singleton (dependecy) subclasses of EntityActionFactoryInterface.
In the interface, the subclass is an InterfaceActionFactory and it will produce things like
ObjectActionSpring. In an entity-server the subclass is an AssignmentActionFactory and it always
produces AssignmentActions.
Depending on the action's type, it will have various arguments. When a script changes an argument of an
action, the argument-holding member-variables of ObjectActionSpring (in this example) are updated and
also serialized into _actionData in the EntityItem. Each subclass of ObjectAction knows how to serialize
and deserialize its own arguments. _actionData is what gets sent over the wire or saved in an svo file.
When a packet-reader receives data for _actionData, it will save it in the EntityItem; this causes the
deserializer in the ObjectAction subclass to be called with the new data, thereby updating its argument
variables. These argument variables are used by the code which is run when bullet does a callback.
*/
#include "EntityItem.h"
#include "EntityActionInterface.h"
EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) {
QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_');
if (normalizedActionTypeString == "none") {
return ACTION_TYPE_NONE;
}
if (normalizedActionTypeString == "offset") {
return ACTION_TYPE_OFFSET;
}
if (normalizedActionTypeString == "spring") {
return ACTION_TYPE_SPRING;
}
if (normalizedActionTypeString == "hold") {
return ACTION_TYPE_HOLD;
}
if (normalizedActionTypeString == "traveloriented") {
return ACTION_TYPE_TRAVEL_ORIENTED;
}
qCDebug(entities) << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
return ACTION_TYPE_NONE;
}
QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
switch(actionType) {
case ACTION_TYPE_NONE:
return "none";
case ACTION_TYPE_OFFSET:
return "offset";
case ACTION_TYPE_SPRING:
return "spring";
case ACTION_TYPE_HOLD:
return "hold";
case ACTION_TYPE_TRAVEL_ORIENTED:
return "travel-oriented";
}
assert(false);
return "none";
}
glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return glm::vec3(0.0f);
}
QVariant resultV = arguments[argumentName];
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
ok = false;
return glm::vec3(0.0f);
}
QVariantMap resultVM = resultV.toMap();
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
ok = false;
return glm::vec3(0.0f);
}
QVariant xV = resultVM["x"];
QVariant yV = resultVM["y"];
QVariant zV = resultVM["z"];
bool xOk = true;
bool yOk = true;
bool zOk = true;
float x = xV.toFloat(&xOk);
float y = yV.toFloat(&yOk);
float z = zV.toFloat(&zOk);
if (!xOk || !yOk || !zOk) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
ok = false;
return glm::vec3(0.0f);
}
if (x != x || y != y || z != z) {
// at least one of the values is NaN
ok = false;
return glm::vec3(0.0f);
}
return glm::vec3(x, y, z);
}
glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return glm::quat();
}
QVariant resultV = arguments[argumentName];
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
ok = false;
return glm::quat();
}
QVariantMap resultVM = resultV.toMap();
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
ok = false;
return glm::quat();
}
QVariant xV = resultVM["x"];
QVariant yV = resultVM["y"];
QVariant zV = resultVM["z"];
QVariant wV = resultVM["w"];
bool xOk = true;
bool yOk = true;
bool zOk = true;
bool wOk = true;
float x = xV.toFloat(&xOk);
float y = yV.toFloat(&yOk);
float z = zV.toFloat(&zOk);
float w = wV.toFloat(&wOk);
if (!xOk || !yOk || !zOk || !wOk) {
qCDebug(entities) << objectName << "argument" << argumentName
<< "must be a map with keys: x, y, z, and w of type float.";
ok = false;
return glm::quat();
}
if (x != x || y != y || z != z || w != w) {
// at least one of the components is NaN!
ok = false;
return glm::quat();
}
return glm::normalize(glm::quat(w, x, y, z));
}
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return 0.0f;
}
QVariant variant = arguments[argumentName];
bool variantOk = true;
float value = variant.toFloat(&variantOk);
if (!variantOk || std::isnan(value)) {
ok = false;
return 0.0f;
}
return value;
}
int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return 0.0f;
}
QVariant variant = arguments[argumentName];
bool variantOk = true;
int value = variant.toInt(&variantOk);
if (!variantOk) {
ok = false;
return 0;
}
return value;
}
QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return "";
}
return arguments[argumentName].toString();
}
bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return false;
}
return arguments[argumentName].toBool();
}
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType)
{
return stream << (quint16)entityActionType;
}
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
{
quint16 actionTypeAsInt;
stream >> actionTypeAsInt;
entityActionType = (EntityActionType)actionTypeAsInt;
return stream;
}
QString serializedActionsToDebugString(QByteArray data) {
if (data.size() == 0) {
return QString();
}
QVector<QByteArray> serializedActions;
QDataStream serializedActionsStream(data);
serializedActionsStream >> serializedActions;
QString result;
foreach(QByteArray serializedAction, serializedActions) {
QDataStream serializedActionStream(serializedAction);
EntityActionType actionType;
QUuid actionID;
serializedActionStream >> actionType;
serializedActionStream >> actionID;
result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " ";
}
return result;
}

View file

@ -1,5 +1,5 @@
//
// EntityActionFactoryInterface.cpp
// EntityDynamicFactoryInterface.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
@ -9,26 +9,26 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_EntityActionFactoryInterface_h
#define hifi_EntityActionFactoryInterface_h
#ifndef hifi_EntityDynamicFactoryInterface_h
#define hifi_EntityDynamicFactoryInterface_h
#include <DependencyManager.h>
#include "EntityActionInterface.h"
#include "EntityDynamicInterface.h"
class EntityActionFactoryInterface : public QObject, public Dependency {
class EntityDynamicFactoryInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
EntityActionFactoryInterface() { }
virtual ~EntityActionFactoryInterface() { }
virtual EntityActionPointer factory(EntityActionType type,
EntityDynamicFactoryInterface() { }
virtual ~EntityDynamicFactoryInterface() { }
virtual EntityDynamicPointer factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) { assert(false); return nullptr; }
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
QByteArray data) { assert(false); return nullptr; }
};
#endif // hifi_EntityActionFactoryInterface_h
#endif // hifi_EntityDynamicFactoryInterface_h

View file

@ -0,0 +1,347 @@
//
// EntityDynamicInterface.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-4
// 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
//
/*
+-------------------------+ +--------------------------------+
| | | |
| EntityDynamicsInterface | | EntityDynamicsFactoryInterface |
| (entities) | | (entities) |
+-------------------------+ +--------------------------------+
| | | |
+----+ +--+ | |
| | | |
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
| | | | | | | |
| AssignmentDynamics | | ObjectDynamics | | InterfaceDynamicsFactory | | AssignmentDynamicsFactory |
|(assignment client) | | (physics) | | (interface) | | (assignment client) |
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
| |
| |
+---------------------+ | |
| | | |
| btActionInterface | | |
| (bullet) | | |
+---------------------+ | |
| | |
+--------------+ +------------------+ +-------------------+
| | | | | |
| ObjectAction | | ObjectConstraint | | btTypedConstraint |
| (physics) | | (physics) --|--->| (bullet) |
+--------------+ +------------------+ +-------------------+
| |
| |
+--------------------+ +-----------------------+
| | | |
| ObjectActionSpring | | ObjectConstraintHinge |
| (physics) | | (physics) |
+--------------------+ +-----------------------+
An dynamic is a callback which is registered with bullet. An dynamic is called-back every physics
simulation step and can do whatever it wants with the various datastructures it has available. An
dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
point by a spring.
In this system, an dynamic is a property of an EntityItem (rather, an EntityItem has a property which
encodes a list of dynamics). Each dynamic has a type and some arguments. Dynamics can be created by a
script or when receiving information via an EntityTree data-stream (either over the network or from an
svo file).
In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic
subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects an dynamic-object
via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass
instance is created, it is registered as an dynamic with bullet. Bullet will call into code in this
instance with the btDynamicInterface every physics-simulation step.
Because the dynamic can exist next to the interface's EntityTree or the entity-server's EntityTree,
parallel versions of the factories and dynamics are needed.
In an entity-server, any type of dynamic is instantiated as an AssignmentDynamic. This dynamic isn't called
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
right now the AssignmentDynamic class is a place-holder.
The dynamic-objects are instantiated by singleton (dependecy) subclasses of EntityDynamicFactoryInterface.
In the interface, the subclass is an InterfaceDynamicFactory and it will produce things like
ObjectDynamicSpring. In an entity-server the subclass is an AssignmentDynamicFactory and it always
produces AssignmentDynamics.
Depending on the dynamic's type, it will have various arguments. When a script changes an argument of an
dynamic, the argument-holding member-variables of ObjectDynamicSpring (in this example) are updated and
also serialized into _dynamicData in the EntityItem. Each subclass of ObjectDynamic knows how to serialize
and deserialize its own arguments. _dynamicData is what gets sent over the wire or saved in an svo file.
When a packet-reader receives data for _dynamicData, it will save it in the EntityItem; this causes the
deserializer in the ObjectDynamic subclass to be called with the new data, thereby updating its argument
variables. These argument variables are used by the code which is run when bullet does a callback.
*/
#include "EntityItem.h"
#include "EntityDynamicInterface.h"
EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicTypeString) {
QString normalizedDynamicTypeString = dynamicTypeString.toLower().remove('-').remove('_');
if (normalizedDynamicTypeString == "none") {
return DYNAMIC_TYPE_NONE;
}
if (normalizedDynamicTypeString == "offset") {
return DYNAMIC_TYPE_OFFSET;
}
if (normalizedDynamicTypeString == "spring") {
return DYNAMIC_TYPE_SPRING;
}
if (normalizedDynamicTypeString == "hold") {
return DYNAMIC_TYPE_HOLD;
}
if (normalizedDynamicTypeString == "traveloriented") {
return DYNAMIC_TYPE_TRAVEL_ORIENTED;
}
if (normalizedDynamicTypeString == "hinge") {
return DYNAMIC_TYPE_HINGE;
}
if (normalizedDynamicTypeString == "fargrab") {
return DYNAMIC_TYPE_FAR_GRAB;
}
qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name"
<< dynamicTypeString;
return DYNAMIC_TYPE_NONE;
}
QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicType) {
switch(dynamicType) {
case DYNAMIC_TYPE_NONE:
return "none";
case DYNAMIC_TYPE_OFFSET:
return "offset";
case DYNAMIC_TYPE_SPRING:
return "spring";
case DYNAMIC_TYPE_HOLD:
return "hold";
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
return "travel-oriented";
case DYNAMIC_TYPE_HINGE:
return "hinge";
case DYNAMIC_TYPE_FAR_GRAB:
return "far-grab";
}
assert(false);
return "none";
}
glm::vec3 EntityDynamicInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return glm::vec3(0.0f);
}
QVariant resultV = arguments[argumentName];
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
ok = false;
return glm::vec3(0.0f);
}
QVariantMap resultVM = resultV.toMap();
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
ok = false;
return glm::vec3(0.0f);
}
QVariant xV = resultVM["x"];
QVariant yV = resultVM["y"];
QVariant zV = resultVM["z"];
bool xOk = true;
bool yOk = true;
bool zOk = true;
float x = xV.toFloat(&xOk);
float y = yV.toFloat(&yOk);
float z = zV.toFloat(&zOk);
if (!xOk || !yOk || !zOk) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
ok = false;
return glm::vec3(0.0f);
}
if (x != x || y != y || z != z) {
// at least one of the values is NaN
ok = false;
return glm::vec3(0.0f);
}
return glm::vec3(x, y, z);
}
glm::quat EntityDynamicInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return glm::quat();
}
QVariant resultV = arguments[argumentName];
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
ok = false;
return glm::quat();
}
QVariantMap resultVM = resultV.toMap();
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
ok = false;
return glm::quat();
}
QVariant xV = resultVM["x"];
QVariant yV = resultVM["y"];
QVariant zV = resultVM["z"];
QVariant wV = resultVM["w"];
bool xOk = true;
bool yOk = true;
bool zOk = true;
bool wOk = true;
float x = xV.toFloat(&xOk);
float y = yV.toFloat(&yOk);
float z = zV.toFloat(&zOk);
float w = wV.toFloat(&wOk);
if (!xOk || !yOk || !zOk || !wOk) {
qCDebug(entities) << objectName << "argument" << argumentName
<< "must be a map with keys: x, y, z, and w of type float.";
ok = false;
return glm::quat();
}
if (x != x || y != y || z != z || w != w) {
// at least one of the components is NaN!
ok = false;
return glm::quat();
}
return glm::normalize(glm::quat(w, x, y, z));
}
float EntityDynamicInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return 0.0f;
}
QVariant variant = arguments[argumentName];
bool variantOk = true;
float value = variant.toFloat(&variantOk);
if (!variantOk || std::isnan(value)) {
ok = false;
return 0.0f;
}
return value;
}
int EntityDynamicInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return 0.0f;
}
QVariant variant = arguments[argumentName];
bool variantOk = true;
int value = variant.toInt(&variantOk);
if (!variantOk) {
ok = false;
return 0;
}
return value;
}
QString EntityDynamicInterface::extractStringArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return "";
}
return arguments[argumentName].toString();
}
bool EntityDynamicInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required) {
if (!arguments.contains(argumentName)) {
if (required) {
qCDebug(entities) << objectName << "requires argument:" << argumentName;
}
ok = false;
return false;
}
return arguments[argumentName].toBool();
}
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType) {
return stream << (quint16)entityDynamicType;
}
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType) {
quint16 dynamicTypeAsInt;
stream >> dynamicTypeAsInt;
entityDynamicType = (EntityDynamicType)dynamicTypeAsInt;
return stream;
}
QString serializedDynamicsToDebugString(QByteArray data) {
if (data.size() == 0) {
return QString();
}
QVector<QByteArray> serializedDynamics;
QDataStream serializedDynamicsStream(data);
serializedDynamicsStream >> serializedDynamics;
QString result;
foreach(QByteArray serializedDynamic, serializedDynamics) {
QDataStream serializedDynamicStream(serializedDynamic);
EntityDynamicType dynamicType;
QUuid dynamicID;
serializedDynamicStream >> dynamicType;
serializedDynamicStream >> dynamicID;
result += EntityDynamicInterface::dynamicTypeToString(dynamicType) + "-" + dynamicID.toString() + " ";
}
return result;
}

View file

@ -1,5 +1,5 @@
//
// EntityActionInterface.h
// EntityDynamicInterface.h
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
@ -9,8 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_EntityActionInterface_h
#define hifi_EntityActionInterface_h
#ifndef hifi_EntityDynamicInterface_h
#define hifi_EntityDynamicInterface_h
#include <memory>
#include <QUuid>
@ -23,22 +23,27 @@ using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
class EntitySimulation;
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
enum EntityActionType {
// keep these synchronized with actionTypeFromString and actionTypeToString
ACTION_TYPE_NONE = 0,
ACTION_TYPE_OFFSET = 1000,
ACTION_TYPE_SPRING = 2000,
ACTION_TYPE_HOLD = 3000,
ACTION_TYPE_TRAVEL_ORIENTED = 4000
enum EntityDynamicType {
// keep these synchronized with dynamicTypeFromString and dynamicTypeToString
DYNAMIC_TYPE_NONE = 0,
DYNAMIC_TYPE_OFFSET = 1000,
DYNAMIC_TYPE_SPRING = 2000,
DYNAMIC_TYPE_HOLD = 3000,
DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000,
DYNAMIC_TYPE_HINGE = 5000,
DYNAMIC_TYPE_FAR_GRAB = 6000
};
class EntityActionInterface {
class EntityDynamicInterface {
public:
EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { }
virtual ~EntityActionInterface() { }
EntityDynamicInterface(EntityDynamicType type, const QUuid& id) : _id(id), _type(type) { }
virtual ~EntityDynamicInterface() { }
const QUuid& getID() const { return _id; }
EntityActionType getType() const { return _type; }
EntityDynamicType getType() const { return _type; }
virtual bool isAction() const { return false; }
virtual bool isConstraint() const { return false; }
virtual bool isReadyForAdd() const { return true; }
bool isActive() { return _active; }
@ -51,8 +56,8 @@ public:
virtual QByteArray serialize() const = 0;
virtual void deserialize(QByteArray serializedArguments) = 0;
static EntityActionType actionTypeFromString(QString actionTypeString);
static QString actionTypeToString(EntityActionType actionType);
static EntityDynamicType dynamicTypeFromString(QString dynamicTypeString);
static QString dynamicTypeToString(EntityDynamicType dynamicType);
virtual bool lifetimeIsOver() { return false; }
virtual quint64 getExpires() { return 0; }
@ -82,26 +87,26 @@ public:
protected:
virtual glm::vec3 getPosition() = 0;
virtual void setPosition(glm::vec3 position) = 0;
// virtual void setPosition(glm::vec3 position) = 0;
virtual glm::quat getRotation() = 0;
virtual void setRotation(glm::quat rotation) = 0;
// virtual void setRotation(glm::quat rotation) = 0;
virtual glm::vec3 getLinearVelocity() = 0;
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
virtual glm::vec3 getAngularVelocity() = 0;
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
QUuid _id;
EntityActionType _type;
EntityDynamicType _type;
bool _active { false };
bool _isMine { false }; // did this interface create / edit this action?
bool _isMine { false }; // did this interface create / edit this dynamic?
};
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType);
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType);
QString serializedActionsToDebugString(QByteArray data);
QString serializedDynamicsToDebugString(QByteArray data);
#endif // hifi_EntityActionInterface_h
#endif // hifi_EntityDynamicInterface_h

View file

@ -30,7 +30,7 @@
#include "EntitiesLogging.h"
#include "EntityTree.h"
#include "EntitySimulation.h"
#include "EntityActionFactoryInterface.h"
#include "EntityDynamicFactoryInterface.h"
int EntityItem::_maxActionsDataSize = 800;
@ -280,7 +280,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData());
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getDynamicData());
// convert AVATAR_SELF_ID to actual sessionUUID.
QUuid actualParentID = getParentID();
@ -821,7 +821,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setDynamicData);
{ // parentID and parentJointIndex are also protected by simulation ownership
bool oldOverwrite = overwriteLocalData;
@ -1251,7 +1251,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getDynamicData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
@ -1358,7 +1358,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setDynamicData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
@ -1872,10 +1872,20 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
iAmHoldingThis = true;
}
// also, don't bootstrap our own avatar with a hold action
QList<EntityActionPointer> holdActions = getActionsOfType(ACTION_TYPE_HOLD);
QList<EntityActionPointer>::const_iterator i = holdActions.begin();
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
while (i != holdActions.end()) {
EntityActionPointer action = *i;
EntityDynamicPointer action = *i;
if (action->isMine()) {
iAmHoldingThis = true;
break;
}
i++;
}
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
i = farGrabActions.begin();
while (i != farGrabActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
iAmHoldingThis = true;
break;
@ -1941,18 +1951,18 @@ void EntityItem::rememberHasSimulationOwnershipBid() const {
QString EntityItem::actionsToDebugString() {
QString result;
QVector<QByteArray> serializedActions;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
EntityActionPointer action = _objectActions[id];
EntityActionType actionType = action->getType();
EntityDynamicPointer action = _objectActions[id];
EntityDynamicType actionType = action->getType();
result += QString("") + actionType + ":" + action->getID().toString() + " ";
i++;
}
return result;
}
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPointer action) {
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPointer action) {
bool result;
withWriteLock([&] {
checkWaitingToRemove(simulation);
@ -1960,7 +1970,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
result = addActionInternal(simulation, action);
if (result) {
action->setIsMine(true);
_actionDataDirty = true;
_dynamicDataDirty = true;
} else {
removeActionInternal(action->getID());
}
@ -1969,7 +1979,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
return result;
}
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action) {
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) {
assert(action);
assert(simulation);
auto actionOwnerEntity = action->getOwnerEntity().lock();
@ -1979,7 +1989,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityAct
const QUuid& actionID = action->getID();
assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action);
_objectActions[actionID] = action;
simulation->addAction(action);
simulation->addDynamic(action);
bool success;
QByteArray newDataCache;
@ -2003,14 +2013,13 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
return;
}
EntityActionPointer action = _objectActions[actionID];
EntityDynamicPointer action = _objectActions[actionID];
success = action->updateArguments(arguments);
if (success) {
action->setIsMine(true);
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
} else {
qCDebug(entities) << "EntityItem::updateAction failed";
}
@ -2035,7 +2044,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
simulation = entityTree ? entityTree->getSimulation() : nullptr;
}
EntityActionPointer action = _objectActions[actionID];
EntityDynamicPointer action = _objectActions[actionID];
action->setOwnerEntity(nullptr);
action->setIsMine(false);
@ -2049,7 +2058,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
setActionDataNeedsTransmit(true);
setDynamicDataNeedsTransmit(true);
return success;
}
return false;
@ -2057,10 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
bool EntityItem::clearActions(EntitySimulationPointer simulation) {
withWriteLock([&] {
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
EntityActionPointer action = _objectActions[id];
EntityDynamicPointer action = _objectActions[id];
i = _objectActions.erase(i);
action->setOwnerEntity(nullptr);
action->removeFromSimulation(simulation);
@ -2101,12 +2110,12 @@ void EntityItem::deserializeActionsInternal() {
serializedActionsStream >> serializedActions;
}
// Keep track of which actions got added or updated by the new actionData
// Keep track of which actions got added or updated by the new dynamicData
QSet<QUuid> updated;
foreach(QByteArray serializedAction, serializedActions) {
QDataStream serializedActionStream(serializedAction);
EntityActionType actionType;
EntityDynamicType actionType;
QUuid actionID;
serializedActionStream >> actionType;
serializedActionStream >> actionID;
@ -2115,7 +2124,7 @@ void EntityItem::deserializeActionsInternal() {
}
if (_objectActions.contains(actionID)) {
EntityActionPointer action = _objectActions[actionID];
EntityDynamicPointer action = _objectActions[actionID];
// TODO: make sure types match? there isn't currently a way to
// change the type of an existing action.
if (!action->isMine()) {
@ -2123,9 +2132,9 @@ void EntityItem::deserializeActionsInternal() {
}
updated << actionID;
} else {
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
EntityItemPointer entity = getThisPointer();
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
EntityDynamicPointer action = actionFactory->factoryBA(entity, serializedAction);
if (action) {
entity->addActionInternal(simulation, action);
updated << actionID;
@ -2140,15 +2149,15 @@ void EntityItem::deserializeActionsInternal() {
}
// remove any actions that weren't included in the new data.
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
QUuid id = i.key();
if (!updated.contains(id)) {
EntityActionPointer action = i.value();
EntityDynamicPointer action = i.value();
if (action->isMine()) {
// we just received an update that didn't include one of our actions. tell the server about it (again).
setActionDataNeedsTransmit(true);
setDynamicDataNeedsTransmit(true);
} else {
// don't let someone else delete my action.
_actionsToRemove << id;
@ -2167,7 +2176,7 @@ void EntityItem::deserializeActionsInternal() {
}
}
_actionDataDirty = true;
_dynamicDataDirty = true;
return;
}
@ -2179,15 +2188,15 @@ void EntityItem::checkWaitingToRemove(EntitySimulationPointer simulation) {
_actionsToRemove.clear();
}
void EntityItem::setActionData(QByteArray actionData) {
void EntityItem::setDynamicData(QByteArray dynamicData) {
withWriteLock([&] {
setActionDataInternal(actionData);
setDynamicDataInternal(dynamicData);
});
}
void EntityItem::setActionDataInternal(QByteArray actionData) {
if (_allActionsDataCache != actionData) {
_allActionsDataCache = actionData;
void EntityItem::setDynamicDataInternal(QByteArray dynamicData) {
if (_allActionsDataCache != dynamicData) {
_allActionsDataCache = dynamicData;
deserializeActionsInternal();
}
checkWaitingToRemove();
@ -2201,10 +2210,10 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
}
QVector<QByteArray> serializedActions;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
EntityActionPointer action = _objectActions[id];
EntityDynamicPointer action = _objectActions[id];
QByteArray bytesForAction = action->serialize();
serializedActions << bytesForAction;
i++;
@ -2224,23 +2233,23 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
return;
}
const QByteArray EntityItem::getActionDataInternal() const {
if (_actionDataDirty) {
const QByteArray EntityItem::getDynamicDataInternal() const {
if (_dynamicDataDirty) {
bool success;
serializeActions(success, _allActionsDataCache);
if (success) {
_actionDataDirty = false;
_dynamicDataDirty = false;
}
}
return _allActionsDataCache;
}
const QByteArray EntityItem::getActionData() const {
const QByteArray EntityItem::getDynamicData() const {
QByteArray result;
if (_actionDataDirty) {
if (_dynamicDataDirty) {
withWriteLock([&] {
getActionDataInternal();
getDynamicDataInternal();
result = _allActionsDataCache;
});
} else {
@ -2255,9 +2264,9 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
QVariantMap result;
withReadLock([&] {
if (_objectActions.contains(actionID)) {
EntityActionPointer action = _objectActions[actionID];
EntityDynamicPointer action = _objectActions[actionID];
result = action->getArguments();
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
result["type"] = EntityDynamicInterface::dynamicTypeToString(action->getType());
}
});
@ -2265,7 +2274,7 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
}
bool EntityItem::shouldSuppressLocationEdits() const {
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
if (i.value()->shouldSuppressLocationEdits()) {
return true;
@ -2276,12 +2285,12 @@ bool EntityItem::shouldSuppressLocationEdits() const {
return false;
}
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) const {
QList<EntityActionPointer> result;
QList<EntityDynamicPointer> EntityItem::getActionsOfType(EntityDynamicType typeToGet) const {
QList<EntityDynamicPointer> result;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
EntityActionPointer action = i.value();
EntityDynamicPointer action = i.value();
if (action->getType() == typeToGet && action->isActive()) {
result += action;
}

View file

@ -36,17 +36,17 @@
#include "EntityTypes.h"
#include "SimulationOwner.h"
#include "SimulationFlags.h"
#include "EntityActionInterface.h"
#include "EntityDynamicInterface.h"
class EntitySimulation;
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
class EntityActionInterface;
class EntityDynamicInterface;
class EntityItemProperties;
class EntityTree;
class btCollisionShape;
typedef std::shared_ptr<EntityTree> EntityTreePointer;
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
@ -398,22 +398,22 @@ public:
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
QString actionsToDebugString();
bool addAction(EntitySimulationPointer simulation, EntityActionPointer action);
bool addAction(EntitySimulationPointer simulation, EntityDynamicPointer action);
bool updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments);
bool removeAction(EntitySimulationPointer simulation, const QUuid& actionID);
bool clearActions(EntitySimulationPointer simulation);
void setActionData(QByteArray actionData);
const QByteArray getActionData() const;
void setDynamicData(QByteArray dynamicData);
const QByteArray getDynamicData() const;
bool hasActions() const { return !_objectActions.empty(); }
QList<QUuid> getActionIDs() const { return _objectActions.keys(); }
QVariantMap getActionArguments(const QUuid& actionID) const;
void deserializeActions();
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
bool actionDataDirty() const { return _actionDataDirty; }
void setDynamicDataDirty(bool value) const { _dynamicDataDirty = value; }
bool dynamicDataDirty() const { return _dynamicDataDirty; }
void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; }
bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; }
void setDynamicDataNeedsTransmit(bool value) const { _dynamicDataNeedsTransmit = value; }
bool dynamicDataNeedsTransmit() const { return _dynamicDataNeedsTransmit; }
bool shouldSuppressLocationEdits() const;
@ -421,7 +421,7 @@ public:
const QUuid& getSourceUUID() const { return _sourceUUID; }
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet) const;
QList<EntityDynamicPointer> getActionsOfType(EntityDynamicType typeToGet) const;
// these are in the frame of this object
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
@ -479,8 +479,8 @@ protected:
void setSimulated(bool simulated) { _simulated = simulated; }
const QByteArray getActionDataInternal() const;
void setActionDataInternal(QByteArray actionData);
const QByteArray getDynamicDataInternal() const;
void setDynamicDataInternal(QByteArray dynamicData);
virtual void locationChanged(bool tellPhysics = true) override;
virtual void dimensionsChanged() override;
@ -570,22 +570,22 @@ protected:
void* _physicsInfo { nullptr }; // set by EntitySimulation
bool _simulated; // set by EntitySimulation
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr);
void deserializeActionsInternal();
void serializeActions(bool& success, QByteArray& result) const;
QHash<QUuid, EntityActionPointer> _objectActions;
QHash<QUuid, EntityDynamicPointer> _objectActions;
static int _maxActionsDataSize;
mutable QByteArray _allActionsDataCache;
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
// when an entity-server starts up, EntityItem::setDynamicData is called before the entity-tree is
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
// are used to keep track of and work around this situation.
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
mutable QSet<QUuid> _actionsToRemove;
mutable bool _actionDataDirty { false };
mutable bool _actionDataNeedsTransmit { false };
mutable bool _dynamicDataDirty { false };
mutable bool _dynamicDataNeedsTransmit { false };
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
static quint64 _rememberDeletedActionTime;
mutable QHash<QUuid, quint64> _previouslyDeletedActions;

View file

@ -24,8 +24,8 @@
#include <model-networking/MeshProxy.h>
#include "EntitiesLogging.h"
#include "EntityActionFactoryInterface.h"
#include "EntityActionInterface.h"
#include "EntityDynamicFactoryInterface.h"
#include "EntityDynamicInterface.h"
#include "EntitySimulation.h"
#include "EntityTree.h"
#include "LightEntityItem.h"
@ -1133,7 +1133,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
PROFILE_RANGE(script_entities, __FUNCTION__);
QUuid actionID = QUuid::createUuid();
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
bool success = false;
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
// create this action even if the entity doesn't have physics info. it will often be the
@ -1142,11 +1142,11 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
// if (!entity->getPhysicsInfo()) {
// return false;
// }
EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
if (actionType == ACTION_TYPE_NONE) {
EntityDynamicType dynamicType = EntityDynamicInterface::dynamicTypeFromString(actionTypeString);
if (dynamicType == DYNAMIC_TYPE_NONE) {
return false;
}
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, entity, arguments);
if (!action) {
return false;
}

View file

@ -279,25 +279,25 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
}
}
void EntitySimulation::addAction(EntityActionPointer action) {
void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
QMutexLocker lock(&_mutex);
_actionsToAdd += action;
_dynamicsToAdd += dynamic;
}
void EntitySimulation::removeAction(const QUuid actionID) {
void EntitySimulation::removeDynamic(const QUuid dynamicID) {
QMutexLocker lock(&_mutex);
_actionsToRemove += actionID;
_dynamicsToRemove += dynamicID;
}
void EntitySimulation::removeActions(QList<QUuid> actionIDsToRemove) {
void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
QMutexLocker lock(&_mutex);
foreach(QUuid uuid, actionIDsToRemove) {
_actionsToRemove.insert(uuid);
foreach(QUuid uuid, dynamicIDsToRemove) {
_dynamicsToRemove.insert(uuid);
}
}
void EntitySimulation::applyActionChanges() {
void EntitySimulation::applyDynamicChanges() {
QMutexLocker lock(&_mutex);
_actionsToAdd.clear();
_actionsToRemove.clear();
_dynamicsToAdd.clear();
_dynamicsToRemove.clear();
}

View file

@ -18,7 +18,7 @@
#include <PerfStat.h>
#include "EntityActionInterface.h"
#include "EntityDynamicInterface.h"
#include "EntityItem.h"
#include "EntityTree.h"
@ -59,10 +59,10 @@ public:
// friend class EntityTree;
virtual void addAction(EntityActionPointer action);
virtual void removeAction(const QUuid actionID);
virtual void removeActions(QList<QUuid> actionIDsToRemove);
virtual void applyActionChanges();
virtual void addDynamic(EntityDynamicPointer dynamic);
virtual void removeDynamic(const QUuid dynamicID);
virtual void removeDynamics(QList<QUuid> dynamicIDsToRemove);
virtual void applyDynamicChanges();
/// \param entity pointer to EntityItem to be added
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
@ -103,8 +103,8 @@ protected:
SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree)
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
QList<EntityActionPointer> _actionsToAdd;
QSet<QUuid> _actionsToRemove;
QList<EntityDynamicPointer> _dynamicsToAdd;
QSet<QUuid> _dynamicsToRemove;
protected:
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)

View file

@ -808,7 +808,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
int index = changedProperties.indexOf("actionData");
if (index >= 0) {
QByteArray value = properties.getActionData();
QString changeHint = serializedActionsToDebugString(value);
QString changeHint = serializedDynamicsToDebugString(value);
changedProperties[index] = QString("actionData:") + changeHint;
}
}

View file

@ -6,9 +6,9 @@ namespace gl {
#ifdef SEPARATE_PROGRAM
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject) {
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error) {
#else
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject) {
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error) {
#endif
if (shaderSource.empty()) {
qCDebug(glLogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
@ -69,6 +69,8 @@ namespace gl {
}
qCWarning(glLogging) << "GLShader::compileShader - errors:";
qCWarning(glLogging) << temp;
error = std::string(temp);
delete[] temp;
glDeleteShader(glshader);
@ -130,7 +132,7 @@ namespace gl {
return true;
}
GLuint compileProgram(const std::vector<GLuint>& glshaders) {
GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& error) {
// A brand new program:
GLuint glprogram = glCreateProgram();
if (!glprogram) {
@ -170,6 +172,9 @@ GLuint compileProgram(const std::vector<GLuint>& glshaders) {
qCDebug(glLogging) << "GLShader::compileProgram - failed to LINK the gl program object :";
qCDebug(glLogging) << temp;
error = std::string(temp);
delete[] temp;
/*
filestream.open("debugshader.glsl.info.txt");
if (filestream.is_open()) {

View file

@ -17,12 +17,12 @@
namespace gl {
#ifdef SEPARATE_PROGRAM
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject);
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error);
#else
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject);
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error);
#endif
GLuint compileProgram(const std::vector<GLuint>& glshaders);
GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& error);
}

View file

@ -101,6 +101,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_setStateScissorRect),
(&::gpu::gl::GLBackend::do_setUniformBuffer),
(&::gpu::gl::GLBackend::do_setResourceBuffer),
(&::gpu::gl::GLBackend::do_setResourceTexture),
(&::gpu::gl::GLBackend::do_setFramebuffer),

View file

@ -93,6 +93,8 @@ public:
// this is the maximum per shader stage on the low end apple
// TODO make it platform dependant at init time
static const int MAX_NUM_RESOURCE_BUFFERS = 16;
size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; }
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
@ -122,6 +124,7 @@ public:
virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final;
// Resource Stage
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
// Pipeline Stage
@ -357,13 +360,19 @@ protected:
void releaseUniformBuffer(uint32_t slot);
void resetUniformStage();
// update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s
// This is using different gl object depending on the gl version
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
virtual void releaseResourceBuffer(uint32_t slot) = 0;
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
void releaseResourceTexture(uint32_t slot);
void resetResourceStage();
struct ResourceStageState {
std::array<BufferPointer, MAX_NUM_RESOURCE_BUFFERS> _buffers;
std::array<TexturePointer, MAX_NUM_RESOURCE_TEXTURES> _textures;
//Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } };
int findEmptyTextureSlot() const;
@ -400,6 +409,28 @@ protected:
}
} _pipeline;
// Backend dependant compilation of the shader
virtual GLShader* compileBackendProgram(const Shader& program);
virtual GLShader* compileBackendShader(const Shader& shader);
virtual std::string getBackendShaderHeader() const;
virtual void makeProgramBindings(ShaderObject& shaderObject);
class ElementResource {
public:
gpu::Element _element;
uint16 _resource;
ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {}
};
ElementResource getFormatFromGLUniform(GLenum gltype);
static const GLint UNUSED_SLOT {-1};
static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); }
virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers);
virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0;
virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
// Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncOutputStateCache();
void resetOutputStage();
@ -425,6 +456,7 @@ protected:
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
friend class GLState;
friend class GLTexture;
friend class GLShader;
};
} }

View file

@ -191,11 +191,45 @@ void GLBackend::releaseResourceTexture(uint32_t slot) {
}
void GLBackend::resetResourceStage() {
for (uint32_t i = 0; i < _resource._buffers.size(); i++) {
releaseResourceBuffer(i);
}
for (uint32_t i = 0; i < _resource._textures.size(); i++) {
releaseResourceTexture(i);
}
}
void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) {
// "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" + slot + " which doesn't exist. MaxNumResourceBuffers = " + getMaxNumResourceBuffers());
return;
}
auto resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint);
if (!resourceBuffer) {
releaseResourceBuffer(slot);
return;
}
// check cache before thinking
if (_resource._buffers[slot] == resourceBuffer) {
return;
}
// One more True Buffer bound
_stats._RSNumResourceBufferBounded++;
// If successful bind then cache it
if (bindResourceBuffer(slot, resourceBuffer)) {
_resource._buffers[slot] = resourceBuffer;
} else { // else clear slot and cache
releaseResourceBuffer(slot);
return;
}
}
void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) {

View file

@ -0,0 +1,517 @@
//
// Created by Sam Gateau on 2017/04/13
// Copyright 2013-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
//
#include "GLBackend.h"
#include "GLShader.h"
#include <gl/GLShaders.h>
using namespace gpu;
using namespace gpu::gl;
// GLSL version
std::string GLBackend::getBackendShaderHeader() const {
return std::string("#version 410 core");
}
// Shader domain
static const size_t NUM_SHADER_DOMAINS = 3;
// GL Shader type enums
// Must match the order of type specified in gpu::Shader::Type
static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
GL_VERTEX_SHADER,
GL_FRAGMENT_SHADER,
GL_GEOMETRY_SHADER,
} };
// Domain specific defines
// Must match the order of type specified in gpu::Shader::Type
static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
"#define GPU_VERTEX_SHADER",
"#define GPU_PIXEL_SHADER",
"#define GPU_GEOMETRY_SHADER",
} };
// Stereo specific defines
static const std::string stereoVersion {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN"
#endif
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
#ifdef GPU_STEREO_CAMERA_BUFFER
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED"
#else
"#define GPU_TRANSFORM_IS_STEREO"
#endif
#endif
};
// Versions specific of the shader
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
"",
stereoVersion
} };
GLShader* GLBackend::compileBackendShader(const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
GLShader::ShaderObjects shaderObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& shaderObject = shaderObjects[version];
std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version];
std::string error;
#ifdef SEPARATE_PROGRAM
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error);
#else
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error);
#endif
if (!result) {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str();
return nullptr;
}
}
// So far so good, the shader is created successfully
GLShader* object = new GLShader(this->shared_from_this());
object->_shaderObjects = shaderObjects;
return object;
}
GLShader* GLBackend::compileBackendProgram(const Shader& program) {
if (!program.isProgram()) {
return nullptr;
}
GLShader::ShaderObjects programObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& programObject = programObjects[version];
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderGLObjects;
for (auto subShader : program.getShaders()) {
auto object = GLShader::sync((*this), *subShader);
if (object) {
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
} else {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?";
return nullptr;
}
}
std::string error;
GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error);
if (glprogram == 0) {
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str();
return nullptr;
}
programObject.glprogram = glprogram;
makeProgramBindings(programObject);
}
// So far so good, the program versions have all been created successfully
GLShader* object = new GLShader(this->shared_from_this());
object->_shaderObjects = programObjects;
return object;
}
GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) {
switch (gltype) {
case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/*
case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
*/
case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER);
#if defined(Q_OS_WIN)
case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER);
#endif
case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/* {GL_FLOAT_MAT2x3 mat2x3},
{GL_FLOAT_MAT2x4 mat2x4},
{GL_FLOAT_MAT3x2 mat3x2},
{GL_FLOAT_MAT3x4 mat3x4},
{GL_FLOAT_MAT4x2 mat4x2},
{GL_FLOAT_MAT4x3 mat4x3},
{GL_DOUBLE_MAT2 dmat2},
{GL_DOUBLE_MAT3 dmat3},
{GL_DOUBLE_MAT4 dmat4},
{GL_DOUBLE_MAT2x3 dmat2x3},
{GL_DOUBLE_MAT2x4 dmat2x4},
{GL_DOUBLE_MAT3x2 dmat3x2},
{GL_DOUBLE_MAT3x4 dmat3x4},
{GL_DOUBLE_MAT4x2 dmat4x2},
{GL_DOUBLE_MAT4x3 dmat4x3},
*/
case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D);
case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D);
case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D);
case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE);
#if defined(Q_OS_WIN)
case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D);
#if defined(Q_OS_WIN)
case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE);
case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_SAMPLER_1D_SHADOW sampler1DShadow},
// {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow},
case GL_SAMPLER_BUFFER: return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER);
// {GL_SAMPLER_2D_RECT sampler2DRect},
// {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow},
#if defined(Q_OS_WIN)
case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D);
case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D);
case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
// {GL_INT_SAMPLER_BUFFER isamplerBuffer},
// {GL_INT_SAMPLER_2D_RECT isampler2DRect},
case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D);
case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D);
case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer},
// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect},
/*
{GL_IMAGE_1D image1D},
{GL_IMAGE_2D image2D},
{GL_IMAGE_3D image3D},
{GL_IMAGE_2D_RECT image2DRect},
{GL_IMAGE_CUBE imageCube},
{GL_IMAGE_BUFFER imageBuffer},
{GL_IMAGE_1D_ARRAY image1DArray},
{GL_IMAGE_2D_ARRAY image2DArray},
{GL_IMAGE_2D_MULTISAMPLE image2DMS},
{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray},
{GL_INT_IMAGE_1D iimage1D},
{GL_INT_IMAGE_2D iimage2D},
{GL_INT_IMAGE_3D iimage3D},
{GL_INT_IMAGE_2D_RECT iimage2DRect},
{GL_INT_IMAGE_CUBE iimageCube},
{GL_INT_IMAGE_BUFFER iimageBuffer},
{GL_INT_IMAGE_1D_ARRAY iimage1DArray},
{GL_INT_IMAGE_2D_ARRAY iimage2DArray},
{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS},
{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray},
{GL_UNSIGNED_INT_IMAGE_1D uimage1D},
{GL_UNSIGNED_INT_IMAGE_2D uimage2D},
{GL_UNSIGNED_INT_IMAGE_3D uimage3D},
{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect},
{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot
{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer},
{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray},
{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray},
{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint}
*/
default:
return ElementResource(Element(), Resource::BUFFER);
}
};
int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glProgramUniform1i(glprogram, location, binding);
}
}
textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
return uniformsCount;
}
int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumUniformBufferSlots = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
struct UniformBlockInfo {
using Vector = std::vector<UniformBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
}
};
UniformBlockInfo::Vector uniformBlocks;
uniformBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
uniformBlocks.push_back(UniformBlockInfo(glprogram, i));
}
for (auto& info : uniformBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
uniformBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : uniformBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is 0, or the binding maps to an already used binding
if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
info.binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
info.binding = -1;
}
}
uniformBufferSlotMap[info.binding] = info.index;
}
for (auto& info : uniformBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}
int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
GLint inputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint binding = glGetAttribLocation(glprogram, name);
auto elementResource = getFormatFromGLUniform(type);
inputs.insert(Shader::Slot(name, binding, elementResource._element, -1));
}
return inputsCount;
}
int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
/* GLint outputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
auto element = getFormatFromGLUniform(type);
outputs.insert(Shader::Slot(name, i, element));
}
*/
return 0; //inputsCount;
}
void GLBackend::makeProgramBindings(ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "inPosition");
if (loc >= 0 && loc != gpu::Stream::POSITION) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition");
}
loc = glGetAttribLocation(glprogram, "inNormal");
if (loc >= 0 && loc != gpu::Stream::NORMAL) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal");
}
loc = glGetAttribLocation(glprogram, "inColor");
if (loc >= 0 && loc != gpu::Stream::COLOR) {
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor");
}
loc = glGetAttribLocation(glprogram, "inTexCoord0");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0");
}
loc = glGetAttribLocation(glprogram, "inTangent");
if (loc >= 0 && loc != gpu::Stream::TANGENT) {
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent");
}
loc = glGetAttribLocation(glprogram, "inTexCoord1");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterIndex");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterWeight");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
}
loc = glGetAttribLocation(glprogram, "_drawCallInfo");
if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) {
glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?";
}
}

View file

@ -51,7 +51,7 @@ GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) {
// Special case for view correction matrices, any pipeline that declares the correction buffer
// uniform will automatically have it provided without any client code necessary.
// Required for stable lighting in the HMD.
object->_cameraCorrection = shader->getBuffers().findLocation("cameraCorrectionBuffer");
object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer");
object->_program = programObject;
object->_state = stateObject;

View file

@ -30,117 +30,6 @@ GLShader::~GLShader() {
}
}
// GLSL version
static const std::string glslVersion {
"#version 410 core"
};
// Shader domain
static const size_t NUM_SHADER_DOMAINS = 3;
// GL Shader type enums
// Must match the order of type specified in gpu::Shader::Type
static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
GL_VERTEX_SHADER,
GL_FRAGMENT_SHADER,
GL_GEOMETRY_SHADER,
} };
// Domain specific defines
// Must match the order of type specified in gpu::Shader::Type
static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
"#define GPU_VERTEX_SHADER",
"#define GPU_PIXEL_SHADER",
"#define GPU_GEOMETRY_SHADER",
} };
// Stereo specific defines
static const std::string stereoVersion {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN"
#endif
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
#ifdef GPU_STEREO_CAMERA_BUFFER
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED"
#else
"#define GPU_TRANSFORM_IS_STEREO"
#endif
#endif
};
// Versions specific of the shader
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
"",
stereoVersion
} };
GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
GLShader::ShaderObjects shaderObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& shaderObject = shaderObjects[version];
std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version];
#ifdef SEPARATE_PROGRAM
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram);
#else
bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader);
#endif
if (!result) {
return nullptr;
}
}
// So far so good, the shader is created successfully
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = shaderObjects;
return object;
}
GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) {
if (!program.isProgram()) {
return nullptr;
}
GLShader::ShaderObjects programObjects;
for (int version = 0; version < GLShader::NumVersions; version++) {
auto& programObject = programObjects[version];
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderGLObjects;
for (auto subShader : program.getShaders()) {
auto object = GLShader::sync(backend, *subShader);
if (object) {
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
} else {
qCWarning(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?";
return nullptr;
}
}
GLuint glprogram = ::gl::compileProgram(shaderGLObjects);
if (glprogram == 0) {
return nullptr;
}
programObject.glprogram = glprogram;
makeProgramBindings(programObject);
}
// So far so good, the program versions have all been created successfully
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = programObjects;
return object;
}
GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) {
GLShader* object = Backend::getGPUObject<GLShader>(shader);
@ -150,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) {
}
// need to have a gpu object?
if (shader.isProgram()) {
GLShader* tempObject = compileBackendProgram(backend, shader);
GLShader* tempObject = backend.compileBackendProgram(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
} else if (shader.isDomain()) {
GLShader* tempObject = compileBackendShader(backend, shader);
GLShader* tempObject = backend.compileBackendShader(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
@ -180,22 +69,25 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
auto& shaderObject = object->_shaderObjects[version];
if (shaderObject.glprogram) {
Shader::SlotSet buffers;
makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
Shader::SlotSet uniforms;
Shader::SlotSet textures;
Shader::SlotSet samplers;
makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
Shader::SlotSet resourceBuffers;
backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers);
Shader::SlotSet inputs;
makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
Shader::SlotSet outputs;
makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
// Define the public slots only from the default version
if (version == 0) {
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
shader.defineSlots(uniforms, buffers, resourceBuffers, textures, samplers, inputs, outputs);
} // else
{
GLShader::UniformMapping mapping;
@ -210,3 +102,5 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
return true;
}

View file

@ -12,6 +12,13 @@
namespace gpu { namespace gl {
struct ShaderObject {
GLuint glshader { 0 };
GLuint glprogram { 0 };
GLint transformCameraSlot { -1 };
GLint transformObjectSlot { -1 };
};
class GLShader : public GPUObject {
public:
static GLShader* sync(GLBackend& backend, const Shader& shader);

View file

@ -356,432 +356,6 @@ void getCurrentGLState(State::Data& state) {
(void)CHECK_GL_ERROR();
}
class ElementResource {
public:
gpu::Element _element;
uint16 _resource;
ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {}
};
ElementResource getFormatFromGLUniform(GLenum gltype) {
switch (gltype) {
case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/*
case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
*/
case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER);
#if defined(Q_OS_WIN)
case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER);
#endif
case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/* {GL_FLOAT_MAT2x3 mat2x3},
{GL_FLOAT_MAT2x4 mat2x4},
{GL_FLOAT_MAT3x2 mat3x2},
{GL_FLOAT_MAT3x4 mat3x4},
{GL_FLOAT_MAT4x2 mat4x2},
{GL_FLOAT_MAT4x3 mat4x3},
{GL_DOUBLE_MAT2 dmat2},
{GL_DOUBLE_MAT3 dmat3},
{GL_DOUBLE_MAT4 dmat4},
{GL_DOUBLE_MAT2x3 dmat2x3},
{GL_DOUBLE_MAT2x4 dmat2x4},
{GL_DOUBLE_MAT3x2 dmat3x2},
{GL_DOUBLE_MAT3x4 dmat3x4},
{GL_DOUBLE_MAT4x2 dmat4x2},
{GL_DOUBLE_MAT4x3 dmat4x3},
*/
case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D);
case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D);
case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D);
case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE);
#if defined(Q_OS_WIN)
case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D);
#if defined(Q_OS_WIN)
case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE);
case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_SAMPLER_1D_SHADOW sampler1DShadow},
// {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow},
// {GL_SAMPLER_BUFFER samplerBuffer},
// {GL_SAMPLER_2D_RECT sampler2DRect},
// {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow},
#if defined(Q_OS_WIN)
case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D);
case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D);
case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
// {GL_INT_SAMPLER_BUFFER isamplerBuffer},
// {GL_INT_SAMPLER_2D_RECT isampler2DRect},
case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D);
case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D);
case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer},
// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect},
/*
{GL_IMAGE_1D image1D},
{GL_IMAGE_2D image2D},
{GL_IMAGE_3D image3D},
{GL_IMAGE_2D_RECT image2DRect},
{GL_IMAGE_CUBE imageCube},
{GL_IMAGE_BUFFER imageBuffer},
{GL_IMAGE_1D_ARRAY image1DArray},
{GL_IMAGE_2D_ARRAY image2DArray},
{GL_IMAGE_2D_MULTISAMPLE image2DMS},
{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray},
{GL_INT_IMAGE_1D iimage1D},
{GL_INT_IMAGE_2D iimage2D},
{GL_INT_IMAGE_3D iimage3D},
{GL_INT_IMAGE_2D_RECT iimage2DRect},
{GL_INT_IMAGE_CUBE iimageCube},
{GL_INT_IMAGE_BUFFER iimageBuffer},
{GL_INT_IMAGE_1D_ARRAY iimage1DArray},
{GL_INT_IMAGE_2D_ARRAY iimage2DArray},
{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS},
{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray},
{GL_UNSIGNED_INT_IMAGE_1D uimage1D},
{GL_UNSIGNED_INT_IMAGE_2D uimage2D},
{GL_UNSIGNED_INT_IMAGE_3D uimage3D},
{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect},
{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot
{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer},
{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray},
{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray},
{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint}
*/
default:
return ElementResource(Element(), Resource::BUFFER);
}
};
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glProgramUniform1i(glprogram, location, binding);
}
}
textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
return uniformsCount;
}
const GLint UNUSED_SLOT = -1;
bool isUnusedSlot(GLint binding) {
return (binding == UNUSED_SLOT);
}
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumUniformBufferSlots = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
struct UniformBlockInfo {
using Vector = std::vector<UniformBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
}
};
UniformBlockInfo::Vector uniformBlocks;
uniformBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
uniformBlocks.push_back(UniformBlockInfo(glprogram, i));
}
for (auto& info : uniformBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
uniformBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : uniformBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is 0, or the binding maps to an already used binding
if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
info.binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
info.binding = -1;
}
}
uniformBufferSlotMap[info.binding] = info.index;
}
for (auto& info : uniformBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}
int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
GLint inputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint binding = glGetAttribLocation(glprogram, name);
auto elementResource = getFormatFromGLUniform(type);
inputs.insert(Shader::Slot(name, binding, elementResource._element, -1));
}
return inputsCount;
}
int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
/* GLint outputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
auto element = getFormatFromGLUniform(type);
outputs.insert(Shader::Slot(name, i, element));
}
*/
return 0; //inputsCount;
}
void makeProgramBindings(ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "inPosition");
if (loc >= 0 && loc != gpu::Stream::POSITION) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition");
}
loc = glGetAttribLocation(glprogram, "inNormal");
if (loc >= 0 && loc != gpu::Stream::NORMAL) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal");
}
loc = glGetAttribLocation(glprogram, "inColor");
if (loc >= 0 && loc != gpu::Stream::COLOR) {
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor");
}
loc = glGetAttribLocation(glprogram, "inTexCoord0");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0");
}
loc = glGetAttribLocation(glprogram, "inTangent");
if (loc >= 0 && loc != gpu::Stream::TANGENT) {
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent");
}
loc = glGetAttribLocation(glprogram, "inTexCoord1");
if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterIndex");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex");
}
loc = glGetAttribLocation(glprogram, "inSkinClusterWeight");
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
}
loc = glGetAttribLocation(glprogram, "_drawCallInfo");
if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) {
glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?";
}
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
#ifdef GPU_SSBO_DRAW_CALL_INFO
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
if (loc >= 0) {
glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
}
#else
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
}
#endif
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
(void)CHECK_GL_ERROR();
}
void serverWait() {
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
assert(fence);

View file

@ -34,20 +34,6 @@ State::BlendOp blendOpFromGL(GLenum blendOp);
State::BlendArg blendArgFromGL(GLenum blendArg);
void getCurrentGLState(State::Data& state);
struct ShaderObject {
GLuint glshader { 0 };
GLuint glprogram { 0 };
GLint transformCameraSlot { -1 };
GLint transformObjectSlot { -1 };
};
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers);
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
void makeProgramBindings(ShaderObject& shaderObject);
enum GLSyncState {
// The object is currently undergoing no processing, although it's content
// may be out of date, or it's storage may be invalid relative to the
@ -150,6 +136,8 @@ class GLQuery;
class GLState;
class GLShader;
class GLTexture;
struct ShaderObject;
} } // namespace gpu::gl

View file

@ -76,10 +76,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
}
break;
case gpu::COMPRESSED_R:
result = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::R11G11B10:
// the type should be float
result = GL_R11F_G11F_B10F;
@ -149,12 +145,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::SRGBA:
result = GL_SRGB8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGB:
result = GL_COMPRESSED_RGB;
break;
case gpu::COMPRESSED_SRGB:
result = GL_COMPRESSED_SRGB;
break;
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
@ -217,29 +207,22 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::SRGBA:
result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGBA:
result = GL_COMPRESSED_RGBA;
break;
case gpu::COMPRESSED_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA;
break;
// FIXME: WE will want to support this later
/*
case gpu::COMPRESSED_BC3_RGBA:
result = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case gpu::COMPRESSED_BC4_RED:
result = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::COMPRESSED_BC3_SRGBA:
case gpu::COMPRESSED_BC1_SRGB:
result = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC1_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC7_RGBA:
result = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
case gpu::COMPRESSED_BC5_XY:
result = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC7_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
*/
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
@ -269,10 +252,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_R8;
break;
case gpu::COMPRESSED_R:
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT;
texel.internalFormat = GL_DEPTH_COMPONENT32;
@ -315,12 +294,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::RGBA:
texel.internalFormat = GL_RGB8;
break;
case gpu::COMPRESSED_RGB:
texel.internalFormat = GL_COMPRESSED_RGB;
break;
case gpu::COMPRESSED_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB;
break;
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
@ -359,30 +332,22 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_SRGB8_ALPHA8;
break;
case gpu::COMPRESSED_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA;
break;
case gpu::COMPRESSED_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
case gpu::COMPRESSED_BC4_RED:
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
// FIXME: WE will want to support this later
/*
case gpu::COMPRESSED_BC3_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case gpu::COMPRESSED_BC1_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
case gpu::COMPRESSED_BC1_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC7_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
*/
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
@ -403,10 +368,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::COMPRESSED_R: {
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
}
case gpu::RED:
case gpu::RGB:
case gpu::RGBA:
@ -564,12 +525,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGB:
texel.internalFormat = GL_COMPRESSED_RGB;
break;
case gpu::COMPRESSED_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB;
break;
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";
}
@ -646,11 +601,21 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA;
case gpu::COMPRESSED_BC4_RED:
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::COMPRESSED_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
case gpu::COMPRESSED_BC1_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC1_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break;
default:
qCWarning(gpugllogging) << "Unknown combination of texel format";

View file

@ -120,11 +120,12 @@ void GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, u
}
auto size = _gpuObject.evalMipDimensions(sourceMip);
auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face);
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
if (mipData) {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat());
copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData());
copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData());
} else {
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
}
}
@ -203,9 +204,11 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t
auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
GLenum format;
GLenum internalFormat;
GLenum type;
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat());
format = texelFormat.format;
internalFormat = texelFormat.internalFormat;
type = texelFormat.type;
auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face);
@ -236,7 +239,7 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t
Backend::updateTextureTransferPendingSize(0, _transferSize);
_transferLambda = [=] {
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data());
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _buffer.size(), _buffer.data());
std::vector<uint8_t> emptyVector;
_buffer.swap(emptyVector);
};

View file

@ -163,7 +163,7 @@ public:
protected:
virtual Size size() const = 0;
virtual void generateMips() const = 0;
virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const = 0;
virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0;
virtual void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final;
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
@ -177,7 +177,7 @@ public:
protected:
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
void generateMips() const override {}
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override {}
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override {}
Size size() const override { return 0; }
};

View file

@ -35,6 +35,11 @@ class GL41Backend : public GLBackend {
friend class Context;
public:
static const GLint TRANSFORM_OBJECT_SLOT { 31 };
static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 };
static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 33 };
static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 34 };
explicit GL41Backend(bool syncCache) : Parent(syncCache) {}
GL41Backend() : Parent() {}
@ -45,7 +50,7 @@ public:
protected:
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
void generateMips() const override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
virtual void syncSampler() const;
void withPreservedTexture(std::function<void()> f) const;
@ -100,7 +105,7 @@ public:
void promote() override;
void demote() override;
void populateTransferQueue() override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
Size size() const override { return _size; }
Size _size { 0 };
@ -119,6 +124,7 @@ protected:
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
GLuint getBufferID(const Buffer& buffer) override;
GLuint getResourceBufferID(const Buffer& buffer);
GLBuffer* syncGPUObject(const Buffer& buffer) override;
GLTexture* syncGPUObject(const TexturePointer& texture) override;
@ -143,8 +149,17 @@ protected:
void initTransform() override;
void updateTransform(const Batch& batch) override;
// Resource Stage
bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) override;
void releaseResourceBuffer(uint32_t slot) override;
// Output stage
void do_blit(const Batch& batch, size_t paramOffset) override;
std::string getBackendShaderHeader() const override;
void makeProgramBindings(ShaderObject& shaderObject) override;
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
};
} }

View file

@ -18,11 +18,17 @@ namespace gpu {
return result;
}
~GL41Buffer() {
if (_texBuffer) {
glDeleteTextures(1, &_texBuffer);
}
}
public:
GL41Buffer(const std::weak_ptr<gl::GLBackend>& backend, const Buffer& buffer, GL41Buffer* original) : Parent(backend, buffer, allocate()) {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer);
glBufferData(GL_COPY_WRITE_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
if (original && original->_size) {
glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer);
@ -36,20 +42,34 @@ namespace gpu {
}
void transfer() override {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer);
(void)CHECK_GL_ERROR();
Size offset;
Size size;
Size currentPage { 0 };
auto data = _gpuObject._renderSysmem.readData();
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset);
glBufferSubData(GL_COPY_WRITE_BUFFER, offset, size, data + offset);
(void)CHECK_GL_ERROR();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
(void)CHECK_GL_ERROR();
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
}
// REsource BUffer are implemented with TextureBuffer
GLuint _texBuffer { 0 };
GLuint getTexBufferId() {
if (!_texBuffer) {
glGenTextures(1, &_texBuffer);
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_BUFFER_TEXBUF_TEX_UNIT);
glBindTexture(GL_TEXTURE_BUFFER, _texBuffer);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _buffer);
glBindTexture(GL_TEXTURE_BUFFER, 0);
(void)CHECK_GL_ERROR();
}
return _texBuffer;
}
};
}
}
@ -63,6 +83,40 @@ GLuint GL41Backend::getBufferID(const Buffer& buffer) {
return GL41Buffer::getId<GL41Buffer>(*this, buffer);
}
GLuint GL41Backend::getResourceBufferID(const Buffer& buffer) {
auto* object = GL41Buffer::sync<GL41Buffer>(*this, buffer);
if (object) {
return object->getTexBufferId();
} else {
return 0;
}
}
GLBuffer* GL41Backend::syncGPUObject(const Buffer& buffer) {
return GL41Buffer::sync<GL41Buffer>(*this, buffer);
}
bool GL41Backend::bindResourceBuffer(uint32_t slot, BufferPointer& buffer) {
GLuint texBuffer = GL41Backend::getResourceBufferID((*buffer));
if (texBuffer) {
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_BUFFER_SLOT0_TEX_UNIT + slot);
glBindTexture(GL_TEXTURE_BUFFER, texBuffer);
(void)CHECK_GL_ERROR();
_resource._buffers[slot] = buffer;
return true;
}
return false;
}
void GL41Backend::releaseResourceBuffer(uint32_t slot) {
auto& buf = _resource._buffers[slot];
if (buf) {
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_BUFFER_SLOT0_TEX_UNIT + slot);
glBindTexture(GL_TEXTURE_BUFFER, 0);
buf.reset();
}
}

View file

@ -112,9 +112,6 @@ void GL41Backend::updateInput() {
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency);
#endif
}
// TODO: Support properly the IAttrib version
(void)CHECK_GL_ERROR();
}
}

View file

@ -0,0 +1,113 @@
//
// Created by Sam Gateau on 2017/04/13
// Copyright 2013-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
//
#include "GL41Backend.h"
#include "../gl/GLShader.h"
//#include <gl/GLShaders.h>
using namespace gpu;
using namespace gpu::gl41;
// GLSL version
std::string GL41Backend::getBackendShaderHeader() const {
return std::string("#version 410 core\n#define GPU_GL410 1");
}
int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
GLint ssboCount = 0;
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
if (elementResource._resource == Resource::BUFFER) {
if (elementResource._element.getSemantic() == gpu::RESOURCE_BUFFER) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
if (binding == GL41Backend::TRANSFORM_OBJECT_SLOT) {
continue;
}
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
GLint requestedLoc = (*requestedBinding)._location + GL41Backend::RESOURCE_BUFFER_SLOT0_TEX_UNIT;
if (binding != requestedLoc) {
binding = requestedLoc;
}
} else {
binding += GL41Backend::RESOURCE_BUFFER_SLOT0_TEX_UNIT;
}
glProgramUniform1i(glprogram, location, binding);
ssboCount++;
resourceBuffers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
}
return ssboCount;
}
void GL41Backend::makeProgramBindings(gl::ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
GLBackend::makeProgramBindings(shaderObject);
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glProgramUniform1i(glprogram, loc, GL41Backend::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = GL41Backend::TRANSFORM_OBJECT_SLOT;
}
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
(void)CHECK_GL_ERROR();
}

View file

@ -69,16 +69,13 @@ GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
GLuint GL41Texture::allocate(const Texture& texture) {
GLuint result;
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
glCreateTextures(getGLTextureType(texture), 1, &result);
//glGenTextures(1, &result);
glGenTextures(1, &result);
return result;
}
void GL41Texture::withPreservedTexture(std::function<void()> f) const {
const GLint TRANSFER_TEXTURE_UNIT = 32;
glActiveTexture(GL_TEXTURE0 + TRANSFER_TEXTURE_UNIT);
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT);
glBindTexture(_target, _texture);
(void)CHECK_GL_ERROR();
@ -95,12 +92,37 @@ void GL41Texture::generateMips() const {
(void)CHECK_GL_ERROR();
}
void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
if (GL_TEXTURE_2D == _target) {
glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:
glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
break;
}
} else if (GL_TEXTURE_CUBE_MAP == _target) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
break;
}
} else {
assert(false);
}
@ -254,9 +276,9 @@ void GL41VariableAllocationTexture::allocateStorage(uint16 allocatedMip) {
}
void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
withPreservedTexture([&] {
Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, format, type, sourcePointer);
Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer);
});
}
@ -281,7 +303,7 @@ void GL41VariableAllocationTexture::promote() {
withPreservedTexture([&] {
GLuint fbo { 0 };
glCreateFramebuffers(1, &fbo);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = _gpuObject.getNumMips();

View file

@ -38,15 +38,9 @@ void GL41Backend::transferTransformState(const Batch& batch) const {
}
if (!batch._objects.empty()) {
#ifdef GPU_SSBO_DRAW_CALL_INFO
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sysmem.getSize(), sysmem.readData(), GL_STREAM_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
#else
glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer);
glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
#endif
}
if (!batch._namedData.empty()) {
@ -64,15 +58,11 @@ void GL41Backend::transferTransformState(const Batch& batch) const {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
#ifdef GPU_SSBO_DRAW_CALL_INFO
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer);
#else
glActiveTexture(GL_TEXTURE0 + TRANSFORM_OBJECT_SLOT);
glActiveTexture(GL_TEXTURE0 + GL41Backend::TRANSFORM_OBJECT_SLOT);
glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture);
if (!batch._objects.empty()) {
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer);
}
#endif
CHECK_GL_ERROR();

View file

@ -18,6 +18,7 @@
#define INCREMENTAL_TRANSFER 0
#define THREADED_TEXTURE_BUFFERING 1
#define GPU_SSBO_TRANSFORM_OBJECT 1
namespace gpu { namespace gl45 {
@ -30,6 +31,13 @@ class GL45Backend : public GLBackend {
friend class Context;
public:
#ifdef GPU_SSBO_TRANSFORM_OBJECT
static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot
#else
static const GLint TRANSFORM_OBJECT_SLOT { 31 }; // TBO binding slot
#endif
explicit GL45Backend(bool syncCache) : Parent(syncCache) {}
GL45Backend() : Parent() {}
@ -40,7 +48,7 @@ public:
protected:
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
void generateMips() const override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
virtual void syncSampler() const;
};
@ -177,9 +185,18 @@ protected:
void initTransform() override;
void updateTransform(const Batch& batch) override;
// Resource Stage
bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) override;
void releaseResourceBuffer(uint32_t slot) override;
// Output stage
void do_blit(const Batch& batch, size_t paramOffset) override;
// Shader Stage
std::string getBackendShaderHeader() const override;
void makeProgramBindings(ShaderObject& shaderObject) override;
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
// Texture Management Stage
void initTextureManagementStage() override;
};

View file

@ -54,3 +54,29 @@ GLuint GL45Backend::getBufferID(const Buffer& buffer) {
GLBuffer* GL45Backend::syncGPUObject(const Buffer& buffer) {
return GL45Buffer::sync<GL45Buffer>(*this, buffer);
}
bool GL45Backend::bindResourceBuffer(uint32_t slot, BufferPointer& buffer) {
GLBuffer* object = syncGPUObject((*buffer));
if (object) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, object->_id);
(void)CHECK_GL_ERROR();
_resource._buffers[slot] = buffer;
return true;
}
return false;
}
void GL45Backend::releaseResourceBuffer(uint32_t slot) {
auto& buf = _resource._buffers[slot];
if (buf) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, 0);
buf.reset();
}
}

View file

@ -0,0 +1,169 @@
//
// Created by Sam Gateau on 2017/04/13
// Copyright 2013-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
//
#include "GL45Backend.h"
#include "../gl/GLShader.h"
//#include <gl/GLShaders.h>
using namespace gpu;
using namespace gpu::gl45;
// GLSL version
std::string GL45Backend::getBackendShaderHeader() const {
const char header[] =
R"GLSL(#version 450 core
#define GPU_GL450
)GLSL"
#ifdef GPU_SSBO_TRANSFORM_OBJECT
R"GLSL(#define GPU_SSBO_TRANSFORM_OBJECT 1)GLSL"
#endif
;
return std::string(header);
}
int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
GLint buffersCount = 0;
glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumResourceBufferSlots = 0;
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots);
std::vector<GLint> resourceBufferSlotMap(maxNumResourceBufferSlots, -1);
struct ResourceBlockInfo {
using Vector = std::vector<ResourceBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE};
glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding);
}
};
ResourceBlockInfo::Vector resourceBlocks;
resourceBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
resourceBlocks.push_back(ResourceBlockInfo(glprogram, i));
}
for (auto& info : resourceBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
resourceBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : resourceBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is -1, or the binding maps to an already used binding
if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), GLBackend::isUnusedSlot);
if (slotIt != resourceBufferSlotMap.end()) {
info.binding = slotIt - resourceBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should never happen, an active ssbo cannot find an available slot among the max available?!
info.binding = -1;
}
}
resourceBufferSlotMap[info.binding] = info.index;
}
for (auto& info : resourceBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER);
resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
/*
GLint ssboCount = 0;
glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssboCount);
if (ssboCount > 0) {
GLint maxNameLength = 0;
glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &maxNameLength);
std::vector<GLchar> nameBytes(maxNameLength);
for (GLint b = 0; b < ssboCount; b++) {
GLint length;
glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, b, maxNameLength, &length, nameBytes.data());
std::string bufferName(nameBytes.data());
GLenum props = GL_BUFFER_BINDING;
GLint binding = -1;
glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, b, 1, &props, 1, nullptr, &binding);
auto requestedBinding = slotBindings.find(std::string(bufferName));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glShaderStorageBlockBinding(glprogram, b, binding);
}
}
static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER);
resourceBuffers.insert(Shader::Slot(bufferName, binding, element, -1));
}
}
return ssboCount;*/
}
void GL45Backend::makeProgramBindings(gl::ShaderObject& shaderObject) {
if (!shaderObject.glprogram) {
return;
}
GLuint glprogram = shaderObject.glprogram;
GLint loc = -1;
GLBackend::makeProgramBindings(shaderObject);
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
#ifdef GPU_SSBO_TRANSFORM_OBJECT
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
if (loc >= 0) {
glShaderStorageBlockBinding(glprogram, loc, GL45Backend::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = GL45Backend::TRANSFORM_OBJECT_SLOT;
}
#else
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glProgramUniform1i(glprogram, loc, GL45Backend::TRANSFORM_OBJECT_SLOT);
shaderObject.transformObjectSlot = GL45Backend::TRANSFORM_OBJECT_SLOT;
}
#endif
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
(void)CHECK_GL_ERROR();
}

View file

@ -117,17 +117,47 @@ void GL45Texture::generateMips() const {
(void)CHECK_GL_ERROR();
}
void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
break;
}
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// DSA ARB does not work on AMD, so use EXT
// unless EXT is not available on the driver
if (glTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
} else {
glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer);
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
if (glCompressedTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
} else {
glCompressedTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
}
break;
default:
// DSA ARB does not work on AMD, so use EXT
// unless EXT is not available on the driver
if (glTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
} else {
glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer);
}
break;
}
} else {
Q_ASSERT(false);

View file

@ -19,7 +19,10 @@ void GL45Backend::initTransform() {
_transform._objectBuffer = transformBuffers[0];
_transform._cameraBuffer = transformBuffers[1];
_transform._drawCallInfoBuffer = transformBuffers[2];
#ifdef GPU_SSBO_TRANSFORM_OBJECT
#else
glCreateTextures(GL_TEXTURE_BUFFER, 1, &_transform._objectBufferTexture);
#endif
size_t cameraSize = sizeof(TransformStageState::CameraBufferElement);
while (_transform._cameraUboSize < cameraSize) {
_transform._cameraUboSize += _uboAlignment;
@ -53,10 +56,10 @@ void GL45Backend::transferTransformState(const Batch& batch) const {
glNamedBufferData(_transform._drawCallInfoBuffer, bufferData.size(), bufferData.data(), GL_STREAM_DRAW);
}
#ifdef GPU_SSBO_DRAW_CALL_INFO
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer);
#ifdef GPU_SSBO_TRANSFORM_OBJECT
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, GL45Backend::TRANSFORM_OBJECT_SLOT, _transform._objectBuffer);
#else
glActiveTexture(GL_TEXTURE0 + TRANSFORM_OBJECT_SLOT);
glActiveTexture(GL_TEXTURE0 + GL45Backend::TRANSFORM_OBJECT_SLOT);
glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture);
glTextureBuffer(_transform._objectBufferTexture, GL_RGBA32F, _transform._objectBuffer);
#endif

View file

@ -292,8 +292,16 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) {
setUniformBuffer(slot, view._buffer, view._offset, view._size);
}
void Batch::setResourceBuffer(uint32 slot, const BufferPointer& buffer) {
ADD_COMMAND(setResourceBuffer);
_params.emplace_back(_buffers.cache(buffer));
_params.emplace_back(slot);
}
void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) {
ADD_COMMAND(setResourceTexture);
_params.emplace_back(_textures.cache(texture));
_params.emplace_back(slot);
}

View file

@ -30,12 +30,6 @@ class QDebug;
namespace gpu {
enum ReservedSlot {
#ifdef GPU_SSBO_DRAW_CALL_INFO
TRANSFORM_OBJECT_SLOT = 14,
#else
TRANSFORM_OBJECT_SLOT = 31,
#endif
TRANSFORM_CAMERA_SLOT = 15,
};
@ -189,9 +183,12 @@ public:
void setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size);
void setUniformBuffer(uint32 slot, const BufferView& view); // not a command, just a shortcut from a BufferView
void setResourceTexture(uint32 slot, const TexturePointer& view);
void setResourceBuffer(uint32 slot, const BufferPointer& buffer);
void setResourceTexture(uint32 slot, const TexturePointer& texture);
void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView
// Ouput Stage
void setFramebuffer(const FramebufferPointer& framebuffer);
@ -290,6 +287,7 @@ public:
COMMAND_setStateScissorRect,
COMMAND_setUniformBuffer,
COMMAND_setResourceBuffer,
COMMAND_setResourceTexture,
COMMAND_setFramebuffer,

View file

@ -34,6 +34,7 @@ public:
int _ISNumInputBufferChanges = 0;
int _ISNumIndexBufferChanges = 0;
int _RSNumResourceBufferBounded = 0;
int _RSNumTextureBounded = 0;
int _RSAmountTextureMemoryBounded = 0;

View file

@ -19,6 +19,12 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA };
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
const Element Element::COLOR_COMPRESSED_RED{ VEC4, NUINT8, COMPRESSED_BC4_RED };
const Element Element::COLOR_COMPRESSED_SRGB{ VEC4, NUINT8, COMPRESSED_BC1_SRGB };
const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1_SRGBA };
const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA };
const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY };
const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 };
const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA };
const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };

View file

@ -157,20 +157,12 @@ enum Semantic {
// These are generic compression format smeantic for images
_FIRST_COMPRESSED,
COMPRESSED_R,
COMPRESSED_RGB,
COMPRESSED_RGBA,
COMPRESSED_SRGB,
COMPRESSED_SRGBA,
// FIXME: Will have to be supported later:
/*COMPRESSED_BC3_RGBA, // RGBA_S3TC_DXT5_EXT,
COMPRESSED_BC3_SRGBA, // SRGB_ALPHA_S3TC_DXT5_EXT
COMPRESSED_BC7_RGBA,
COMPRESSED_BC7_SRGBA, */
COMPRESSED_BC1_SRGB,
COMPRESSED_BC1_SRGBA,
COMPRESSED_BC3_SRGBA,
COMPRESSED_BC4_RED,
COMPRESSED_BC5_XY,
_LAST_COMPRESSED,
@ -178,6 +170,7 @@ enum Semantic {
UNIFORM,
UNIFORM_BUFFER,
RESOURCE_BUFFER,
SAMPLER,
SAMPLER_MULTISAMPLE,
SAMPLER_SHADOW,
@ -236,6 +229,11 @@ public:
static const Element COLOR_BGRA_32;
static const Element COLOR_SBGRA_32;
static const Element COLOR_R11G11B10;
static const Element COLOR_COMPRESSED_RED;
static const Element COLOR_COMPRESSED_SRGB;
static const Element COLOR_COMPRESSED_SRGBA_MASK;
static const Element COLOR_COMPRESSED_SRGBA;
static const Element COLOR_COMPRESSED_XY;
static const Element VEC4F_COLOR_RGBA;
static const Element VEC2F_UV;
static const Element VEC2F_XY;

View file

@ -72,9 +72,10 @@ Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer
return Pointer();
}
void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) {
void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& uniformBuffers, const SlotSet& resourceBuffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) {
_uniforms = uniforms;
_buffers = buffers;
_uniformBuffers = uniformBuffers;
_resourceBuffers = resourceBuffers;
_textures = textures;
_samplers = samplers;
_inputs = inputs;

View file

@ -134,10 +134,11 @@ public:
// Access the exposed uniform, input and output slot
const SlotSet& getUniforms() const { return _uniforms; }
const SlotSet& getBuffers() const { return _buffers; }
const SlotSet& getUniformBuffers() const { return _uniformBuffers; }
const SlotSet& getResourceBuffers() const { return _resourceBuffers; }
const SlotSet& getTextures() const { return _textures; }
const SlotSet& getSamplers() const { return _samplers; }
const SlotSet& getInputs() const { return _inputs; }
const SlotSet& getOutputs() const { return _outputs; }
@ -146,7 +147,13 @@ public:
// to correctly bind resource to the shader.
// These can be build "manually" from knowledge of the atual shader code
// or automatically by calling "makeShader()", this is the preferred way
void defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs);
void defineSlots(const SlotSet& uniforms,
const SlotSet& uniformBuffers,
const SlotSet& resourceBuffers,
const SlotSet& textures,
const SlotSet& samplers,
const SlotSet& inputs,
const SlotSet& outputs);
// makeProgram(...) make a program shader ready to be used in a Batch.
// It compiles the sub shaders, link them and defines the Slots and their bindings.
@ -181,7 +188,8 @@ protected:
// List of exposed uniform, input and output slots
SlotSet _uniforms;
SlotSet _buffers;
SlotSet _uniformBuffers;
SlotSet _resourceBuffers;
SlotSet _textures;
SlotSet _samplers;
SlotSet _inputs;

View file

@ -212,8 +212,8 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s
}
}
Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
Texture* tex = new Texture(TextureUsageType::EXTERNAL);
TexturePointer Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
TexturePointer tex = std::make_shared<Texture>(TextureUsageType::EXTERNAL);
tex->_type = TEX_2D;
tex->_maxMipLevel = 0;
tex->_sampler = sampler;
@ -221,36 +221,36 @@ Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler
return tex;
}
Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
}
Texture* Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, numMips, sampler);
}
Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
}
Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
}
Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, numMips, sampler);
}
Texture* Texture::createCube(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
TexturePointer Texture::createCube(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, numMips, sampler);
}
Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler)
TexturePointer Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler)
{
Texture* tex = new Texture(usageType);
TexturePointer tex = std::make_shared<Texture>(usageType);
tex->_storage.reset(new MemoryStorage());
tex->_type = type;
tex->_storage->assignTexture(tex);
tex->_storage->assignTexture(tex.get());
tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices, numMips);
tex->_sampler = sampler;
@ -434,7 +434,7 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) {
// THen check that the mem texture passed make sense with its format
Size expectedSize = evalStoredMipSize(level, getStoredMipFormat());
auto size = storage->size();
if (storage->size() == expectedSize) {
if (storage->size() <= expectedSize) {
_storage->assignMipData(level, storage);
_stamp++;
} else if (size > expectedSize) {
@ -461,7 +461,7 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin
// THen check that the mem texture passed make sense with its format
Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat());
auto size = storage->size();
if (size == expectedSize) {
if (size <= expectedSize) {
_storage->assignMipFaceData(level, face, storage);
_stamp++;
} else if (size > expectedSize) {
@ -752,7 +752,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
boffset = 0;
}
auto data = cubeTexture.accessStoredMipFace(0,face)->readData();
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
if (data == nullptr) {
continue;
}

View file

@ -328,13 +328,13 @@ public:
static const uint16 MAX_NUM_MIPS = 0;
static const uint16 SINGLE_MIP = 1;
static Texture* create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static Texture* createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler());
static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
static TexturePointer create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler());
static TexturePointer createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
// After the texture has been created, it should be defined
bool isDefined() const { return _defined; }
@ -435,6 +435,8 @@ public:
// For convenience assign a source name
const std::string& source() const { return _source; }
void setSource(const std::string& source) { _source = source; }
const std::string& sourceHash() const { return _sourceHash; }
void setSourceHash(const std::string& sourceHash) { _sourceHash = sourceHash; }
// Potentially change the minimum mip (mostly for debugging purpose)
bool setMinMip(uint16 newMinMip);
@ -482,6 +484,7 @@ public:
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture
bool generateIrradiance();
const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; }
void overrideIrradiance(SHPointer irradiance) { _irradiance = irradiance; }
bool isIrradianceValid() const { return _isIrradianceValid; }
// Own sampler
@ -502,7 +505,7 @@ public:
// Textures can be serialized directly to ktx data file, here is how
static ktx::KTXUniquePointer serialize(const Texture& texture);
static Texture* unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc());
static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc());
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
@ -518,6 +521,7 @@ protected:
std::weak_ptr<Texture> _fallback;
// Not strictly necessary, but incredibly useful for debugging
std::string _source;
std::string _sourceHash;
std::unique_ptr< Storage > _storage;
Stamp _stamp { 0 };
@ -552,7 +556,7 @@ protected:
bool _isIrradianceValid = false;
bool _defined = false;
static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
static TexturePointer create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips);
};

View file

@ -23,6 +23,7 @@ struct GPUKTXPayload {
Texture::Usage _usage;
TextureUsageType _usageType;
static std::string KEY;
static bool isGPUKTX(const ktx::KeyValue& val) {
return (val._key.compare(KEY) == 0);
@ -161,7 +162,13 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
keyval._usage = texture.getUsage();
keyval._usageType = texture.getUsageType();
ktx::KeyValues keyValues;
keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval));
keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval));
static const std::string SOURCE_HASH_KEY = "hifi.sourceHash";
auto hash = texture.sourceHash();
if (!hash.empty()) {
keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str()));
}
auto ktxBuffer = ktx::KTX::create(header, images, keyValues);
#if 0
@ -193,7 +200,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
return ktxBuffer;
}
Texture* Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) {
TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(ktx::StoragePointer { new storage::FileStorage(ktxfile.c_str()) });
if (!ktxPointer) {
return nullptr;
@ -260,6 +267,16 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA);
} else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) {
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGBA);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA && mipFormat == Format::COLOR_COMPRESSED_SRGBA) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, ktx::GLBaseInternalFormat::RGBA);
} else if (texelFormat == Format::COLOR_COMPRESSED_RED && mipFormat == Format::COLOR_COMPRESSED_RED) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED);
} else if (texelFormat == Format::COLOR_COMPRESSED_XY && mipFormat == Format::COLOR_COMPRESSED_XY) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG);
} else {
return false;
}
@ -295,6 +312,25 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
} else {
return false;
}
} else if (header.getGLFormat() == ktx::GLFormat::COMPRESSED_FORMAT && header.getGLType() == ktx::GLType::COMPRESSED_TYPE) {
if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
mipFormat = Format::COLOR_COMPRESSED_SRGB;
texelFormat = Format::COLOR_COMPRESSED_SRGB;
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
mipFormat = Format::COLOR_COMPRESSED_SRGBA_MASK;
texelFormat = Format::COLOR_COMPRESSED_SRGBA_MASK;
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
mipFormat = Format::COLOR_COMPRESSED_SRGBA;
texelFormat = Format::COLOR_COMPRESSED_SRGBA;
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) {
mipFormat = Format::COLOR_COMPRESSED_RED;
texelFormat = Format::COLOR_COMPRESSED_RED;
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) {
mipFormat = Format::COLOR_COMPRESSED_XY;
texelFormat = Format::COLOR_COMPRESSED_XY;
} else {
return false;
}
} else {
return false;
}

View file

@ -119,15 +119,15 @@ struct TransformObject {
layout(location=15) in ivec2 _drawCallInfo;
<@if FALSE @>
// Disable SSBOs for now
#if defined(GPU_SSBO_TRANSFORM_OBJECT)
layout(std140) buffer transformObjectBuffer {
TransformObject _object[];
};
TransformObject getTransformObject() {
return _object[_drawCallInfo.x];
TransformObject transformObject = _object[_drawCallInfo.x];
return transformObject;
}
<@else@>
#else
uniform samplerBuffer transformObjectBuffer;
TransformObject getTransformObject() {
@ -145,7 +145,8 @@ TransformObject getTransformObject() {
return object;
}
<@endif@>
#endif
<@endfunc@>

View file

@ -0,0 +1,11 @@
set(TARGET_NAME image)
setup_hifi_library()
link_hifi_libraries(shared gpu)
target_glm()
add_dependency_external_projects(nvtt)
find_package(NVTT REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES})
add_paths_to_fixup_libs(${NVTT_DLL_PATH})

View file

@ -0,0 +1,934 @@
//
// Image.cpp
// image/src/image
//
// Created by Clement Brisset on 4/5/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
//
#include "Image.h"
#include <nvtt/nvtt.h>
#include <QUrl>
#include <QImage>
#include <QBuffer>
#include <QImageReader>
#include <Finally.h>
#include <Profile.h>
#include <StatTracker.h>
#include <GLMHelpers.h>
#include "ImageLogging.h"
using namespace gpu;
#define CPU_MIPMAPS 1
#define COMPRESS_COLOR_TEXTURES 0
#define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now
#define COMPRESS_GRAYSCALE_TEXTURES 0
#define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
bool needsSparseRectification(const glm::uvec2& size) {
// Don't attempt to rectify small textures (textures less than the sparse page size in any dimension)
if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) {
return false;
}
// Don't rectify textures that are already an exact multiple of sparse page size
if (glm::uvec2(0) == (size % SPARSE_PAGE_SIZE)) {
return false;
}
// Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify!
return true;
}
glm::uvec2 rectifyToSparseSize(const glm::uvec2& size) {
glm::uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, glm::uvec2(0), glm::uvec2(1)));
glm::uvec2 result = pages * SPARSE_PAGE_SIZE;
return result;
}
namespace image {
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) {
case ALBEDO_TEXTURE:
return image::TextureUsage::createAlbedoTextureFromImage;
case EMISSIVE_TEXTURE:
return image::TextureUsage::createEmissiveTextureFromImage;
case LIGHTMAP_TEXTURE:
return image::TextureUsage::createLightmapTextureFromImage;
case CUBE_TEXTURE:
if (options.value("generateIrradiance", true).toBool()) {
return image::TextureUsage::createCubeTextureFromImage;
} else {
return image::TextureUsage::createCubeTextureFromImageWithoutIrradiance;
}
case BUMP_TEXTURE:
return image::TextureUsage::createNormalTextureFromBumpImage;
case NORMAL_TEXTURE:
return image::TextureUsage::createNormalTextureFromNormalImage;
case ROUGHNESS_TEXTURE:
return image::TextureUsage::createRoughnessTextureFromImage;
case GLOSS_TEXTURE:
return image::TextureUsage::createRoughnessTextureFromGlossImage;
case SPECULAR_TEXTURE:
return image::TextureUsage::createMetallicTextureFromImage;
case STRICT_TEXTURE:
return image::TextureUsage::createStrict2DTextureFromImage;
case DEFAULT_TEXTURE:
default:
return image::TextureUsage::create2DTextureFromImage;
}
}
gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, srcImageName, true);
}
gpu::TexturePointer TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureNormalMapFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureNormalMapFromImage(srcImage, srcImageName, true);
}
gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, true);
}
gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return processCubeTextureColorFromImage(srcImage, srcImageName, true);
}
gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) {
return processCubeTextureColorFromImage(srcImage, srcImageName, false);
}
gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) {
// Help the QImage loader by extracting the image file format from the url filename ext.
// Some tga are not created properly without it.
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
QBuffer buffer;
buffer.setData(content);
QImageReader imageReader(&buffer, filenameExtension.c_str());
QImage image;
if (imageReader.canRead()) {
image = imageReader.read();
} else {
// Extension could be incorrect, try to detect the format from the content
QImageReader newImageReader;
newImageReader.setDecideFormatFromContent(true);
buffer.setData(content);
newImageReader.setDevice(&buffer);
if (newImageReader.canRead()) {
qCWarning(imagelogging) << "Image file" << filename.c_str() << "has extension" << filenameExtension.c_str()
<< "but is actually a" << qPrintable(newImageReader.format()) << "(recovering)";
image = newImageReader.read();
}
}
int imageWidth = image.width();
int imageHeight = image.height();
// Validate that the image loaded
if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) {
QString reason(image.format() == QImage::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
qCWarning(imagelogging) << "Failed to load" << filename.c_str() << qPrintable(reason);
return nullptr;
}
// Validate the image is less than _maxNumPixels, and downscale if necessary
if (imageWidth * imageHeight > maxNumPixels) {
float scaleFactor = sqrtf(maxNumPixels / (float)(imageWidth * imageHeight));
int originalWidth = imageWidth;
int originalHeight = imageHeight;
imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f);
imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f);
QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
image.swap(newImage);
qCDebug(imagelogging).nospace() << "Downscaled " << filename.c_str() << " (" <<
QSize(originalWidth, originalHeight) << " to " <<
QSize(imageWidth, imageHeight) << ")";
}
auto loader = TextureUsage::getTextureLoaderForType(textureType);
auto texture = loader(image, filename);
return texture;
}
QImage processSourceImage(const QImage& srcImage, bool cubemap) {
PROFILE_RANGE(resource_parse, "processSourceImage");
const glm::uvec2 srcImageSize = toGlm(srcImage.size());
glm::uvec2 targetSize = srcImageSize;
while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) {
targetSize /= 2;
}
if (targetSize != srcImageSize) {
++DECIMATED_TEXTURE_COUNT;
}
if (!cubemap && needsSparseRectification(targetSize)) {
++RECTIFIED_TEXTURE_COUNT;
targetSize = rectifyToSparseSize(targetSize);
}
if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, glm::uvec2(2)))) {
targetSize /= 2;
}
if (targetSize != srcImageSize) {
PROFILE_RANGE(resource_parse, "processSourceImage Rectify");
qCDebug(imagelogging) << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y;
return srcImage.scaled(fromGlm(targetSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
return srcImage;
}
struct MyOutputHandler : public nvtt::OutputHandler {
MyOutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {}
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) override {
_size = size;
_miplevel = miplevel;
_data = static_cast<gpu::Byte*>(malloc(size));
_current = _data;
}
virtual bool writeData(const void* data, int size) override {
assert(_current + size <= _data + _size);
memcpy(_current, data, size);
_current += size;
return true;
}
virtual void endImage() override {
if (_face >= 0) {
_texture->assignStoredMipFace(_miplevel, _face, _size, static_cast<const gpu::Byte*>(_data));
} else {
_texture->assignStoredMip(_miplevel, _size, static_cast<const gpu::Byte*>(_data));
}
free(_data);
_data = nullptr;
}
gpu::Byte* _data{ nullptr };
gpu::Byte* _current{ nullptr };
gpu::Texture* _texture{ nullptr };
int _miplevel = 0;
int _size = 0;
int _face = -1;
};
struct MyErrorHandler : public nvtt::ErrorHandler {
virtual void error(nvtt::Error e) override {
qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e);
}
};
void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
#if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips");
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
const int width = image.width(), height = image.height();
const void* data = static_cast<const void*>(image.constBits());
nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Repeat;
nvtt::RoundMode roundMode = nvtt::RoundMode_None;
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
float inputGamma = 2.2f;
float outputGamma = 2.2f;
nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production);
auto mipFormat = texture->getStoredMipFormat();
if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGB) {
compressionOptions.setFormat(nvtt::Format_BC1);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGBA_MASK) {
alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC1a);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGBA) {
alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC3);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_RED) {
compressionOptions.setFormat(nvtt::Format_BC4);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_XY) {
compressionOptions.setFormat(nvtt::Format_BC5);
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
inputGamma = 1.0f;
outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
inputGamma = 1.0f;
outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_R_8) {
compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(8, 0, 0, 0);
} else {
qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE();
return;
}
nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(textureType, width, height);
inputOptions.setMipmapData(data, width, height);
inputOptions.setFormat(inputFormat);
inputOptions.setGamma(inputGamma, outputGamma);
inputOptions.setAlphaMode(alphaMode);
inputOptions.setWrapMode(wrapMode);
inputOptions.setRoundMode(roundMode);
inputOptions.setMipmapGeneration(true);
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
nvtt::OutputOptions outputOptions;
outputOptions.setOutputHeader(false);
MyOutputHandler outputHandler(texture, face);
outputOptions.setOutputHandler(&outputHandler);
MyErrorHandler errorHandler;
outputOptions.setErrorHandler(&errorHandler);
nvtt::Compressor compressor;
compressor.process(inputOptions, compressionOptions, outputOptions);
#else
texture->autoGenerateMips(-1);
#endif
}
void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) {
PROFILE_RANGE(resource_parse, "processTextureAlpha");
validAlpha = false;
alphaAsMask = true;
const uint8 OPAQUE_ALPHA = 255;
const uint8 TRANSPARENT_ALPHA = 0;
// Figure out if we can use a mask for alpha or not
int numOpaques = 0;
int numTranslucents = 0;
const int NUM_PIXELS = srcImage.width() * srcImage.height();
const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS));
const QRgb* data = reinterpret_cast<const QRgb*>(srcImage.constBits());
for (int i = 0; i < NUM_PIXELS; ++i) {
auto alpha = qAlpha(data[i]);
if (alpha == OPAQUE_ALPHA) {
numOpaques++;
} else if (alpha != TRANSPARENT_ALPHA) {
if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) {
alphaAsMask = false;
break;
}
}
}
validAlpha = (numOpaques != NUM_PIXELS);
}
gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict) {
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
QImage image = processSourceImage(srcImage, false);
bool validAlpha = image.hasAlphaChannel();
bool alphaAsMask = false;
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
if (validAlpha) {
processTextureAlpha(image, validAlpha, alphaAsMask);
}
gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
#if CPU_MIPMAPS && COMPRESS_COLOR_TEXTURES
gpu::Element formatGPU;
if (validAlpha) {
formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA;
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB;
}
gpu::Element formatMip = formatGPU;
#else
gpu::Element formatMip = gpu::Element::COLOR_SBGRA_32;
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32;
#endif
if (isStrict) {
theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
} else {
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
}
theTexture->setSource(srcImageName);
auto usage = gpu::Texture::Usage::Builder().withColor();
if (validAlpha) {
usage.withAlpha();
if (alphaAsMask) {
usage.withAlphaMask();
}
}
theTexture->setUsage(usage.build());
theTexture->setStoredMipFormat(formatMip);
generateMips(theTexture.get(), image);
}
return theTexture;
}
int clampPixelCoordinate(int coordinate, int maxCoordinate) {
return coordinate - ((int)(coordinate < 0) * coordinate) + ((int)(coordinate > maxCoordinate) * (maxCoordinate - coordinate));
}
const int RGBA_MAX = 255;
// transform -1 - 1 to 0 - 255 (from sobel value to rgb)
double mapComponent(double sobelValue) {
const double factor = RGBA_MAX / 2.0;
return (sobelValue + 1.0) * factor;
}
QImage processBumpMap(QImage& image) {
if (image.format() != QImage::Format_Grayscale8) {
image = image.convertToFormat(QImage::Format_Grayscale8);
}
// PR 5540 by AlessandroSigna integrated here as a specialized TextureLoader for bumpmaps
// The conversion is done using the Sobel Filter to calculate the derivatives from the grayscale image
const double pStrength = 2.0;
int width = image.width();
int height = image.height();
QImage result(width, height, QImage::Format_ARGB32);
for (int i = 0; i < width; i++) {
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
const int iPrevClamped = clampPixelCoordinate(i - 1, width - 1);
for (int j = 0; j < height; j++) {
const int jNextClamped = clampPixelCoordinate(j + 1, height - 1);
const int jPrevClamped = clampPixelCoordinate(j - 1, height - 1);
// surrounding pixels
const QRgb topLeft = image.pixel(iPrevClamped, jPrevClamped);
const QRgb top = image.pixel(iPrevClamped, j);
const QRgb topRight = image.pixel(iPrevClamped, jNextClamped);
const QRgb right = image.pixel(i, jNextClamped);
const QRgb bottomRight = image.pixel(iNextClamped, jNextClamped);
const QRgb bottom = image.pixel(iNextClamped, j);
const QRgb bottomLeft = image.pixel(iNextClamped, jPrevClamped);
const QRgb left = image.pixel(i, jPrevClamped);
// take their gray intensities
// since it's a grayscale image, the value of each component RGB is the same
const double tl = qRed(topLeft);
const double t = qRed(top);
const double tr = qRed(topRight);
const double r = qRed(right);
const double br = qRed(bottomRight);
const double b = qRed(bottom);
const double bl = qRed(bottomLeft);
const double l = qRed(left);
// apply the sobel filter
const double dX = (tr + pStrength * r + br) - (tl + pStrength * l + bl);
const double dY = (bl + pStrength * b + br) - (tl + pStrength * t + tr);
const double dZ = RGBA_MAX / pStrength;
glm::vec3 v(dX, dY, dZ);
glm::normalize(v);
// convert to rgb from the value obtained computing the filter
QRgb qRgbValue = qRgba(mapComponent(v.z), mapComponent(v.y), mapComponent(v.x), 1.0);
result.setPixel(i, j, qRgbValue);
}
}
return result;
}
gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap) {
PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage");
QImage image = processSourceImage(srcImage, false);
if (isBumpMap) {
image = processBumpMap(image);
}
// Make sure the normal map source image is ARGB32
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
#if CPU_MIPMAPS && COMPRESS_NORMALMAP_TEXTURES
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY;
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY;
#else
gpu::Element formatMip = gpu::Element::COLOR_RGBA_32;
gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32;
#endif
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
theTexture->setStoredMipFormat(formatMip);
generateMips(theTexture.get(), image);
}
return theTexture;
}
gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels) {
PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage");
QImage image = processSourceImage(srcImage, false);
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
if (isInvertedPixels) {
// Gloss turned into Rough
image.invertPixels(QImage::InvertRgba);
}
gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
#if CPU_MIPMAPS && COMPRESS_GRAYSCALE_TEXTURES
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_RED;
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_RED;
#else
gpu::Element formatMip = gpu::Element::COLOR_R_8;
gpu::Element formatGPU = gpu::Element::COLOR_R_8;
#endif
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
theTexture->setStoredMipFormat(formatMip);
generateMips(theTexture.get(), image);
}
return theTexture;
}
class CubeLayout {
public:
enum SourceProjection {
FLAT = 0,
EQUIRECTANGULAR,
};
int _type = FLAT;
int _widthRatio = 1;
int _heightRatio = 1;
class Face {
public:
int _x = 0;
int _y = 0;
bool _horizontalMirror = false;
bool _verticalMirror = false;
Face() {}
Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}
};
Face _faceXPos;
Face _faceXNeg;
Face _faceYPos;
Face _faceYNeg;
Face _faceZPos;
Face _faceZNeg;
CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :
_type(FLAT),
_widthRatio(wr),
_heightRatio(hr),
_faceXPos(fXP),
_faceXNeg(fXN),
_faceYPos(fYP),
_faceYNeg(fYN),
_faceZPos(fZP),
_faceZNeg(fZN) {}
CubeLayout(int wr, int hr) :
_type(EQUIRECTANGULAR),
_widthRatio(wr),
_heightRatio(hr) {}
static const CubeLayout CUBEMAP_LAYOUTS[];
static const int NUM_CUBEMAP_LAYOUTS;
static int findLayout(int width, int height) {
// Find the layout of the cubemap in the 2D image
int foundLayout = -1;
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
if ((height * CUBEMAP_LAYOUTS[i]._widthRatio) == (width * CUBEMAP_LAYOUTS[i]._heightRatio)) {
foundLayout = i;
break;
}
}
return foundLayout;
}
static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) {
QImage image(faceWidth, faceWidth, source.format());
glm::vec2 dstInvSize(1.0f / (float)image.width(), 1.0f / (float)image.height());
struct CubeToXYZ {
gpu::Texture::CubeFace _face;
CubeToXYZ(gpu::Texture::CubeFace face) : _face(face) {}
glm::vec3 xyzFrom(const glm::vec2& uv) {
auto faceDir = glm::normalize(glm::vec3(-1.0f + 2.0f * uv.x, -1.0f + 2.0f * uv.y, 1.0f));
switch (_face) {
case gpu::Texture::CubeFace::CUBE_FACE_BACK_POS_Z:
return glm::vec3(-faceDir.x, faceDir.y, faceDir.z);
case gpu::Texture::CubeFace::CUBE_FACE_FRONT_NEG_Z:
return glm::vec3(faceDir.x, faceDir.y, -faceDir.z);
case gpu::Texture::CubeFace::CUBE_FACE_LEFT_NEG_X:
return glm::vec3(faceDir.z, faceDir.y, faceDir.x);
case gpu::Texture::CubeFace::CUBE_FACE_RIGHT_POS_X:
return glm::vec3(-faceDir.z, faceDir.y, -faceDir.x);
case gpu::Texture::CubeFace::CUBE_FACE_BOTTOM_NEG_Y:
return glm::vec3(-faceDir.x, -faceDir.z, faceDir.y);
case gpu::Texture::CubeFace::CUBE_FACE_TOP_POS_Y:
default:
return glm::vec3(-faceDir.x, faceDir.z, -faceDir.y);
}
}
};
CubeToXYZ cubeToXYZ(face);
struct RectToXYZ {
RectToXYZ() {}
glm::vec2 uvFrom(const glm::vec3& xyz) {
auto flatDir = glm::normalize(glm::vec2(xyz.x, xyz.z));
auto uvRad = glm::vec2(atan2(flatDir.x, flatDir.y), asin(xyz.y));
const float LON_TO_RECT_U = 1.0f / (glm::pi<float>());
const float LAT_TO_RECT_V = 2.0f / glm::pi<float>();
return glm::vec2(0.5f * uvRad.x * LON_TO_RECT_U + 0.5f, 0.5f * uvRad.y * LAT_TO_RECT_V + 0.5f);
}
};
RectToXYZ rectToXYZ;
int srcFaceHeight = source.height();
int srcFaceWidth = source.width();
glm::vec2 dstCoord;
glm::ivec2 srcPixel;
for (int y = 0; y < faceWidth; ++y) {
dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom
for (int x = 0; x < faceWidth; ++x) {
dstCoord.x = (x + 0.5f) * dstInvSize.x;
auto xyzDir = cubeToXYZ.xyzFrom(dstCoord);
auto srcCoord = rectToXYZ.uvFrom(xyzDir);
srcPixel.x = floor(srcCoord.x * srcFaceWidth);
// Flip the vertical axis to QImage going top to bottom
srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight);
if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) {
image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y)));
// Keep for debug, this is showing the dir as a color
// glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256);
// unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16);
// image.setPixel(x, y, val);
}
}
}
return image;
}
};
const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
// Here is the expected layout for the faces in an image with the 2/1 aspect ratio:
// THis is detected as an Equirectangular projection
// WIDTH
// <--------------------------->
// ^ +------+------+------+------+
// H | | | | |
// E | | | | |
// I | | | | |
// G +------+------+------+------+
// H | | | | |
// T | | | | |
// | | | | | |
// v +------+------+------+------+
//
// FaceWidth = width = height / 6
{ 2, 1 },
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
//
// WIDTH
// <------>
// ^ +------+
// | | |
// | | +X |
// | | |
// H +------+
// E | |
// I | -X |
// G | |
// H +------+
// T | |
// | | +Y |
// | | |
// | +------+
// | | |
// | | -Y |
// | | |
// H +------+
// E | |
// I | +Z |
// G | |
// H +------+
// T | |
// | | -Z |
// | | |
// V +------+
//
// FaceWidth = width = height / 6
{ 1, 6,
{ 0, 0, true, false },
{ 0, 1, true, false },
{ 0, 2, false, true },
{ 0, 3, false, true },
{ 0, 4, true, false },
{ 0, 5, true, false }
},
// Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
//
// <-----------WIDTH----------->
// ^ +------+------+------+------+
// | | | | | |
// | | | +Y | | |
// | | | | | |
// H +------+------+------+------+
// E | | | | |
// I | -X | -Z | +X | +Z |
// G | | | | |
// H +------+------+------+------+
// T | | | | |
// | | | -Y | | |
// | | | | | |
// V +------+------+------+------+
//
// FaceWidth = width / 4 = height / 3
{ 4, 3,
{ 2, 1, true, false },
{ 0, 1, true, false },
{ 1, 0, false, true },
{ 1, 2, false, true },
{ 3, 1, true, false },
{ 1, 1, true, false }
},
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
//
// <-------WIDTH-------->
// ^ +------+------+------+
// | | | | |
// | | | +Y | |
// | | | | |
// H +------+------+------+
// E | | | |
// I | -X | -Z | +X |
// G | | | |
// H +------+------+------+
// T | | | |
// | | | -Y | |
// | | | | |
// | +------+------+------+
// | | | | |
// | | | +Z! | | <+Z is upside down!
// | | | | |
// V +------+------+------+
//
// FaceWidth = width / 3 = height / 4
{ 3, 4,
{ 2, 1, true, false },
{ 0, 1, true, false },
{ 1, 0, false, true },
{ 1, 2, false, true },
{ 1, 3, false, true },
{ 1, 1, true, false }
}
};
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance) {
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
gpu::TexturePointer theTexture = nullptr;
if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
QImage image = processSourceImage(srcImage, true);
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
#if CPU_MIPMAPS && COMPRESS_CUBEMAP_TEXTURES
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA;
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA;
#else
gpu::Element formatMip = gpu::Element::COLOR_SRGBA_32;
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32;
#endif
// Find the layout of the cubemap in the 2D image
// Use the original image size since processSourceImage may have altered the size / aspect ratio
int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height());
std::vector<QImage> faces;
// If found, go extract the faces as separate images
if (foundLayout >= 0) {
auto& layout = CubeLayout::CUBEMAP_LAYOUTS[foundLayout];
if (layout._type == CubeLayout::FLAT) {
int faceWidth = image.width() / layout._widthRatio;
faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
} else if (layout._type == CubeLayout::EQUIRECTANGULAR) {
// THe face width is estimated from the input image
const int EQUIRECT_FACE_RATIO_TO_WIDTH = 4;
const int EQUIRECT_MAX_FACE_WIDTH = 2048;
int faceWidth = std::min(image.width() / EQUIRECT_FACE_RATIO_TO_WIDTH, EQUIRECT_MAX_FACE_WIDTH);
for (int face = gpu::Texture::CUBE_FACE_RIGHT_POS_X; face < gpu::Texture::NUM_CUBE_FACES; face++) {
QImage faceImage = CubeLayout::extractEquirectangularFace(image, (gpu::Texture::CubeFace) face, faceWidth);
faces.push_back(faceImage);
}
}
} else {
qCDebug(imagelogging) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
return nullptr;
}
// If the 6 faces have been created go on and define the true Texture
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
theTexture->setSource(srcImageName);
theTexture->setStoredMipFormat(formatMip);
for (uint8 face = 0; face < faces.size(); ++face) {
generateMips(theTexture.get(), faces[face], face);
}
// Generate irradiance while we are at it
if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "generateIrradiance");
auto irradianceTexture = gpu::Texture::createCube(gpu::Element::COLOR_SRGBA_32, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
irradianceTexture->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(gpu::Element::COLOR_SBGRA_32);
for (uint8 face = 0; face < faces.size(); ++face) {
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
}
irradianceTexture->generateIrradiance();
auto irradiance = irradianceTexture->getIrradiance();
theTexture->overrideIrradiance(irradiance);
}
}
}
return theTexture;
}
} // namespace image

View file

@ -0,0 +1,70 @@
//
// Image.h
// image/src/image
//
// Created by Clement Brisset on 4/5/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
//
#ifndef hifi_image_Image_h
#define hifi_image_Image_h
#include <QVariant>
#include <gpu/Texture.h>
class QByteArray;
class QImage;
namespace image {
namespace TextureUsage {
enum Type {
DEFAULT_TEXTURE,
STRICT_TEXTURE,
ALBEDO_TEXTURE,
NORMAL_TEXTURE,
BUMP_TEXTURE,
SPECULAR_TEXTURE,
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
ROUGHNESS_TEXTURE,
GLOSS_TEXTURE,
EMISSIVE_TEXTURE,
CUBE_TEXTURE,
OCCLUSION_TEXTURE,
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
LIGHTMAP_TEXTURE
};
using TextureLoader = std::function<gpu::TexturePointer(const QImage&, const std::string&)>;
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
gpu::TexturePointer create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);
gpu::TexturePointer process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict);
gpu::TexturePointer process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap);
gpu::TexturePointer process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels);
gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance);
} // namespace TextureUsage
gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType);
} // namespace image
#endif // hifi_image_Image_h

View file

@ -0,0 +1,14 @@
//
// ImageLogging.cpp
// image/src/image
//
// Created by Clement Brisset on 4/5/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
//
#include "ImageLogging.h"
Q_LOGGING_CATEGORY(imagelogging, "hifi.image")

View file

@ -0,0 +1,14 @@
//
// ImageLogging.h
// image/src/image
//
// Created by Clement Brisset on 4/5/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
//
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(imagelogging)

View file

@ -101,8 +101,6 @@ namespace ktx {
UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B,
UNSIGNED_INT_5_9_9_9_REV = 0x8C3E,
FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD,
NUM_GLTYPES = 25,
};
enum class GLFormat : uint32_t {
@ -130,8 +128,6 @@ namespace ktx {
RGBA_INTEGER = 0x8D99,
BGR_INTEGER = 0x8D9A,
BGRA_INTEGER = 0x8D9B,
NUM_GLFORMATS = 20,
};
enum class GLInternalFormat_Uncompressed : uint32_t {
@ -232,8 +228,6 @@ namespace ktx {
STENCIL_INDEX4 = 0x8D47,
STENCIL_INDEX8 = 0x8D48,
STENCIL_INDEX16 = 0x8D49,
NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74,
};
enum class GLInternalFormat_Compressed : uint32_t {
@ -246,6 +240,11 @@ namespace ktx {
COMPRESSED_SRGB = 0x8C48,
COMPRESSED_SRGB_ALPHA = 0x8C49,
COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C,
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D,
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E,
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F,
COMPRESSED_RED_RGTC1 = 0x8DBB,
COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC,
COMPRESSED_RG_RGTC2 = 0x8DBD,
@ -267,8 +266,6 @@ namespace ktx {
COMPRESSED_SIGNED_R11_EAC = 0x9271,
COMPRESSED_RG11_EAC = 0x9272,
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
NUM_COMPRESSED_GLINTERNALFORMATS = 24,
};
enum class GLBaseInternalFormat : uint32_t {
@ -280,8 +277,6 @@ namespace ktx {
RGB = 0x1907,
RGBA = 0x1908,
STENCIL_INDEX = 0x1901,
NUM_GLBASEINTERNALFORMATS = 7,
};
enum CubeMapFace {

View file

@ -1,4 +1,4 @@
set(TARGET_NAME model-networking)
setup_hifi_library()
link_hifi_libraries(shared networking model fbx ktx)
link_hifi_libraries(shared networking model fbx ktx image)

View file

@ -489,7 +489,7 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
}
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
TextureType type, MapChannel channel) {
image::TextureUsage::Type type, MapChannel channel) {
const auto url = getTextureUrl(baseUrl, fbxTexture);
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels);
_textures[channel] = Texture { fbxTexture.name, texture };
@ -503,7 +503,7 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c
return map;
}
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel) {
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type);
_textures[channel].texture = texture;
@ -518,7 +518,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
{
_textures = Textures(MapChannel::NUM_MAP_CHANNELS);
if (!material.albedoTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
_albedoTransform = material.albedoTexture.transform;
map->setTextureTransform(_albedoTransform);
@ -535,45 +535,45 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
if (!material.normalTexture.filename.isEmpty()) {
auto type = (material.normalTexture.isBumpmap ? NetworkTexture::BUMP_TEXTURE : NetworkTexture::NORMAL_TEXTURE);
auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
setTextureMap(MapChannel::NORMAL_MAP, map);
}
if (!material.roughnessTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
} else if (!material.glossTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, NetworkTexture::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
}
if (!material.metallicTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
setTextureMap(MapChannel::METALLIC_MAP, map);
} else if (!material.specularTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, NetworkTexture::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
setTextureMap(MapChannel::METALLIC_MAP, map);
}
if (!material.occlusionTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
map->setTextureTransform(material.occlusionTexture.transform);
setTextureMap(MapChannel::OCCLUSION_MAP, map);
}
if (!material.emissiveTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
setTextureMap(MapChannel::EMISSIVE_MAP, map);
}
if (!material.scatteringTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, NetworkTexture::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
setTextureMap(MapChannel::SCATTERING_MAP, map);
}
if (!material.lightmapTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
_lightmapTransform = material.lightmapTexture.transform;
_lightmapParams = material.lightmapParams;
map->setTextureTransform(_lightmapTransform);
@ -596,7 +596,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!albedoName.isEmpty()) {
auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
map->setTextureTransform(_albedoTransform);
// when reassigning the albedo texture we also check for the alpha channel used as opacity
map->setUseAlphaChannel(true);
@ -605,45 +605,45 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!normalName.isEmpty()) {
auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
setTextureMap(MapChannel::NORMAL_MAP, map);
}
if (!roughnessName.isEmpty()) {
auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
// FIXME: If passing a gloss map instead of a roughmap how do we know?
auto map = fetchTextureMap(url, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
}
if (!metallicName.isEmpty()) {
auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
// FIXME: If passing a specular map instead of a metallic how do we know?
auto map = fetchTextureMap(url, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
setTextureMap(MapChannel::METALLIC_MAP, map);
}
if (!occlusionName.isEmpty()) {
auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
setTextureMap(MapChannel::OCCLUSION_MAP, map);
}
if (!emissiveName.isEmpty()) {
auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
setTextureMap(MapChannel::EMISSIVE_MAP, map);
}
if (!scatteringName.isEmpty()) {
auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
setTextureMap(MapChannel::SCATTERING_MAP, map);
}
if (!lightmapName.isEmpty()) {
auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
auto map = fetchTextureMap(url, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
map->setTextureTransform(_lightmapTransform);
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
setTextureMap(MapChannel::LIGHTMAP_MAP, map);

View file

@ -180,13 +180,11 @@ protected:
const bool& isOriginal() const { return _isOriginal; }
private:
using TextureType = NetworkTexture::Type;
// Helpers for the ctors
QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture);
model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
TextureType type, MapChannel channel);
model::TextureMapPointer fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel);
image::TextureUsage::Type type, MapChannel channel);
model::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
Transform _albedoTransform;
Transform _lightmapTransform;

View file

@ -13,11 +13,12 @@
#include <mutex>
#include <QNetworkReply>
#include <QPainter>
#include <QCryptographicHash>
#include <QImageReader>
#include <QRunnable>
#include <QThreadPool>
#include <QImageReader>
#include <QNetworkReply>
#include <QPainter>
#if DEBUG_DUMP_TEXTURE_LOADS
#include <QtCore/QFile>
@ -31,10 +32,13 @@
#include <ktx/KTX.h>
#include <image/Image.h>
#include <NumericalConstants.h>
#include <shared/NsightHelpers.h>
#include <Finally.h>
#include <Profile.h>
#include "ModelNetworkingLogging.h"
#include <Trace.h>
@ -51,16 +55,6 @@ TextureCache::TextureCache() :
_ktxCache(KTX_DIRNAME, KTX_EXT) {
setUnusedResourceCacheSize(0);
setObjectName("TextureCache");
// Expose enum Type to JS/QML via properties
// Despite being one-off, this should be fine, because TextureCache is a SINGLETON_DEPENDENCY
QObject* type = new QObject(this);
type->setObjectName("TextureType");
setProperty("Type", QVariant::fromValue(type));
auto metaEnum = QMetaEnum::fromType<Type>();
for (int i = 0; i < metaEnum.keyCount(); ++i) {
type->setProperty(metaEnum.key(i), metaEnum.value(i));
}
}
TextureCache::~TextureCache() {
@ -117,7 +111,7 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() {
data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;
}
_permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2));
_permutationNormalTexture = gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2);
_permutationNormalTexture->setStoredMipFormat(_permutationNormalTexture->getTexelFormat());
_permutationNormalTexture->assignStoredMip(0, sizeof(data), data);
}
@ -131,7 +125,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF };
const gpu::TexturePointer& TextureCache::getWhiteTexture() {
if (!_whiteTexture) {
_whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
_whiteTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
_whiteTexture->setSource("TextureCache::_whiteTexture");
_whiteTexture->setStoredMipFormat(_whiteTexture->getTexelFormat());
_whiteTexture->assignStoredMip(0, sizeof(OPAQUE_WHITE), OPAQUE_WHITE);
@ -141,7 +135,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() {
const gpu::TexturePointer& TextureCache::getGrayTexture() {
if (!_grayTexture) {
_grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
_grayTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
_grayTexture->setSource("TextureCache::_grayTexture");
_grayTexture->setStoredMipFormat(_grayTexture->getTexelFormat());
_grayTexture->assignStoredMip(0, sizeof(OPAQUE_GRAY), OPAQUE_GRAY);
@ -151,7 +145,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() {
const gpu::TexturePointer& TextureCache::getBlueTexture() {
if (!_blueTexture) {
_blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
_blueTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
_blueTexture->setSource("TextureCache::_blueTexture");
_blueTexture->setStoredMipFormat(_blueTexture->getTexelFormat());
_blueTexture->assignStoredMip(0, sizeof(OPAQUE_BLUE), OPAQUE_BLUE);
@ -161,7 +155,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() {
const gpu::TexturePointer& TextureCache::getBlackTexture() {
if (!_blackTexture) {
_blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
_blackTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
_blackTexture->setSource("TextureCache::_blackTexture");
_blackTexture->setStoredMipFormat(_blackTexture->getTexelFormat());
_blackTexture->assignStoredMip(0, sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
@ -172,18 +166,18 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() {
/// Extra data for creating textures.
class TextureExtra {
public:
NetworkTexture::Type type;
image::TextureUsage::Type type;
const QByteArray& content;
int maxNumPixels;
};
ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) {
auto byteArray = QByteArray();
TextureExtra extra = { (Type)type, byteArray, maxNumPixels };
TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels };
return ResourceCache::prefetch(url, &extra);
}
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) {
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) {
TextureExtra extra = { type, content, maxNumPixels };
return ResourceCache::getResource(url, QUrl(), &extra).staticCast<NetworkTexture>();
}
@ -216,8 +210,7 @@ gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, co
return result;
}
gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) {
gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) {
gpu::TexturePointer result;
auto textureCache = DependencyManager::get<TextureCache>();
// Since this can be called on a background thread, there's a chance that the cache
@ -226,116 +219,51 @@ gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) {
return result;
}
switch (type) {
case NetworkTexture::DEFAULT_TEXTURE:
case NetworkTexture::ALBEDO_TEXTURE:
case NetworkTexture::ROUGHNESS_TEXTURE:
case NetworkTexture::OCCLUSION_TEXTURE:
case image::TextureUsage::DEFAULT_TEXTURE:
case image::TextureUsage::ALBEDO_TEXTURE:
case image::TextureUsage::ROUGHNESS_TEXTURE:
case image::TextureUsage::OCCLUSION_TEXTURE:
result = textureCache->getWhiteTexture();
break;
case NetworkTexture::NORMAL_TEXTURE:
case image::TextureUsage::NORMAL_TEXTURE:
result = textureCache->getBlueTexture();
break;
case NetworkTexture::EMISSIVE_TEXTURE:
case NetworkTexture::LIGHTMAP_TEXTURE:
case image::TextureUsage::EMISSIVE_TEXTURE:
case image::TextureUsage::LIGHTMAP_TEXTURE:
result = textureCache->getBlackTexture();
break;
case NetworkTexture::BUMP_TEXTURE:
case NetworkTexture::SPECULAR_TEXTURE:
case NetworkTexture::GLOSS_TEXTURE:
case NetworkTexture::CUBE_TEXTURE:
case NetworkTexture::CUSTOM_TEXTURE:
case NetworkTexture::STRICT_TEXTURE:
case image::TextureUsage::BUMP_TEXTURE:
case image::TextureUsage::SPECULAR_TEXTURE:
case image::TextureUsage::GLOSS_TEXTURE:
case image::TextureUsage::CUBE_TEXTURE:
case image::TextureUsage::STRICT_TEXTURE:
default:
break;
}
return result;
}
NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type,
const QVariantMap& options = QVariantMap()) {
using Type = NetworkTexture;
switch (type) {
case Type::ALBEDO_TEXTURE: {
return model::TextureUsage::createAlbedoTextureFromImage;
break;
}
case Type::EMISSIVE_TEXTURE: {
return model::TextureUsage::createEmissiveTextureFromImage;
break;
}
case Type::LIGHTMAP_TEXTURE: {
return model::TextureUsage::createLightmapTextureFromImage;
break;
}
case Type::CUBE_TEXTURE: {
if (options.value("generateIrradiance", true).toBool()) {
return model::TextureUsage::createCubeTextureFromImage;
} else {
return model::TextureUsage::createCubeTextureFromImageWithoutIrradiance;
}
break;
}
case Type::BUMP_TEXTURE: {
return model::TextureUsage::createNormalTextureFromBumpImage;
break;
}
case Type::NORMAL_TEXTURE: {
return model::TextureUsage::createNormalTextureFromNormalImage;
break;
}
case Type::ROUGHNESS_TEXTURE: {
return model::TextureUsage::createRoughnessTextureFromImage;
break;
}
case Type::GLOSS_TEXTURE: {
return model::TextureUsage::createRoughnessTextureFromGlossImage;
break;
}
case Type::SPECULAR_TEXTURE: {
return model::TextureUsage::createMetallicTextureFromImage;
break;
}
case Type::STRICT_TEXTURE: {
return model::TextureUsage::createStrict2DTextureFromImage;
break;
}
case Type::CUSTOM_TEXTURE: {
Q_ASSERT(false);
return NetworkTexture::TextureLoaderFunc();
break;
}
case Type::DEFAULT_TEXTURE:
default: {
return model::TextureUsage::create2DTextureFromImage;
break;
}
}
}
/// Returns a texture version of an image file
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type, QVariantMap options) {
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) {
QImage image = QImage(path);
auto loader = getTextureLoaderForType(type, options);
auto loader = image::TextureUsage::getTextureLoaderForType(type, options);
return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString()));
}
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) {
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE;
auto type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
auto content = textureExtra ? textureExtra->content : QByteArray();
auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS;
NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels);
return QSharedPointer<Resource>(texture, &Resource::deleter);
}
NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) :
NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) :
Resource(url),
_type(type),
_maxNumPixels(maxNumPixels)
@ -353,13 +281,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con
}
}
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
if (_type == CUSTOM_TEXTURE) {
return _textureLoader;
}
return getTextureLoaderForType(_type);
}
void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
int originalHeight) {
_originalWidth = originalWidth;
@ -384,34 +305,22 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
}
gpu::TexturePointer NetworkTexture::getFallbackTexture() const {
if (_type == CUSTOM_TEXTURE) {
return gpu::TexturePointer();
}
return getFallbackTextureForType(_type);
}
class Reader : public QRunnable {
public:
Reader(const QWeakPointer<Resource>& resource, const QUrl& url);
void run() override final;
virtual void read() = 0;
protected:
QWeakPointer<Resource> _resource;
QUrl _url;
};
class ImageReader : public Reader {
class ImageReader : public QRunnable {
public:
ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
const QByteArray& data, const std::string& hash, int maxNumPixels);
void read() override final;
const QByteArray& data, int maxNumPixels);
void run() override final;
void read();
private:
static void listSupportedImageFormats();
QWeakPointer<Resource> _resource;
QUrl _url;
QByteArray _content;
std::string _hash;
int _maxNumPixels;
};
@ -420,71 +329,16 @@ void NetworkTexture::downloadFinished(const QByteArray& data) {
}
void NetworkTexture::loadContent(const QByteArray& content) {
// Hash the source image to for KTX caching
std::string hash;
{
QCryptographicHash hasher(QCryptographicHash::Md5);
hasher.addData(content);
hash = hasher.result().toHex().toStdString();
}
auto textureCache = static_cast<TextureCache*>(_cache.data());
if (textureCache != nullptr) {
// If we already have a live texture with the same hash, use it
auto texture = textureCache->getTextureByHash(hash);
// If there is no live texture, check if there's an existing KTX file
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
if (ktxFile) {
texture.reset(gpu::Texture::unserialize(ktxFile->getFilepath()));
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
}
}
}
// If we found the texture either because it's in use or via KTX deserialization,
// set the image and return immediately.
if (texture) {
setImage(texture, texture->getWidth(), texture->getHeight());
return;
}
}
// We failed to find an existing live or KTX texture, so trigger an image reader
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, hash, _maxNumPixels));
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels));
}
Reader::Reader(const QWeakPointer<Resource>& resource, const QUrl& url) :
_resource(resource), _url(url) {
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) :
_resource(resource),
_url(url),
_content(data),
_maxNumPixels(maxNumPixels)
{
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
}
void Reader::run() {
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority;
}
QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority]{ QThread::currentThread()->setPriority(originalPriority); });
if (!_resource.data()) {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
return;
}
read();
}
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
const QByteArray& data, const std::string& hash, int maxNumPixels) :
Reader(resource, url), _content(data), _hash(hash), _maxNumPixels(maxNumPixels) {
listSupportedImageFormats();
#if DEBUG_DUMP_TEXTURE_LOADS
@ -515,89 +369,110 @@ void ImageReader::listSupportedImageFormats() {
});
}
void ImageReader::read() {
// Help the QImage loader by extracting the image file format from the url filename ext.
// Some tga are not created properly without it.
auto filename = _url.fileName().toStdString();
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
QImage image = QImage::fromData(_content, filenameExtension.c_str());
int imageWidth = image.width();
int imageHeight = image.height();
void ImageReader::run() {
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
// Validate that the image loaded
if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) {
QString reason(filenameExtension.empty() ? "" : "(no file extension)");
qCWarning(modelnetworking) << "Failed to load" << _url << reason;
auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority;
}
QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
read();
}
void ImageReader::read() {
auto resource = _resource.lock(); // to ensure the resource is still needed
if (!resource) {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
return;
}
auto networkTexture = resource.staticCast<NetworkTexture>();
// Validate the image is less than _maxNumPixels, and downscale if necessary
if (imageWidth * imageHeight > _maxNumPixels) {
float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight));
int originalWidth = imageWidth;
int originalHeight = imageHeight;
imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f);
imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f);
QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
image.swap(newImage);
qCDebug(modelnetworking).nospace() << "Downscaled " << _url << " (" <<
QSize(originalWidth, originalHeight) << " to " <<
QSize(imageWidth, imageHeight) << ")";
// Hash the source image to for KTX caching
std::string hash;
{
QCryptographicHash hasher(QCryptographicHash::Md5);
hasher.addData(_content);
hash = hasher.result().toHex().toStdString();
}
gpu::TexturePointer texture = nullptr;
// Maybe load from cache
auto textureCache = DependencyManager::get<TextureCache>();
if (textureCache) {
// If we already have a live texture with the same hash, use it
auto texture = textureCache->getTextureByHash(hash);
// If there is no live texture, check if there's an existing KTX file
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile->getFilepath());
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
}
}
}
// If we found the texture either because it's in use or via KTX deserialization,
// set the image and return immediately.
if (texture) {
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight()));
return;
}
}
// Proccess new texture
gpu::TexturePointer texture;
{
auto resource = _resource.lock(); // to ensure the resource is still needed
if (!resource) {
qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope";
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
texture = image::processImage(_content, _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType());
if (!texture) {
qCWarning(modelnetworking) << "Could not process:" << _url;
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
auto url = _url.toString().toStdString();
texture->setSourceHash(hash);
texture->setFallbackTexture(networkTexture->getFallbackTexture());
}
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
// Load the image into a gpu::Texture
auto networkTexture = resource.staticCast<NetworkTexture>();
texture.reset(networkTexture->getTextureLoader()(image, url));
texture->setSource(url);
if (texture) {
texture->setFallbackTexture(networkTexture->getFallbackTexture());
}
auto textureCache = DependencyManager::get<TextureCache>();
// Save the image into a KTXFile
// Save the image into a KTXFile
if (texture && textureCache) {
auto memKtx = gpu::Texture::serialize(*texture);
if (!memKtx) {
qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url;
}
if (memKtx && textureCache) {
if (memKtx) {
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
KTXFilePointer file;
auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) {
networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length));
if (!networkTexture->_file) {
qCWarning(modelnetworking) << _url << "file cache failed";
} else {
resource.staticCast<NetworkTexture>()->_file = file;
texture->setKtxBacking(file->getFilepath());
texture->setKtxBacking(networkTexture->_file->getFilepath());
}
} else {
qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url;
}
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
// be the winner
if (textureCache) {
texture = textureCache->cacheTextureByHash(_hash, texture);
}
texture = textureCache->cacheTextureByHash(hash, texture);
}
auto resource = _resource.lock(); // to ensure the resource is still needed
if (resource) {
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, imageWidth), Q_ARG(int, imageHeight));
} else {
qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope";
}
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight()));
}

View file

@ -22,6 +22,7 @@
#include <DependencyManager.h>
#include <ResourceCache.h>
#include <model/TextureMap.h>
#include <image/Image.h>
#include "KTXCache.h"
@ -43,29 +44,7 @@ class NetworkTexture : public Resource, public Texture {
Q_OBJECT
public:
enum Type {
DEFAULT_TEXTURE,
STRICT_TEXTURE,
ALBEDO_TEXTURE,
NORMAL_TEXTURE,
BUMP_TEXTURE,
SPECULAR_TEXTURE,
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
ROUGHNESS_TEXTURE,
GLOSS_TEXTURE,
EMISSIVE_TEXTURE,
CUBE_TEXTURE,
OCCLUSION_TEXTURE,
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
LIGHTMAP_TEXTURE,
CUSTOM_TEXTURE
};
Q_ENUM(Type)
typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName);
using TextureLoaderFunc = std::function<TextureLoader>;
NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels);
NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels);
QString getType() const override { return "NetworkTexture"; }
@ -73,9 +52,8 @@ public:
int getOriginalHeight() const { return _originalHeight; }
int getWidth() const { return _width; }
int getHeight() const { return _height; }
Type getTextureType() const { return _type; }
image::TextureUsage::Type getTextureType() const { return _type; }
TextureLoaderFunc getTextureLoader() const;
gpu::TexturePointer getFallbackTexture() const;
signals:
@ -93,8 +71,7 @@ private:
friend class KTXReader;
friend class ImageReader;
Type _type;
TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } };
image::TextureUsage::Type _type;
KTXFilePointer _file;
int _originalWidth { 0 };
int _originalHeight { 0 };
@ -110,8 +87,6 @@ class TextureCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
using Type = NetworkTexture::Type;
public:
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
@ -131,10 +106,10 @@ public:
const gpu::TexturePointer& getBlackTexture();
/// Returns a texture version of an image file
static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
static gpu::TexturePointer getImageTexture(const QString& path, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
/// Loads a texture from the specified URL.
NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE,
NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE,
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);

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