blob: 815c082d523eb66174f2d11a7e7521b3451e4587 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2008-2012 NVIDIA Corporation.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtQuick3DRender/private/qssgrenderbackendgl3_p.h>
#include <QtQuick3DRender/private/qssgrenderbackendinputassemblergl_p.h>
#include <QtQuick3DRender/private/qssgrenderbackendrenderstatesgl_p.h>
#include <QtQuick3DRender/private/qssgrenderbackendshaderprogramgl_p.h>
#include <QtQuick3DRender/private/qssgopenglextensions_p.h>
QT_BEGIN_NAMESPACE
#ifdef RENDER_BACKEND_LOG_GL_ERRORS
#define RENDER_LOG_ERROR_PARAMS(x) checkGLError(#x, __FILE__, __LINE__)
#else
#define RENDER_LOG_ERROR_PARAMS(x) checkGLError()
#endif
#define GL_CALL_EXTRA_FUNCTION(x) \
m_glExtraFunctions->x; \
RENDER_LOG_ERROR_PARAMS(x);
#if defined(QT_OPENGL_ES)
#define GL_CALL_TIMER_EXT(x) \
m_QSSGExtensions->x; \
RENDER_LOG_ERROR_PARAMS(x);
#define GL_CALL_TESSELATION_EXT(x) \
m_QSSGExtensions->x; \
RENDER_LOG_ERROR_PARAMS(x);
#else
#define GL_CALL_TIMER_EXT(x) \
m_timerExtension->x; \
RENDER_LOG_ERROR_PARAMS(x);
#define GL_CALL_TESSELATION_EXT(x) \
m_tessellationShader->x; \
RENDER_LOG_ERROR_PARAMS(x);
#define GL_CALL_MULTISAMPLE_EXT(x) \
m_multiSample->x; \
RENDER_LOG_ERROR_PARAMS(x);
#endif
#ifndef GL_PATCH_VERTICES
#define GL_PATCH_VERTICES 0x8E72
#endif
namespace QSSGGlExtStrings {
QByteArray extsAstcHDR()
{
return QByteArrayLiteral("GL_KHR_texture_compression_astc_hdr");
}
QByteArray extsAstcLDR()
{
return QByteArrayLiteral("GL_KHR_texture_compression_astc_ldr");
}
}
/// constructor
QSSGRenderBackendGL3Impl::QSSGRenderBackendGL3Impl(const QSurfaceFormat &format) : QSSGRenderBackendGLBase(format)
{
// clear support bits
m_backendSupport.caps.u32Values = 0;
// get extension count
GLint numExtensions = 0;
GL_CALL_EXTRA_FUNCTION(glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions));
QByteArray extensionBuffer;
for (qint32 i = 0; i < numExtensions; i++) {
const GLubyte *glExt = GL_CALL_EXTRA_FUNCTION(glGetStringi(GL_EXTENSIONS, GLuint(i)));
const QByteArray extensionString(reinterpret_cast<const char *>(glExt));
m_extensions.push_back(extensionString);
if (extensionBuffer.size())
extensionBuffer.append(" ");
extensionBuffer.append(extensionString);
// search for extension
if (!m_backendSupport.caps.bits.bDXTImagesSupported
&& (QSSGGlExtStrings::exts3tc().compare(extensionString) == 0
|| QSSGGlExtStrings::extsdxt().compare(extensionString) == 0)) {
m_backendSupport.caps.bits.bDXTImagesSupported = true;
} else if (!m_backendSupport.caps.bits.bAnistropySupported && QSSGGlExtStrings::extsAniso().compare(extensionString) == 0) {
m_backendSupport.caps.bits.bAnistropySupported = true;
} else if (!m_backendSupport.caps.bits.bFPRenderTargetsSupported
&& QSSGGlExtStrings::extsFPRenderTarget().compare(extensionString) == 0) {
m_backendSupport.caps.bits.bFPRenderTargetsSupported = true;
} else if (!m_backendSupport.caps.bits.bTimerQuerySupported
&& QSSGGlExtStrings::extsTimerQuery().compare(extensionString) == 0) {
m_backendSupport.caps.bits.bTimerQuerySupported = true;
} else if (!m_backendSupport.caps.bits.bGPUShader5ExtensionSupported
&& QSSGGlExtStrings::extsGpuShader5().compare(extensionString) == 0) {
m_backendSupport.caps.bits.bGPUShader5ExtensionSupported = true;
}
}
qCInfo(RENDER_TRACE_INFO, "OpenGL extensions: %s", extensionBuffer.constData());
// texture swizzle is always true
m_backendSupport.caps.bits.bTextureSwizzleSupported = true;
// depthstencil renderbuffer support is always true
m_backendSupport.caps.bits.bDepthStencilSupported = true;
// constant buffers support is always true
m_backendSupport.caps.bits.bConstantBufferSupported = true;
m_backendSupport.caps.bits.bStandardDerivativesSupported = true;
m_backendSupport.caps.bits.bVertexArrayObjectSupported = true;
m_backendSupport.caps.bits.bTextureLodSupported = true;
if (!isESCompatible()) {
// render to float textures is always supported on none ES systems which support >=GL3
m_backendSupport.caps.bits.bFPRenderTargetsSupported = true;
// multisampled texture is always supported on none ES systems which support >=GL3
m_backendSupport.caps.bits.bMsTextureSupported = true;
// timer queries are always supported on none ES systems which support >=GL3
m_backendSupport.caps.bits.bTimerQuerySupported = true;
}
// query hardware
GL_CALL_EXTRA_FUNCTION(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_maxAttribCount));
// internal state tracker
m_currentMiscState = new QSSGRenderBackendMiscStateGL();
// finally setup caps based on device
setAndInspectHardwareCaps();
// Initialize extensions
#if defined(QT_OPENGL_ES_2)
m_QSSGExtensions = new QSSGOpenGLES2Extensions;
m_QSSGExtensions->initializeOpenGLFunctions();
#else
m_timerExtension = new QOpenGLExtension_ARB_timer_query;
m_timerExtension->initializeOpenGLFunctions();
m_tessellationShader = new QOpenGLExtension_ARB_tessellation_shader;
m_tessellationShader->initializeOpenGLFunctions();
m_multiSample = new QOpenGLExtension_ARB_texture_multisample;
m_multiSample->initializeOpenGLFunctions();
m_QSSGExtensions = new QSSGOpenGLExtensions;
m_QSSGExtensions->initializeOpenGLFunctions();
#endif
}
/// destructor
QSSGRenderBackendGL3Impl::~QSSGRenderBackendGL3Impl()
{
delete m_currentMiscState;
#if !defined(QT_OPENGL_ES_2)
delete m_timerExtension;
delete m_tessellationShader;
delete m_multiSample;
#endif
delete m_QSSGExtensions;
}
void QSSGRenderBackendGL3Impl::setMultisampledTextureData2D(QSSGRenderBackendTextureObject to,
QSSGRenderTextureTargetType target,
qint32 samples,
QSSGRenderTextureFormat internalFormat,
qint32 width,
qint32 height,
bool fixedsamplelocations)
{
// Not supported by ES 3 yet
#if defined(QT_OPENGL_ES)
Q_UNUSED(to)
Q_UNUSED(target)
Q_UNUSED(samples)
Q_UNUSED(internalFormat)
Q_UNUSED(width)
Q_UNUSED(height)
Q_UNUSED(fixedsamplelocations)
#else
GLuint texID = HandleToID_cast(GLuint, quintptr, to);
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
setActiveTexture(GL_TEXTURE0);
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID));
QSSGRenderTextureSwizzleMode swizzleMode = QSSGRenderTextureSwizzleMode::NoSwizzle;
internalFormat = GLConversion::replaceDeprecatedTextureFormat(getRenderContextType(), internalFormat, swizzleMode);
GLenum glformat = 0, glInternalFormat = 0, gltype = GL_UNSIGNED_BYTE;
if (internalFormat.isUncompressedTextureFormat())
GLConversion::fromUncompressedTextureFormatToGL(getRenderContextType(), internalFormat, glformat, gltype, glInternalFormat);
else if (internalFormat.isDepthTextureFormat())
GLConversion::fromDepthTextureFormatToGL(getRenderContextType(), internalFormat, glformat, gltype, glInternalFormat);
GL_CALL_MULTISAMPLE_EXT(
glTexImage2DMultisample(glTarget, GLsizei(samples), GLint(glInternalFormat), GLsizei(width), GLsizei(height), fixedsamplelocations));
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0));
#endif
}
void QSSGRenderBackendGL3Impl::setTextureData3D(QSSGRenderBackendTextureObject to,
QSSGRenderTextureTargetType target,
qint32 level,
QSSGRenderTextureFormat internalFormat,
qint32 width,
qint32 height,
qint32 depth,
qint32 border,
QSSGRenderTextureFormat format,
QSSGByteView hostData)
{
GLuint texID = HandleToID_cast(GLuint, quintptr, to);
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
setActiveTexture(GL_TEXTURE0);
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID));
bool conversionRequired = format != internalFormat;
QSSGRenderTextureSwizzleMode swizzleMode = QSSGRenderTextureSwizzleMode::NoSwizzle;
internalFormat = GLConversion::replaceDeprecatedTextureFormat(getRenderContextType(), internalFormat, swizzleMode);
GLenum glformat = 0, glInternalFormat = 0, gltype = GL_UNSIGNED_BYTE;
if (internalFormat.isUncompressedTextureFormat())
GLConversion::fromUncompressedTextureFormatToGL(getRenderContextType(), internalFormat, glformat, gltype, glInternalFormat);
if (conversionRequired) {
GLenum dummy;
GLConversion::fromUncompressedTextureFormatToGL(getRenderContextType(), format, glformat, gltype, dummy);
} else if (internalFormat.isCompressedTextureFormat()) {
GLConversion::fromUncompressedTextureFormatToGL(getRenderContextType(), format, glformat, gltype, glInternalFormat);
glInternalFormat = GLConversion::fromCompressedTextureFormatToGL(internalFormat);
} else if (format.isDepthTextureFormat()) {
GLConversion::fromDepthTextureFormatToGL(getRenderContextType(), format, glformat, gltype, glInternalFormat);
}
GL_CALL_EXTRA_FUNCTION(
glTexImage3D(glTarget, level, GLint(glInternalFormat), GLsizei(width), GLsizei(height), GLsizei(depth), border, glformat, gltype, hostData));
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0));
}
void QSSGRenderBackendGL3Impl::updateSampler(QSSGRenderBackendSamplerObject /* so */,
QSSGRenderTextureTargetType target,
QSSGRenderTextureMinifyingOp minFilter,
QSSGRenderTextureMagnifyingOp magFilter,
QSSGRenderTextureCoordOp wrapS,
QSSGRenderTextureCoordOp wrapT,
QSSGRenderTextureCoordOp wrapR,
float minLod,
float maxLod,
float lodBias,
QSSGRenderTextureCompareMode compareMode,
QSSGRenderTextureCompareOp compareFunc,
float anisotropy,
float *borderColor)
{
// Satisfy the compiler
// These are not available in GLES 3 and we don't use them right now
Q_ASSERT(qFuzzyIsNull(lodBias));
Q_ASSERT(!borderColor);
Q_UNUSED(lodBias)
Q_UNUSED(borderColor)
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_MIN_FILTER, GLint(m_conversion.fromTextureMinifyingOpToGL(minFilter))));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_MAG_FILTER, GLint(m_conversion.fromTextureMagnifyingOpToGL(magFilter))));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_WRAP_S, GLint(m_conversion.fromTextureCoordOpToGL(wrapS))));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_WRAP_T, GLint(m_conversion.fromTextureCoordOpToGL(wrapT))));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_WRAP_R, GLint(m_conversion.fromTextureCoordOpToGL(wrapR))));
GL_CALL_EXTRA_FUNCTION(glTexParameterf(glTarget, GL_TEXTURE_MIN_LOD, minLod));
GL_CALL_EXTRA_FUNCTION(glTexParameterf(glTarget, GL_TEXTURE_MAX_LOD, maxLod));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_COMPARE_MODE, GLint(m_conversion.fromTextureCompareModeToGL(compareMode))));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_COMPARE_FUNC, GLint(m_conversion.fromTextureCompareFuncToGL(compareFunc))));
if (m_backendSupport.caps.bits.bAnistropySupported) {
GL_CALL_EXTRA_FUNCTION(glTexParameterf(glTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy));
}
}
void QSSGRenderBackendGL3Impl::updateTextureObject(QSSGRenderBackendTextureObject to,
QSSGRenderTextureTargetType target,
qint32 baseLevel,
qint32 maxLevel)
{
Q_UNUSED(to)
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_BASE_LEVEL, baseLevel));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_MAX_LEVEL, maxLevel));
}
void QSSGRenderBackendGL3Impl::updateTextureSwizzle(QSSGRenderBackendTextureObject to,
QSSGRenderTextureTargetType target,
QSSGRenderTextureSwizzleMode swizzleMode)
{
Q_UNUSED(to)
if (m_backendSupport.caps.bits.bTextureSwizzleSupported) {
GLint glSwizzle[4];
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
GLConversion::NVRenderConvertSwizzleModeToGL(swizzleMode, glSwizzle);
#if defined(QT_OPENGL_ES)
// since ES3 spec has no GL_TEXTURE_SWIZZLE_RGBA set it separately
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_SWIZZLE_R, glSwizzle[0]));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_SWIZZLE_G, glSwizzle[1]));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_SWIZZLE_B, glSwizzle[2]));
GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_SWIZZLE_A, glSwizzle[3]));
#else
GL_CALL_EXTRA_FUNCTION(glTexParameteriv(glTarget, GL_TEXTURE_SWIZZLE_RGBA, glSwizzle));
#endif
}
}
qint32 QSSGRenderBackendGL3Impl::getDepthBits() const
{
qint32 depthBits;
GL_CALL_EXTRA_FUNCTION(
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depthBits));
return depthBits;
}
qint32 QSSGRenderBackendGL3Impl::getStencilBits() const
{
qint32 stencilBits;
GL_CALL_EXTRA_FUNCTION(
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits));
return stencilBits;
}
void QSSGRenderBackendGL3Impl::generateMipMaps(QSSGRenderBackendTextureObject to,
QSSGRenderTextureTargetType target,
QSSGRenderHint /*genType*/)
{
GLuint texID = HandleToID_cast(GLuint, quintptr, to);
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
setActiveTexture(GL_TEXTURE0);
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID));
GL_CALL_EXTRA_FUNCTION(glGenerateMipmap(glTarget));
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0));
}
QByteArray QSSGRenderBackendGL3Impl::getShadingLanguageVersion()
{
Q_ASSERT(m_format.majorVersion() >= 3);
QByteArray ver("#version 300");
if (m_format.majorVersion() == 3)
ver[10] = '0' + char(m_format.minorVersion());
else if (m_format.majorVersion() > 3)
ver[10] = '3';
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
ver.append(" es");
return ver.append("\n");
}
QSSGRenderContextType QSSGRenderBackendGL3Impl::getRenderContextType() const
{
Q_ASSERT(m_format.majorVersion() >= 3);
if (m_format.renderableType() == QSurfaceFormat::OpenGLES) {
if (m_format.minorVersion() >= 1)
return QSSGRenderContextType::GLES3PLUS;
return QSSGRenderContextType::GLES3;
}
return QSSGRenderContextType::GL3;
}
bool QSSGRenderBackendGL3Impl::setInputAssembler(QSSGRenderBackendInputAssemblerObject iao, QSSGRenderBackendShaderProgramObject po)
{
if (iao == nullptr) {
// unbind and return;
GL_CALL_EXTRA_FUNCTION(glBindVertexArray(0));
return true;
}
QSSGRenderBackendInputAssemblerGL *inputAssembler = reinterpret_cast<QSSGRenderBackendInputAssemblerGL *>(iao);
QSSGRenderBackendAttributeLayoutGL *attribLayout = inputAssembler->m_attribLayout;
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
QSSGDataRef<QSSGRenderBackendShaderInputEntryGL> shaderAttribBuffer;
if (pProgram->m_shaderInput)
shaderAttribBuffer = pProgram->m_shaderInput->m_shaderInputEntries;
// Need to be careful with the attributes. shaderAttribBuffer contains
// whatever glGetActiveAttrib() returns. There can however be differences
// between OpenGL implementations: some will optimize out unused
// attributes, while others could report all attributes as active,
// regardless of them being used in practice or not.
//
// In addition, not binding any data to an attribute is not an error with
// OpenGL, and in fact is unavoidable when a model, for example, has no UV
// coordinates (and associated data, such as tangetst and binormals), but
// is then used with a shader with lighting, shadows, and such. This needs
// to be handled gracefully. It can lead to incorrect rendering but the
// object still needs to be there, without bailing out or flooding the
// output with warnings.
// if (attribLayout->m_layoutAttribEntries.size() < shaderAttribBuffer.size())
if (inputAssembler->m_vertexbufferHandles.size() <= attribLayout->m_maxInputSlot)
return false;
if (inputAssembler->m_vaoID == 0) {
// generate vao
GL_CALL_EXTRA_FUNCTION(glGenVertexArrays(1, &inputAssembler->m_vaoID));
Q_ASSERT(inputAssembler->m_vaoID);
}
// set patch parameter count if changed
if (m_backendSupport.caps.bits.bTessellationSupported && m_currentMiscState->m_patchVertexCount != inputAssembler->m_patchVertexCount) {
m_currentMiscState->m_patchVertexCount = inputAssembler->m_patchVertexCount;
#if defined(QT_OPENGL_ES)
GL_CALL_TESSELATION_EXT(glPatchParameteriEXT(GL_PATCH_VERTICES, inputAssembler->m_patchVertexCount));
#else
GL_CALL_TESSELATION_EXT(glPatchParameteri(GL_PATCH_VERTICES, GLint(inputAssembler->m_patchVertexCount)));
#endif
}
if (inputAssembler->m_cachedShaderHandle != programID) {
GL_CALL_EXTRA_FUNCTION(glBindVertexArray(inputAssembler->m_vaoID));
inputAssembler->m_cachedShaderHandle = programID;
for (const auto &attrib : qAsConst(shaderAttribBuffer)) {
QSSGRenderBackendLayoutEntryGL *entry = attribLayout->getEntryByName(attrib.m_attribName);
if (entry) {
QSSGRenderBackendLayoutEntryGL &entryData(*entry);
if (Q_UNLIKELY(entryData.m_type != attrib.m_type || entryData.m_numComponents != attrib.m_numComponents)) {
qCCritical(RENDER_INVALID_OPERATION, "Attrib %s doesn't match vertex layout", attrib.m_attribName.constData());
Q_ASSERT(false);
return false;
}
entryData.m_attribIndex = attrib.m_attribLocation;
} else {
qCWarning(RENDER_WARNING, "Failed to bind attribute %s", attrib.m_attribName.constData());
}
}
// disable max possible used first
// this is currently sufficient since we always re-arrange input attributes from 0
for (int i = 0; i < attribLayout->m_layoutAttribEntries.size(); i++)
GL_CALL_EXTRA_FUNCTION(glDisableVertexAttribArray(GLuint(i)));
// setup all attribs
GLuint boundArrayBufferId = 0; // 0 means unbound
for (int idx = 0; idx != shaderAttribBuffer.size(); ++idx) {
QSSGRenderBackendLayoutEntryGL *entry = attribLayout->getEntryByName(shaderAttribBuffer[idx].m_attribName);
if (entry) {
const QSSGRenderBackendLayoutEntryGL &entryData(*entry);
GLuint id = HandleToID_cast(GLuint, quintptr, inputAssembler->m_vertexbufferHandles.mData[entryData.m_inputSlot]);
if (boundArrayBufferId != id) {
GL_CALL_EXTRA_FUNCTION(glBindBuffer(GL_ARRAY_BUFFER, id));
boundArrayBufferId = id;
}
GL_CALL_EXTRA_FUNCTION(glEnableVertexAttribArray(entryData.m_attribIndex));
GLuint offset = inputAssembler->m_offsets.at(int(entryData.m_inputSlot));
GLuint stride = inputAssembler->m_strides.at(int(entryData.m_inputSlot));
GL_CALL_EXTRA_FUNCTION(glVertexAttribPointer(entryData.m_attribIndex,
GLint(entryData.m_numComponents),
GL_FLOAT,
GL_FALSE,
GLsizei(stride),
reinterpret_cast<const void *>(quintptr(entryData.m_offset + offset))));
} else {
GL_CALL_EXTRA_FUNCTION(glDisableVertexAttribArray(GLuint(idx)));
}
}
// setup index buffer.
if (inputAssembler->m_indexbufferHandle) {
GL_CALL_EXTRA_FUNCTION(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
HandleToID_cast(GLuint, quintptr, inputAssembler->m_indexbufferHandle)));
} else {
GL_CALL_EXTRA_FUNCTION(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
} else {
GL_CALL_EXTRA_FUNCTION(glBindVertexArray(inputAssembler->m_vaoID));
}
#ifdef _DEBUG
if (inputAssembler->m_vaoID) {
for (const auto &attrib : qAsConst(shaderAttribBuffer)) {
QSSGRenderBackendLayoutEntryGL *entry = attribLayout->getEntryByName(attrib.m_attribName);
if (entry) {
QSSGRenderBackendLayoutEntryGL &entryData(*entry);
if (entryData.m_type != attrib.m_type || entryData.m_numComponents != attrib.m_numComponents
|| entryData.m_attribIndex != attrib.m_attribLocation) {
qCCritical(RENDER_INVALID_OPERATION, "Attrib %s doesn't match vertex layout", qPrintable(attrib.m_attribName));
Q_ASSERT(false);
}
} else {
qCWarning(RENDER_WARNING, "Failed to bind attribute %s", qPrintable(attrib.m_attribName));
}
}
}
#endif // _DEBUG
return true;
}
void QSSGRenderBackendGL3Impl::setDrawBuffers(QSSGRenderBackendRenderTargetObject rto, QSSGDataView<qint32> inDrawBufferSet)
{
Q_UNUSED(rto)
m_drawBuffersArray.clear();
for (int idx = 0, end = inDrawBufferSet.size(); idx < end; ++idx) {
if (inDrawBufferSet[idx] < 0)
m_drawBuffersArray.push_back(GL_NONE);
else
m_drawBuffersArray.push_back(GL_COLOR_ATTACHMENT0 + GLuint(inDrawBufferSet[idx]));
}
GL_CALL_EXTRA_FUNCTION(glDrawBuffers(m_drawBuffersArray.size(), m_drawBuffersArray.data()));
}
void QSSGRenderBackendGL3Impl::setReadBuffer(QSSGRenderBackendRenderTargetObject rto, QSSGReadFace inReadFace)
{
Q_UNUSED(rto)
GL_CALL_EXTRA_FUNCTION(glReadBuffer(m_conversion.fromReadFacesToGL(inReadFace)));
}
void QSSGRenderBackendGL3Impl::renderTargetAttach(QSSGRenderBackendRenderTargetObject,
QSSGRenderFrameBufferAttachment attachment,
QSSGRenderBackendTextureObject to,
qint32 level,
qint32 layer)
{
// rto must be the current render target
GLuint texID = HandleToID_cast(GLuint, quintptr, to);
GLenum glAttach = GLConversion::fromFramebufferAttachmentsToGL(attachment);
GL_CALL_EXTRA_FUNCTION(glFramebufferTextureLayer(GL_FRAMEBUFFER, glAttach, texID, level, layer))
}
void QSSGRenderBackendGL3Impl::setReadTarget(QSSGRenderBackendRenderTargetObject rto)
{
GLuint fboID = HandleToID_cast(GLuint, quintptr, rto);
GL_CALL_EXTRA_FUNCTION(glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID));
}
void QSSGRenderBackendGL3Impl::blitFramebuffer(qint32 srcX0,
qint32 srcY0,
qint32 srcX1,
qint32 srcY1,
qint32 dstX0,
qint32 dstY0,
qint32 dstX1,
qint32 dstY1,
QSSGRenderClearFlags flags,
QSSGRenderTextureMagnifyingOp filter)
{
GL_CALL_EXTRA_FUNCTION(glBlitFramebuffer(srcX0,
srcY0,
srcX1,
srcY1,
dstX0,
dstY0,
dstX1,
dstY1,
m_conversion.fromClearFlagsToGL(flags),
m_conversion.fromTextureMagnifyingOpToGL(filter)));
}
void QSSGRenderBackendGL3Impl::copyFramebufferTexture(qint32 srcX0,
qint32 srcY0,
qint32 width,
qint32 height,
qint32 dstX0,
qint32 dstY0,
QSSGRenderBackendTextureObject texture,
QSSGRenderTextureTargetType target)
{
GLuint texID = HandleToID_cast(GLuint, quintptr, texture);
GLenum glTarget = GLConversion::fromTextureTargetToGL(target);
setActiveTexture(GL_TEXTURE0);
GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID))
GL_CALL_EXTRA_FUNCTION(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, srcX0, srcY0, dstX0, dstY0,
width, height))
}
void *QSSGRenderBackendGL3Impl::mapBuffer(QSSGRenderBackendBufferObject,
QSSGRenderBufferType bindFlags,
size_t offset,
size_t length,
QSSGRenderBufferAccessFlags accessFlags)
{
void *ret = nullptr;
ret = GL_CALL_EXTRA_FUNCTION(glMapBufferRange(m_conversion.fromBindBufferFlagsToGL(bindFlags),
GLintptr(offset),
GLintptr(length),
m_conversion.fromBufferAccessBitToGL(accessFlags)));
return ret;
}
bool QSSGRenderBackendGL3Impl::unmapBuffer(QSSGRenderBackendBufferObject, QSSGRenderBufferType bindFlags)
{
const GLboolean ret = GL_CALL_EXTRA_FUNCTION(glUnmapBuffer(m_conversion.fromBindBufferFlagsToGL(bindFlags)));
return (ret != 0);
}
qint32 QSSGRenderBackendGL3Impl::getConstantBufferCount(QSSGRenderBackendShaderProgramObject po)
{
Q_ASSERT(po);
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
GLint numUniformBuffers;
GL_CALL_EXTRA_FUNCTION(glGetProgramiv(programID, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBuffers));
return numUniformBuffers;
}
qint32 QSSGRenderBackendGL3Impl::getConstantBufferInfoByID(QSSGRenderBackendShaderProgramObject po,
quint32 id,
quint32 nameBufSize,
qint32 *paramCount,
qint32 *bufferSize,
qint32 *length,
char *nameBuf)
{
Q_ASSERT(po);
Q_ASSERT(length);
Q_ASSERT(nameBuf);
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
GLuint blockIndex = GL_INVALID_INDEX;
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformBlockName(programID, id, GLsizei(nameBufSize), length, nameBuf));
if (*length > 0) {
blockIndex = GL_CALL_EXTRA_FUNCTION(glGetUniformBlockIndex(programID, nameBuf));
if (blockIndex != GL_INVALID_INDEX) {
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformBlockiv(programID, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, bufferSize));
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformBlockiv(programID, blockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, paramCount));
}
}
return qint32(blockIndex);
}
void QSSGRenderBackendGL3Impl::getConstantBufferParamIndices(QSSGRenderBackendShaderProgramObject po, quint32 id, qint32 *indices)
{
Q_ASSERT(po);
Q_ASSERT(indices);
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
if (indices) {
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformBlockiv(programID, id, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices));
}
}
void QSSGRenderBackendGL3Impl::getConstantBufferParamInfoByIndices(QSSGRenderBackendShaderProgramObject po,
quint32 count,
quint32 *indices,
QSSGRenderShaderDataType *type,
qint32 *size,
qint32 *offset)
{
Q_ASSERT(po);
Q_ASSERT(count && count <= INT32_MAX);
Q_ASSERT(indices);
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
if (count && indices) {
if (type) {
QVarLengthArray<qint32, 1024> glTypes(count);
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformsiv(programID, GLsizei(count), indices, GL_UNIFORM_TYPE, glTypes.data()));
// convert to UIC types
for (qint32 idx = 0; idx != qint32(count); ++idx)
type[idx] = GLConversion::fromShaderGLToPropertyDataTypes(GLenum(glTypes[idx]));
}
if (size)
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformsiv(programID, GLsizei(count), indices, GL_UNIFORM_SIZE, size));
if (offset)
GL_CALL_EXTRA_FUNCTION(glGetActiveUniformsiv(programID, GLsizei(count), indices, GL_UNIFORM_OFFSET, offset));
}
}
void QSSGRenderBackendGL3Impl::programSetConstantBlock(QSSGRenderBackendShaderProgramObject po, quint32 blockIndex, quint32 binding)
{
QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po);
GLuint programID = static_cast<GLuint>(pProgram->m_programID);
GL_CALL_EXTRA_FUNCTION(glUniformBlockBinding(programID, blockIndex, binding));
}
void QSSGRenderBackendGL3Impl::programSetConstantBuffer(quint32 index, QSSGRenderBackendBufferObject bo)
{
Q_ASSERT(bo);
GLuint bufID = HandleToID_cast(GLuint, quintptr, bo);
GL_CALL_EXTRA_FUNCTION(glBindBufferBase(GL_UNIFORM_BUFFER, index, bufID));
}
QSSGRenderBackend::QSSGRenderBackendQueryObject QSSGRenderBackendGL3Impl::createQuery()
{
quint32 glQueryID = 0;
GL_CALL_EXTRA_FUNCTION(glGenQueries(1, &glQueryID));
return reinterpret_cast<QSSGRenderBackendQueryObject>(quintptr(glQueryID));
}
void QSSGRenderBackendGL3Impl::releaseQuery(QSSGRenderBackendQueryObject qo)
{
GLuint queryID = HandleToID_cast(GLuint, quintptr, qo);
GL_CALL_EXTRA_FUNCTION(glDeleteQueries(1, &queryID));
}
void QSSGRenderBackendGL3Impl::beginQuery(QSSGRenderBackendQueryObject qo, QSSGRenderQueryType type)
{
GLuint queryID = HandleToID_cast(GLuint, quintptr, qo);
GL_CALL_EXTRA_FUNCTION(glBeginQuery(m_conversion.fromQueryTypeToGL(type), queryID));
}
void QSSGRenderBackendGL3Impl::endQuery(QSSGRenderBackendQueryObject, QSSGRenderQueryType type)
{
GL_CALL_EXTRA_FUNCTION(glEndQuery(m_conversion.fromQueryTypeToGL(type)));
}
void QSSGRenderBackendGL3Impl::getQueryResult(QSSGRenderBackendQueryObject qo,
QSSGRenderQueryResultType resultType,
quint32 *params)
{
GLuint queryID = HandleToID_cast(GLuint, quintptr, qo);
if (params)
GL_CALL_EXTRA_FUNCTION(glGetQueryObjectuiv(queryID, m_conversion.fromQueryResultTypeToGL(resultType), params));
}
void QSSGRenderBackendGL3Impl::getQueryResult(QSSGRenderBackendQueryObject qo,
QSSGRenderQueryResultType resultType,
quint64 *params)
{
// TODO: params type!
if (m_backendSupport.caps.bits.bTimerQuerySupported) {
GLuint queryID = HandleToID_cast(GLuint, quintptr, qo);
if (params)
#if defined(QT_OPENGL_ES)
GL_CALL_TIMER_EXT(glGetQueryObjectui64vEXT(queryID, m_conversion.fromQueryResultTypeToGL(resultType), reinterpret_cast<GLuint64 *>(params)));
#else
GL_CALL_TIMER_EXT(glGetQueryObjectui64v(queryID, m_conversion.fromQueryResultTypeToGL(resultType), reinterpret_cast<GLuint64 *>(params)));
#endif
}
}
void QSSGRenderBackendGL3Impl::setQueryTimer(QSSGRenderBackendQueryObject qo)
{
if (m_backendSupport.caps.bits.bTimerQuerySupported) {
GLuint queryID = HandleToID_cast(GLuint, quintptr, qo);
#if defined(QT_OPENGL_ES)
GL_CALL_TIMER_EXT(glQueryCounterEXT(queryID, GL_TIMESTAMP));
#else
GL_CALL_TIMER_EXT(glQueryCounter(queryID, GL_TIMESTAMP));
#endif
}
}
QSSGRenderBackend::QSSGRenderBackendSyncObject QSSGRenderBackendGL3Impl::createSync(QSSGRenderSyncType syncType,
QSSGRenderSyncFlags)
{
GLsync syncID = nullptr;
syncID = GL_CALL_EXTRA_FUNCTION(glFenceSync(m_conversion.fromSyncTypeToGL(syncType), 0));
return QSSGRenderBackendSyncObject(syncID);
}
void QSSGRenderBackendGL3Impl::releaseSync(QSSGRenderBackendSyncObject so)
{
GLsync syncID = GLsync(so);
GL_CALL_EXTRA_FUNCTION(glDeleteSync(syncID));
}
void QSSGRenderBackendGL3Impl::waitSync(QSSGRenderBackendSyncObject so, QSSGRenderCommandFlushFlags, quint64)
{
GLsync syncID = GLsync(so);
GL_CALL_EXTRA_FUNCTION(glWaitSync(syncID, 0, GL_TIMEOUT_IGNORED));
}
void QSSGRenderBackendGL3Impl::releaseInputAssembler(QSSGRenderBackendInputAssemblerObject iao)
{
QSSGRenderBackendInputAssemblerGL *inputAssembler = reinterpret_cast<QSSGRenderBackendInputAssemblerGL *>(iao);
GL_CALL_EXTRA_FUNCTION(glDeleteVertexArrays(1, &inputAssembler->m_vaoID));
delete inputAssembler;
}
QT_END_NAMESPACE