diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-05-19 19:30:53 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-05-31 17:16:57 +0200 |
commit | 51c22a1f51e2f91289c938be7754f957c9bfa47e (patch) | |
tree | 58eea1c4aeb83f4ee2ede516f321624ab5558181 /src/gui/rhi/qrhigles2.cpp | |
parent | 5eab5d62530f351307a01ca57af964e5c506079c (diff) |
rhi: Add support for 3D textures
Supported on OpenGL (and ES) 3.0+ and everywhere else.
Can also be a render target, targeting a single slice at a time.
Can be mipmapped, cannot be multisample.
Reading back a given slice from a 3D texture is left as a future
exercise, for now it is documented to be not supported.
Upload is going to be limited to one slice in one upload entry,
just like we specify one face or one miplevel for cubemap and
mipmapped textures.
This also involves some welcome hardening of how texture subresources
are described internally: as we no longer can count on a layer index
between 0..5 (as is the case with cubemaps), simply arrays with
MAX_LAYER==6 are no longer sufficient. Switch to sufficiently dynamic
data structures where applicable.
On Vulkan rendering to a slice needs Vulkan 1.1 (and 1.1 enabled on the
VkInstance).
Task-number: QTBUG-89703
Change-Id: Ide6c20124ec9201d94ffc339dd479cd1ece777b0
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui/rhi/qrhigles2.cpp')
-rw-r--r-- | src/gui/rhi/qrhigles2.cpp | 183 |
1 files changed, 144 insertions, 39 deletions
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 8a5361b29a..5f8ef08e31 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -348,6 +348,14 @@ QT_BEGIN_NAMESPACE #define GL_UNPACK_ROW_LENGTH 0x0CF2 #endif +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif + /*! Constructs a new QRhiGles2InitParams. @@ -621,6 +629,8 @@ bool QRhiGles2::create(QRhi::Flags flags) caps.programBinary = false; } + caps.texture3D = caps.ctxMajor >= 3; // 3.0 + if (!caps.gles) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_POINT_SPRITE); @@ -1013,6 +1023,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return !caps.gles || caps.ctxMajor >= 3; case QRhi::RenderBufferImport: return true; + case QRhi::ThreeDimensionalTextures: + return caps.texture3D; + case QRhi::RenderTo3DTextureSlice: + return caps.texture3D; default: Q_UNREACHABLE(); return false; @@ -1229,10 +1243,11 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint); } -QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize, +QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, + const QSize &pixelSize, int depth, int sampleCount, QRhiTexture::Flags flags) { - return new QGles2Texture(this, format, pixelSize, sampleCount, flags); + return new QGles2Texture(this, format, pixelSize, depth, sampleCount, flags); } QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -1812,6 +1827,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.subImage.level = level; cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dy = dp.y(); + cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; cmd.args.subImage.w = size.width(); cmd.args.subImage.h = size.height(); cmd.args.subImage.glformat = texD->glformat; @@ -1820,10 +1836,19 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.subImage.rowLength = 0; cmd.args.subImage.data = cbD->retainImage(img); } else if (!rawData.isEmpty() && isCompressed) { - if (!texD->compressedAtlasBuilt && (texD->flags() & QRhiTexture::UsedAsCompressedAtlas)) { - // Create on first upload since glCompressedTexImage2D cannot take nullptr data + const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional); + if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D) + && !texD->zeroInitialized) + { + // Create on first upload since glCompressedTexImage2D cannot take + // nullptr data. We have a rule in the QRhi docs that the first + // upload for a compressed texture must cover the entire image, but + // that is clearly not ideal when building a texture atlas, or when + // having a 3D texture with per-slice data. quint32 byteSize = 0; compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr); + if (is3D) + byteSize *= texD->m_depth; QByteArray zeroBuf(byteSize, 0); QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; @@ -1834,14 +1859,15 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.w = texD->m_pixelSize.width(); cmd.args.compressedImage.h = texD->m_pixelSize.height(); + cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0; cmd.args.compressedImage.size = byteSize; cmd.args.compressedImage.data = cbD->retainData(zeroBuf); - texD->compressedAtlasBuilt = true; + texD->zeroInitialized = true; } const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize) : subresDesc.sourceSize(); - if (texD->specified || texD->compressedAtlasBuilt) { + if (texD->specified || texD->zeroInitialized) { QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage; cmd.args.compressedSubImage.target = texD->target; @@ -1850,6 +1876,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.compressedSubImage.level = level; cmd.args.compressedSubImage.dx = dp.x(); cmd.args.compressedSubImage.dy = dp.y(); + cmd.args.compressedSubImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; cmd.args.compressedSubImage.w = size.width(); cmd.args.compressedSubImage.h = size.height(); cmd.args.compressedSubImage.glintformat = texD->glintformat; @@ -1865,6 +1892,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.w = size.width(); cmd.args.compressedImage.h = size.height(); + cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0; cmd.args.compressedImage.size = rawData.size(); cmd.args.compressedImage.data = cbD->retainData(rawData); } @@ -1882,6 +1910,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.subImage.level = level; cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dy = dp.y(); + cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; cmd.args.subImage.w = size.width(); cmd.args.subImage.h = size.height(); cmd.args.subImage.glformat = texD->glformat; @@ -1961,8 +1990,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]); if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst); - for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { - for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) { + for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) { for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) enqueueSubresUpload(texD, cbD, layer, level, subresDesc); } @@ -1990,18 +2019,21 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::CopyTex; + cmd.args.copyTex.srcTarget = srcD->target; cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer()); cmd.args.copyTex.srcTexture = srcD->texture; cmd.args.copyTex.srcLevel = u.desc.sourceLevel(); cmd.args.copyTex.srcX = sp.x(); cmd.args.copyTex.srcY = sp.y(); + cmd.args.copyTex.srcZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.sourceLayer() : 0; cmd.args.copyTex.dstTarget = dstD->target; - cmd.args.copyTex.dstTexture = dstD->texture; cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer()); + cmd.args.copyTex.dstTexture = dstD->texture; cmd.args.copyTex.dstLevel = u.desc.destinationLevel(); cmd.args.copyTex.dstX = dp.x(); cmd.args.copyTex.dstY = dp.y(); + cmd.args.copyTex.dstZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.destinationLayer() : 0; cmd.args.copyTex.w = copySize.width(); cmd.args.copyTex.h = copySize.height(); @@ -2734,13 +2766,25 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) GLuint fbo; f->glGenFramebuffers(1, &fbo); f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); - f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel); + if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D) { + f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture, + cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ); + } else { + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel); + } f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture); - f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel, - cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, - cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, - cmd.args.copyTex.w, cmd.args.copyTex.h); + if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D) { + f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel, + cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ, + cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, + cmd.args.copyTex.w, cmd.args.copyTex.h); + } else { + f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel, + cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, + cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, + cmd.args.copyTex.w, cmd.args.copyTex.h); + } f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); f->glDeleteFramebuffers(1, &fbo); } @@ -2825,11 +2869,19 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign); if (cmd.args.subImage.rowLength != 0) f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength); - f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level, - cmd.args.subImage.dx, cmd.args.subImage.dy, - cmd.args.subImage.w, cmd.args.subImage.h, - cmd.args.subImage.glformat, cmd.args.subImage.gltype, - cmd.args.subImage.data); + if (cmd.args.subImage.target == GL_TEXTURE_3D) { + f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level, + cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz, + cmd.args.subImage.w, cmd.args.subImage.h, 1, + cmd.args.subImage.glformat, cmd.args.subImage.gltype, + cmd.args.subImage.data); + } else { + f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level, + cmd.args.subImage.dx, cmd.args.subImage.dy, + cmd.args.subImage.w, cmd.args.subImage.h, + cmd.args.subImage.glformat, cmd.args.subImage.gltype, + cmd.args.subImage.data); + } if (cmd.args.subImage.rowStartAlign != 4) f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); if (cmd.args.subImage.rowLength != 0) @@ -2837,18 +2889,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) break; case QGles2CommandBuffer::Command::CompressedImage: f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture); - f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level, - cmd.args.compressedImage.glintformat, - cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0, - cmd.args.compressedImage.size, cmd.args.compressedImage.data); + if (cmd.args.compressedImage.target == GL_TEXTURE_3D) { + f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level, + cmd.args.compressedImage.glintformat, + cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth, + 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data); + } else { + f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level, + cmd.args.compressedImage.glintformat, + cmd.args.compressedImage.w, cmd.args.compressedImage.h, + 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data); + } break; case QGles2CommandBuffer::Command::CompressedSubImage: f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture); - f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level, - cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, - cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, - cmd.args.compressedSubImage.glintformat, - cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data); + if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D) { + f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level, + cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz, + cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1, + cmd.args.compressedSubImage.glintformat, + cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data); + } else { + f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level, + cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, + cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, + cmd.args.compressedSubImage.glintformat, + cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data); + } break; case QGles2CommandBuffer::Command::BlitFromRenderbuffer: { @@ -3399,6 +3466,8 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD, f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter)); f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps)); f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt)); + if (caps.texture3D) + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr)); if (caps.textureCompareMode) { if (samplerD->d.gltexcomparefunc != GL_NEVER) { f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); @@ -4418,9 +4487,9 @@ QRhiTexture::Format QGles2RenderBuffer::backingFormat() const return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; } -QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, +QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) + : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) { } @@ -4441,7 +4510,7 @@ void QGles2Texture::destroy() texture = 0; specified = false; - compressedAtlasBuilt = false; + zeroInitialized = false; QRHI_RES_RHI(QRhiGles2); if (owns) @@ -4463,11 +4532,26 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize) const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool isCompressed = rhiD->isCompressedFormat(m_format); + if (is3D && !rhiD->caps.texture3D) { + qWarning("3D textures are not supported"); + return false; + } + if (isCube && is3D) { + qWarning("Texture cannot be both cube and 3D"); + return false; + } + m_depth = qMax(1, m_depth); + if (m_depth > 1 && !is3D) { + qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); + return false; + } + target = isCube ? GL_TEXTURE_CUBE_MAP - : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : (is3D ? GL_TEXTURE_3D : GL_TEXTURE_2D); if (m_flags.testFlag(ExternalOES)) target = GL_TEXTURE_EXTERNAL_OES; @@ -4511,12 +4595,24 @@ bool QGles2Texture::create() rhiD->f->glGenTextures(1, &texture); const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool isCompressed = rhiD->isCompressedFormat(m_format); if (!isCompressed) { rhiD->f->glBindTexture(target, texture); if (!m_flags.testFlag(UsedWithLoadStore)) { - if (hasMipMaps || isCube) { + if (is3D) { + if (hasMipMaps) { + for (int level = 0; level != mipLevelCount; ++level) { + const QSize mipSize = rhiD->q->sizeForMipLevel(level, size); + rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), m_depth, + 0, glformat, gltype, nullptr); + } + } else { + rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), m_depth, + 0, glformat, gltype, nullptr); + } + } else if (hasMipMaps || isCube) { const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target; for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) { for (int level = 0; level != mipLevelCount; ++level) { @@ -4534,7 +4630,10 @@ bool QGles2Texture::create() // Must be specified with immutable storage functions otherwise // bindImageTexture may fail. Also, the internal format must be a // sized format here. - rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height()); + if (is3D) + rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), m_depth); + else + rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height()); } specified = true; } else { @@ -4565,7 +4664,7 @@ bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src) texture = textureId; specified = true; - compressedAtlasBuilt = true; + zeroInitialized = true; QRHI_RES_RHI(QRhiGles2); QRHI_PROF; @@ -4605,6 +4704,7 @@ bool QGles2Sampler::create() d.glmagfilter = toGlMagFilter(m_magFilter); d.glwraps = toGlWrapMode(m_addressU); d.glwrapt = toGlWrapMode(m_addressV); + d.glwrapr = toGlWrapMode(m_addressW); d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp); generation += 1; @@ -4744,9 +4844,14 @@ bool QGles2TextureRenderTarget::create() if (texture) { QGles2Texture *texD = QRHI_RES(QGles2Texture, texture); Q_ASSERT(texD->texture && texD->specified); - const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; - rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()), - texD->texture, colorAtt.level()); + if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { + rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture, + colorAtt.level(), colorAtt.layer()); + } else { + const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; + rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()), + texD->texture, colorAtt.level()); + } if (attIndex == 0) { d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize()); d.sampleCount = 1; |