summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi/qrhigles2.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-05-19 19:30:53 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2021-05-31 17:16:57 +0200
commit51c22a1f51e2f91289c938be7754f957c9bfa47e (patch)
tree58eea1c4aeb83f4ee2ede516f321624ab5558181 /src/gui/rhi/qrhigles2.cpp
parent5eab5d62530f351307a01ca57af964e5c506079c (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.cpp183
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;