| /**************************************************************************** |
| ** |
| ** 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/qssgrenderbackendgles2_p.h> |
| #include <QtQuick3DRender/private/qssgrenderbackendinputassemblergl_p.h> |
| #include <QtQuick3DRender/private/qssgrenderbackendrenderstatesgl_p.h> |
| #include <QtQuick3DRender/private/qssgrenderbackendshaderprogramgl_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 |
| |
| #if defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2_ANGLE) |
| #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); |
| #define GL_CALL_MULTISAMPLE_EXT(x) \ |
| m_QSSGExtensions->x; \ |
| RENDER_LOG_ERROR_PARAMS(x); |
| #define GL_CALL_EXTRA_FUNCTION(x) \ |
| m_glExtraFunctions->x; \ |
| RENDER_LOG_ERROR_PARAMS(x); |
| #define GL_CALL_EXTENSION_FUNCTION(x) \ |
| m_QSSGExtensions->x; \ |
| RENDER_LOG_ERROR_PARAMS(x); |
| #else |
| #define GL_CALL_TIMER_EXT(x) |
| #define GL_CALL_TESSELATION_EXT(x) |
| #define GL_CALL_MULTISAMPLE_EXT(x) |
| #define GL_CALL_EXTRA_FUNCTION(x) \ |
| m_glExtraFunctions->x; \ |
| RENDER_LOG_ERROR_PARAMS(x); |
| #define GL_CALL_EXTENSION_FUNCTION(x) |
| #endif |
| |
| #ifndef GL_DEPTH_STENCIL_OES |
| #define GL_DEPTH_STENCIL_OES 0x84F9 |
| #endif |
| |
| QByteArray exts3tc() |
| { |
| return QByteArrayLiteral("GL_EXT_texture_compression_s3tc"); |
| } |
| QByteArray extsdxt() |
| { |
| return QByteArrayLiteral("GL_EXT_texture_compression_dxt1"); |
| } |
| QByteArray extsAniso() |
| { |
| return QByteArrayLiteral("GL_EXT_texture_filter_anisotropic"); |
| } |
| QByteArray extsTexSwizzle() |
| { |
| return QByteArrayLiteral("GL_ARB_texture_swizzle"); |
| } |
| QByteArray extsFPRenderTarget() |
| { |
| return QByteArrayLiteral("GL_EXT_color_buffer_float"); |
| } |
| QByteArray extsTimerQuery() |
| { |
| return QByteArrayLiteral("GL_EXT_timer_query"); |
| } |
| QByteArray extsGpuShader5() |
| { |
| return QByteArrayLiteral("EXT_gpu_shader5"); |
| } |
| |
| QByteArray extDepthTexture() |
| { |
| return QByteArrayLiteral("GL_OES_packed_depth_stencil"); |
| } |
| QByteArray extvao() |
| { |
| return QByteArrayLiteral("GL_OES_vertex_array_object"); |
| } |
| QByteArray extStdDd() |
| { |
| return QByteArrayLiteral("GL_OES_standard_derivatives"); |
| } |
| QByteArray extTexLod() |
| { |
| return QByteArrayLiteral("GL_EXT_shader_texture_lod"); |
| } |
| |
| /// constructor |
| QSSGRenderBackendGLES2Impl::QSSGRenderBackendGLES2Impl(const QSurfaceFormat &format) |
| : QSSGRenderBackendGLBase(format) |
| { |
| // clear support bits |
| m_backendSupport.caps.u32Values = 0; |
| |
| const char *extensions = getExtensionString(); |
| m_extensions = QByteArray(extensions).split(' '); |
| |
| // get extension count |
| GLint numExtensions = m_extensions.size(); |
| |
| for (qint32 i = 0; i < numExtensions; i++) { |
| |
| const QByteArray &extensionString = m_extensions.at(i); |
| |
| // search for extension |
| if (!m_backendSupport.caps.bits.bDXTImagesSupported |
| && (exts3tc().compare(extensionString) == 0 || extsdxt().compare(extensionString) == 0)) { |
| m_backendSupport.caps.bits.bDXTImagesSupported = true; |
| } else if (!m_backendSupport.caps.bits.bAnistropySupported && extsAniso().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bAnistropySupported = true; |
| } else if (!m_backendSupport.caps.bits.bFPRenderTargetsSupported && extsFPRenderTarget().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bFPRenderTargetsSupported = true; |
| } else if (!m_backendSupport.caps.bits.bTimerQuerySupported && extsTimerQuery().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bTimerQuerySupported = true; |
| } else if (!m_backendSupport.caps.bits.bGPUShader5ExtensionSupported && extsGpuShader5().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bGPUShader5ExtensionSupported = true; |
| } else if (!m_backendSupport.caps.bits.bTextureSwizzleSupported && extsTexSwizzle().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bTextureSwizzleSupported = true; |
| } else if (!m_backendSupport.caps.bits.bDepthStencilSupported && extDepthTexture().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bDepthStencilSupported = true; |
| } else if (!m_backendSupport.caps.bits.bVertexArrayObjectSupported && extvao().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bVertexArrayObjectSupported = true; |
| } else if (!m_backendSupport.caps.bits.bStandardDerivativesSupported && extStdDd().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bStandardDerivativesSupported = true; |
| } else if (!m_backendSupport.caps.bits.bTextureLodSupported && extTexLod().compare(extensionString) == 0) { |
| m_backendSupport.caps.bits.bTextureLodSupported = true; |
| } |
| } |
| |
| qCInfo(TRACE_INFO, "OpenGL extensions: %s", extensions); |
| |
| // constant buffers support is always not true |
| m_backendSupport.caps.bits.bConstantBufferSupported = false; |
| |
| // query hardware |
| GL_CALL_EXTRA_FUNCTION(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_maxAttribCount)); |
| |
| // internal state tracker |
| m_pCurrentMiscState = new QSSGRenderBackendMiscStateGL(); |
| |
| // finally setup caps based on device |
| setAndInspectHardwareCaps(); |
| |
| // Initialize extensions |
| #if defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2_ANGLE) |
| m_QSSGExtensions = new QSSGOpenGLES2Extensions; |
| m_QSSGExtensions->initializeOpenGLFunctions(); |
| #endif |
| } |
| /// destructor |
| QSSGRenderBackendGLES2Impl::~QSSGRenderBackendGLES2Impl() |
| { |
| delete m_pCurrentMiscState; |
| #if defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2_ANGLE) |
| delete m_QSSGExtensions; |
| #endif |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setMultisampledTextureData2D(QSSGRenderBackendTextureObject to, |
| QSSGRenderTextureTargetType target, |
| qint32 samples, |
| QSSGRenderTextureFormat internalFormat, |
| qint32 width, |
| qint32 height, |
| bool fixedsamplelocations) |
| { |
| Q_UNUSED(to) |
| Q_UNUSED(target) |
| Q_UNUSED(samples) |
| Q_UNUSED(internalFormat) |
| Q_UNUSED(width) |
| Q_UNUSED(height) |
| Q_UNUSED(fixedsamplelocations) |
| } |
| |
| void QSSGRenderBackendGLES2Impl::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); |
| GL_CALL_EXTRA_FUNCTION(glActiveTexture(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, glInternalFormat, GLsizei(width), GLsizei(height), GLsizei(depth), border, glformat, gltype, hostData)); |
| |
| GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setTextureData2D(QSSGRenderBackendTextureObject to, |
| QSSGRenderTextureTargetType target, |
| qint32 level, |
| QSSGRenderTextureFormat internalFormat, |
| qint32 width, |
| qint32 height, |
| qint32 border, |
| QSSGRenderTextureFormat format, |
| QSSGByteView hostData) |
| { |
| GLuint texID = HandleToID_cast(GLuint, quintptr, to); |
| GLenum glTarget = GLConversion::fromTextureTargetToGL(target); |
| GL_CALL_EXTRA_FUNCTION(glActiveTexture(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); |
| glInternalFormat = glformat; |
| } |
| |
| 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); |
| if (format == QSSGRenderTextureFormat::Depth24Stencil8) { |
| glformat = GL_DEPTH_STENCIL_OES; |
| gltype = GL_UNSIGNED_INT_24_8; |
| } |
| glInternalFormat = glformat; |
| } |
| |
| Q_ASSERT(glformat == glInternalFormat); |
| GL_CALL_EXTRA_FUNCTION( |
| glTexImage2D(glTarget, level, glInternalFormat, GLsizei(width), GLsizei(height), border, glformat, gltype, hostData)); |
| GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::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) |
| Q_UNUSED(wrapR) |
| Q_UNUSED(minLod) |
| Q_UNUSED(maxLod) |
| Q_UNUSED(compareMode) |
| Q_UNUSED(compareFunc) |
| |
| GLenum glTarget = GLConversion::fromTextureTargetToGL(target); |
| |
| GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_MIN_FILTER, m_conversion.fromTextureMinifyingOpToGL(minFilter))); |
| GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_MAG_FILTER, m_conversion.fromTextureMagnifyingOpToGL(magFilter))); |
| GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_WRAP_S, m_conversion.fromTextureCoordOpToGL(wrapS))); |
| GL_CALL_EXTRA_FUNCTION(glTexParameteri(glTarget, GL_TEXTURE_WRAP_T, m_conversion.fromTextureCoordOpToGL(wrapT))); |
| |
| if (m_backendSupport.caps.bits.bAnistropySupported) { |
| GL_CALL_EXTRA_FUNCTION(glTexParameterf(glTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy)); |
| } |
| } |
| |
| void QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::updateTextureSwizzle(QSSGRenderBackendTextureObject to, |
| QSSGRenderTextureTargetType target, |
| QSSGRenderTextureSwizzleMode swizzleMode) |
| { |
| Q_UNUSED(to) |
| Q_UNUSED(target) |
| Q_UNUSED(swizzleMode) |
| #if defined(QT_OPENGL_ES) |
| if (m_backendSupport.caps.bits.bTextureSwizzleSupported) { |
| GLint glSwizzle[4]; |
| GLenum glTarget = m_conversion.fromTextureTargetToGL(target); |
| m_conversion.NVRenderConvertSwizzleModeToGL(swizzleMode, glSwizzle); |
| |
| // 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])); |
| } |
| #endif |
| } |
| |
| qint32 QSSGRenderBackendGLES2Impl::getDepthBits() const |
| { |
| qint32 depthBits; |
| GL_CALL_EXTRA_FUNCTION( |
| glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depthBits)); |
| |
| return depthBits; |
| } |
| |
| qint32 QSSGRenderBackendGLES2Impl::getStencilBits() const |
| { |
| qint32 stencilBits; |
| GL_CALL_EXTRA_FUNCTION( |
| glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits)); |
| |
| return stencilBits; |
| } |
| |
| void QSSGRenderBackendGLES2Impl::generateMipMaps(QSSGRenderBackendTextureObject to, |
| QSSGRenderTextureTargetType target, |
| QSSGRenderHint /*genType*/) |
| { |
| GLuint texID = HandleToID_cast(GLuint, quintptr, to); |
| GLenum glTarget = GLConversion::fromTextureTargetToGL(target); |
| GL_CALL_EXTRA_FUNCTION(glActiveTexture(GL_TEXTURE0)); |
| GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID)); |
| GL_CALL_EXTRA_FUNCTION(glGenerateMipmap(glTarget)); |
| GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, 0)); |
| } |
| |
| QByteArray QSSGRenderBackendGLES2Impl::getShadingLanguageVersion() |
| { |
| return QByteArrayLiteral("#version 100\n"); |
| } |
| |
| bool QSSGRenderBackendGLES2Impl::setInputAssembler(QSSGRenderBackendInputAssemblerObject iao, QSSGRenderBackendShaderProgramObject po) |
| { |
| if (iao == nullptr) { |
| // unbind and return; |
| GL_CALL_EXTENSION_FUNCTION(glBindVertexArrayOES(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; |
| |
| if ((attribLayout->m_layoutAttribEntries.size() < shaderAttribBuffer.size()) |
| || (inputAssembler->m_vertexbufferHandles.size() <= attribLayout->m_maxInputSlot)) { |
| return false; |
| } |
| |
| if (inputAssembler->m_vaoID == 0) { |
| // generate vao |
| GL_CALL_EXTENSION_FUNCTION(glGenVertexArraysOES(1, &inputAssembler->m_vaoID)); |
| Q_ASSERT(inputAssembler->m_vaoID); |
| } |
| |
| if (inputAssembler->m_cachedShaderHandle != programID) { |
| GL_CALL_EXTENSION_FUNCTION(glBindVertexArrayOES(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(INVALID_OPERATION, "Attrib %s dn't match vertex layout", attrib.m_attribName.constData()); |
| Q_ASSERT(false); |
| return false; |
| } else { |
| entryData.m_attribIndex = attrib.m_attribLocation; |
| } |
| } else { |
| qCWarning(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 |
| 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]); |
| GL_CALL_EXTRA_FUNCTION(glBindBuffer(GL_ARRAY_BUFFER, 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_EXTENSION_FUNCTION(glBindVertexArrayOES(inputAssembler->m_vaoID)); |
| } |
| |
| return true; |
| } |
| |
| void QSSGRenderBackendGLES2Impl::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(GLenum(GL_COLOR_ATTACHMENT0 + inDrawBufferSet[idx])); |
| } |
| |
| GL_CALL_EXTRA_FUNCTION(glDrawBuffers(m_drawBuffersArray.size(), m_drawBuffersArray.data())); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setReadBuffer(QSSGRenderBackendRenderTargetObject rto, QSSGReadFace inReadFace) |
| { |
| Q_UNUSED(rto) |
| Q_UNUSED(inReadFace) |
| } |
| |
| void QSSGRenderBackendGLES2Impl::renderTargetAttach(QSSGRenderBackendRenderTargetObject, |
| QSSGRenderFrameBufferAttachment attachment, |
| QSSGRenderBackendTextureObject to, |
| qint32 level, |
| qint32 layer) |
| { |
| Q_UNUSED(attachment) |
| Q_UNUSED(to) |
| Q_UNUSED(level) |
| Q_UNUSED(layer) |
| Q_ASSERT(false); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::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); |
| GL_CALL_EXTRA_FUNCTION(glActiveTexture(GL_TEXTURE0)) |
| GL_CALL_EXTRA_FUNCTION(glBindTexture(glTarget, texID)) |
| GL_CALL_EXTRA_FUNCTION(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, srcX0, srcY0, dstX0, dstY0, |
| width, height)) |
| } |
| |
| QSSGRenderBackend::QSSGRenderBackendRenderTargetObject QSSGRenderBackendGLES2Impl::createRenderTarget() |
| { |
| GLuint fboID = 0; |
| GL_CALL_EXTRA_FUNCTION(glGenFramebuffers(1, &fboID)); |
| return reinterpret_cast<QSSGRenderBackend::QSSGRenderBackendRenderTargetObject>(quintptr(fboID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::releaseRenderTarget(QSSGRenderBackendRenderTargetObject rto) |
| { |
| GLuint fboID = HandleToID_cast(GLuint, quintptr, rto); |
| |
| if (fboID) |
| GL_CALL_EXTRA_FUNCTION(glDeleteFramebuffers(1, &fboID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::renderTargetAttach(QSSGRenderBackendRenderTargetObject /* rto */, |
| QSSGRenderFrameBufferAttachment attachment, |
| QSSGRenderBackendRenderbufferObject rbo) |
| { |
| // rto must be the current render target |
| GLuint rbID = HandleToID_cast(GLuint, quintptr, rbo); |
| |
| GLenum glAttach = GLConversion::fromFramebufferAttachmentsToGL(attachment); |
| |
| GL_CALL_EXTRA_FUNCTION(glFramebufferRenderbuffer(GL_FRAMEBUFFER, glAttach, GL_RENDERBUFFER, rbID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::renderTargetAttach(QSSGRenderBackendRenderTargetObject /* rto */, |
| QSSGRenderFrameBufferAttachment attachment, |
| QSSGRenderBackendTextureObject to, |
| QSSGRenderTextureTargetType target) |
| { |
| // rto must be the current render target |
| GLuint texID = HandleToID_cast(GLuint, quintptr, to); |
| |
| Q_ASSERT(target == QSSGRenderTextureTargetType::Texture2D || m_backendSupport.caps.bits.bMsTextureSupported); |
| |
| GLenum glAttach = GLConversion::fromFramebufferAttachmentsToGL(attachment); |
| GLenum glTarget = GLConversion::fromTextureTargetToGL(target); |
| |
| if (attachment == QSSGRenderFrameBufferAttachment::DepthStencil) { |
| GL_CALL_EXTRA_FUNCTION(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, glTarget, texID, 0)); |
| GL_CALL_EXTRA_FUNCTION(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, glTarget, texID, 0)); |
| } else { |
| GL_CALL_EXTRA_FUNCTION(glFramebufferTexture2D(GL_FRAMEBUFFER, glAttach, glTarget, texID, 0)); |
| } |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setRenderTarget(QSSGRenderBackendRenderTargetObject rto) |
| { |
| GLuint fboID = HandleToID_cast(GLuint, quintptr, rto); |
| |
| GL_CALL_EXTRA_FUNCTION(glBindFramebuffer(GL_FRAMEBUFFER, fboID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setReadTarget(QSSGRenderBackendRenderTargetObject rto) |
| { |
| GLuint fboID = HandleToID_cast(GLuint, quintptr, rto); |
| |
| GL_CALL_EXTRA_FUNCTION(glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID)); |
| } |
| |
| bool QSSGRenderBackendGLES2Impl::renderTargetIsValid(QSSGRenderBackendRenderTargetObject /* rto */) |
| { |
| GLenum completeStatus = GL_CALL_EXTRA_FUNCTION(glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| switch (completeStatus) { |
| #define HANDLE_INCOMPLETE_STATUS(x) \ |
| case x: \ |
| qCCritical(INTERNAL_ERROR, "Framebuffer is not complete: %s", #x); \ |
| return false; |
| HANDLE_INCOMPLETE_STATUS(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) |
| HANDLE_INCOMPLETE_STATUS(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) |
| HANDLE_INCOMPLETE_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) |
| HANDLE_INCOMPLETE_STATUS(GL_FRAMEBUFFER_UNSUPPORTED) |
| #undef HANDLE_INCOMPLETE_STATUS |
| } |
| return true; |
| } |
| |
| QSSGRenderBackend::QSSGRenderBackendRenderbufferObject QSSGRenderBackendGLES2Impl::createRenderbuffer(QSSGRenderRenderBufferFormat storageFormat, |
| qint32 width, |
| qint32 height) |
| { |
| GLuint bufID = 0; |
| |
| GL_CALL_EXTRA_FUNCTION(glGenRenderbuffers(1, &bufID)); |
| GL_CALL_EXTRA_FUNCTION(glBindRenderbuffer(GL_RENDERBUFFER, bufID)); |
| GL_CALL_EXTRA_FUNCTION(glRenderbufferStorage(GL_RENDERBUFFER, |
| GLConversion::fromRenderBufferFormatsToRenderBufferGL(storageFormat), |
| GLsizei(width), |
| GLsizei(height))); |
| |
| // check for error |
| GLenum error = m_glFunctions->glGetError(); |
| if (error != GL_NO_ERROR) { |
| qCCritical(GL_ERROR, "%s", GLConversion::processGLError(error)); |
| Q_ASSERT(false); |
| GL_CALL_EXTRA_FUNCTION(glDeleteRenderbuffers(1, &bufID)); |
| bufID = 0; |
| } |
| |
| GL_CALL_EXTRA_FUNCTION(glBindRenderbuffer(GL_RENDERBUFFER, 0)); |
| |
| return reinterpret_cast<QSSGRenderBackend::QSSGRenderBackendRenderbufferObject>(quintptr(bufID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::releaseRenderbuffer(QSSGRenderBackendRenderbufferObject rbo) |
| { |
| GLuint bufID = HandleToID_cast(GLuint, quintptr, rbo); |
| |
| if (bufID) |
| GL_CALL_EXTRA_FUNCTION(glDeleteRenderbuffers(1, &bufID)); |
| } |
| |
| bool QSSGRenderBackendGLES2Impl::resizeRenderbuffer(QSSGRenderBackendRenderbufferObject rbo, |
| QSSGRenderRenderBufferFormat storageFormat, |
| qint32 width, |
| qint32 height) |
| { |
| bool success = true; |
| GLuint bufID = HandleToID_cast(GLuint, quintptr, rbo); |
| |
| Q_ASSERT(bufID); |
| |
| GL_CALL_EXTRA_FUNCTION(glBindRenderbuffer(GL_RENDERBUFFER, bufID)); |
| GL_CALL_EXTRA_FUNCTION(glRenderbufferStorage(GL_RENDERBUFFER, |
| GLConversion::fromRenderBufferFormatsToRenderBufferGL(storageFormat), |
| GLsizei(width), |
| GLsizei(height))); |
| |
| // check for error |
| GLenum error = m_glFunctions->glGetError(); |
| if (error != GL_NO_ERROR) { |
| qCCritical(GL_ERROR, "%s", GLConversion::processGLError(error)); |
| Q_ASSERT(false); |
| success = false; |
| } |
| |
| return success; |
| } |
| |
| void *QSSGRenderBackendGLES2Impl::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), |
| GLsizeiptr(length), |
| m_conversion.fromBufferAccessBitToGL(accessFlags))); |
| |
| return ret; |
| } |
| |
| bool QSSGRenderBackendGLES2Impl::unmapBuffer(QSSGRenderBackendBufferObject, QSSGRenderBufferType bindFlags) |
| { |
| GLboolean ret; |
| |
| ret = GL_CALL_EXTRA_FUNCTION(glUnmapBuffer(m_conversion.fromBindBufferFlagsToGL(bindFlags))); |
| |
| return (ret) ? true : false; |
| } |
| |
| qint32 QSSGRenderBackendGLES2Impl::getConstantBufferCount(QSSGRenderBackendShaderProgramObject po) |
| { |
| Q_ASSERT(po); |
| GLint numUniformBuffers = 0; |
| if (getRenderBackendCap(QSSGRenderBackendCaps::ConstantBuffer)) { |
| QSSGRenderBackendShaderProgramGL *pProgram = reinterpret_cast<QSSGRenderBackendShaderProgramGL *>(po); |
| GLuint programID = static_cast<GLuint>(pProgram->m_programID); |
| |
| GL_CALL_EXTRA_FUNCTION(glGetProgramiv(programID, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBuffers)); |
| } |
| return numUniformBuffers; |
| } |
| |
| qint32 QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::getConstantBufferParamInfoByIndices(QSSGRenderBackendShaderProgramObject po, |
| quint32 count, |
| quint32 *indices, |
| QSSGRenderShaderDataType *type, |
| qint32 *size, |
| qint32 *offset) |
| { |
| Q_ASSERT(po); |
| Q_ASSERT(count); |
| 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 (int idx = 0; idx != int(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 QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::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 QSSGRenderBackendGLES2Impl::createQuery() |
| { |
| quint32 glQueryID = 0; |
| |
| return reinterpret_cast<QSSGRenderBackendQueryObject>(quintptr(glQueryID)); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::releaseQuery(QSSGRenderBackendQueryObject) {} |
| |
| void QSSGRenderBackendGLES2Impl::beginQuery(QSSGRenderBackendQueryObject, QSSGRenderQueryType) {} |
| |
| void QSSGRenderBackendGLES2Impl::endQuery(QSSGRenderBackendQueryObject, QSSGRenderQueryType) {} |
| |
| void QSSGRenderBackendGLES2Impl::getQueryResult(QSSGRenderBackendQueryObject, QSSGRenderQueryResultType, quint32 *) |
| { |
| } |
| |
| void QSSGRenderBackendGLES2Impl::getQueryResult(QSSGRenderBackendQueryObject, QSSGRenderQueryResultType, quint64 *) |
| { |
| } |
| |
| void QSSGRenderBackendGLES2Impl::setQueryTimer(QSSGRenderBackendQueryObject) {} |
| |
| QSSGRenderBackend::QSSGRenderBackendSyncObject QSSGRenderBackendGLES2Impl::createSync(QSSGRenderSyncType, QSSGRenderSyncFlags) |
| { |
| GLsync syncID = nullptr; |
| return QSSGRenderBackendSyncObject(syncID); |
| } |
| |
| void QSSGRenderBackendGLES2Impl::releaseSync(QSSGRenderBackendSyncObject) {} |
| |
| void QSSGRenderBackendGLES2Impl::waitSync(QSSGRenderBackendSyncObject, QSSGRenderCommandFlushFlags, quint64) {} |
| |
| QT_END_NAMESPACE |