summaryrefslogtreecommitdiffstats
path: root/src/render/graphicshelpers/graphicscontext.cpp
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2018-03-02 10:27:07 +0100
committerPaul Lemire <paul.lemire@kdab.com>2018-03-16 11:21:02 +0000
commitc1b495f70c3b4f0c23d44dfcd99aed5882312100 (patch)
treeced8484d36d999395f520f2d7262d832afc73c99 /src/render/graphicshelpers/graphicscontext.cpp
parent02dff327fae71d6efdda091f8ae0a4483e72e849 (diff)
Split GraphicsContext in two
- GraphicsContext is now a stateless wrapper around GL calls - SubmissionContext is a GraphicsContext + caches and states used to render a frame Change-Id: I147b56dfd4c403911faadc0e9821fff9a892f71c Reviewed-by: Mike Krus <mike.krus@kdab.com>
Diffstat (limited to 'src/render/graphicshelpers/graphicscontext.cpp')
-rw-r--r--src/render/graphicshelpers/graphicscontext.cpp1119
1 files changed, 26 insertions, 1093 deletions
diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp
index 93f924052..ddd7281d1 100644
--- a/src/render/graphicshelpers/graphicscontext.cpp
+++ b/src/render/graphicshelpers/graphicscontext.cpp
@@ -111,88 +111,45 @@ QOpenGLShader::ShaderType shaderType(Qt3DRender::QShaderProgram::ShaderType type
namespace Qt3DRender {
namespace Render {
-static QHash<unsigned int, GraphicsContext*> static_contexts;
-
-static void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage)
-{
- qDebug() << "OpenGL debug message:" << debugMessage;
-}
-
namespace {
-GLBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type)
+void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage)
{
- switch (type) {
- case QAttribute::VertexAttribute:
- return GLBuffer::ArrayBuffer;
- case QAttribute::IndexAttribute:
- return GLBuffer::IndexBuffer;
- case QAttribute::DrawIndirectAttribute:
- return GLBuffer::DrawIndirectBuffer;
- default:
- Q_UNREACHABLE();
- }
+ qDebug() << "OpenGL debug message:" << debugMessage;
}
} // anonymous
-unsigned int nextFreeContextId()
-{
- for (unsigned int i=0; i < 0xffff; ++i) {
- if (!static_contexts.contains(i))
- return i;
- }
-
- qFatal("Couldn't find free context ID");
- return 0;
-}
-
GraphicsContext::GraphicsContext()
: m_initialized(false)
- , m_id(nextFreeContextId())
+ , m_supportsVAO(false)
+ , m_maxTextureUnits(0)
+ , m_defaultFBO(0)
, m_gl(nullptr)
- , m_surface(nullptr)
, m_glHelper(nullptr)
- , m_ownCurrent(true)
- , m_activeShader(nullptr)
- , m_activeShaderDNA(0)
- , m_renderTargetFormat(QAbstractTexture::NoFormat)
- , m_currClearStencilValue(0)
- , m_currClearDepthValue(1.f)
- , m_currClearColorValue(0,0,0,0)
- , m_material(nullptr)
- , m_activeFBO(0)
- , m_defaultFBO(0)
- , m_boundArrayBuffer(nullptr)
- , m_stateSet(nullptr)
- , m_renderer(nullptr)
- , m_uboTempArray(QByteArray(1024, 0))
- , m_supportsVAO(true)
+ , m_shaderCache(nullptr)
, m_debugLogger(nullptr)
- , m_currentVAO(nullptr)
{
- static_contexts[m_id] = this;
}
GraphicsContext::~GraphicsContext()
{
- releaseOpenGL();
+}
- Q_ASSERT(static_contexts[m_id] == this);
- static_contexts.remove(m_id);
+void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx)
+{
+ Q_ASSERT(ctx);
+ m_gl = ctx;
}
void GraphicsContext::initialize()
{
m_initialized = true;
- Q_ASSERT(m_gl);
+ Q_ASSERT(m_gl && m_shaderCache);
- GLint numTexUnits;
- m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTexUnits);
- qCDebug(Backend) << "context supports" << numTexUnits << "texture units";
-
- m_activeTextures.resize(numTexUnits);
+ m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits);
+ qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units";
if (m_gl->format().majorVersion() >= 3) {
m_supportsVAO = true;
@@ -207,96 +164,6 @@ void GraphicsContext::initialize()
qCDebug(Backend) << "VAO support = " << m_supportsVAO;
}
-void GraphicsContext::resolveRenderTargetFormat()
-{
- const QSurfaceFormat format = m_gl->format();
- const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize();
- const uint r = format.redBufferSize();
- const uint g = format.greenBufferSize();
- const uint b = format.blueBufferSize();
-
-#define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18))
-
- const uint bits = RGBA_BITS(r,g,b,a);
- switch (bits) {
- case RGBA_BITS(8,8,8,8):
- m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm;
- break;
- case RGBA_BITS(8,8,8,0):
- m_renderTargetFormat = QAbstractTexture::RGB8_UNorm;
- break;
- case RGBA_BITS(5,6,5,0):
- m_renderTargetFormat = QAbstractTexture::R5G6B5;
- break;
- }
-#undef RGBA_BITS
-}
-
-bool GraphicsContext::beginDrawing(QSurface *surface)
-{
- Q_ASSERT(surface);
- Q_ASSERT(m_gl);
-
- m_surface = surface;
-
- // TO DO: Find a way to make to pause work if the window is not exposed
- // if (m_surface && m_surface->surfaceClass() == QSurface::Window) {
- // qDebug() << Q_FUNC_INFO << 1;
- // if (!static_cast<QWindow *>(m_surface)->isExposed())
- // return false;
- // qDebug() << Q_FUNC_INFO << 2;
- // }
-
- // Makes the surface current on the OpenGLContext
- // and sets the right glHelper
- m_ownCurrent = !(m_gl->surface() == m_surface);
- if (m_ownCurrent && !makeCurrent(m_surface))
- return false;
-
- // TODO: cache surface format somewhere rather than doing this every time render surface changes
- resolveRenderTargetFormat();
-
- // Sets or Create the correct m_glHelper
- // for the current surface
- activateGLHelper();
-
-#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
- GLint err = m_gl->functions()->glGetError();
- if (err != 0) {
- qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err;
- }
-#endif
-
- if (!m_initialized) {
- initialize();
- }
-
- // need to reset these values every frame, may get overwritten elsewhere
- m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF());
- m_gl->functions()->glClearDepthf(m_currClearDepthValue);
- m_gl->functions()->glClearStencil(m_currClearStencilValue);
-
-
- if (m_activeShader) {
- m_activeShader = nullptr;
- m_activeShaderDNA = 0;
- }
-
- // reset active textures
- for (int u = 0; u < m_activeTextures.size(); ++u)
- m_activeTextures[u].texture = nullptr;
-
- m_boundArrayBuffer = nullptr;
-
- static int callCount = 0;
- ++callCount;
- const int shaderPurgePeriod = 600;
- if (callCount % shaderPurgePeriod == 0)
- m_shaderCache.purge();
-
- return true;
-}
-
void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers)
{
if (buffers != QClearBuffers::None) {
@@ -313,120 +180,6 @@ void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers)
}
}
-void GraphicsContext::endDrawing(bool swapBuffers)
-{
- if (swapBuffers)
- m_gl->swapBuffers(m_surface);
- if (m_ownCurrent)
- m_gl->doneCurrent();
- decayTextureScores();
-}
-
-QSize GraphicsContext::renderTargetSize(const QSize &surfaceSize) const
-{
- QSize renderTargetSize;
- if (m_activeFBO != m_defaultFBO) {
- // For external FBOs we may not have a m_renderTargets entry.
- if (m_renderTargetsSize.contains(m_activeFBO)) {
- renderTargetSize = m_renderTargetsSize[m_activeFBO];
- } else if (surfaceSize.isValid()) {
- renderTargetSize = surfaceSize;
- } else {
- // External FBO (when used with QtQuick2 Scene3D)
-
- // Query FBO color attachment 0 size
- GLint attachmentObjectType = GL_NONE;
- GLint attachment0Name = 0;
- m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
- GL_COLOR_ATTACHMENT0,
- GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
- &attachmentObjectType);
- m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
- GL_COLOR_ATTACHMENT0,
- GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
- &attachment0Name);
-
- if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval))
- renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name);
- else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval))
- // Assumes texture level 0 and GL_TEXTURE_2D target
- renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D);
- else
- return renderTargetSize;
- }
- } else {
- renderTargetSize = m_surface->size();
- if (m_surface->surfaceClass() == QSurface::Window) {
- int dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio();
- renderTargetSize *= dpr;
- }
- }
- return renderTargetSize;
-}
-
-void GraphicsContext::setViewport(const QRectF &viewport, const QSize &surfaceSize)
-{
- // save for later use; this has nothing to do with the viewport but it is
- // here that we get to know the surfaceSize from the RenderView.
- m_surfaceSize = surfaceSize;
-
- m_viewport = viewport;
- QSize size = renderTargetSize(surfaceSize);
-
- // Check that the returned size is before calling glViewport
- if (size.isEmpty())
- return;
-
- // Qt3D 0------------------> 1 OpenGL 1^
- // | |
- // | |
- // | |
- // V |
- // 1 0---------------------> 1
- // The Viewport is defined between 0 and 1 which allows us to automatically
- // scale to the size of the provided window surface
- m_gl->functions()->glViewport(m_viewport.x() * size.width(),
- (1.0 - m_viewport.y() - m_viewport.height()) * size.height(),
- m_viewport.width() * size.width(),
- m_viewport.height() * size.height());
-}
-
-void GraphicsContext::releaseOpenGL()
-{
- m_shaderCache.clear();
- m_renderBufferHash.clear();
-
- // Stop and destroy the OpenGL logger
- if (m_debugLogger) {
- m_debugLogger->stopLogging();
- m_debugLogger.reset(nullptr);
- }
-
- qDeleteAll(m_glHelpers);
-}
-
-// The OpenGLContext is not current on any surface at this point
-void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx)
-{
- Q_ASSERT(ctx);
-
- releaseOpenGL();
- m_gl = ctx;
-}
-
-void GraphicsContext::activateGLHelper()
-{
- // Sets the correct GL Helper depending on the surface
- // If no helper exists, create one
- m_glHelper = m_glHelpers.value(m_surface);
- if (!m_glHelper) {
- m_glHelper = resolveHighestOpenGLFunctions();
- m_glHelpers.insert(m_surface, m_glHelper);
- // Note: OpenGLContext is current at this point
- m_gl->functions()->glDisable(GL_DITHER);
- }
-}
-
bool GraphicsContext::hasValidGLHelper() const
{
return m_glHelper != nullptr;
@@ -510,13 +263,13 @@ void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderPro
void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
{
bool wasPresent = false;
- QOpenGLShaderProgram *shaderProgram = m_shaderCache.getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent);
+ QOpenGLShaderProgram *shaderProgram = m_shaderCache->getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent);
if (!shaderProgram && !wasPresent) {
// No matching QOpenGLShader in the cache so create one
shaderProgram = createShaderProgram(shader);
// Store in cache (even when failed and shaderProgram is null)
- m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram);
+ m_shaderCache->insert(shader->dna(), shader->peerId(), shaderProgram);
}
// Ensure the Shader node knows about the program interface
@@ -524,7 +277,7 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
// Find an already loaded shader that shares the same QOpenGLShaderProgram
Shader *refShader = nullptr;
- const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache.shaderIdsForProgram(shader->dna());
+ const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache->shaderIdsForProgram(shader->dna());
for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) {
Shader *sharedShader = manager->lookupResource(sharedShaderId);
// Note: no need to check if shader->peerId != sharedShader->peerId
@@ -548,137 +301,9 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
}
}
-// Called only from RenderThread
-bool GraphicsContext::activateShader(ProgramDNA shaderDNA)
-{
- if (shaderDNA != m_activeShaderDNA) {
- // Ensure material uniforms are re-applied
- m_material = nullptr;
-
- m_activeShader = m_shaderCache.getShaderProgramForDNA(shaderDNA);
- if (Q_LIKELY(m_activeShader != nullptr)) {
- m_activeShader->bind();
- m_activeShaderDNA = shaderDNA;
- } else {
- m_glHelper->useProgram(0);
- qWarning() << "No shader program found for DNA";
- m_activeShaderDNA = 0;
- return false;
- }
- }
- return true;
-}
-
void GraphicsContext::removeShaderProgramReference(Shader *shaderNode)
{
- m_shaderCache.removeRef(shaderNode->dna(), shaderNode->peerId());
-}
-
-void GraphicsContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId)
-{
- GLuint fboId = defaultFboId; // Default FBO
- if (renderTargetNodeId) {
- // New RenderTarget
- if (!m_renderTargets.contains(renderTargetNodeId)) {
- if (m_defaultFBO && fboId == m_defaultFBO) {
- // this is the default fbo that some platforms create (iOS), we just register it
- // Insert FBO into hash
- m_renderTargets.insert(renderTargetNodeId, fboId);
- } else {
- fboId = createRenderTarget(renderTargetNodeId, attachments);
- }
- } else {
- fboId = updateRenderTarget(renderTargetNodeId, attachments, true);
- }
- }
- m_activeFBO = fboId;
- m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw);
- // Set active drawBuffers
- activateDrawBuffers(attachments);
-}
-
-GLuint GraphicsContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments)
-{
- const GLuint fboId = m_glHelper->createFrameBufferObject();
- if (fboId) {
- // The FBO is created and its attachments are set once
- // Insert FBO into hash
- m_renderTargets.insert(renderTargetNodeId, fboId);
- // Bind FBO
- m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
- bindFrameBufferAttachmentHelper(fboId, attachments);
- } else {
- qCritical("Failed to create FBO");
- }
- return fboId;
-}
-
-GLuint GraphicsContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget)
-{
- const GLuint fboId = m_renderTargets.value(renderTargetNodeId);
-
- // We need to check if one of the attachment was resized
- bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet?
- if (!needsResize) {
- // render target exists, has attachment been resized?
- GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager();
- const QSize s = m_renderTargetsSize[fboId];
- const auto attachments_ = attachments.attachments();
- for (const Attachment &attachment : attachments_) {
- GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid);
- // ### TODO QTBUG-64757 this check is insufficient since the
- // texture may have changed to another one with the same size. That
- // case is not handled atm.
- needsResize |= (rTex != nullptr && rTex->size() != s);
- if (isActiveRenderTarget) {
- if (attachment.m_point == QRenderTargetOutput::Color0)
- m_renderTargetFormat = rTex->properties().format;
- }
- }
- }
-
- if (needsResize) {
- m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
- bindFrameBufferAttachmentHelper(fboId, attachments);
- }
-
- return fboId;
-}
-
-void GraphicsContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments)
-{
- // Set FBO attachments. These are normally textures, except that on Open GL
- // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is
- // desired since this cannot be achieved neither with a single texture (not
- // before GLES 3.2) nor with separate textures (no suitable format for
- // stencil before 3.1 with the appropriate extension).
-
- QSize fboSize;
- GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager();
- const auto attachments_ = attachments.attachments();
- for (const Attachment &attachment : attachments_) {
- GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid);
- if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
- QOpenGLTexture *glTex = rTex ? rTex->getOrCreateGLTexture() : nullptr;
- if (glTex != nullptr) {
- if (fboSize.isEmpty())
- fboSize = QSize(glTex->width(), glTex->height());
- else
- fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height()));
- m_glHelper->bindFrameBufferAttachment(glTex, attachment);
- }
- } else {
- RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr;
- if (renderBuffer) {
- if (fboSize.isEmpty())
- fboSize = QSize(renderBuffer->width(), renderBuffer->height());
- else
- fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height()));
- m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment);
- }
- }
- }
- m_renderTargetsSize.insert(fboId, fboSize);
+ m_shaderCache->removeRef(shaderNode->dna(), shaderNode->peerId());
}
void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments)
@@ -697,64 +322,6 @@ void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments)
}
}
-
-void GraphicsContext::setActiveMaterial(Material *rmat)
-{
- if (m_material == rmat)
- return;
-
- deactivateTexturesWithScope(TextureScopeMaterial);
- m_material = rmat;
-}
-
-int GraphicsContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit)
-{
- // Returns the texture unit to use for the texture
- // This always return a valid unit, unless there are more textures than
- // texture unit available for the current material
- onUnit = assignUnitForTexture(tex);
-
- // check we didn't overflow the available units
- if (onUnit == -1)
- return -1;
-
- // actually re-bind if required, the tex->dna on the unit not being the same
- // Note: tex->dna() could be 0 if the texture has not been created yet
- if (m_activeTextures[onUnit].texture != tex) {
- QOpenGLTexture *glTex = tex->getOrCreateGLTexture();
- if (glTex == nullptr)
- return -1;
- glTex->bind(onUnit);
- m_activeTextures[onUnit].texture = tex;
- }
-
-#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
- int err = m_gl->functions()->glGetError();
- if (err)
- qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16)
- << tex->textureId() << "on unit" << onUnit;
-#endif
-
- m_activeTextures[onUnit].score = 200;
- m_activeTextures[onUnit].pinned = true;
- m_activeTextures[onUnit].scope = scope;
-
- return onUnit;
-}
-
-void GraphicsContext::deactivateTexturesWithScope(TextureScope ts)
-{
- for (int u=0; u<m_activeTextures.size(); ++u) {
- if (!m_activeTextures[u].pinned)
- continue; // inactive, ignore
-
- if (m_activeTextures[u].scope == ts) {
- m_activeTextures[u].pinned = false;
- m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1;
- }
- } // of units iteration
-}
-
/*!
* \internal
* Finds the highest supported opengl version and internally use the most optimized
@@ -843,34 +410,6 @@ GraphicsHelperInterface *GraphicsContext::resolveHighestOpenGLFunctions()
return glHelper;
}
-void GraphicsContext::deactivateTexture(GLTexture* tex)
-{
- for (int u=0; u<m_activeTextures.size(); ++u) {
- if (m_activeTextures[u].texture == tex) {
- Q_ASSERT(m_activeTextures[u].pinned);
- m_activeTextures[u].pinned = false;
- return;
- }
- } // of units iteration
-
- qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex;
-}
-
-void GraphicsContext::setCurrentStateSet(RenderStateSet *ss)
-{
- if (ss == m_stateSet)
- return;
-
- if (ss)
- ss->apply(this);
- m_stateSet = ss;
-}
-
-RenderStateSet *GraphicsContext::currentStateSet() const
-{
- return m_stateSet;
-}
-
const GraphicsApiFilterData *GraphicsContext::contextInfo() const
{
return &m_contextInfo;
@@ -1064,26 +603,17 @@ GLuint GraphicsContext::boundFrameBufferObject()
void GraphicsContext::clearColor(const QColor &color)
{
- if (m_currClearColorValue != color) {
- m_currClearColorValue = color;
- m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- }
+ m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
}
void GraphicsContext::clearDepthValue(float depth)
{
- if (m_currClearDepthValue != depth) {
- m_currClearDepthValue = depth;
- m_gl->functions()->glClearDepthf(depth);
- }
+ m_gl->functions()->glClearDepthf(depth);
}
void GraphicsContext::clearStencilValue(int stencil)
{
- if (m_currClearStencilValue != stencil) {
- m_currClearStencilValue = stencil;
- m_gl->functions()->glClearStencil(stencil);
- }
+ m_gl->functions()->glClearStencil(stencil);
}
void GraphicsContext::enableClipPlane(int clipPlane)
@@ -1106,6 +636,11 @@ GLint GraphicsContext::maxClipPlaneCount()
return m_glHelper->maxClipPlaneCount();
}
+GLint GraphicsContext::maxTextureUnitsCount()
+{
+ return m_maxTextureUnits;
+}
+
void GraphicsContext::enablePrimitiveRestart(int restartIndex)
{
if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart))
@@ -1154,140 +689,6 @@ void GraphicsContext::setSeamlessCubemap(bool enable)
m_glHelper->setSeamlessCubemap(enable);
}
-/*!
- \internal
- Returns a texture unit for a texture, -1 if all texture units are assigned.
- Tries to use the texture unit with the texture that hasn't been used for the longest time
- if the texture happens not to be already pinned on a texture unit.
- */
-GLint GraphicsContext::assignUnitForTexture(GLTexture *tex)
-{
- int lowestScoredUnit = -1;
- int lowestScore = 0xfffffff;
-
- for (int u=0; u<m_activeTextures.size(); ++u) {
- if (m_activeTextures[u].texture == tex)
- return u;
-
- // No texture is currently active on the texture unit
- // we save the texture unit with the texture that has been on there
- // the longest time while not being used
- if (!m_activeTextures[u].pinned) {
- int score = m_activeTextures[u].score;
- if (score < lowestScore) {
- lowestScore = score;
- lowestScoredUnit = u;
- }
- }
- } // of units iteration
-
- if (lowestScoredUnit == -1)
- qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!";
-
- return lowestScoredUnit;
-}
-
-void GraphicsContext::decayTextureScores()
-{
- for (int u = 0; u < m_activeTextures.size(); u++)
- m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0);
-}
-
-QOpenGLShaderProgram* GraphicsContext::activeShader() const
-{
- Q_ASSERT(m_activeShader);
- return m_activeShader;
-}
-
-void GraphicsContext::setRenderer(Renderer *renderer)
-{
- m_renderer = renderer;
-}
-
-// It will be easier if the QGraphicContext applies the QUniformPack
-// than the other way around
-bool GraphicsContext::setParameters(ShaderParameterPack &parameterPack)
-{
- // Activate textures and update TextureUniform in the pack
- // with the correct textureUnit
-
- // Set the pinned texture of the previous material texture
- // to pinable so that we should easily find an available texture unit
- NodeManagers *manager = m_renderer->nodeManagers();
-
- deactivateTexturesWithScope(TextureScopeMaterial);
- // Update the uniforms with the correct texture unit id's
- PackUniformHash &uniformValues = parameterPack.uniforms();
-
- for (int i = 0; i < parameterPack.textures().size(); ++i) {
- const ShaderParameterPack::NamedTexture &namedTex = parameterPack.textures().at(i);
- // Given a Texture QNodeId, we retrieve the associated shared GLTexture
- if (uniformValues.contains(namedTex.glslNameId)) {
- GLTexture *t = manager->glTextureManager()->lookupResource(namedTex.texId);
- if (t != nullptr) {
- UniformValue &texUniform = uniformValues[namedTex.glslNameId];
- Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue);
- const int texUnit = activateTexture(TextureScopeMaterial, t);
- texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit;
- if (texUnit == -1)
- return false;
- }
- }
- }
-
- QOpenGLShaderProgram *shader = activeShader();
-
- // TO DO: We could cache the binding points somehow and only do the binding when necessary
- // for SSBO and UBO
-
- // Bind Shader Storage block to SSBO and update SSBO
- const QVector<BlockToSSBO> blockToSSBOs = parameterPack.shaderStorageBuffers();
- int ssboIndex = 0;
- for (const BlockToSSBO b : blockToSSBOs) {
- Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
- GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::ShaderStorageBuffer);
- bindShaderStorageBlock(shader->programId(), b.m_blockIndex, ssboIndex);
- // Needed to avoid conflict where the buffer would already
- // be bound as a VertexArray
- bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer);
- ssbo->bindBufferBase(this, ssboIndex++, GLBuffer::ShaderStorageBuffer);
- // TO DO: Make sure that there's enough binding points
- }
-
- // Bind UniformBlocks to UBO and update UBO from Buffer
- // TO DO: Convert ShaderData to Buffer so that we can use that generic process
- const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers();
- int uboIndex = 0;
- for (const BlockToUBO &b : blockToUBOs) {
- Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
- GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::UniformBuffer);
- bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
- // Needed to avoid conflict where the buffer would already
- // be bound as a VertexArray
- bindGLBuffer(ubo, GLBuffer::UniformBuffer);
- ubo->bindBufferBase(this, uboIndex++, GLBuffer::UniformBuffer);
- // TO DO: Make sure that there's enough binding points
- }
-
- // Update uniforms in the Default Uniform Block
- const PackUniformHash values = parameterPack.uniforms();
- const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms();
-
- for (const ShaderUniform &uniform : activeUniforms) {
- // We can use [] as we are sure the the uniform wouldn't
- // be un activeUniforms if there wasn't a matching value
- const UniformValue &v = values[uniform.m_nameId];
-
- // skip invalid textures
- if (v.valueType() == UniformValue::TextureValue && *v.constData<int>() == -1)
- continue;
-
- applyUniform(uniform, v);
- }
- // if not all data is valid, the next frame will be rendered immediately
- return true;
-}
-
void GraphicsContext::readBuffer(GLenum mode)
{
m_glHelper->readBuffer(mode);
@@ -1298,36 +699,6 @@ void GraphicsContext::drawBuffer(GLenum mode)
m_glHelper->drawBuffer(mode);
}
-void GraphicsContext::enableAttribute(const VAOVertexAttribute &attr)
-{
- // Bind buffer within the current VAO
- GLBuffer *buf = m_renderer->nodeManagers()->glBufferManager()->data(attr.bufferHandle);
- Q_ASSERT(buf);
- bindGLBuffer(buf, attr.attributeType);
-
- // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199.
- // Use the introspection data and set the attribute explicitly
- m_glHelper->enableVertexAttributeArray(attr.location);
- m_glHelper->vertexAttributePointer(attr.shaderDataType,
- attr.location,
- attr.vertexSize,
- attr.dataType,
- GL_TRUE, // TODO: Support normalization property on QAttribute
- attr.byteStride,
- reinterpret_cast<const void *>(qintptr(attr.byteOffset)));
-
-
- // Done by the helper if it supports it
- if (attr.divisor != 0)
- m_glHelper->vertexAttribDivisor(attr.location, attr.divisor);
-}
-
-void GraphicsContext::disableAttribute(const GraphicsContext::VAOVertexAttribute &attr)
-{
- QOpenGLShaderProgram *prog = activeShader();
- prog->disableAttributeArray(attr.location);
-}
-
void GraphicsContext::applyUniform(const ShaderUniform &description, const UniformValue &v)
{
const UniformType type = m_glHelper->uniformTypeFromGLType(description.m_type);
@@ -1439,282 +810,11 @@ void GraphicsContext::applyUniform(const ShaderUniform &description, const Unifo
}
}
-// Note: needs to be called while VAO is bound
-void GraphicsContext::specifyAttribute(const Attribute *attribute,
- Buffer *buffer,
- const ShaderAttribute *attributeDescription)
-{
- const int location = attributeDescription->m_location;
- if (location < 0) {
- qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name();
- return;
- }
-
- const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType());
- const HGLBuffer glBufferHandle = m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId());
- Q_ASSERT(!glBufferHandle.isNull());
- const GLBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType());
-
- int typeSize = 0;
- int attrCount = 0;
-
- if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) {
- attrCount = 1;
- } else if (attribute->vertexSize() == 9) {
- typeSize = byteSizeFromType(attributeDataType);
- attrCount = 3;
- } else if (attribute->vertexSize() == 16) {
- typeSize = byteSizeFromType(attributeDataType);
- attrCount = 4;
- } else {
- Q_UNREACHABLE();
- }
-
- for (int i = 0; i < attrCount; i++) {
- VAOVertexAttribute attr;
- attr.bufferHandle = glBufferHandle;
- attr.attributeType = attributeType;
- attr.location = location + i;
- attr.dataType = attributeDataType;
- attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize);
- attr.vertexSize = attribute->vertexSize() / attrCount;
- attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize);
- attr.divisor = attribute->divisor();
- attr.shaderDataType = attributeDescription->m_type;
-
- enableAttribute(attr);
-
- // Save this in the current emulated VAO
- if (m_currentVAO)
- m_currentVAO->saveVertexAttribute(attr);
- }
-}
-
-void GraphicsContext::specifyIndices(Buffer *buffer)
-{
- GLBuffer *buf = glBufferForRenderBuffer(buffer, GLBuffer::IndexBuffer);
- if (!bindGLBuffer(buf, GLBuffer::IndexBuffer))
- qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed";
-
- // bound within the current VAO
- // Save this in the current emulated VAO
- if (m_currentVAO)
- m_currentVAO->saveIndexAttribute(m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()));
-}
-
-void GraphicsContext::updateBuffer(Buffer *buffer)
-{
- const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
- if (it != m_renderBufferHash.end())
- uploadDataToGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value()));
-}
-
-QByteArray GraphicsContext::downloadBufferContent(Buffer *buffer)
-{
- const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
- if (it != m_renderBufferHash.end())
- return downloadDataFromGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value()));
- return QByteArray();
-}
-
-
-
-void GraphicsContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
-{
- auto it = m_renderBufferHash.find(bufferId);
- if (it != m_renderBufferHash.end()) {
- HGLBuffer glBuffHandle = it.value();
- GLBuffer *glBuff = m_renderer->nodeManagers()->glBufferManager()->data(glBuffHandle);
-
- Q_ASSERT(glBuff);
- // Destroy the GPU resource
- glBuff->destroy(this);
- // Destroy the GLBuffer instance
- m_renderer->nodeManagers()->glBufferManager()->releaseResource(bufferId);
- // Remove Id - HGLBuffer entry
- m_renderBufferHash.erase(it);
- }
-}
-
-bool GraphicsContext::hasGLBufferForBuffer(Buffer *buffer)
-{
- const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
- return (it != m_renderBufferHash.end());
-}
-
-void GraphicsContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
- Qt3DCore::QNodeId outputRenderTargetId,
- QRect inputRect, QRect outputRect,
- uint defaultFboId,
- QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
- QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
- QBlitFramebuffer::InterpolationMethod interpolationMethod)
-{
- GLuint inputFboId = defaultFboId;
- bool inputBufferIsDefault = true;
- if (!inputRenderTargetId.isNull()) {
- RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId);
- if (renderTarget) {
- AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager());
- if (m_renderTargets.contains(inputRenderTargetId))
- inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false);
- else
- inputFboId = createRenderTarget(inputRenderTargetId, attachments);
- }
- inputBufferIsDefault = false;
- }
-
- GLuint outputFboId = defaultFboId;
- bool outputBufferIsDefault = true;
- if (!outputRenderTargetId.isNull()) {
- RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId);
- if (renderTarget) {
- AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager());
- if (m_renderTargets.contains(outputRenderTargetId))
- outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false);
- else
- outputFboId = createRenderTarget(outputRenderTargetId, attachments);
- }
- outputBufferIsDefault = false;
- }
-
- // Up until this point the input and output rects are normal Qt rectangles.
- // Convert them to GL rectangles (Y at bottom).
- const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height();
- const GLint srcX0 = inputRect.left();
- const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height());
- const GLint srcX1 = srcX0 + inputRect.width();
- const GLint srcY1 = srcY0 + inputRect.height();
-
- const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height();
- const GLint dstX0 = outputRect.left();
- const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height());
- const GLint dstX1 = dstX0 + outputRect.width();
- const GLint dstY1 = dstY0 + outputRect.height();
-
- //Get the last bounded framebuffers
- const GLuint lastDrawFboId = boundFrameBufferObject();
-
- // Activate input framebuffer for reading
- bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead);
-
- // Activate output framebuffer for writing
- bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw);
-
- //Bind texture
- if (!inputBufferIsDefault)
- readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint);
-
- if (!outputBufferIsDefault)
- drawBuffer(GL_COLOR_ATTACHMENT0 + outputAttachmentPoint);
-
- // Blit framebuffer
- const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR;
- m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1,
- dstX0, dstY0, dstX1, dstY1,
- GL_COLOR_BUFFER_BIT, mode);
-
- // Reset draw buffer
- bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw);
-}
-
void GraphicsContext::memoryBarrier(QMemoryBarrier::Operations barriers)
{
m_glHelper->memoryBarrier(barriers);
}
-GLBuffer *GraphicsContext::glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type)
-{
- if (!m_renderBufferHash.contains(buf->peerId()))
- m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf, type));
- return m_renderer->nodeManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId()));
-}
-
-HGLBuffer GraphicsContext::createGLBufferFor(Buffer *buffer, GLBuffer::Type type)
-{
- GLBuffer *b = m_renderer->nodeManagers()->glBufferManager()->getOrCreateResource(buffer->peerId());
- // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage()));
- Q_ASSERT(b);
- if (!b->create(this))
- qCWarning(Render::Io) << Q_FUNC_INFO << "buffer creation failed";
-
- if (!bindGLBuffer(b, type))
- qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed";
-
- return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId());
-}
-
-bool GraphicsContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type)
-{
- if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer)
- return true;
-
- if (buffer->bind(this, type)) {
- if (type == GLBuffer::ArrayBuffer)
- m_boundArrayBuffer = buffer;
- return true;
- }
- return false;
-}
-
-void GraphicsContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer)
-{
- if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here
- qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed";
- // If the buffer is dirty (hence being called here)
- // there are two possible cases
- // * setData was called changing the whole data or functor (or the usage pattern)
- // * partial buffer updates where received
-
- // TO DO: Handle usage pattern
- QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates());
- for (auto it = updates.begin(); it != updates.end(); ++it) {
- auto update = it;
- // We have a partial update
- if (update->offset >= 0) {
- //accumulate sequential updates as single one
- int bufferSize = update->data.size();
- auto it2 = it + 1;
- while ((it2 != updates.end())
- && (it2->offset - update->offset == bufferSize)) {
- bufferSize += it2->data.size();
- ++it2;
- }
- update->data.resize(bufferSize);
- while (it + 1 != it2) {
- ++it;
- update->data.replace(it->offset - update->offset, it->data.size(), it->data);
- it->data.clear();
- }
- // TO DO: based on the number of updates .., it might make sense to
- // sometime use glMapBuffer rather than glBufferSubData
- b->update(this, update->data.constData(), update->data.size(), update->offset);
- } else {
- // We have an update that was done by calling QBuffer::setData
- // which is used to resize or entirely clear the buffer
- // Note: we use the buffer data directly in that case
- const int bufferSize = buffer->data().size();
- b->allocate(this, bufferSize, false); // orphan the buffer
- b->allocate(this, buffer->data().constData(), bufferSize, false);
- }
- }
-
- if (releaseBuffer) {
- b->release(this);
- m_boundArrayBuffer = nullptr;
- }
- qCDebug(Render::Io) << "uploaded buffer size=" << buffer->data().size();
-}
-
-QByteArray GraphicsContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b)
-{
- if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
- qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed";
-
- QByteArray data = b->download(this, buffer->data().size());
- return data;
-}
-
GLint GraphicsContext::elementType(GLint type)
{
switch (type) {
@@ -1832,173 +932,6 @@ GLint GraphicsContext::glDataTypeFromAttributeDataType(QAttribute::VertexBaseTyp
return GL_FLOAT;
}
-static void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format)
-{
- switch (format) {
- case QAbstractTexture::RGBA32F:
- {
- uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
- for (uint i = 0; i < height; ++i) {
- uchar *dstScanline = img.scanLine(i);
- float *pSrc = (float*)srcScanline;
- for (uint j = 0; j < width; j++) {
- *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+2], 1.0f));
- *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+1], 1.0f));
- *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+0], 1.0f));
- *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+3], 1.0f));
- }
- srcScanline -= stride;
- }
- } break;
- default:
- {
- uchar* srcScanline = (uchar *)srcData + stride * (height - 1);
- for (uint i = 0; i < height; ++i) {
- memcpy(img.scanLine(i), srcScanline, stride);
- srcScanline -= stride;
- }
- } break;
- }
-}
-
-QImage GraphicsContext::readFramebuffer(const QRect &rect)
-{
- QImage img;
- const unsigned int area = rect.width() * rect.height();
- unsigned int bytes;
- GLenum format, type;
- QImage::Format imageFormat;
- uint stride;
-
- /* format value should match GL internalFormat */
- GLenum internalFormat = m_renderTargetFormat;
-
- switch (m_renderTargetFormat) {
- case QAbstractTexture::RGBAFormat:
- case QAbstractTexture::RGBA8_SNorm:
- case QAbstractTexture::RGBA8_UNorm:
- case QAbstractTexture::RGBA8U:
- case QAbstractTexture::SRGB8_Alpha8:
-#ifdef QT_OPENGL_ES_2
- format = GL_RGBA;
- imageFormat = QImage::Format_RGBA8888_Premultiplied;
-#else
- format = GL_BGRA;
- imageFormat = QImage::Format_ARGB32_Premultiplied;
- internalFormat = GL_RGBA8;
-#endif
- type = GL_UNSIGNED_BYTE;
- bytes = area * 4;
- stride = rect.width() * 4;
- break;
- case QAbstractTexture::SRGB8:
- case QAbstractTexture::RGBFormat:
- case QAbstractTexture::RGB8U:
- case QAbstractTexture::RGB8_UNorm:
-#ifdef QT_OPENGL_ES_2
- format = GL_RGBA;
- imageFormat = QImage::Format_RGBX8888;
-#else
- format = GL_BGRA;
- imageFormat = QImage::Format_RGB32;
- internalFormat = GL_RGB8;
-#endif
- type = GL_UNSIGNED_BYTE;
- bytes = area * 4;
- stride = rect.width() * 4;
- break;
-#ifndef QT_OPENGL_ES_2
- case QAbstractTexture::RG11B10F:
- bytes = area * 4;
- format = GL_RGB;
- type = GL_UNSIGNED_INT_10F_11F_11F_REV;
- imageFormat = QImage::Format_RGB30;
- stride = rect.width() * 4;
- break;
- case QAbstractTexture::RGB10A2:
- bytes = area * 4;
- format = GL_RGBA;
- type = GL_UNSIGNED_INT_2_10_10_10_REV;
- imageFormat = QImage::Format_A2BGR30_Premultiplied;
- stride = rect.width() * 4;
- break;
- case QAbstractTexture::R5G6B5:
- bytes = area * 2;
- format = GL_RGB;
- type = GL_UNSIGNED_SHORT;
- internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV;
- imageFormat = QImage::Format_RGB16;
- stride = rect.width() * 2;
- break;
- case QAbstractTexture::RGBA16F:
- case QAbstractTexture::RGBA16U:
- case QAbstractTexture::RGBA32F:
- case QAbstractTexture::RGBA32U:
- bytes = area * 16;
- format = GL_RGBA;
- type = GL_FLOAT;
- imageFormat = QImage::Format_ARGB32_Premultiplied;
- stride = rect.width() * 16;
- break;
-#endif
- default:
- // unsupported format
- Q_UNREACHABLE();
- return img;
- }
-
- GLint samples = 0;
- m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples);
- if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) {
- qWarning () << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; "
- "Required feature BlitFramebuffer is missing.";
- return img;
- }
-
- img = QImage(rect.width(), rect.height(), imageFormat);
-
- QScopedArrayPointer<uchar> data(new uchar [bytes]);
-
- if (samples > 0) {
- // resolve multisample-framebuffer to renderbuffer and read pixels from it
- GLuint fbo, rb;
- QOpenGLFunctions *gl = m_gl->functions();
- gl->glGenFramebuffers(1, &fbo);
- gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- gl->glGenRenderbuffers(1, &rb);
- gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
- gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height());
- gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb);
-
- const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- gl->glDeleteRenderbuffers(1, &rb);
- gl->glDeleteFramebuffers(1, &fbo);
- qWarning () << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status;
- return img;
- }
-
- m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(),
- 0, 0, rect.width(), rect.height(),
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
- gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
- gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data());
-
- copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat);
-
- gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
- gl->glDeleteRenderbuffers(1, &rb);
- gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO);
- gl->glDeleteFramebuffers(1, &fbo);
- } else {
- // read pixels directly from framebuffer
- m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data());
- copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat);
- }
-
- return img;
-}
-
QT3D_UNIFORM_TYPE_IMPL(UniformType::Float, float, glUniform1fv)
QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec2, float, glUniform2fv)
QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec3, float, glUniform3fv)