diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 740f456595..d0433b5d60 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -144,6 +144,9 @@ void Agent::run() { connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); messagesThread->start(); + // make sure we hear about connected nodes so we can grab an ATP script if a request is pending + connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated); + nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer }); @@ -164,52 +167,85 @@ void Agent::requestScript() { scriptURL = QUrl(_payload); } - // setup a network access manager and - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + // make sure this is not a script request for the file scheme + if (scriptURL.scheme() == URL_SCHEME_FILE) { + qWarning() << "Cannot load script for Agent from local filesystem."; + scriptRequestFinished(); + return; + } - QNetworkDiskCache* cache = new QNetworkDiskCache(); - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); - networkAccessManager.setCache(cache); + auto request = ResourceManager::createResourceRequest(this, scriptURL); - QNetworkRequest networkRequest = QNetworkRequest(scriptURL); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + if (!request) { + qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString(); + scriptRequestFinished(); + return; + } // setup a timeout for script request static const int SCRIPT_TIMEOUT_MS = 10000; - _scriptRequestTimeout = new QTimer(this); + _scriptRequestTimeout = new QTimer; connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished); _scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS); - qDebug() << "Downloading script at" << scriptURL.toString(); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); + connect(request, &ResourceRequest::finished, this, &Agent::scriptRequestFinished); + + if (scriptURL.scheme() == URL_SCHEME_ATP) { + // we have an ATP URL for the script - if we're not currently connected to the AssetServer + // then wait for the nodeConnected signal to fire off the request + + auto assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + if (!assetServer || !assetServer->getActiveSocket()) { + qDebug() << "Waiting to connect to Asset Server for ATP script download."; + _pendingScriptRequest = request; + + return; + } + } + + qInfo() << "Requesting script at URL" << qPrintable(request->getUrl().toString()); + + request->send(); +} + +void Agent::nodeActivated(SharedNodePointer activatedNode) { + if (_pendingScriptRequest) { + qInfo() << "Requesting script at URL" << qPrintable(_pendingScriptRequest->getUrl().toString()); + + _pendingScriptRequest->send(); + + _pendingScriptRequest = nullptr; + } } void Agent::scriptRequestFinished() { - auto reply = qobject_cast(sender()); + auto request = qobject_cast(sender()); - _scriptRequestTimeout->stop(); + // stop the script request timeout, if it's running + if (_scriptRequestTimeout) { + QMetaObject::invokeMethod(_scriptRequestTimeout, "stop"); + _scriptRequestTimeout->deleteLater(); + } - if (reply && reply->error() == QNetworkReply::NoError) { - _scriptContents = reply->readAll(); - qDebug() << "Downloaded script:" << _scriptContents; + if (request && request->getResult() == ResourceRequest::Success) { + _scriptContents = request->getData(); + qInfo() << "Downloaded script:" << _scriptContents; // we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished // to return before calling executeScript QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); } else { - if (reply) { - qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; - qDebug() << "QNetworkReply error was" << reply->errorString(); + if (request) { + qWarning() << "Failed to download script at" << request->getUrl().toString() << " - bailing on assignment."; + qWarning() << "ResourceRequest error was" << request->getResult(); } else { - qDebug() << "Failed to download script - request timed out. Bailing on assignment."; + qWarning() << "Failed to download script - request timed out. Bailing on assignment."; } setFinished(true); } - reply->deleteLater(); + request->deleteLater(); } void Agent::executeScript() { diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index d86b99db33..2b0d22385d 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -69,6 +69,8 @@ private slots: void processAgentAvatarAndAudio(float deltaTime); + void nodeActivated(SharedNodePointer activatedNode); + private: std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; @@ -84,6 +86,7 @@ private: QString _scriptContents; QTimer* _scriptRequestTimeout { nullptr }; + ResourceRequest* _pendingScriptRequest { nullptr }; bool _isListeningToAudioStream = false; SharedSoundPointer _avatarSound; int _numAvatarSoundSentBytes = 0; diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 0bf59eb8ad..1fb0674e7d 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -122,67 +122,49 @@ void AssetServer::completeSetup() { } // load whatever mappings we currently have from the local file - loadMappingsFromFile(); + if (loadMappingsFromFile()) { + qInfo() << "Serving files from: " << _filesDirectory.path(); - qInfo() << "Serving files from: " << _filesDirectory.path(); + // Check the asset directory to output some information about what we have + auto files = _filesDirectory.entryList(QDir::Files); - // Check the asset directory to output some information about what we have - auto files = _filesDirectory.entryList(QDir::Files); + QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING }; + auto hashedFiles = files.filter(hashFileRegex); - QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING }; - auto hashedFiles = files.filter(hashFileRegex); + qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory."; - qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory."; + if (_fileMappings.count() > 0) { + cleanupUnmappedFiles(); + } - performMappingMigration(); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); + } else { + qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded."; + setFinished(true); + } - nodeList->addNodeTypeToInterestSet(NodeType::Agent); } -void AssetServer::performMappingMigration() { - QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}(\\.[\\w]+)+$" }; +void AssetServer::cleanupUnmappedFiles() { + QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}" }; - auto files = _resourcesDirectory.entryInfoList(QDir::Files); + auto files = _filesDirectory.entryInfoList(QDir::Files); + + // grab the currently mapped hashes + auto mappedHashes = _fileMappings.values(); + + qInfo() << "Performing unmapped asset cleanup."; for (const auto& fileInfo : files) { if (hashFileRegex.exactMatch(fileInfo.fileName())) { - // we have a pre-mapping file that we should migrate to the new mapping system - qDebug() << "Migrating pre-mapping file" << fileInfo.fileName(); + if (!mappedHashes.contains(fileInfo.fileName())) { + // remove the unmapped file + QFile removeableFile { fileInfo.absoluteFilePath() }; - // rename the file to the same name with no extension - QFile oldFile { fileInfo.absoluteFilePath() }; - - auto oldAbsolutePath = fileInfo.absoluteFilePath(); - auto oldFilename = fileInfo.fileName(); - auto hash = oldFilename.left(SHA256_HASH_HEX_LENGTH); - auto fullExtension = oldFilename.mid(oldFilename.indexOf('.')); - - qDebug() << "\tMoving" << oldAbsolutePath << "to" << oldAbsolutePath.replace(fullExtension, ""); - - bool renamed = oldFile.copy(_filesDirectory.filePath(hash)); - if (!renamed) { - qWarning() << "\tCould not migrate pre-mapping file" << fileInfo.fileName(); - } else { - qDebug() << "\tRenamed pre-mapping file" << fileInfo.fileName(); - - // add a new mapping with the old extension and a truncated version of the hash - const int TRUNCATED_HASH_NUM_CHAR = 16; - auto fakeFileName = "/" + hash.left(TRUNCATED_HASH_NUM_CHAR) + fullExtension; - - qDebug() << "\tAdding a migration mapping from" << fakeFileName << "to" << hash; - - auto it = _fileMappings.find(fakeFileName); - if (it == _fileMappings.end()) { - _fileMappings[fakeFileName] = hash; - - if (writeMappingsToFile()) { - // mapping added and persisted, we can remove the migrated file - oldFile.remove(); - qDebug() << "\tMigration completed for" << oldFilename; - } + if (removeableFile.remove()) { + qDebug() << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped."; } else { - qDebug() << "\tCould not add migration mapping for" << hash << "since a mapping for" << fakeFileName - << "already exists."; + qDebug() << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed"; } } } @@ -451,7 +433,7 @@ void AssetServer::sendStatsPacket() { static const QString MAP_FILE_NAME = "map.json"; -void AssetServer::loadMappingsFromFile() { +bool AssetServer::loadMappingsFromFile() { auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME); @@ -488,15 +470,17 @@ void AssetServer::loadMappingsFromFile() { } qInfo() << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath; - return; + return true; } } - qCritical() << "Failed to read mapping file at" << mapFilePath << "- assignment will not continue."; - setFinished(true); + qCritical() << "Failed to read mapping file at" << mapFilePath; + return false; } else { qInfo() << "No existing mappings loaded from file since no file was found at" << mapFilePath; } + + return true; } bool AssetServer::writeMappingsToFile() { @@ -566,6 +550,8 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { // take a copy of the current mappings in case persistence of these deletes fails auto oldMappings = _fileMappings; + QSet hashesToCheckForDeletion; + // enumerate the paths to delete and remove them all for (auto& path : paths) { @@ -579,6 +565,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { while (it != _fileMappings.end()) { if (it.key().startsWith(path)) { + // add this hash to the list we need to check for asset removal from the server + hashesToCheckForDeletion << it.value().toString(); + it = _fileMappings.erase(it); } else { ++it; @@ -595,6 +584,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { } else { auto oldMapping = _fileMappings.take(path); if (!oldMapping.isNull()) { + // add this hash to the list we need to check for asset removal from server + hashesToCheckForDeletion << oldMapping.toString(); + qDebug() << "Deleted a mapping:" << path << "=>" << oldMapping.toString(); } else { qDebug() << "Unable to delete a mapping that was not found:" << path; @@ -605,6 +597,30 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { // deleted the old mappings, attempt to persist to file if (writeMappingsToFile()) { // persistence succeeded we are good to go + + // grab the current mapped hashes + auto mappedHashes = _fileMappings.values(); + + // enumerate the mapped hashes and clear the list of hashes to check for anything that's present + for (auto& hashVariant : mappedHashes) { + auto it = hashesToCheckForDeletion.find(hashVariant.toString()); + if (it != hashesToCheckForDeletion.end()) { + hashesToCheckForDeletion.erase(it); + } + } + + // we now have a set of hashes that are unmapped - we will delete those asset files + for (auto& hash : hashesToCheckForDeletion) { + // remove the unmapped file + QFile removeableFile { _filesDirectory.absoluteFilePath(hash) }; + + if (removeableFile.remove()) { + qDebug() << "\tDeleted" << hash << "from asset files directory since it is now unmapped."; + } else { + qDebug() << "\tAttempt to delete unmapped file" << hash << "failed"; + } + } + return true; } else { qWarning() << "Failed to persist deleted mappings, rolling back"; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 1a8ebed50b..07ff0a92b3 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -48,7 +48,7 @@ private: void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); // Mapping file operations must be called from main assignment thread only - void loadMappingsFromFile(); + bool loadMappingsFromFile(); bool writeMappingsToFile(); /// Set the mapping for path to hash @@ -60,7 +60,8 @@ private: /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); - void performMappingMigration(); + // deletes any unmapped files from the local asset directory + void cleanupUnmappedFiles(); Mappings _fileMappings; diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index dfdfeded04..e17a886e10 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -870,7 +870,7 @@ function saveSettings() { // check if we've set the basic http password - if so convert it to base64 if (formJSON["security"]) { var password = formJSON["security"]["http_password"]; - if (password.length > 0) { + if (password && password.length > 0) { formJSON["security"]["http_password"] = sha256_digest(password); } } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4ff3564e72..16928f3dee 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1678,10 +1678,12 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl // we've pulled a username and password - now check if there is a match in our basic auth hash QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString(); const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH); + QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : ""; QString hexHeaderPassword = QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); - if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) { + if (settingsUsername == headerUsername + && (settingsPassword.isEmpty() || hexHeaderPassword == settingsPassword)) { return true; } } diff --git a/examples/utilities/render/stats.qml b/examples/utilities/render/stats.qml index 5f0628c312..c444cbd226 100644 --- a/examples/utilities/render/stats.qml +++ b/examples/utilities/render/stats.qml @@ -84,6 +84,11 @@ Item { prop: "frameTextureCount", label: "Frame", color: "#E2334D" + }, + { + prop: "textureGPUTransferCount", + label: "Transfer", + color: "#9495FF" } ] } @@ -104,7 +109,18 @@ Item { prop: "textureGPUMemoryUsage", label: "GPU", color: "#1AC567" + }, + { + prop: "textureGPUVirtualMemoryUsage", + label: "GPU Virtual", + color: "#9495FF" + }, + { + prop: "frameTextureMemoryUsage", + label: "Frame", + color: "#E2334D" } + ] } @@ -179,7 +195,7 @@ Item { object: Render.getConfig("DrawLight"), prop: "numDrawn", label: "Lights", - color: "#E2334D" + color: "#FED959" } ] } diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index b14c461bc5..c8e379480d 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -114,6 +114,8 @@ std::atomic Context::_bufferGPUMemoryUsage{ 0 }; std::atomic Context::_textureGPUCount{ 0 }; std::atomic Context::_textureGPUMemoryUsage{ 0 }; +std::atomic Context::_textureGPUVirtualMemoryUsage{ 0 }; +std::atomic Context::_textureGPUTransferCount{ 0 }; void Context::incrementBufferGPUCount() { _bufferGPUCount++; @@ -149,6 +151,24 @@ void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSiz } } +void Context::updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize) { + if (prevObjectSize == newObjectSize) { + return; + } + if (newObjectSize > prevObjectSize) { + _textureGPUVirtualMemoryUsage.fetch_add(newObjectSize - prevObjectSize); + } else { + _textureGPUVirtualMemoryUsage.fetch_sub(prevObjectSize - newObjectSize); + } +} + +void Context::incrementTextureGPUTransferCount() { + _textureGPUTransferCount++; +} +void Context::decrementTextureGPUTransferCount() { + _textureGPUTransferCount--; +} + uint32_t Context::getBufferGPUCount() { return _bufferGPUCount.load(); } @@ -165,10 +185,20 @@ Context::Size Context::getTextureGPUMemoryUsage() { return _textureGPUMemoryUsage.load(); } +Context::Size Context::getTextureGPUVirtualMemoryUsage() { + return _textureGPUVirtualMemoryUsage.load(); +} + +uint32_t Context::getTextureGPUTransferCount() { + return _textureGPUTransferCount.load(); +} + void Backend::incrementBufferGPUCount() { Context::incrementBufferGPUCount(); } void Backend::decrementBufferGPUCount() { Context::decrementBufferGPUCount(); } void Backend::updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateBufferGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); } void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); } void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); } - +void Backend::updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUVirtualMemoryUsage(prevObjectSize, newObjectSize); } +void Backend::incrementTextureGPUTransferCount() { Context::incrementTextureGPUTransferCount(); } +void Backend::decrementTextureGPUTransferCount() { Context::decrementTextureGPUTransferCount(); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7f442895a5..869db97be7 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -34,6 +34,7 @@ public: int _ISNumIndexBufferChanges = 0; int _RSNumTextureBounded = 0; + int _RSAmountTextureMemoryBounded = 0; int _DSNumAPIDrawcalls = 0; int _DSNumDrawcalls = 0; @@ -128,6 +129,9 @@ public: static void incrementTextureGPUCount(); static void decrementTextureGPUCount(); static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); + static void updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); + static void incrementTextureGPUTransferCount(); + static void decrementTextureGPUTransferCount(); protected: StereoState _stereo; @@ -177,6 +181,8 @@ public: static uint32_t getTextureGPUCount(); static Size getTextureGPUMemoryUsage(); + static Size getTextureGPUVirtualMemoryUsage(); + static uint32_t getTextureGPUTransferCount(); protected: Context(const Context& context); @@ -202,6 +208,9 @@ protected: static void incrementTextureGPUCount(); static void decrementTextureGPUCount(); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); + static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize); + static void incrementTextureGPUTransferCount(); + static void decrementTextureGPUTransferCount(); // Buffer and Texture Counters static std::atomic _bufferGPUCount; @@ -209,6 +218,9 @@ protected: static std::atomic _textureGPUCount; static std::atomic _textureGPUMemoryUsage; + static std::atomic _textureGPUVirtualMemoryUsage; + static std::atomic _textureGPUTransferCount; + friend class Backend; }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 625ca6cec3..940b0eb85b 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -192,6 +192,25 @@ enum Semantic { SRGBA, SBGRA, + // 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, */ + + _LAST_COMPRESSED, + R11G11B10, UNIFORM, @@ -224,6 +243,7 @@ public: Dimension getDimension() const { return (Dimension)_dimension; } + bool isCompressed() const { return uint8(getSemantic() - _FIRST_COMPRESSED) <= uint8(_LAST_COMPRESSED - _FIRST_COMPRESSED); } Type getType() const { return (Type)_type; } bool isNormalized() const { return (getType() >= NORMALIZED_START); } diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 7c04add684..c0d1e90831 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -77,15 +77,22 @@ public: class GLTexture : public GPUObject { public: + // The public gl texture object + GLuint _texture{ 0 }; + const Stamp _storageStamp; Stamp _contentStamp { 0 }; - const GLuint _texture; const GLenum _target; GLTexture(const gpu::Texture& gpuTexture); ~GLTexture(); + void createTexture(); + GLuint size() const { return _size; } + GLuint virtualSize() const { return _virtualSize; } + + void updateSize(); enum SyncState { // The texture is currently undergoing no processing, although it's content @@ -120,16 +127,26 @@ public: static const size_t CUBE_NUM_FACES = 6; static const GLenum CUBE_FACE_LAYOUT[6]; - + private: + // at creation the true texture is created in GL + // it becomes public only when ready. + GLuint _privateTexture{ 0 }; + + void setSize(GLuint size); + void setVirtualSize(GLuint size); + + GLuint _size; // true size as reported by the gl api + GLuint _virtualSize; // theorical size as expected + GLuint _numLevels{ 0 }; + void transferMip(GLenum target, const Texture::PixelsPointer& mip) const; - const GLuint _size; // The owning texture const Texture& _gpuTexture; std::atomic _syncState { SyncState::Idle }; }; - static GLTexture* syncGPUObject(const TexturePointer& texture); + static GLTexture* syncGPUObject(const TexturePointer& texture, bool needTransfer = true); static GLuint getTextureID(const TexturePointer& texture, bool sync = true); // very specific for now diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 4f714fb53d..545d0a8cdb 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe for (auto& b : framebuffer.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = GLBackend::syncGPUObject(surface); + gltexture = GLBackend::syncGPUObject(surface, false); // Grab the gltexture and don't transfer } else { gltexture = nullptr; } @@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe if (framebuffer.getDepthStamp() != object->_depthStamp) { auto surface = framebuffer.getDepthStencilBuffer(); if (framebuffer.hasDepthStencil() && surface) { - gltexture = GLBackend::syncGPUObject(surface); + gltexture = GLBackend::syncGPUObject(surface, false); // Grab the gltexture and don't transfer } if (gltexture) { diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index f43ea1ea73..f13c5804f5 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -271,6 +271,8 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { _resource._textures[slot] = resourceTexture; + _stats._RSAmountTextureMemoryBounded += object->size(); + } else { releaseResourceTexture(slot); return; diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 59da9ab9e9..aef670e6b0 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -45,6 +45,20 @@ static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { GL_UNSIGNED_BYTE }; +class GLTexelFormat { +public: + GLenum internalFormat; + GLenum format; + GLenum type; + + static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) { + return evalGLTexelFormat(dstFormat, dstFormat); + } + static GLTexelFormat evalGLTexelFormatInternal(const gpu::Element& dstFormat); + + static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat); +}; + // Stupid preprocessor trick to turn the line macro into a string #define CHECK_GL_ERROR_HELPER(x) #x // FIXME doesn't build on Linux or Mac. Hmmmm diff --git a/libraries/gpu/src/gpu/GLBackendTexelFormat.cpp b/libraries/gpu/src/gpu/GLBackendTexelFormat.cpp new file mode 100644 index 0000000000..1a7b969e13 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTexelFormat.cpp @@ -0,0 +1,426 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 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 "GLBackendShared.h" + +using namespace gpu; + +GLTexelFormat GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { + GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; + return texel; +} + +GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { + if (dstFormat != srcFormat) { + GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_R8; + break; + + case gpu::COMPRESSED_R: + texel.internalFormat = GL_COMPRESSED_RED_RGTC1; + break; + + case gpu::DEPTH: + texel.internalFormat = GL_DEPTH_COMPONENT32; + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + 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: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (srcFormat.getSemantic()) { + case gpu::BGRA: + case gpu::SBGRA: + texel.format = GL_BGRA; + break; + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + default: + break; + }; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + break; + case gpu::SRGB: + texel.internalFormat = GL_SRGB8; + break; + case gpu::SRGBA: + 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; + + break; + + // FIXME: WE will want to support this later + /* + case gpu::COMPRESSED_BC3_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_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; + break; + case gpu::COMPRESSED_BC7_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + break; + */ + + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + return texel; + } else { + GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::COMPRESSED_R: { + texel.internalFormat = GL_COMPRESSED_RED_RGTC1; + break; + } + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + texel.internalFormat = GL_RED; + switch (dstFormat.getType()) { + case gpu::UINT32: { + texel.internalFormat = GL_R32UI; + break; + } + case gpu::INT32: { + texel.internalFormat = GL_R32I; + break; + } + case gpu::NUINT32: { + texel.internalFormat = GL_R8; + break; + } + case gpu::NINT32: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_R32F; + break; + } + case gpu::UINT16: { + texel.internalFormat = GL_R16UI; + break; + } + case gpu::INT16: { + texel.internalFormat = GL_R16I; + break; + } + case gpu::NUINT16: { + texel.internalFormat = GL_R16; + break; + } + case gpu::NINT16: { + texel.internalFormat = GL_R16_SNORM; + break; + } + case gpu::HALF: { + texel.internalFormat = GL_R16F; + break; + } + case gpu::UINT8: { + texel.internalFormat = GL_R8UI; + break; + } + case gpu::INT8: { + texel.internalFormat = GL_R8I; + break; + } + case gpu::NUINT8: { + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { + texel.internalFormat = GL_SLUMINANCE8; + } else { + texel.internalFormat = GL_R8; + } + break; + } + case gpu::NINT8: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + + } + break; + + case gpu::R11G11B10: + texel.format = GL_RGB; + // the type should be float + texel.internalFormat = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it + texel.internalFormat = GL_DEPTH_COMPONENT32; + switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: { + texel.internalFormat = GL_DEPTH_COMPONENT32; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_DEPTH_COMPONENT32F; + break; + } + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: { + texel.internalFormat = GL_DEPTH_COMPONENT16; + break; + } + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: { + texel.internalFormat = GL_DEPTH_COMPONENT24; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + } + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RGB8; + break; + case gpu::SRGB: + 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: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = _elementTypeToGLType[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32UI; + break; + case gpu::INT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RGBA32F; + break; + case gpu::UINT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16UI; + break; + case gpu::INT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16I; + break; + case gpu::NUINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16; + break; + case gpu::NINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16_SNORM; + break; + case gpu::HALF: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16F; + break; + case gpu::UINT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8UI; + break; + case gpu::INT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8I; + break; + case gpu::NUINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; + case gpu::NINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + texel.format = GL_RGB; + texel.internalFormat = GL_SRGB8; + break; + case gpu::SRGBA: + texel.format = GL_RGBA; + texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + case gpu::COMPRESSED_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA; + break; + case gpu::COMPRESSED_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; + break; + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpulogging) << "Unknown combination of texel format"; + } + return texel; + } +} diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index f38cc89a8c..8da6b6d300 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -13,7 +13,6 @@ #include #include "GLBackendShared.h" -#include "GLTexelFormat.h" #include "GLBackendTextureTransfer.h" using namespace gpu; @@ -49,49 +48,145 @@ const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = { // Create the texture and allocate storage GLBackend::GLTexture::GLTexture(const Texture& texture) : - _storageStamp(texture.getStamp()), _texture(allocateSingleTexture()), - _target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture) + _storageStamp(texture.getStamp()), + _target(gpuToGLTextureType(texture)), + _size(0), + _virtualSize(0), + _numLevels(texture.maxMip() + 1), + _gpuTexture(texture) { Backend::incrementTextureGPUCount(); - Backend::updateTextureGPUMemoryUsage(0, _size); Backend::setGPUObject(texture, this); - GLsizei width = texture.getWidth(); - GLsizei height = texture.getHeight(); - GLsizei levels = 1; - if (texture.maxMip() > 0) { - if (texture.isAutogenerateMips()) { - while ((width | height) >> levels) { - ++levels; + + // updateSize(); + GLuint virtualSize = _gpuTexture.evalTotalSize(); + setVirtualSize(virtualSize); + setSize(virtualSize); +} + +void GLBackend::GLTexture::createTexture() { + _privateTexture = allocateSingleTexture(); + + GLsizei width = _gpuTexture.getWidth(); + GLsizei height = _gpuTexture.getHeight(); + + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat()); + + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _privateTexture); + + (void)CHECK_GL_ERROR(); + // Fixme: this usage of TexStorage doesn;t work wtih compressed texture, altuogh it should. + // GO through the process of allocating the correct storage + /* if (GLEW_VERSION_4_2 && !texture.getTexelFormat().isCompressed()) { + glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height); + (void)CHECK_GL_ERROR(); + } else*/ + { + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _numLevels - 1); + + // for (int l = 0; l < _numLevels; l++) { + { int l = 0; + if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) { + for (size_t face = 0; face < CUBE_NUM_FACES; face++) { + glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); } + } else { + glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); } - levels = std::max(1, std::min(texture.maxMip() + 1, levels)); + width = std::max(1, (width / 2)); + height = std::max(1, (height / 2)); + } + (void)CHECK_GL_ERROR(); } - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat()); - withPreservedTexture(_target, [&] { - glBindTexture(_target, _texture); - (void)CHECK_GL_ERROR(); - // GO through the process of allocating the correct storage - if (GLEW_VERSION_4_2) { - glTexStorage2D(_target, levels, texelFormat.internalFormat, width, height); - } else { - glTexImage2D(_target, 0, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, 0); - } - (void)CHECK_GL_ERROR(); - syncSampler(texture.getSampler(), texture.getType(), this); - (void)CHECK_GL_ERROR(); - }); + syncSampler(_gpuTexture.getSampler(), _gpuTexture.getType(), this); + (void)CHECK_GL_ERROR(); + + + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); } GLBackend::GLTexture::~GLTexture() { - if (_texture != 0) { - glDeleteTextures(1, &_texture); + if (_privateTexture != 0) { + glDeleteTextures(1, &_privateTexture); } + Backend::updateTextureGPUMemoryUsage(_size, 0); + Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); Backend::decrementTextureGPUCount(); } + +void GLBackend::GLTexture::setSize(GLuint size) { + Backend::updateTextureGPUMemoryUsage(_size, size); + _size = size; +} + +void GLBackend::GLTexture::setVirtualSize(GLuint size) { + Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, size); + _virtualSize = size; +} + +void GLBackend::GLTexture::updateSize() { + GLuint virtualSize = _gpuTexture.evalTotalSize(); + setVirtualSize(virtualSize); + if (!_texture) { + setSize(virtualSize); + } + + if (_gpuTexture.getTexelFormat().isCompressed()) { + GLenum proxyType = GL_TEXTURE_2D; + GLuint numFaces = 1; + if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) { + proxyType = CUBE_FACE_LAYOUT[0]; + numFaces = CUBE_NUM_FACES; + } + GLint gpuSize{ 0 }; + glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); + (void)CHECK_GL_ERROR(); + + if (gpuSize) { + for (GLuint level = 0; level < _numLevels; level++) { + GLint levelSize{ 0 }; + glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); + levelSize *= numFaces; + + if (levelSize <= 0) { + break; + } + gpuSize += levelSize; + } + (void)CHECK_GL_ERROR(); + + setSize(gpuSize); + } else { + setSize(virtualSize); + } + + } else { + setSize(virtualSize); + } +} + + bool GLBackend::GLTexture::isInvalid() const { return _storageStamp < _gpuTexture.getStamp(); } @@ -117,44 +212,25 @@ bool GLBackend::GLTexture::isReady() const { return Idle == syncState; } -//#define USE_PBO - // Move content bits from the CPU to the GPU for a given mip / face void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat()); -#ifdef USE_PBO - GLuint pixelBufferID; - glGenBuffers(1, &pixelBufferID); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBufferID); - //if (GLEW_VERSION_4_4) { - // glBufferStorage(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); - //} else { - glBufferData(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); - //} - void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - memcpy(mappedBuffer, mip->readData(), mip->getSize()); - //// use while PBO is still bound, assumes GL_TEXTURE_2D and offset 0 - glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, 0); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glDeleteBuffers(1, &pixelBufferID); -#else - //glTexImage2D(target, 0, internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes); - glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); + glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); (void)CHECK_GL_ERROR(); -#endif } // Move content bits from the CPU to the GPU void GLBackend::GLTexture::transfer() const { PROFILE_RANGE(__FUNCTION__); - qDebug() << "Transferring texture: " << _texture; + //qDebug() << "Transferring texture: " << _privateTexture; // Need to update the content of the GPU object from the source sysmem of the texture if (_contentStamp >= _gpuTexture.getDataStamp()) { return; } - glBindTexture(_target, _texture); + //_secretTexture + glBindTexture(_target, _privateTexture); + // glBindTexture(_target, _texture); // GO through the process of allocating the correct storage and/or update the content switch (_gpuTexture.getType()) { case Texture::TEX_2D: @@ -186,6 +262,10 @@ void GLBackend::GLTexture::transfer() const { // Do any post-transfer operations that might be required on the main context / rendering thread void GLBackend::GLTexture::postTransfer() { setSyncState(GLTexture::Idle); + + // The public gltexture becaomes available + _texture = _privateTexture; + // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory switch (_gpuTexture.getType()) { case Texture::TEX_2D: @@ -204,7 +284,7 @@ void GLBackend::GLTexture::postTransfer() { } } -GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { +GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer, bool needTransfer) { const Texture& texture = *texturePointer; if (!texture.isDefined()) { // NO texture definition yet so let's avoid thinking @@ -218,7 +298,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin } // Object isn't ready, check what we need to do... - + // Create the texture if need be (force re-creation if the storage stamp changes // for easier use of immutable storage) if (!object || object->isInvalid()) { @@ -226,18 +306,23 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin object = new GLTexture(texture); } - // need to have a gpu object? - if (texture.getNumSlices() != 1) { + // Object maybe doens't neet to be tranasferred after creation + if (!needTransfer) { + object->createTexture(); + object->_contentStamp = texturePointer->getDataStamp(); + object->postTransfer(); return object; } // Object might be outdated, if so, start the transfer // (outdated objects that are already in transfer will have reported 'true' for ready() if (object->isOutdated()) { + Backend::incrementTextureGPUTransferCount(); _textureTransferHelper->transferTexture(texturePointer); } if (GLTexture::Transferred == object->getSyncState()) { + Backend::decrementTextureGPUTransferCount(); object->postTransfer(); } @@ -338,7 +423,8 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { return; } - GLTexture* object = GLBackend::syncGPUObject(resourceTexture); + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = GLBackend::syncGPUObject(resourceTexture, false); if (!object) { return; } diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp index 907bc6a538..90995c8e6f 100644 --- a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp @@ -9,26 +9,12 @@ #include "GLBackendTextureTransfer.h" #include "GLBackendShared.h" -#include "GLTexelFormat.h" #ifdef THREADED_TEXTURE_TRANSFER #include #include -//#define FORCE_DRAW_AFTER_TRANSFER - -#ifdef FORCE_DRAW_AFTER_TRANSFER - -#include - -static ProgramPtr _program; -static ProgramPtr _cubeProgram; -static ShapeWrapperPtr _plane; -static ShapeWrapperPtr _skybox; -static BasicFramebufferWrapperPtr _framebuffer; - -#endif #endif @@ -50,30 +36,18 @@ GLTextureTransferHelper::GLTextureTransferHelper() { void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); #ifdef THREADED_TEXTURE_TRANSFER - TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; - glFlush(); + TextureTransferPackage package{ texturePointer, 0}; object->setSyncState(GLBackend::GLTexture::Pending); queueItem(package); #else object->transfer(); - object->postTransfer(); + object->setSyncState(GLBackend::GLTexture::Transferred); #endif } void GLTextureTransferHelper::setup() { #ifdef THREADED_TEXTURE_TRANSFER _canvas->makeCurrent(); - -#ifdef FORCE_DRAW_AFTER_TRANSFER - _program = loadDefaultShader(); - _plane = loadPlane(_program); - _cubeProgram = loadCubemapShader(); - _skybox = loadSkybox(_cubeProgram); - _framebuffer = std::make_shared(); - _framebuffer->Init({ 100, 100 }); - _framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw); -#endif - #endif } @@ -85,8 +59,6 @@ void GLTextureTransferHelper::shutdown() { bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { for (auto package : messages) { - glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(package.fence); TexturePointer texturePointer = package.texture.lock(); // Texture no longer exists, move on to the next if (!texturePointer) { @@ -94,37 +66,17 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { } GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); + object->createTexture(); + object->transfer(); -#ifdef FORCE_DRAW_AFTER_TRANSFER - // Now force a draw using the texture - try { - switch (texturePointer->getType()) { - case Texture::TEX_2D: - _program->Use(); - _plane->Use(); - _plane->Draw(); - break; - - case Texture::TEX_CUBE: - _cubeProgram->Use(); - _skybox->Use(); - _skybox->Draw(); - break; - - default: - qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << texturePointer->getType() << " not supported"; - break; - } - } catch (const std::runtime_error& error) { - qWarning() << "Failed to render texture on background thread: " << error.what(); - } -#endif + object->updateSize(); glBindTexture(object->_target, 0); auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); glDeleteSync(writeSync); + object->_contentStamp = texturePointer->getDataStamp(); object->setSyncState(GLBackend::GLTexture::Transferred); } diff --git a/libraries/gpu/src/gpu/GLTexelFormat.h b/libraries/gpu/src/gpu/GLTexelFormat.h deleted file mode 100644 index 189189f68b..0000000000 --- a/libraries/gpu/src/gpu/GLTexelFormat.h +++ /dev/null @@ -1,375 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 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 "GLBackendShared.h" - -class GLTexelFormat { -public: - GLenum internalFormat; - GLenum format; - GLenum type; - - static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) { - return evalGLTexelFormat(dstFormat, dstFormat); - } - static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) { - using namespace gpu; - if (dstFormat != srcFormat) { - GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; - - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - texel.format = GL_RED; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_R8; - break; - case gpu::DEPTH: - texel.internalFormat = GL_DEPTH_COMPONENT32; - break; - case gpu::DEPTH_STENCIL: - texel.type = GL_UNSIGNED_INT_24_8; - texel.format = GL_DEPTH_STENCIL; - texel.internalFormat = GL_DEPTH24_STENCIL8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC2: { - texel.format = GL_RG; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RG8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC3: { - texel.format = GL_RGB; - - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RGB8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC4: { - texel.format = GL_RGBA; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (srcFormat.getSemantic()) { - case gpu::BGRA: - case gpu::SBGRA: - texel.format = GL_BGRA; - break; - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - case gpu::SRGBA: - default: - break; - }; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - texel.internalFormat = GL_RGB8; - break; - case gpu::RGBA: - texel.internalFormat = GL_RGBA8; - break; - case gpu::SRGB: - texel.internalFormat = GL_SRGB8; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - break; - } - - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - return texel; - } else { - GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; - - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - texel.format = GL_RED; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - case gpu::SRGBA: - texel.internalFormat = GL_R8; - switch (dstFormat.getType()) { - case gpu::UINT32: { - texel.internalFormat = GL_R32UI; - break; - } - case gpu::INT32: { - texel.internalFormat = GL_R32I; - break; - } - case gpu::NUINT32: { - texel.internalFormat = GL_R8; - break; - } - case gpu::NINT32: { - texel.internalFormat = GL_R8_SNORM; - break; - } - case gpu::FLOAT: { - texel.internalFormat = GL_R32F; - break; - } - case gpu::UINT16: { - texel.internalFormat = GL_R16UI; - break; - } - case gpu::INT16: { - texel.internalFormat = GL_R16I; - break; - } - case gpu::NUINT16: { - texel.internalFormat = GL_R16; - break; - } - case gpu::NINT16: { - texel.internalFormat = GL_R16_SNORM; - break; - } - case gpu::HALF: { - texel.internalFormat = GL_R16F; - break; - } - case gpu::UINT8: { - texel.internalFormat = GL_R8UI; - break; - } - case gpu::INT8: { - texel.internalFormat = GL_R8I; - break; - } - case gpu::NUINT8: { - if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - texel.internalFormat = GL_SLUMINANCE8; - } else { - texel.internalFormat = GL_R8; - } - break; - } - case gpu::NINT8: { - texel.internalFormat = GL_R8_SNORM; - break; - } - case gpu::NUM_TYPES: { // quiet compiler - Q_UNREACHABLE(); - } - - } - break; - - case gpu::R11G11B10: - texel.format = GL_RGB; - // the type should be float - texel.internalFormat = GL_R11F_G11F_B10F; - break; - - case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it - texel.internalFormat = GL_DEPTH_COMPONENT32; - switch (dstFormat.getType()) { - case gpu::UINT32: - case gpu::INT32: - case gpu::NUINT32: - case gpu::NINT32: { - texel.internalFormat = GL_DEPTH_COMPONENT32; - break; - } - case gpu::FLOAT: { - texel.internalFormat = GL_DEPTH_COMPONENT32F; - break; - } - case gpu::UINT16: - case gpu::INT16: - case gpu::NUINT16: - case gpu::NINT16: - case gpu::HALF: { - texel.internalFormat = GL_DEPTH_COMPONENT16; - break; - } - case gpu::UINT8: - case gpu::INT8: - case gpu::NUINT8: - case gpu::NINT8: { - texel.internalFormat = GL_DEPTH_COMPONENT24; - break; - } - case gpu::NUM_TYPES: { // quiet compiler - Q_UNREACHABLE(); - } - } - break; - case gpu::DEPTH_STENCIL: - texel.type = GL_UNSIGNED_INT_24_8; - texel.format = GL_DEPTH_STENCIL; - texel.internalFormat = GL_DEPTH24_STENCIL8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC2: { - texel.format = GL_RG; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RG8; - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC3: { - texel.format = GL_RGB; - - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RGB8; - break; - case gpu::SRGB: - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC4: { - texel.format = GL_RGBA; - texel.type = _elementTypeToGLType[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - texel.internalFormat = GL_RGB8; - break; - case gpu::RGBA: - texel.internalFormat = GL_RGBA8; - switch (dstFormat.getType()) { - case gpu::UINT32: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA32UI; - break; - case gpu::INT32: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA32I; - break; - case gpu::FLOAT: - texel.internalFormat = GL_RGBA32F; - break; - case gpu::UINT16: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA16UI; - break; - case gpu::INT16: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA16I; - break; - case gpu::NUINT16: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA16; - break; - case gpu::NINT16: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA16_SNORM; - break; - case gpu::HALF: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA16F; - break; - case gpu::UINT8: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA8UI; - break; - case gpu::INT8: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA8I; - break; - case gpu::NUINT8: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8; - break; - case gpu::NINT8: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8_SNORM; - break; - case gpu::NUINT32: - case gpu::NINT32: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - case gpu::SRGB: - texel.internalFormat = GL_SRGB8; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color - break; - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - break; - } - - default: - qCDebug(gpulogging) << "Unknown combination of texel format"; - } - return texel; - } - } -}; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index ad0fae577c..15ae609fb9 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -46,10 +46,17 @@ uint32_t Texture::getTextureGPUCount() { Texture::Size Texture::getTextureGPUMemoryUsage() { return Context::getTextureGPUMemoryUsage(); - } -uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = {1, 1, 1, 6}; +Texture::Size Texture::getTextureGPUVirtualMemoryUsage() { + return Context::getTextureGPUVirtualMemoryUsage(); +} + +uint32_t Texture::getTextureGPUTransferCount() { + return Context::getTextureGPUTransferCount(); +} + +uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : _format(format), diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 1a8885f075..9dde359596 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -146,6 +146,8 @@ public: static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); static Size getTextureGPUMemoryUsage(); + static Size getTextureGPUVirtualMemoryUsage(); + static uint32_t getTextureGPUTransferCount(); class Usage { public: diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 852e3d9e81..b8deef9f27 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -417,7 +417,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, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); _albedoTransform = material.albedoTexture.transform; map->setTextureTransform(_albedoTransform); @@ -488,7 +488,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!albedoName.isEmpty()) { auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); + auto map = fetchTextureMap(url, 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); @@ -497,7 +497,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!normalName.isEmpty()) { auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::NORMAL_MAP); + auto map = fetchTextureMap(url, NORMAL_TEXTURE, MapChannel::NORMAL_MAP); setTextureMap(MapChannel::NORMAL_MAP, map); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3878249f16..f314f6cb06 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -203,6 +203,18 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { switch (_type) { + case ALBEDO_TEXTURE: { + return TextureLoaderFunc(model::TextureUsage::createAlbedoTextureFromImage); + break; + } + case EMISSIVE_TEXTURE: { + return TextureLoaderFunc(model::TextureUsage::createEmissiveTextureFromImage); + break; + } + case LIGHTMAP_TEXTURE: { + return TextureLoaderFunc(model::TextureUsage::createLightmapTextureFromImage); + break; + } case CUBE_TEXTURE: { return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage); break; @@ -232,7 +244,6 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { break; } case DEFAULT_TEXTURE: - case EMISSIVE_TEXTURE: default: { return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage); break; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 858a40de36..4fe9a89460 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -31,6 +31,7 @@ typedef QSharedPointer NetworkTexturePointer; enum TextureType { DEFAULT_TEXTURE, + ALBEDO_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 5173af438c..014431619c 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -19,6 +19,9 @@ using namespace model; using namespace gpu; +// FIXME: Declare this to enable compression +//#define COMPRESS_TEXTURES + void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; @@ -49,12 +52,10 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) { _lightmapOffsetScale.y = scale; } - -// FIXME why is this in the model library? Move to GPU or GPU_GL -gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { +const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) { QImage image = srcImage; - bool validAlpha = false; - bool alphaAsMask = true; + validAlpha = false; + alphaAsMask = true; const uint8 OPAQUE_ALPHA = 255; const uint8 TRANSPARENT_ALPHA = 0; if (image.hasAlphaChannel()) { @@ -63,7 +64,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } - + // Actual alpha channel? create the histogram for (int y = 0; y < image.height(); ++y) { const QRgb* data = reinterpret_cast(image.constScanLine(y)); @@ -83,23 +84,77 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); } - } - + } + if (!validAlpha && image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } - + + return image; +} + +void TextureUsage::defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, +const QImage& image, bool isLinear, bool doCompress) { + +#ifdef COMPRESS_TEXTURES +#else + doCompress = false; +#endif + + if (image.hasAlphaChannel()) { + gpu::Semantic gpuSemantic; + gpu::Semantic mipSemantic; + if (isLinear) { + mipSemantic = gpu::SBGRA; + if (doCompress) { + gpuSemantic = gpu::COMPRESSED_SRGBA; + } else { + gpuSemantic = gpu::SRGBA; + } + } else { + mipSemantic = gpu::BGRA; + if (doCompress) { + gpuSemantic = gpu::COMPRESSED_RGBA; + } else { + gpuSemantic = gpu::RGBA; + } + } + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpuSemantic); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, mipSemantic); + } else { + gpu::Semantic gpuSemantic; + gpu::Semantic mipSemantic; + if (isLinear) { + mipSemantic = gpu::SRGB; + if (doCompress) { + gpuSemantic = gpu::COMPRESSED_SRGB; + } else { + gpuSemantic = gpu::SRGB; + } + } else { + mipSemantic = gpu::RGB; + if (doCompress) { + gpuSemantic = gpu::COMPRESSED_RGB; + } else { + gpuSemantic = gpu::RGB; + } + } + formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpuSemantic); + formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, mipSemantic); + } +} + +gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips) { + bool validAlpha = false; + bool alphaAsMask = true; + QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); + gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); - } + gpu::Element formatGPU; + gpu::Element formatMip; + defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -113,40 +168,46 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con theTexture->setUsage(usage.build()); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); - - // FIXME queue for transfer to GPU and block on completion + if (generateMips) { + theTexture->autoGenerateMips(-1); + } } - + return theTexture; } +gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, true, false, true); +} + + +gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, true, true, true); +} + +gpu::Texture* TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, true, true, true); +} + +gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, true, true, true); +} + gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; - if (!image.hasAlphaChannel()) { - if (image.format() != QImage::Format_RGB888) { - image = image.convertToFormat(QImage::Format_RGB888); - } - } else { - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - } + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); } gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - bool isLinearRGB = true; - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); - } theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); @@ -179,7 +240,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const double pStrength = 2.0; int width = image.width(); int height = image.height(); - QImage result(width, height, image.format()); + QImage result(width, height, QImage::Format_RGB888); for (int i = 0; i < width; i++) { const int iNextClamped = clampPixelCoordinate(i + 1, width - 1); @@ -227,23 +288,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - - // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); - } - - + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->autoGenerateMips(-1); } - + return theTexture; } @@ -263,8 +317,11 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - +#ifdef COMPRESS_TEXTURES + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); +#else gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); +#endif gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -297,9 +354,13 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { +#ifdef COMPRESS_TEXTURES + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); +#else gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); +#endif gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); - + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->autoGenerateMips(-1); @@ -327,7 +388,11 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { +#ifdef COMPRESS_TEXTURES + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); +#else gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); +#endif gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -372,315 +437,181 @@ public: _faceYNeg(fYN), _faceZPos(fZP), _faceZNeg(fZN) {} + + + 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; + } }; -gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = srcImage; - - int imageArea = image.width() * image.height(); - - - qCDebug(modelLog) << "Cube map size:" << QString(srcImageName.c_str()) << image.width() << image.height(); - - int opaquePixels = 0; - int translucentPixels = 0; - //bool isTransparent = false; - int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; - const int EIGHT_BIT_MAXIMUM = 255; - QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); - - if (!image.hasAlphaChannel()) { - if (image.format() != QImage::Format_RGB888) { - image = image.convertToFormat(QImage::Format_RGB888); - } - // int redTotal = 0, greenTotal = 0, blueTotal = 0; - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - QRgb rgb = image.pixel(x, y); - redTotal += qRed(rgb); - greenTotal += qGreen(rgb); - blueTotal += qBlue(rgb); - } - } - if (imageArea > 0) { - averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); - } - } else { - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - } - - // check for translucency/false transparency - // int opaquePixels = 0; - // int translucentPixels = 0; - // int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - QRgb rgb = image.pixel(x, y); - redTotal += qRed(rgb); - greenTotal += qGreen(rgb); - blueTotal += qBlue(rgb); - int alpha = qAlpha(rgb); - alphaTotal += alpha; - if (alpha == EIGHT_BIT_MAXIMUM) { - opaquePixels++; - } else if (alpha != 0) { - translucentPixels++; - } - } - } - if (opaquePixels == imageArea) { - qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str()); - image = image.convertToFormat(QImage::Format_RGB888); - } - - averageColor = QColor(redTotal / imageArea, - greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); - - //isTransparent = (translucentPixels >= imageArea / 2); +const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = { + // 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::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) { + + bool validAlpha = false; + bool alphaAsMask = true; + QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); + gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { + + gpu::Element formatGPU; + gpu::Element formatMip; + defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); + + // Find the layout of the cubemap in the 2D image + int foundLayout = CubeLayout::findLayout(image.width(), image.height()); - // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + std::vector faces; + // If found, go extract the faces as separate images + if (foundLayout >= 0) { + auto& layout = CubeLayout::CUBEMAP_LAYOUTS[foundLayout]; + 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 { + qCDebug(modelLog) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str()); + return nullptr; } - - const CubeLayout CUBEMAP_LAYOUTS[] = { - // 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 NUM_CUBEMAP_LAYOUTS = sizeof(CUBEMAP_LAYOUTS) / sizeof(CubeLayout); - - // Find the layout of the cubemap in the 2D image - int foundLayout = -1; - for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) { - if ((image.height() * CUBEMAP_LAYOUTS[i]._widthRatio) == (image.width() * CUBEMAP_LAYOUTS[i]._heightRatio)) { - foundLayout = i; - break; - } + // 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::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); + int f = 0; + for (auto& face : faces) { + theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); + f++; } - - std::vector faces; - // If found, go extract the faces as separate images - if (foundLayout >= 0) { - auto& layout = CUBEMAP_LAYOUTS[foundLayout]; - 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 { - qCDebug(modelLog) << "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::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); + + if (generateMips) { theTexture->autoGenerateMips(-1); - int f = 0; - for (auto& face : faces) { - theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); - f++; - } + } - // Generate irradiance while we are at it + // Generate irradiance while we are at it + if (generateIrradiance) { theTexture->generateIrradiance(); } - } - - return theTexture; -} - - -gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = srcImage; - - int imageArea = image.width() * image.height(); - - int opaquePixels = 0; - int translucentPixels = 0; - //bool isTransparent = false; - int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; - const int EIGHT_BIT_MAXIMUM = 255; - QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); - - if (!image.hasAlphaChannel()) { - if (image.format() != QImage::Format_RGB888) { - image = image.convertToFormat(QImage::Format_RGB888); } - // int redTotal = 0, greenTotal = 0, blueTotal = 0; - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - QRgb rgb = image.pixel(x, y); - redTotal += qRed(rgb); - greenTotal += qGreen(rgb); - blueTotal += qBlue(rgb); - } - } - if (imageArea > 0) { - averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); - } - } else { - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - } - - // check for translucency/false transparency - // int opaquePixels = 0; - // int translucentPixels = 0; - // int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - QRgb rgb = image.pixel(x, y); - redTotal += qRed(rgb); - greenTotal += qGreen(rgb); - blueTotal += qBlue(rgb); - int alpha = qAlpha(rgb); - alphaTotal += alpha; - if (alpha == EIGHT_BIT_MAXIMUM) { - opaquePixels++; - } else if (alpha != 0) { - translucentPixels++; - } - } - } - if (opaquePixels == imageArea) { - qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str()); - image = image.convertToFormat(QImage::Format_RGB888); - } - - averageColor = QColor(redTotal / imageArea, - greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); - - //isTransparent = (translucentPixels >= imageArea / 2); - } - - gpu::Texture* theTexture = nullptr; - if ((image.width() > 0) && (image.height() > 0)) { - - // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); - } - - - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); } return theTexture; } + +gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true); +} diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index e845aebb81..daa4b0d7bb 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -32,6 +32,8 @@ public: int _environmentUsage = 0; static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName); @@ -40,6 +42,13 @@ public: static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); + + static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask); + static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, + const QImage& srcImage, bool isLinear, bool doCompress); + static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips); + static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance); + }; diff --git a/libraries/networking/src/ResourceRequest.cpp b/libraries/networking/src/ResourceRequest.cpp index e6402d6b25..bce6af7091 100644 --- a/libraries/networking/src/ResourceRequest.cpp +++ b/libraries/networking/src/ResourceRequest.cpp @@ -20,6 +20,7 @@ void ResourceRequest::send() { QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection); return; } + Q_ASSERT(_state == NotStarted); _state = InProgress; diff --git a/libraries/render/src/render/EngineStats.cpp b/libraries/render/src/render/EngineStats.cpp index 794f278fcf..4b0936d326 100644 --- a/libraries/render/src/render/EngineStats.cpp +++ b/libraries/render/src/render/EngineStats.cpp @@ -32,6 +32,8 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte config->textureGPUCount = gpu::Texture::getTextureGPUCount(); config->textureCPUMemoryUsage = gpu::Texture::getTextureCPUMemoryUsage(); config->textureGPUMemoryUsage = gpu::Texture::getTextureGPUMemoryUsage(); + config->textureGPUVirtualMemoryUsage = gpu::Texture::getTextureGPUVirtualMemoryUsage(); + config->textureGPUTransferCount = gpu::Texture::getTextureGPUTransferCount(); gpu::ContextStats gpuStats(_gpuStats); renderContext->args->_context->getStats(_gpuStats); @@ -45,6 +47,7 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte config->frameTextureCount = _gpuStats._RSNumTextureBounded - gpuStats._RSNumTextureBounded; config->frameTextureRate = config->frameTextureCount * frequency; + config->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded; config->emitDirty(); } diff --git a/libraries/render/src/render/EngineStats.h b/libraries/render/src/render/EngineStats.h index 4a57724644..ab58d2af38 100644 --- a/libraries/render/src/render/EngineStats.h +++ b/libraries/render/src/render/EngineStats.h @@ -33,6 +33,8 @@ namespace render { Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty) Q_PROPERTY(qint64 textureCPUMemoryUsage MEMBER textureCPUMemoryUsage NOTIFY dirty) Q_PROPERTY(qint64 textureGPUMemoryUsage MEMBER textureGPUMemoryUsage NOTIFY dirty) + Q_PROPERTY(qint64 textureGPUVirtualMemoryUsage MEMBER textureGPUVirtualMemoryUsage NOTIFY dirty) + Q_PROPERTY(quint32 textureGPUTransferCount MEMBER textureGPUTransferCount NOTIFY dirty) Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY dirty) Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty) @@ -43,7 +45,7 @@ namespace render { Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty) Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty) - + Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty) public: EngineStatsConfig() : Job::Config(true) {} @@ -57,6 +59,8 @@ namespace render { quint32 textureGPUCount{ 0 }; qint64 textureCPUMemoryUsage{ 0 }; qint64 textureGPUMemoryUsage{ 0 }; + qint64 textureGPUVirtualMemoryUsage{ 0 }; + quint32 textureGPUTransferCount{ 0 }; quint32 frameAPIDrawcallCount{ 0 }; quint32 frameDrawcallCount{ 0 }; @@ -67,6 +71,7 @@ namespace render { quint32 frameTextureCount{ 0 }; quint32 frameTextureRate{ 0 }; + qint64 frameTextureMemoryUsage{ 0 }; void emitDirty() { emit dirty(); } diff --git a/libraries/shared/src/CPUID.cpp b/libraries/shared/src/CPUID.cpp deleted file mode 100644 index e0eae248f3..0000000000 --- a/libraries/shared/src/CPUID.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// CPUID.cpp -// -// Created by Ryan Huffman on 3/25/16. -// Copyright 2016 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 "CPUID.h" - -#ifdef Q_OS_WIN - -const CPUID::CPUID_Internal CPUID::CPU_Rep; - -std::vector CPUID::getAllFeatures() { - std::vector features; - - features.push_back({ "3DNOW", CPUID::_3DNOW() }); - features.push_back({ "3DNOWEXT", CPUID::_3DNOWEXT() }); - features.push_back({ "ABM", CPUID::ABM() }); - features.push_back({ "ADX", CPUID::ADX() }); - features.push_back({ "AES", CPUID::AES() }); - features.push_back({ "AVX", CPUID::AVX() }); - features.push_back({ "AVX2", CPUID::AVX2() }); - features.push_back({ "AVX512CD", CPUID::AVX512CD() }); - features.push_back({ "AVX512ER", CPUID::AVX512ER() }); - features.push_back({ "AVX512F", CPUID::AVX512F() }); - features.push_back({ "AVX512PF", CPUID::AVX512PF() }); - features.push_back({ "BMI1", CPUID::BMI1() }); - features.push_back({ "BMI2", CPUID::BMI2() }); - features.push_back({ "CLFSH", CPUID::CLFSH() }); - features.push_back({ "CMPXCHG16B", CPUID::CMPXCHG16B() }); - features.push_back({ "CX8", CPUID::CX8() }); - features.push_back({ "ERMS", CPUID::ERMS() }); - features.push_back({ "F16C", CPUID::F16C() }); - features.push_back({ "FMA", CPUID::FMA() }); - features.push_back({ "FSGSBASE", CPUID::FSGSBASE() }); - features.push_back({ "FXSR", CPUID::FXSR() }); - features.push_back({ "HLE", CPUID::HLE() }); - features.push_back({ "INVPCID", CPUID::INVPCID() }); - features.push_back({ "LAHF", CPUID::LAHF() }); - features.push_back({ "LZCNT", CPUID::LZCNT() }); - features.push_back({ "MMX", CPUID::MMX() }); - features.push_back({ "MMXEXT", CPUID::MMXEXT() }); - features.push_back({ "MONITOR", CPUID::MONITOR() }); - features.push_back({ "MOVBE", CPUID::MOVBE() }); - features.push_back({ "MSR", CPUID::MSR() }); - features.push_back({ "OSXSAVE", CPUID::OSXSAVE() }); - features.push_back({ "PCLMULQDQ", CPUID::PCLMULQDQ() }); - features.push_back({ "POPCNT", CPUID::POPCNT() }); - features.push_back({ "PREFETCHWT1", CPUID::PREFETCHWT1() }); - features.push_back({ "RDRAND", CPUID::RDRAND() }); - features.push_back({ "RDSEED", CPUID::RDSEED() }); - features.push_back({ "RDTSCP", CPUID::RDTSCP() }); - features.push_back({ "RTM", CPUID::RTM() }); - features.push_back({ "SEP", CPUID::SEP() }); - features.push_back({ "SHA", CPUID::SHA() }); - features.push_back({ "SSE", CPUID::SSE() }); - features.push_back({ "SSE2", CPUID::SSE2() }); - features.push_back({ "SSE3", CPUID::SSE3() }); - features.push_back({ "SSE4.1", CPUID::SSE41() }); - features.push_back({ "SSE4.2", CPUID::SSE42() }); - features.push_back({ "SSE4a", CPUID::SSE4a() }); - features.push_back({ "SSSE3", CPUID::SSSE3() }); - features.push_back({ "SYSCALL", CPUID::SYSCALL() }); - features.push_back({ "TBM", CPUID::TBM() }); - features.push_back({ "XOP", CPUID::XOP() }); - features.push_back({ "XSAVE", CPUID::XSAVE() }); - - return features; -}; - -#endif \ No newline at end of file diff --git a/libraries/shared/src/CPUIdent.cpp b/libraries/shared/src/CPUIdent.cpp new file mode 100644 index 0000000000..718b01b196 --- /dev/null +++ b/libraries/shared/src/CPUIdent.cpp @@ -0,0 +1,75 @@ +// +// CPUIdent.cpp +// +// Created by Ryan Huffman on 3/25/16. +// Copyright 2016 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 "CPUIdent.h" + +#ifdef Q_OS_WIN + +const CPUIdent::CPUIdent_Internal CPUIdent::CPU_Rep; + +std::vector CPUIdent::getAllFeatures() { + std::vector features; + + features.push_back({ "3DNOW", CPUIdent::_3DNOW() }); + features.push_back({ "3DNOWEXT", CPUIdent::_3DNOWEXT() }); + features.push_back({ "ABM", CPUIdent::ABM() }); + features.push_back({ "ADX", CPUIdent::ADX() }); + features.push_back({ "AES", CPUIdent::AES() }); + features.push_back({ "AVX", CPUIdent::AVX() }); + features.push_back({ "AVX2", CPUIdent::AVX2() }); + features.push_back({ "AVX512CD", CPUIdent::AVX512CD() }); + features.push_back({ "AVX512ER", CPUIdent::AVX512ER() }); + features.push_back({ "AVX512F", CPUIdent::AVX512F() }); + features.push_back({ "AVX512PF", CPUIdent::AVX512PF() }); + features.push_back({ "BMI1", CPUIdent::BMI1() }); + features.push_back({ "BMI2", CPUIdent::BMI2() }); + features.push_back({ "CLFSH", CPUIdent::CLFSH() }); + features.push_back({ "CMPXCHG16B", CPUIdent::CMPXCHG16B() }); + features.push_back({ "CX8", CPUIdent::CX8() }); + features.push_back({ "ERMS", CPUIdent::ERMS() }); + features.push_back({ "F16C", CPUIdent::F16C() }); + features.push_back({ "FMA", CPUIdent::FMA() }); + features.push_back({ "FSGSBASE", CPUIdent::FSGSBASE() }); + features.push_back({ "FXSR", CPUIdent::FXSR() }); + features.push_back({ "HLE", CPUIdent::HLE() }); + features.push_back({ "INVPCID", CPUIdent::INVPCID() }); + features.push_back({ "LAHF", CPUIdent::LAHF() }); + features.push_back({ "LZCNT", CPUIdent::LZCNT() }); + features.push_back({ "MMX", CPUIdent::MMX() }); + features.push_back({ "MMXEXT", CPUIdent::MMXEXT() }); + features.push_back({ "MONITOR", CPUIdent::MONITOR() }); + features.push_back({ "MOVBE", CPUIdent::MOVBE() }); + features.push_back({ "MSR", CPUIdent::MSR() }); + features.push_back({ "OSXSAVE", CPUIdent::OSXSAVE() }); + features.push_back({ "PCLMULQDQ", CPUIdent::PCLMULQDQ() }); + features.push_back({ "POPCNT", CPUIdent::POPCNT() }); + features.push_back({ "PREFETCHWT1", CPUIdent::PREFETCHWT1() }); + features.push_back({ "RDRAND", CPUIdent::RDRAND() }); + features.push_back({ "RDSEED", CPUIdent::RDSEED() }); + features.push_back({ "RDTSCP", CPUIdent::RDTSCP() }); + features.push_back({ "RTM", CPUIdent::RTM() }); + features.push_back({ "SEP", CPUIdent::SEP() }); + features.push_back({ "SHA", CPUIdent::SHA() }); + features.push_back({ "SSE", CPUIdent::SSE() }); + features.push_back({ "SSE2", CPUIdent::SSE2() }); + features.push_back({ "SSE3", CPUIdent::SSE3() }); + features.push_back({ "SSE4.1", CPUIdent::SSE41() }); + features.push_back({ "SSE4.2", CPUIdent::SSE42() }); + features.push_back({ "SSE4a", CPUIdent::SSE4a() }); + features.push_back({ "SSSE3", CPUIdent::SSSE3() }); + features.push_back({ "SYSCALL", CPUIdent::SYSCALL() }); + features.push_back({ "TBM", CPUIdent::TBM() }); + features.push_back({ "XOP", CPUIdent::XOP() }); + features.push_back({ "XSAVE", CPUIdent::XSAVE() }); + + return features; +}; + +#endif diff --git a/libraries/shared/src/CPUID.h b/libraries/shared/src/CPUIdent.h similarity index 96% rename from libraries/shared/src/CPUID.h rename to libraries/shared/src/CPUIdent.h index a8b8921d19..32668149f4 100644 --- a/libraries/shared/src/CPUID.h +++ b/libraries/shared/src/CPUIdent.h @@ -1,5 +1,5 @@ // -// CPUID.h +// CPUIdent.h // // Adapted from Microsoft's example for using the cpuid intrinsic, // found at https://msdn.microsoft.com/en-us/library/hskdteyh.aspx @@ -15,8 +15,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CPUID_h -#define hifi_CPUID_h +#ifndef hifi_CPUIdent_h +#define hifi_CPUIdent_h #include @@ -29,10 +29,10 @@ #include -class CPUID +class CPUIdent { // forward declarations - class CPUID_Internal; + class CPUIdent_Internal; public: struct Feature { @@ -104,12 +104,12 @@ public: static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } private: - static const CPUID_Internal CPU_Rep; + static const CPUIdent_Internal CPU_Rep; - class CPUID_Internal + class CPUIdent_Internal { public: - CPUID_Internal() + CPUIdent_Internal() : nIds_ { 0 }, nExIds_ { 0 }, isIntel_ { false }, @@ -209,4 +209,4 @@ private: #endif -#endif // hifi_CPUID_h +#endif // hifi_CPUIdent_h diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 30d4726bcc..bdc5d4c60d 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -24,7 +24,7 @@ #endif #ifdef Q_OS_WIN -#include "CPUID.h" +#include "CPUIdent.h" #endif @@ -758,10 +758,10 @@ void printSystemInformation() { qDebug() << "CPUID"; - qDebug() << "\tCPU Vendor: " << CPUID::Vendor().c_str(); - qDebug() << "\tCPU Brand: " << CPUID::Brand().c_str(); + qDebug() << "\tCPU Vendor: " << CPUIdent::Vendor().c_str(); + qDebug() << "\tCPU Brand: " << CPUIdent::Brand().c_str(); - for (auto& feature : CPUID::getAllFeatures()) { + for (auto& feature : CPUIdent::getAllFeatures()) { qDebug().nospace().noquote() << "\t[" << (feature.supported ? "x" : " ") << "] " << feature.name.c_str(); } #endif @@ -777,4 +777,4 @@ void printSystemInformation() { qDebug().noquote().nospace() << "\t" << (envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND"); } -} \ No newline at end of file +} diff --git a/libraries/shared/src/shared/RateCounter.h b/libraries/shared/src/shared/RateCounter.h index a160adadcf..754439ec15 100644 --- a/libraries/shared/src/shared/RateCounter.h +++ b/libraries/shared/src/shared/RateCounter.h @@ -27,6 +27,8 @@ public: float currentCount = _count; float intervalSeconds = (float)currentIntervalMs / (float)MSECS_PER_SECOND; _rate = roundf(currentCount / intervalSeconds * _scale) / _scale; + _start = now; + _count = 0; }; _count += count; }