diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp | 20 | ||||
-rw-r--r-- | src/render/renderers/opengl/renderer/renderer.cpp | 33 | ||||
-rw-r--r-- | src/render/renderers/opengl/textures/gltexture.cpp | 258 | ||||
-rw-r--r-- | src/render/renderers/opengl/textures/gltexture_p.h | 8 | ||||
-rw-r--r-- | src/render/texture/apitexturemanager_p.h | 14 | ||||
-rw-r--r-- | src/render/texture/texture.cpp | 2 |
6 files changed, 263 insertions, 72 deletions
diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp index 26ee94305..e7ebf3322 100644 --- a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -926,10 +926,22 @@ int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int o // Note: tex->dna() could be 0 if the texture has not been created yet if (m_activeTextures[onUnit].texture != tex) { // Texture must have been created and updated at this point - QOpenGLTexture *glTex = tex->getGLTexture(); - if (glTex == nullptr) - return -1; - glTex->bind(onUnit); + + const int sharedTextureId = tex->sharedTextureId(); + + // We have a valid texture id provided by a shared context + if (sharedTextureId > 0) { + m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit); + const QAbstractTexture::Target target = tex->properties().target; + // For now we know that target values correspond to the GL values + m_gl->functions()->glBindTexture(target, tex->sharedTextureId()); + } else { + QOpenGLTexture *glTex = tex->getGLTexture(); + if (glTex == nullptr) + return -1; + glTex->bind(onUnit); + } + if (m_activeTextures[onUnit].texture) TextureExtRendererLocker::unlock(m_activeTextures[onUnit].texture); m_activeTextures[onUnit].texture = tex; diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index c9ba6409d..e08c953f9 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -387,6 +387,9 @@ void Renderer::initialize() [this] { releaseGraphicsResources(); }); } + qCDebug(Backend) << "Qt3D shared context:" << ctx->shareContext(); + qCDebug(Backend) << "Qt global shared context:" << qt_gl_global_share_context(); + if (!ctx->shareContext()) { m_shareContext = new QOpenGLContext; m_shareContext->setFormat(ctx->format()); @@ -1086,7 +1089,7 @@ void Renderer::lookForDirtyTextures() } // Dirty meaning that something has changed on the texture - // either properties, parameters, generator or a texture image + // either properties, parameters, shared texture id, generator or a texture image if (texture->dirtyFlags() != Texture::NotDirty) m_dirtyTextures.push_back(handle); // Note: texture dirty flags are reset when actually updating the @@ -1292,18 +1295,21 @@ void Renderer::updateTexture(Texture *texture) return; // For implementing unique, non-shared, non-cached textures. - // for now, every texture is shared by default - - bool isUnique = false; + // for now, every texture is shared by default except if: + // - texture is reference by a render attachment + // - texture is referencing a shared texture id + bool isUnique = texture->sharedTextureId() > 0; - // TO DO: Update the vector once per frame (or in a job) - const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles(); - // A texture is unique if it's being reference by a render target output - for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) { - RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle); - if (attachment->textureUuid() == texture->peerId()) { - isUnique = true; - break; + if (!isUnique) { + // TO DO: Update the vector once per frame (or in a job) + const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles(); + // A texture is unique if it's being reference by a render target output + for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) { + RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle); + if (attachment->textureUuid() == texture->peerId()) { + isUnique = true; + break; + } } } @@ -1350,6 +1356,9 @@ void Renderer::updateTexture(Texture *texture) // we hold a reference to a unique or exclusive access to a shared texture // we can thus modify the texture directly. const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags(); + if (dirtyFlags.testFlag(Texture::DirtySharedTextureId) && + !glTextureManager->setSharedTextureId(glTexture, texture->sharedTextureId())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setSharedTextureId failed, should be non-shared"; if (dirtyFlags.testFlag(Texture::DirtyProperties) && !glTextureManager->setProperties(glTexture, texture->properties())) diff --git a/src/render/renderers/opengl/textures/gltexture.cpp b/src/render/renderers/opengl/textures/gltexture.cpp index b61d06a80..5c565470c 100644 --- a/src/render/renderers/opengl/textures/gltexture.cpp +++ b/src/render/renderers/opengl/textures/gltexture.cpp @@ -56,6 +56,11 @@ #include <Qt3DCore/qpropertynodeaddedchange.h> #include <Qt3DCore/qpropertynoderemovedchange.h> +#if !defined(QT_OPENGL_ES_2) +#include <QOpenGLFunctions_3_1> +#include <QOpenGLFunctions_4_5_Core> +#endif + QT_BEGIN_NAMESPACE using namespace Qt3DCore; @@ -73,6 +78,7 @@ GLTexture::GLTexture(TextureDataManager *texDataMgr, , m_textureDataManager(texDataMgr) , m_textureImageDataManager(texImgDataMgr) , m_dataFunctor(texGen) + , m_sharedTextureId(-1) , m_externalRendering(false) { // make sure texture generator is executed @@ -179,75 +185,88 @@ GLTexture::TextureUpdateInfo GLTexture::createOrUpdateGLTexture() m_properties.status = QAbstractTexture::Error; - // on the first invocation in the render thread, make sure to - // evaluate the texture data generator output - // (this might change some property values) - if (m_dataFunctor && !m_textureData) { - const bool successfullyLoadedTextureData = loadTextureDataFromGenerator(); - if (successfullyLoadedTextureData) { - setDirtyFlag(Properties, true); - needUpload = true; - } else { - qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; - textureInfo.properties.status = QAbstractTexture::Loading; - return textureInfo; + const bool hasSharedTextureId = m_sharedTextureId > 0; + + // Only load texture data if we are not using a sharedTextureId + if (!hasSharedTextureId) { + // on the first invocation in the render thread, make sure to + // evaluate the texture data generator output + // (this might change some property values) + if (m_dataFunctor && !m_textureData) { + const bool successfullyLoadedTextureData = loadTextureDataFromGenerator(); + if (successfullyLoadedTextureData) { + setDirtyFlag(Properties, true); + needUpload = true; + } else { + qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; + textureInfo.properties.status = QAbstractTexture::Loading; + return textureInfo; + } } - } - // additional texture images may be defined through image data generators - if (testDirtyFlag(TextureData)) { - m_imageData.clear(); - loadTextureDataFromImages(); - needUpload = true; - } + // additional texture images may be defined through image data generators + if (testDirtyFlag(TextureData)) { + m_imageData.clear(); + loadTextureDataFromImages(); + needUpload = true; + } - // don't try to create the texture if the format was not set - if (m_properties.format == QAbstractTexture::Automatic) { - textureInfo.properties.status = QAbstractTexture::Error; - return textureInfo; + // don't try to create the texture if the format was not set + if (m_properties.format == QAbstractTexture::Automatic) { + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } } // if the properties changed, we need to re-allocate the texture - if (testDirtyFlag(Properties)) { + if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) { delete m_gl; m_gl = nullptr; textureInfo.wasUpdated = true; } + m_properties.status = QAbstractTexture::Ready; - if (!m_gl) { - m_gl = buildGLTexture(); + if (hasSharedTextureId && testDirtyFlag(SharedTextureId)) { + // Update m_properties by doing introspection on the texture + introspectPropertiesFromSharedTextureId(); + } else { + // We only build a QOpenGLTexture if we have no shared textureId set if (!m_gl) { - textureInfo.properties.status = QAbstractTexture::Error; - return textureInfo; - } + m_gl = buildGLTexture(); + if (!m_gl) { + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } - m_gl->allocateStorage(); - if (!m_gl->isStorageAllocated()) { - textureInfo.properties.status = QAbstractTexture::Error; - return textureInfo; + m_gl->allocateStorage(); + if (!m_gl->isStorageAllocated()) { + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } } - } - m_properties.status = QAbstractTexture::Ready; - textureInfo.properties = m_properties; - textureInfo.texture = m_gl; + textureInfo.texture = m_gl; - // need to (re-)upload texture data? - if (needUpload) { - uploadGLTextureData(); - setDirtyFlag(TextureData, false); - } + // need to (re-)upload texture data? + if (needUpload) { + uploadGLTextureData(); + setDirtyFlag(TextureData, false); + } - // need to set texture parameters? - if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) { - updateGLTextureParameters(); + // need to set texture parameters? + if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) { + updateGLTextureParameters(); + } } + textureInfo.properties = m_properties; + // un-set properties and parameters. The TextureData flag might have been set by another thread // in the meantime, so don't clear that. setDirtyFlag(Properties, false); setDirtyFlag(Parameters, false); + setDirtyFlag(SharedTextureId, false); return textureInfo; } @@ -343,6 +362,14 @@ void GLTexture::setGenerator(const QTextureGeneratorPtr &generator) } } +void GLTexture::setSharedTextureId(int textureId) +{ + if (m_sharedTextureId != textureId) { + m_sharedTextureId = textureId; + setDirtyFlag(SharedTextureId); + } +} + // Return nullptr if // - context cannot be obtained // - texture hasn't yet been loaded @@ -389,8 +416,8 @@ QOpenGLTexture *GLTexture::buildGLTexture() // is written against GLES 1.0. if (m_properties.format == QAbstractTexture::RGB8_ETC1) { if ((ctx->isOpenGLES() && ctx->format().majorVersion() >= 3) - || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture")) - || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility"))) + || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture")) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility"))) format = m_properties.format = QAbstractTexture::RGB8_ETC2; } @@ -400,15 +427,15 @@ QOpenGLTexture *GLTexture::buildGLTexture() glTex->setSize(m_properties.width, m_properties.height, m_properties.depth); // Set layers count if texture array if (m_actualTarget == QAbstractTexture::Target1DArray || - m_actualTarget == QAbstractTexture::Target2DArray || - m_actualTarget == QAbstractTexture::Target3D || - m_actualTarget == QAbstractTexture::Target2DMultisampleArray || - m_actualTarget == QAbstractTexture::TargetCubeMapArray) { + m_actualTarget == QAbstractTexture::Target2DArray || + m_actualTarget == QAbstractTexture::Target3D || + m_actualTarget == QAbstractTexture::Target2DMultisampleArray || + m_actualTarget == QAbstractTexture::TargetCubeMapArray) { glTex->setLayers(m_properties.layers); } if (m_actualTarget == QAbstractTexture::Target2DMultisample || - m_actualTarget == QAbstractTexture::Target2DMultisampleArray) { + m_actualTarget == QAbstractTexture::Target2DMultisampleArray) { // Set samples count if multisampled texture // (multisampled textures don't have mipmaps) glTex->setSamples(m_properties.samples); @@ -484,8 +511,8 @@ void GLTexture::updateGLTextureParameters() { m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX)); if (m_actualTarget != QAbstractTexture::Target1D && - m_actualTarget != QAbstractTexture::Target1DArray && - m_actualTarget != QAbstractTexture::TargetBuffer) + m_actualTarget != QAbstractTexture::Target1DArray && + m_actualTarget != QAbstractTexture::TargetBuffer) m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY)); if (m_actualTarget == QAbstractTexture::Target3D) m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ)); @@ -499,6 +526,129 @@ void GLTexture::updateGLTextureParameters() } } +void GLTexture::introspectPropertiesFromSharedTextureId() +{ + // We know that the context is active when this function is called + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning() << Q_FUNC_INFO << "requires an OpenGL context"; + return; + } + QOpenGLFunctions *gl = ctx->functions(); + + // If the user has set the target format himself, we won't try to deduce it + if (m_properties.target != QAbstractTexture::TargetAutomatic) + return; + + const QAbstractTexture::Target targets[] = { + QAbstractTexture::Target2D, + QAbstractTexture::TargetCubeMap, +#ifndef QT_OPENGL_ES_2 + QAbstractTexture::Target1D, + QAbstractTexture::Target1DArray, + QAbstractTexture::Target3D, + QAbstractTexture::Target2DArray, + QAbstractTexture::TargetCubeMapArray, + QAbstractTexture::Target2DMultisample, + QAbstractTexture::Target2DMultisampleArray, + QAbstractTexture::TargetRectangle, + QAbstractTexture::TargetBuffer, +#endif + }; + +#ifndef QT_OPENGL_ES_2 + // Try to find texture target with GL 4.5 functions + const QPair<int, int> ctxGLVersion = ctx->format().version(); + if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) { + // Only for GL 4.5+ + QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>(); +#ifdef GL_TEXTURE_TARGET + if (gl5 != nullptr) + gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET, reinterpret_cast<int *>(&m_properties.target)); +#endif + } +#endif + + // If GL 4.5 function unavailable or not working, try a slower way + if (m_properties.target == QAbstractTexture::TargetAutomatic) { + // // OpenGL offers no proper way of querying for the target of a texture given its id + gl->glActiveTexture(GL_TEXTURE0); + + const GLenum targetBindings[] = { + GL_TEXTURE_BINDING_2D, + GL_TEXTURE_BINDING_CUBE_MAP, +#ifndef QT_OPENGL_ES_2 + GL_TEXTURE_BINDING_1D, + GL_TEXTURE_BINDING_1D_ARRAY, + GL_TEXTURE_BINDING_3D, + GL_TEXTURE_BINDING_2D_ARRAY, + GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, + GL_TEXTURE_BINDING_2D_MULTISAMPLE, + GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, + GL_TEXTURE_BINDING_RECTANGLE, + GL_TEXTURE_BINDING_BUFFER +#endif + }; + + Q_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0] == sizeof(targets) / sizeof(targets[0]))); + + for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) { + const int target = targets[i]; + gl->glBindTexture(target, m_sharedTextureId); + int boundId = 0; + gl->glGetIntegerv(targetBindings[i], &boundId); + gl->glBindTexture(target, 0); + if (boundId == m_sharedTextureId) { + m_properties.target = static_cast<QAbstractTexture::Target>(target); + break; + } + } + } + + // Return early if we weren't able to find texture target + if (std::find(std::begin(targets), std::end(targets), m_properties.target) == std::end(targets)) { + qWarning() << "Unable to determine texture target for shared GL texture"; + return; + } + + // Bind texture once we know its target + gl->glBindTexture(m_properties.target, m_sharedTextureId); + + // TO DO: Improve by using glGetTextureParameters when available which + // support direct state access +#ifndef GL_TEXTURE_MAX_LEVEL +#define GL_TEXTURE_MAX_LEVEL 0x813D +#endif + +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif + + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL, reinterpret_cast<int *>(&m_properties.mipLevels)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER, reinterpret_cast<int *>(&m_parameters.minificationFilter)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER, reinterpret_cast<int *>(&m_parameters.magnificationFilter)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int *>(&m_parameters.wrapModeX)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int *>(&m_parameters.wrapModeZ)); + +#ifndef QT_OPENGL_ES_2 + // Try to retrieve dimensions (not available on ES 2.0) + if (!ctx->isOpenGLES()) { + QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>(); + if (!gl3) { + qWarning() << "Failed to retrieve shared texture dimensions"; + return; + } + + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH, reinterpret_cast<int *>(&m_properties.width)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT, reinterpret_cast<int *>(&m_properties.height)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH, reinterpret_cast<int *>(&m_properties.depth)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<int *>(&m_properties.format)); + } +#endif + + gl->glBindTexture(m_properties.target, 0); +} } // namespace Render } // namespace Qt3DRender diff --git a/src/render/renderers/opengl/textures/gltexture_p.h b/src/render/renderers/opengl/textures/gltexture_p.h index dd0e05e36..47ecfdfa8 100644 --- a/src/render/renderers/opengl/textures/gltexture_p.h +++ b/src/render/renderers/opengl/textures/gltexture_p.h @@ -125,6 +125,7 @@ public: inline TextureProperties properties() const { return m_properties; } inline TextureParameters parameters() const { return m_parameters; } inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; } + inline int sharedTextureId() const { return m_sharedTextureId; } inline QVector<Image> images() const { return m_images; } inline QSize size() const { return QSize(m_properties.width, m_properties.height); } @@ -204,14 +205,15 @@ protected: void setProperties(const TextureProperties &props); void setImages(const QVector<Image> &images); void setGenerator(const QTextureGeneratorPtr &generator); + void setSharedTextureId(int textureId); private: enum DirtyFlag { TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU Properties = 0x02, // texture needs to be (re-)created - Parameters = 0x04 // texture parameters need to be (re-)set - + Parameters = 0x04, // texture parameters need to be (re-)set + SharedTextureId = 0x08 // texture id from shared context }; bool testDirtyFlag(DirtyFlag flag) @@ -232,6 +234,7 @@ private: void loadTextureDataFromImages(); void uploadGLTextureData(); void updateGLTextureParameters(); + void introspectPropertiesFromSharedTextureId(); void destroyResources(); bool m_unique; @@ -256,6 +259,7 @@ private: QTextureDataPtr m_textureData; QVector<QTextureImageDataPtr> m_imageData; + int m_sharedTextureId; bool m_externalRendering; }; diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h index 58e6e6420..79dc9af94 100644 --- a/src/render/texture/apitexturemanager_p.h +++ b/src/render/texture/apitexturemanager_p.h @@ -257,6 +257,19 @@ public: return true; } + // Change the texture's referenced texture Id from a shared context + bool setSharedTextureId(APITexture *tex, int textureId) + { + Q_ASSERT(tex); + + if (isShared(tex)) + return false; + + tex->setSharedTextureId(textureId); + m_updatedTextures.push_back(tex); + return true; + } + // Retrieves abandoned textures. This should be regularly called from the OpenGL thread // to make sure needed GL resources are de-allocated. QVector<APITexture*> takeAbandonedTextures() @@ -344,6 +357,7 @@ private: newTex->setProperties(node->properties()); newTex->setParameters(node->parameters()); newTex->setImages(texImgs); + newTex->setSharedTextureId(node->sharedTextureId()); m_updatedTextures.push_back(newTex); diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp index b87ea9c6d..5a45d2bc9 100644 --- a/src/render/texture/texture.cpp +++ b/src/render/texture/texture.cpp @@ -325,6 +325,8 @@ void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chan addTextureImage(imgId); addDirtyFlag(DirtyFlags(DirtyImageGenerators|DirtyProperties|DirtyParameters)); + if (m_sharedTextureId > 0) + addDirtyFlag(DirtySharedTextureId); } |