summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp20
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp33
-rw-r--r--src/render/renderers/opengl/textures/gltexture.cpp258
-rw-r--r--src/render/renderers/opengl/textures/gltexture_p.h8
-rw-r--r--src/render/texture/apitexturemanager_p.h14
-rw-r--r--src/render/texture/texture.cpp2
-rw-r--r--tests/auto/render/textures/tst_textures.cpp94
-rw-r--r--tests/manual/manual.pro6
-rw-r--r--tests/manual/sharedtexture/main.cpp159
-rw-r--r--tests/manual/sharedtexture/sharedtexture.pro12
-rw-r--r--tests/manual/sharedtexture/videoplayer.cpp233
-rw-r--r--tests/manual/sharedtexture/videoplayer.h122
-rw-r--r--tests/manual/sharedtextureqml/main.cpp131
-rw-r--r--tests/manual/sharedtextureqml/main.qml144
-rw-r--r--tests/manual/sharedtextureqml/qml.qrc5
-rw-r--r--tests/manual/sharedtextureqml/sharedtextureqml.pro17
16 files changed, 1186 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);
}
diff --git a/tests/auto/render/textures/tst_textures.cpp b/tests/auto/render/textures/tst_textures.cpp
index 8bd4b355c..390853e77 100644
--- a/tests/auto/render/textures/tst_textures.cpp
+++ b/tests/auto/render/textures/tst_textures.cpp
@@ -112,6 +112,24 @@ private:
Q_DECLARE_PRIVATE(TestTexture)
};
+class TestSharedGLTexturePrivate : public Qt3DRender::QAbstractTexturePrivate
+{
+};
+
+class TestSharedGLTexture : public Qt3DRender::QAbstractTexture
+{
+public:
+ TestSharedGLTexture(int textureId, Qt3DCore::QNode *p = nullptr)
+ : QAbstractTexture(*new TestSharedGLTexturePrivate(), p)
+ {
+ d_func()->m_sharedTextureId = textureId;
+ }
+
+private:
+ Q_DECLARE_PRIVATE(TestSharedGLTexture)
+};
+
+
/**
* @brief Test QTextureImage
*/
@@ -163,6 +181,11 @@ class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
return tex;
}
+ Qt3DRender::QAbstractTexture *createQTextureWithTextureId(int textureId)
+ {
+ return new TestSharedGLTexture(textureId);
+ }
+
Qt3DRender::Render::Texture *createBackendTexture(Qt3DRender::QAbstractTexture *frontend,
Qt3DRender::Render::TextureManager *texMgr,
Qt3DRender::Render::TextureImageManager *texImgMgr,
@@ -270,6 +293,77 @@ private Q_SLOTS:
renderer.shutdown();
}
+ void shouldCreateDifferentGLTexturesWhenUsingSharedTextureIds()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ // both texture having the same sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(1);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 1);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::GLTexture *glt1 = mgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::GLTexture *glt2 = mgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ // textures having a different sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(2);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 2);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::GLTexture *glt1 = mgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::GLTexture *glt2 = mgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ renderer.shutdown();
+ }
+
void generatorsShouldCreateSameData()
{
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 643d03e6e..585928237 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -61,6 +61,12 @@ SUBDIRS += \
shared_texture_image \
texture_property_updates
+qtHaveModule(multimedia): {
+ SUBDIRS += \
+ sharedtexture \
+ sharedtextureqml
+}
+
qtHaveModule(widgets): {
SUBDIRS += \
assimp-cpp \
diff --git a/tests/manual/sharedtexture/main.cpp b/tests/manual/sharedtexture/main.cpp
new file mode 100644
index 000000000..a85f90ee6
--- /dev/null
+++ b/tests/manual/sharedtexture/main.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QPropertyAnimation>
+
+#include <Qt3DCore/QEntity>
+#include <Qt3DCore/QTransform>
+#include <Qt3DCore/QAspectEngine>
+
+#include <Qt3DRender/QCamera>
+#include <Qt3DRender/QCameraLens>
+#include <Qt3DRender/QRenderAspect>
+#include <Qt3DRender/QTexture>
+#include <Qt3DRender/QDirectionalLight>
+
+#include <Qt3DInput/QInputAspect>
+
+#include <Qt3DExtras/QForwardRenderer>
+#include <Qt3DExtras/QDiffuseMapMaterial>
+#include <Qt3DExtras/QCuboidMesh>
+#include <Qt3DExtras/QOrbitCameraController>
+#include <Qt3DExtras/Qt3DWindow>
+
+#include "videoplayer.h"
+
+Qt3DCore::QEntity *createScene(Qt3DExtras::Qt3DWindow *view, Qt3DRender::QAbstractTexture *diffuseTexture)
+{
+ // Root entity
+ Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
+
+ // Material
+ Qt3DExtras::QDiffuseMapMaterial *material = new Qt3DExtras::QDiffuseMapMaterial(rootEntity);
+ material->setDiffuse(diffuseTexture);
+ material->setAmbient(QColor(30, 30, 30));
+
+ // Sphere
+ Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity);
+ Qt3DExtras::QCuboidMesh *cuboidMesh = new Qt3DExtras::QCuboidMesh;
+ Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
+
+ transform->setRotationX(180);
+
+ QPropertyAnimation *cubeRotateTransformAnimation = new QPropertyAnimation(transform);
+ cubeRotateTransformAnimation->setTargetObject(transform);
+ cubeRotateTransformAnimation->setPropertyName("rotationY");
+ cubeRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
+ cubeRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
+ cubeRotateTransformAnimation->setDuration(10000);
+ cubeRotateTransformAnimation->setLoopCount(-1);
+ cubeRotateTransformAnimation->start();
+
+ sphereEntity->addComponent(cuboidMesh);
+ sphereEntity->addComponent(transform);
+ sphereEntity->addComponent(material);
+
+ // Camera
+ Qt3DRender::QCamera *camera = view->camera();
+ camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
+ camera->setPosition(QVector3D(0, 0, -5.0f));
+ camera->setViewCenter(QVector3D(0, 0, 0));
+
+ // For camera controls
+ Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
+ camController->setLinearSpeed( 50.0f );
+ camController->setLookSpeed( 180.0f );
+ camController->setCamera(camera);
+
+ Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight();
+ light->setIntensity(0.8f);
+ light->setWorldDirection(camera->viewVector());
+ rootEntity->addComponent(light);
+
+ return rootEntity;
+}
+
+int main(int argc, char* argv[])
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setMajorVersion(4);
+ format.setMinorVersion(5);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ QSurfaceFormat::setDefaultFormat(format);
+
+ // Will make Qt3D and QOpenGLWidget share a common context
+ QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+
+ QApplication app(argc, argv);
+
+ // Multimedia player
+ TextureWidget textureWidget;
+ VideoPlayerThread *videoPlayer = new VideoPlayerThread(&textureWidget);
+ videoPlayer->start();
+
+ textureWidget.resize(800, 600);
+ textureWidget.show();
+
+ // Texture object that Qt3D uses to access the texture from the video player
+ Qt3DRender::QSharedGLTexture *sharedTexture = new Qt3DRender::QSharedGLTexture();
+
+ QObject::connect(&textureWidget, &TextureWidget::textureIdChanged,
+ sharedTexture, &Qt3DRender::QSharedGLTexture::setTextureId);
+
+ // Qt3D Scene
+ Qt3DExtras::Qt3DWindow view;
+ Qt3DCore::QEntity *scene = createScene(&view, sharedTexture);
+ view.setRootEntity(scene);
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/sharedtexture/sharedtexture.pro b/tests/manual/sharedtexture/sharedtexture.pro
new file mode 100644
index 000000000..b41f43d71
--- /dev/null
+++ b/tests/manual/sharedtexture/sharedtexture.pro
@@ -0,0 +1,12 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += widgets 3dcore 3drender 3dinput 3dextras multimedia
+
+SOURCES += \
+ videoplayer.cpp \
+ main.cpp
+
+HEADERS += \
+ videoplayer.h
diff --git a/tests/manual/sharedtexture/videoplayer.cpp b/tests/manual/sharedtexture/videoplayer.cpp
new file mode 100644
index 000000000..f970116b5
--- /dev/null
+++ b/tests/manual/sharedtexture/videoplayer.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QMutexLocker>
+#include <QtMultimedia/QVideoFrame>
+
+#include "videoplayer.h"
+
+TextureWidget::TextureWidget(QWidget *parent)
+ : QOpenGLWidget(parent)
+ , m_texture(QOpenGLTexture::Target2D)
+{
+ // Lock mutex so that we never process a frame until we have been initialized
+ m_mutex.lock();
+}
+
+// Main thread
+void TextureWidget::initializeGL()
+{
+ initializeOpenGLFunctions();
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Vertex,
+ "#version 330\n"
+ "out vec2 coords;\n"
+ "const vec2 positions[6] = vec2[] ("
+ " vec2(-1.0, 1.0),"
+ " vec2(-1.0, -1.0),"
+ " vec2(1.0, 1.0),"
+ " vec2(1.0, 1.0),"
+ " vec2(-1.0, -1.0),"
+ " vec2(1.0, -1.0));\n"
+ "const vec2 texCoords[6] = vec2[] ("
+ " vec2(0.0, 0.0),"
+ " vec2(0.0, 1.0),"
+ " vec2(1.0, 0.0),"
+ " vec2(1.0, 0.0),"
+ " vec2(0.0, 1.0),"
+ " vec2(1.0, 1.0));\n"
+ "void main() {\n"
+ " coords = texCoords[gl_VertexID];\n"
+ " gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n"
+ "}"))
+ qDebug() << "Failed to load vertex shader" << m_shader.log();
+ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Fragment,
+ "#version 330\n"
+ "in vec2 coords;\n"
+ "uniform sampler2D video_texture;\n"
+ "out vec4 fragColor;\n"
+ "void main() {\n"
+ " fragColor = texture(video_texture, coords);\n"
+ "}"))
+ qDebug() << "Failed to load fragment shader" << m_shader.log();
+ if (!m_shader.link())
+ qDebug() << "Failed to link shaders" << m_shader.log();
+
+ qDebug() << Q_FUNC_INFO << context()->shareContext();
+
+ m_vao.create();
+ // Allow rendering/frame acquisition to go on
+ m_mutex.unlock();
+}
+
+// Main thread
+void TextureWidget::paintGL()
+{
+ QMutexLocker lock(&m_mutex);
+ glViewport(0, 0, width(), height());
+
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ if (!m_texture.isCreated())
+ return;
+
+ m_shader.bind();
+
+ m_texture.bind(0);
+ m_shader.setUniformValue("video_texture", 0);
+
+ m_vao.bind();
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ m_vao.release();
+ m_shader.release();
+}
+
+// Video Player thread
+void TextureWidget::setVideoFrame(const QVideoFrame &frame)
+{
+ // Ensure we won't be rendering while we are processing the frame
+ QMutexLocker lock(&m_mutex);
+
+ QVideoFrame f = frame;
+
+ // Map frame
+ if (!f.map(QAbstractVideoBuffer::ReadOnly))
+ return;
+
+ makeCurrent();
+
+ // Create or recreate texture
+ if (m_texture.width() != f.width() || m_texture.height() != f.height()) {
+ if (m_texture.isCreated())
+ m_texture.destroy();
+
+ m_texture.setSize(f.width(), f.height());
+ m_texture.setFormat(QOpenGLTexture::RGBA32F);
+ m_texture.setWrapMode(QOpenGLTexture::ClampToBorder);
+ m_texture.setMinificationFilter(QOpenGLTexture::Nearest);
+ m_texture.setMagnificationFilter(QOpenGLTexture::Nearest);
+ m_texture.allocateStorage();
+
+ m_texture.create();
+ emit textureIdChanged(m_texture.textureId());
+ }
+
+ const QVideoFrame::PixelFormat pFormat = f.pixelFormat();
+ if (pFormat == QVideoFrame::Format_RGB32) {
+ m_texture.setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, f.bits());
+ }
+
+ doneCurrent();
+
+ // Request display udpate
+ QOpenGLWidget::update();
+
+ // Unmap
+ f.unmap();
+}
+
+QList<QVideoFrame::PixelFormat> GLVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
+{
+ if (type == QAbstractVideoBuffer::NoHandle)
+ return {
+ QVideoFrame::Format_RGB32,
+ QVideoFrame::Format_ARGB32,
+ QVideoFrame::Format_BGR32,
+ QVideoFrame::Format_BGRA32
+ };
+ return {};
+}
+
+// Video player thread
+bool GLVideoSurface::present(const QVideoFrame &frame)
+{
+ emit onNewFrame(frame);
+ return true;
+}
+
+VideoPlayerThread::VideoPlayerThread(TextureWidget *textureWidget)
+ : QThread(textureWidget)
+ , m_player(new QMediaPlayer(nullptr, QMediaPlayer::VideoSurface))
+ , m_surface(new GLVideoSurface())
+{
+ m_player->moveToThread(this);
+ m_player->setMedia(QUrl("https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"));
+
+ // Tell player to render on GLVideoSurface
+ m_surface->moveToThread(this);
+ m_player->setVideoOutput(m_surface.get());
+
+ // Display errors
+ QObject::connect(m_player.get(), QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error),
+ m_player.get(), [this] (QMediaPlayer::Error e) {
+ qDebug() << Q_FUNC_INFO << e << m_player->errorString();
+ });
+
+ // Repeat video indefinitely
+ QObject::connect(m_player.get(), &QMediaPlayer::stateChanged, m_player.get(), [this] (QMediaPlayer::State state) {
+ if (state == QMediaPlayer::StoppedState)
+ m_player->play();
+ });
+
+ // Start playing when thread starts
+ QObject::connect(this, &QThread::started, this, [this] { m_player->play(); });
+
+ // Direct connection between 2 objects living in different threads
+ QObject::connect(m_surface.get(), &GLVideoSurface::onNewFrame,
+ textureWidget, &TextureWidget::setVideoFrame, Qt::DirectConnection);
+}
+
+VideoPlayerThread::~VideoPlayerThread()
+{
+ exit(0);
+ wait();
+}
diff --git a/tests/manual/sharedtexture/videoplayer.h b/tests/manual/sharedtexture/videoplayer.h
new file mode 100644
index 000000000..377ea57fe
--- /dev/null
+++ b/tests/manual/sharedtexture/videoplayer.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QThread>
+#include <QMutex>
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QOpenGLTexture>
+#include <QOpenGLShaderProgram>
+
+#include <QtMultimedia/QAbstractVideoSurface>
+#include <QtMultimedia/QMediaPlayer>
+
+#include <memory>
+
+class TextureWidget : public QOpenGLWidget, private QOpenGLFunctions
+{
+ Q_OBJECT
+ Q_PROPERTY(int textureId READ textureId NOTIFY textureIdChanged)
+public:
+ TextureWidget(QWidget *parent = nullptr);
+
+ int textureId() { return m_texture.textureId(); }
+
+private:
+ // MainThread
+ void initializeGL() override;
+
+ // Main thread
+ void paintGL() override;
+
+public Q_SLOTS:
+ // Called from Video player thread
+ void setVideoFrame(const QVideoFrame &frame);
+
+Q_SIGNALS:
+ void textureIdChanged(int textureId);
+
+private:
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLShaderProgram m_shader;
+ QOpenGLTexture m_texture;
+ QMutex m_mutex;
+};
+
+
+class GLVideoSurface : public QAbstractVideoSurface
+{
+ Q_OBJECT
+public:
+ QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const override;
+
+ // Call in VideaPlayerThread context
+ bool present(const QVideoFrame &frame) override;
+
+Q_SIGNALS:
+ void onNewFrame(const QVideoFrame &frame);
+};
+
+
+class VideoPlayerThread : public QThread
+{
+ Q_OBJECT
+public:
+ VideoPlayerThread(TextureWidget *textureWidget);
+ ~VideoPlayerThread();
+
+private:
+ TextureWidget *m_textureWidget;
+ std::unique_ptr<QMediaPlayer> m_player;
+ std::unique_ptr<GLVideoSurface> m_surface;
+};
diff --git a/tests/manual/sharedtextureqml/main.cpp b/tests/manual/sharedtextureqml/main.cpp
new file mode 100644
index 000000000..5c7ae9cff
--- /dev/null
+++ b/tests/manual/sharedtextureqml/main.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QtQml/QQmlContext>
+#include <QtQuick/QQuickView>
+#include <Qt3DRender/QAbstractTexture>
+#include <QWindow>
+#include <QTimer>
+
+#include <QQuickView>
+#include "videoplayer.h"
+
+
+template<typename Obj>
+QHash<int, QString> enumToNameMap(const char *enumName)
+{
+ const QMetaObject metaObj = Obj::staticMetaObject;
+ const int indexOfEnum = metaObj.indexOfEnumerator(enumName);
+ const QMetaEnum metaEnum = metaObj.enumerator(indexOfEnum);
+ const int keysCount = metaEnum.keyCount();
+
+ QHash<int, QString> v;
+ v.reserve(keysCount);
+ for (int i = 0; i < keysCount; ++i)
+ v[metaEnum.value(i)] = metaEnum.key(i);
+ return v;
+}
+
+
+class EnumNameMapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE QString statusName(int v) const { return m_statusMap.value(v); }
+ Q_INVOKABLE QString formatName(int v) const { return m_formatMap.value(v); }
+ Q_INVOKABLE QString targetName(int v) const { return m_targetMap.value(v); }
+
+private:
+ const QHash<int, QString> m_statusMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Status");
+ const QHash<int, QString> m_formatMap = enumToNameMap<Qt3DRender::QAbstractTexture>("TextureFormat");
+ const QHash<int, QString> m_targetMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Target");
+};
+
+int main(int argc, char* argv[])
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setMajorVersion(4);
+ format.setMinorVersion(5);
+ format.setDepthBufferSize(16);
+ format.setStencilBufferSize(8);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ QSurfaceFormat::setDefaultFormat(format);
+
+
+ // Make the OpenGLWidget's shared context be qt_gl_global_share_context
+ QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+ QApplication app(argc, argv);
+
+ // Multimedia player
+ TextureWidget textureWidget;
+ VideoPlayerThread *videoPlayer = new VideoPlayerThread(&textureWidget);
+ videoPlayer->start();
+
+ textureWidget.resize(800, 600);
+ textureWidget.show();
+
+ // Qt3D QtQuick Scene (uses qt_global_share_context by default)
+ QQuickView view;
+ QQmlContext *ctx = view.rootContext();
+ EnumNameMapper mapper;
+ ctx->setContextProperty(QStringLiteral("_nameMapper"), &mapper);
+ ctx->setContextProperty(QStringLiteral("_textureWidget"), &textureWidget);
+
+ view.resize(800, 600);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/sharedtextureqml/main.qml b/tests/manual/sharedtextureqml/main.qml
new file mode 100644
index 000000000..fd529e51c
--- /dev/null
+++ b/tests/manual/sharedtextureqml/main.qml
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.10
+import QtQuick.Scene3D 2.0
+import Qt3D.Core 2.10
+import Qt3D.Render 2.13
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+Item {
+ width: 800
+ height: 600
+
+ Scene3D {
+ anchors.fill: parent
+
+ Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -4.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { },
+ DirectionalLight {
+ intensity: 0.8
+ worldDirection: camera.viewVector
+ }
+ ]
+
+
+ NumberAnimation {
+ target: cubeTransform
+ property: "rotationY"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: Animation.Infinite
+ running: true
+ }
+
+ SharedGLTexture {
+ id: shaderGLTexture
+ textureId: _textureWidget.textureId
+ }
+
+ Entity {
+ id: cubeEntity
+ readonly property CuboidMesh cuboid: CuboidMesh {}
+ readonly property DiffuseMapMaterial material: DiffuseMapMaterial {
+ diffuse: shaderGLTexture
+ }
+ Transform {
+ id: cubeTransform
+ rotationX: 180
+ }
+
+ components: [ cuboid, material, cubeTransform ]
+ }
+ }
+ }
+
+ Grid {
+ spacing: 10
+ columns: 2
+ Text { text: "Target: " + _nameMapper.targetName(shaderGLTexture.target) }
+ Text { text: "Format: " + _nameMapper.formatName(shaderGLTexture.format) }
+ Text { text: "Width: " + shaderGLTexture.width }
+ Text { text: "Height: " + shaderGLTexture.height }
+ Text { text: "Depth: " + shaderGLTexture.depth }
+ Text { text: "Layers: " + shaderGLTexture.layers }
+ Text { text: "GL Texture Id: " + shaderGLTexture.textureId }
+ }
+
+}
diff --git a/tests/manual/sharedtextureqml/qml.qrc b/tests/manual/sharedtextureqml/qml.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/sharedtextureqml/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/sharedtextureqml/sharedtextureqml.pro b/tests/manual/sharedtextureqml/sharedtextureqml.pro
new file mode 100644
index 000000000..066bb8446
--- /dev/null
+++ b/tests/manual/sharedtextureqml/sharedtextureqml.pro
@@ -0,0 +1,17 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += widgets gui-private 3dcore 3drender 3dinput 3dextras multimedia quick 3dquickextras
+
+SOURCES += \
+ main.cpp \
+ ../sharedtexture/videoplayer.cpp
+
+HEADERS += \
+ ../sharedtexture/videoplayer.h
+
+INCLUDEPATH += ../sharedtexture
+
+RESOURCES += \
+ qml.qrc