summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/rhi/qrhi.cpp51
-rw-r--r--src/gui/rhi/qrhi_p.h7
-rw-r--r--src/gui/rhi/qrhid3d11.cpp75
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h3
-rw-r--r--src/gui/rhi/qrhigles2.cpp151
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h18
-rw-r--r--src/gui/rhi/qrhimetal.mm20
-rw-r--r--src/gui/rhi/qrhinull.cpp4
-rw-r--r--src/gui/rhi/qrhivulkan.cpp45
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp510
-rw-r--r--tests/manual/rhi/CMakeLists.txt1
-rw-r--r--tests/manual/rhi/tex1d/CMakeLists.txt23
-rw-r--r--tests/manual/rhi/tex1d/buildshaders.bat2
-rw-r--r--tests/manual/rhi/tex1d/tex1d.cpp625
-rw-r--r--tests/manual/rhi/tex1d/texture1d.frag51
-rw-r--r--tests/manual/rhi/tex1d/texture1d.frag.qsbbin0 -> 2451 bytes
-rw-r--r--tests/manual/rhi/tex1d/texture1d.vert14
-rw-r--r--tests/manual/rhi/tex1d/texture1d.vert.qsbbin0 -> 816 bytes
18 files changed, 1559 insertions, 41 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 20f4b4beae..79c7c96158 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -727,6 +727,15 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
for changing the mode to Line is to get wireframe rendering. This however
is not available as a core OpenGL ES feature, and is optional with Vulkan
as well as some mobile GPUs may not offer the feature.
+
+ \value OneDimensionalTextures Indicates that 1D textures are supported.
+ In practice this feature will be unsupported on OpenGL ES.
+
+ \value OneDimensionalTextureMipmaps Indicates that 1D texture mipmaps and
+ 1D texture render targets are supported. In practice this feature will be
+ unsupported on backends that do not report support for
+ \l{OneDimensionalTextures}, and Metal.
+
*/
/*!
@@ -7296,14 +7305,21 @@ QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type,
}
/*!
- \return a new 2D texture with the specified \a format, \a pixelSize, \a
+ \return a new 1D or 2D texture with the specified \a format, \a pixelSize, \a
sampleCount, and \a flags.
+ A 1D texture array must have QRhiTexture::OneDimensional set in \a flags. This
+ function will implicitly set this flag if the \a pixelSize height is 0.
+
\note \a format specifies the requested internal and external format,
meaning the data to be uploaded to the texture will need to be in a
compatible format, while the native texture may (but is not guaranteed to,
in case of OpenGL at least) use this format internally.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
\sa QRhiResource::destroy()
*/
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@@ -7311,22 +7327,32 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
int sampleCount,
QRhiTexture::Flags flags)
{
+ if (pixelSize.height() == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, pixelSize, 1, 0, sampleCount, flags);
}
/*!
- \return a new 2D or 3D texture with the specified \a format, \a width, \a
+ \return a new 1D, 2D or 3D texture with the specified \a format, \a width, \a
height, \a depth, \a sampleCount, and \a flags.
This overload is suitable for 3D textures because it allows specifying \a
depth. A 3D texture must have QRhiTexture::ThreeDimensional set in \a
flags, but using this overload that can be omitted because the flag is set
- implicitly whenever \a depth is greater than 0. For 2D and cube textures \a
+ implicitly whenever \a depth is greater than 0. For 1D, 2D and cube textures \a
depth should be set to 0.
+ A 1D texture must have QRhiTexture::OneDimensional set in \a flags. This overload
+ will implicitly set this flag if both \a height and \a depth are 0.
+
\note 3D textures are only functional when the ThreeDimensionalTextures
feature is reported as supported at run time.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
\overload
*/
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@@ -7337,17 +7363,23 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
if (depth > 0)
flags |= QRhiTexture::ThreeDimensional;
+ if (height == 0 && depth == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, QSize(width, height), depth, 0, sampleCount, flags);
}
/*!
- \return a new 2D texture array with the specified \a format, \a arraySize,
+ \return a new 1D or 2D texture array with the specified \a format, \a arraySize,
\a pixelSize, \a sampleCount, and \a flags.
This function implicitly sets QRhiTexture::TextureArray in \a flags.
+ A 1D texture array must have QRhiTexture::OneDimensional set in \a flags. This
+ function will implicitly set this flag if the \a pixelSize height is 0.
+
\note Do not confuse texture arrays with arrays of textures. A QRhiTexture
- created by this function is usable with 2D array samplers in the shader, for
+ created by this function is usable with 1D or 2D array samplers in the shader, for
example: \c{layout(binding = 1) uniform sampler2DArray texArr;}. Arrays of
textures refers to a list of textures that are exposed to the shader via
QRhiShaderResourceBinding::sampledTextures() and a count > 1, and declared
@@ -7357,6 +7389,11 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
\note This is only functional when the TextureArrays feature is reported as
supported at run time.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
+
\sa newTexture()
*/
QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
@@ -7366,6 +7403,10 @@ QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
QRhiTexture::Flags flags)
{
flags |= QRhiTexture::TextureArray;
+
+ if (pixelSize.height() == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, pixelSize, 1, arraySize, sampleCount, flags);
}
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 5ee0d6e1fd..6514688836 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -760,7 +760,8 @@ public:
ExternalOES = 1 << 9,
ThreeDimensional = 1 << 10,
TextureRectangleGL = 1 << 11,
- TextureArray = 1 << 12
+ TextureArray = 1 << 12,
+ OneDimensional = 1 << 13
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -1672,7 +1673,9 @@ public:
Tessellation,
GeometryShader,
TextureArrayRange,
- NonFillPolygonMode
+ NonFillPolygonMode,
+ OneDimensionalTextures,
+ OneDimensionalTextureMipmaps
};
enum BeginFrameFlag {
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 5fed1dedd5..fb569d0a97 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -543,6 +543,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::NonFillPolygonMode:
return true;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -3057,7 +3061,7 @@ QD3D11Texture::~QD3D11Texture()
void QD3D11Texture::destroy()
{
- if (!tex && !tex3D)
+ if (!tex && !tex3D && !tex1D)
return;
if (srv) {
@@ -3077,10 +3081,13 @@ void QD3D11Texture::destroy()
tex->Release();
if (tex3D)
tex3D->Release();
+ if (tex1D)
+ tex1D->Release();
}
tex = nullptr;
tex3D = nullptr;
+ tex1D = nullptr;
QRHI_RES_RHI(QRhiD3D11);
if (rhiD)
@@ -3123,15 +3130,18 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
{
- if (tex || tex3D)
+ if (tex || tex3D || tex1D)
destroy();
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
QRHI_RES_RHI(QRhiD3D11);
dxgiFormat = toD3DTextureFormat(m_format, m_flags);
@@ -3163,6 +3173,14 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D 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);
@@ -3191,6 +3209,7 @@ bool QD3D11Texture::finishCreate()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
@@ -3198,7 +3217,22 @@ bool QD3D11Texture::finishCreate()
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = mipLevelCount;
} else {
- if (isArray) {
+ if (is1D) {
+ if (isArray) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
+ srvDesc.Texture1DArray.MipLevels = mipLevelCount;
+ if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
+ srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
+ srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
+ } else {
+ srvDesc.Texture1DArray.FirstArraySlice = 0;
+ srvDesc.Texture1DArray.ArraySize = UINT(m_arraySize);
+ }
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
+ srvDesc.Texture1D.MipLevels = mipLevelCount;
+ }
+ } else if (isArray) {
if (sampleDesc.Count > 1) {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
@@ -3252,6 +3286,7 @@ bool QD3D11Texture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
@@ -3273,7 +3308,25 @@ bool QD3D11Texture::create()
bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
QRHI_RES_RHI(QRhiD3D11);
- if (!is3D) {
+ if (is1D) {
+ D3D11_TEXTURE1D_DESC desc = {};
+ desc.Width = UINT(size.width());
+ desc.MipLevels = mipLevelCount;
+ desc.ArraySize = isArray ? UINT(m_arraySize) : 1;
+ desc.Format = dxgiFormat;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ HRESULT hr = rhiD->dev->CreateTexture1D(&desc, nullptr, &tex1D);
+ if (FAILED(hr)) {
+ qWarning("Failed to create 1D texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()),
+ m_objectName.constData());
+ } else if (!is3D) {
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = UINT(size.width());
desc.Height = UINT(size.height());
@@ -3330,6 +3383,8 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
if (m_flags.testFlag(ThreeDimensional))
tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
+ else if (m_flags.testFlags(OneDimensional))
+ tex1D = reinterpret_cast<ID3D11Texture1D *>(src.object);
else
tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
@@ -3649,6 +3704,16 @@ bool QD3D11TextureRenderTarget::create()
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
rtvDesc.Texture2DArray.ArraySize = 1;
+ } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
+ if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
+ rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture1DArray.ArraySize = 1;
+ } else {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
+ rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
+ }
} else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
if (texD->sampleDesc.Count > 1) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 857ab2a2aa..3f4d8e8e31 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -82,11 +82,14 @@ struct QD3D11Texture : public QRhiTexture
{
if (tex)
return tex;
+ else if (tex1D)
+ return tex1D;
return tex3D;
}
ID3D11Texture2D *tex = nullptr;
ID3D11Texture3D *tex3D = nullptr;
+ ID3D11Texture1D *tex1D = nullptr;
bool owns = true;
ID3D11ShaderResourceView *srv = nullptr;
DXGI_FORMAT dxgiFormat;
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 180bb7e360..11b1b965b8 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -443,6 +443,14 @@ QT_BEGIN_NAMESPACE
#define GL_BACK_RIGHT 0x0403
#endif
+#ifndef GL_TEXTURE_1D
+# define GL_TEXTURE_1D 0x0DE0
+#endif
+
+#ifndef GL_TEXTURE_1D_ARRAY
+# define GL_TEXTURE_1D_ARRAY 0x8C18
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -647,8 +655,38 @@ bool QRhiGles2::create(QRhi::Flags flags)
return false;
f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
- glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
- ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
+
+ if (!caps.gles) {
+ glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
+ ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
+
+ glTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexImage1D")));
+
+ glTexStorage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexStorage1D")));
+
+ glTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexSubImage1D")));
+
+ glCopyTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint,
+ GLint, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCopyTexSubImage1D")));
+
+ glCompressedTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCompressedTexImage1D")));
+
+ glCompressedTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D")));
+
+ glFramebufferTexture1D =
+ reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint)>(
+ ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture1D")));
+ }
const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
@@ -848,6 +886,11 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.texture3D = caps.ctxMajor >= 3; // 3.0
if (caps.gles)
+ caps.texture1D = false; // ES
+ else
+ caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
+
+ if (caps.gles)
caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
else
caps.tessellation = caps.ctxMajor >= 4; // 4.0
@@ -1262,6 +1305,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return false;
case QRhi::NonFillPolygonMode:
return !caps.gles;
+ case QRhi::OneDimensionalTextures:
+ return caps.texture1D;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return caps.texture1D;
default:
Q_UNREACHABLE_RETURN(false);
}
@@ -2092,6 +2139,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
const bool isCompressed = isCompressedFormat(texD->m_format);
const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
@@ -2113,7 +2161,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.subImage.faceTarget = effectiveTarget;
cmd.args.subImage.level = level;
cmd.args.subImage.dx = dp.x();
- cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
cmd.args.subImage.dz = is3D || isArray ? layer : 0;
cmd.args.subImage.w = size.width();
cmd.args.subImage.h = size.height();
@@ -2146,7 +2194,8 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.compressedImage.level = level;
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.h =
+ is1D && isArray ? texD->m_arraySize : texD->m_pixelSize.height();
cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
cmd.args.compressedImage.size = byteSize;
cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
@@ -2163,7 +2212,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.compressedSubImage.faceTarget = effectiveTarget;
cmd.args.compressedSubImage.level = level;
cmd.args.compressedSubImage.dx = dp.x();
- cmd.args.compressedSubImage.dy = dp.y();
+ cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
cmd.args.compressedSubImage.w = size.width();
cmd.args.compressedSubImage.h = size.height();
@@ -2179,7 +2228,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.compressedImage.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = size.width();
- cmd.args.compressedImage.h = size.height();
+ cmd.args.compressedImage.h = is1D && isArray ? texD->m_arraySize : size.height();
cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
cmd.args.compressedImage.size = rawData.size();
cmd.args.compressedImage.data = cbD->retainData(rawData);
@@ -2197,7 +2246,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.subImage.faceTarget = effectiveTarget;
cmd.args.subImage.level = level;
cmd.args.subImage.dx = dp.x();
- cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
cmd.args.subImage.dz = is3D || isArray ? layer : 0;
cmd.args.subImage.w = size.width();
cmd.args.subImage.h = size.height();
@@ -2309,6 +2358,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
+ const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
+ && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
cmd.args.copyTex.srcTarget = srcD->target;
cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
@@ -2323,7 +2374,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
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.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
cmd.args.copyTex.w = copySize.width();
@@ -3085,9 +3136,15 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo;
f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY) {
+ if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
+ || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
+ || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
+ } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
+ glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
+ cmd.args.copyTex.srcLevel);
} else {
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
@@ -3098,6 +3155,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
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 if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
+ glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
+ cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
+ cmd.args.copyTex.srcY, cmd.args.copyTex.w);
} else {
f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
@@ -3124,6 +3185,9 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
if (cmd.args.readPixels.slice3D >= 0) {
f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
tex, mipLevel, cmd.args.readPixels.slice3D);
+ } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
+ glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.readPixels.readTarget, tex, mipLevel);
} else {
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.readPixels.readTarget, tex, mipLevel);
@@ -3203,6 +3267,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.subImage.w, cmd.args.subImage.h, 1,
cmd.args.subImage.glformat, cmd.args.subImage.gltype,
cmd.args.subImage.data);
+ } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
+ glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
+ cmd.args.subImage.dx, cmd.args.subImage.w,
+ 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,
@@ -3222,6 +3291,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
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 if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
+ glCompressedTexImage1D(
+ cmd.args.compressedImage.target, cmd.args.compressedImage.level,
+ cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
+ cmd.args.compressedImage.size, cmd.args.compressedImage.data);
} else {
f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
cmd.args.compressedImage.glintformat,
@@ -3237,6 +3311,12 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
cmd.args.compressedSubImage.glintformat,
cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
+ glCompressedTexSubImage1D(
+ cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
+ cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
+ 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,
@@ -5005,13 +5085,15 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
if (!rhiD->ensureContext())
return false;
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
-
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
if (is3D && !rhiD->caps.texture3D) {
qWarning("3D textures are not supported");
@@ -5025,6 +5107,19 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
+ if (is1D && !rhiD->caps.texture1D) {
+ qWarning("1D textures are not supported");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
+ if (is1D && isCube) {
+ qWarning("Texture cannot be both 1D and cube");
+ 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);
@@ -5040,9 +5135,12 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
return false;
}
- target = isCube ? GL_TEXTURE_CUBE_MAP
- : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE
- : (is3D ? GL_TEXTURE_3D : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
+ target = isCube ? GL_TEXTURE_CUBE_MAP
+ : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE
+ : (is3D ? GL_TEXTURE_3D
+ : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
+ : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
+
if (m_flags.testFlag(ExternalOES))
target = GL_TEXTURE_EXTERNAL_OES;
else if (m_flags.testFlag(TextureRectangleGL))
@@ -5092,10 +5190,22 @@ bool QGles2Texture::create()
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
if (!isCompressed) {
rhiD->f->glBindTexture(target, texture);
if (!m_flags.testFlag(UsedWithLoadStore)) {
- if (is3D || isArray) {
+ if (is1D) {
+ for (int level = 0; level < mipLevelCount; ++level) {
+ const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
+ if (isArray)
+ rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
+ m_arraySize, 0, glformat, gltype, nullptr);
+ else
+ rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
+ glformat, gltype, nullptr);
+ }
+ } else if (is3D || isArray) {
const int layerCount = is3D ? m_depth : m_arraySize;
if (hasMipMaps) {
for (int level = 0; level != mipLevelCount; ++level) {
@@ -5125,10 +5235,13 @@ bool QGles2Texture::create()
// Must be specified with immutable storage functions otherwise
// bindImageTexture may fail. Also, the internal format must be a
// sized format here.
- if (is3D || isArray)
+ if (is1D && !isArray)
+ rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
+ else if (!is1D && (is3D || isArray))
rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), is3D ? m_depth : m_arraySize);
else
- rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
+ rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
+ is1D ? m_arraySize : size.height());
}
specified = true;
} else {
@@ -5342,6 +5455,10 @@ bool QGles2TextureRenderTarget::create()
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
colorAtt.level(), colorAtt.layer());
+ } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
+ rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
+ texD->target + uint(colorAtt.layer()), texD->texture,
+ colorAtt.level());
} 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()),
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 41854f931d..f27320cab5 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -896,6 +896,20 @@ public:
mutable bool needsMakeCurrentDueToSwap = false;
QOpenGLExtensions *f = nullptr;
void (QOPENGLF_APIENTRYP glPolygonMode) (GLenum, GLenum) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexImage1D)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum,
+ const void *) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexStorage1D)(GLenum, GLint, GLenum, GLsizei) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexSubImage1D)(GLenum, GLint, GLint, GLsizei, GLenum, GLenum,
+ const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glCopyTexSubImage1D)(GLenum, GLint, GLint, GLint, GLint,
+ GLsizei) = nullptr;
+ void(QOPENGLF_APIENTRYP glCompressedTexImage1D)(GLenum, GLint, GLenum, GLsizei, GLint, GLsizei,
+ const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glCompressedTexSubImage1D)(GLenum, GLint, GLint, GLsizei, GLenum,
+ GLsizei, const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint,
+ GLint) = nullptr;
+
uint vao = 0;
struct Caps {
Caps()
@@ -945,7 +959,8 @@ public:
programBinary(false),
texture3D(false),
tessellation(false),
- geometryShader(false)
+ geometryShader(false),
+ texture1D(false)
{ }
int ctxMajor;
int ctxMinor;
@@ -996,6 +1011,7 @@ public:
uint texture3D : 1;
uint tessellation : 1;
uint geometryShader : 1;
+ uint texture1D : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QSet<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 61b12ad46d..45bb1296b0 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -772,6 +772,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return false;
case QRhi::NonFillPolygonMode:
return true;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return false;
default:
Q_UNREACHABLE();
return false;
@@ -3414,11 +3418,14 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
if (d->tex)
destroy();
- 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 isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
QRHI_RES_RHI(QRhiMetal);
d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
@@ -3446,6 +3453,14 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
+ if (is1D && isCube) {
+ qWarning("Texture cannot be both 1D and cube");
+ 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);
@@ -3478,10 +3493,13 @@ bool QMetalTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
if (isCube) {
desc.textureType = MTLTextureTypeCube;
} else if (is3D) {
desc.textureType = MTLTextureType3D;
+ } else if (is1D) {
+ desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
} else if (isArray) {
#ifdef Q_OS_IOS
if (samples > 1) {
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 88afb004f7..ec1b5aa64a 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -661,7 +661,9 @@ bool QNullTexture::create()
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
- QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool is1D = m_flags.testFlags(OneDimensional);
+ QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
m_depth = qMax(1, m_depth);
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
m_arraySize = qMax(0, m_arraySize);
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 9ed7f89b2a..41834a1c43 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -2939,6 +2939,7 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qsizetype imageSizeBytes = 0;
const void *src = nullptr;
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
VkBufferImageCopy copyInfo = {};
copyInfo.bufferOffset = *curOfs;
@@ -2949,6 +2950,8 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
copyInfo.imageExtent.depth = 1;
if (is3D)
copyInfo.imageOffset.z = uint32_t(layer);
+ if (is1D)
+ copyInfo.imageOffset.y = uint32_t(layer);
const QByteArray rawData = subresDesc.data();
const QPoint dp = subresDesc.destinationTopLeft();
@@ -4257,6 +4260,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::NonFillPolygonMode:
return caps.nonFillPolygonMode;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return true;
default:
Q_UNREACHABLE_RETURN(false);
}
@@ -5874,12 +5881,15 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
return false;
}
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
+
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
const int maxLevels = QRhi::MAX_MIP_LEVELS;
if (mipLevelCount > maxLevels) {
@@ -5909,6 +5919,14 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D 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);
@@ -5942,13 +5960,16 @@ bool QVkTexture::finishCreate()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -5987,6 +6008,7 @@ bool QVkTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
@@ -6009,7 +6031,7 @@ bool QVkTexture::create()
#endif
}
- imageInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
+ imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
imageInfo.format = vkformat;
imageInfo.extent.width = uint32_t(size.width());
imageInfo.extent.height = uint32_t(size.height());
@@ -6097,13 +6119,16 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -6487,7 +6512,9 @@ bool QVkTextureRenderTarget::create()
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texD->image;
- viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.viewType = texD->flags().testFlag(QRhiTexture::OneDimensional)
+ ? VK_IMAGE_VIEW_TYPE_1D
+ : VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = texD->vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
index f92b26a300..cf67708bca 100644
--- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
+++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
@@ -128,6 +128,8 @@ private slots:
void renderbufferImportOpenGL();
void threeDimTexture_data();
void threeDimTexture();
+ void oneDimTexture_data();
+ void oneDimTexture();
void leakedResourceDestroy_data();
void leakedResourceDestroy();
@@ -4677,6 +4679,514 @@ void tst_QRhi::threeDimTexture()
QVERIFY(imageRGBAEquals(result, referenceImage));
}
}
+void tst_QRhi::oneDimTexture_data()
+{
+ rhiTestData();
+}
+
+void tst_QRhi::oneDimTexture()
+{
+ QFETCH(QRhi::Implementation, impl);
+ QFETCH(QRhiInitParams *, initParams);
+
+ QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams));
+ if (!rhi)
+ QSKIP("QRhi could not be created, skipping testing 1D textures");
+
+ if (!rhi->isFeatureSupported(QRhi::OneDimensionalTextures))
+ QSKIP("Skipping testing 1D textures because they are reported as unsupported");
+
+ const int WIDTH = 512;
+ const int LAYERS = 128;
+
+ {
+ QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, WIDTH, 0, 0));
+ QVERIFY(texture->create());
+
+ QVERIFY(texture->flags().testFlag(QRhiTexture::Flag::OneDimensional));
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(255, 0, 0));
+
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), upload);
+
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ }
+
+ {
+ QScopedPointer<QRhiTexture> texture(
+ rhi->newTextureArray(QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0)));
+ QVERIFY(texture->create());
+
+ QVERIFY(texture->flags().testFlag(QRhiTexture::Flag::OneDimensional));
+ QVERIFY(texture->flags().testFlag(QRhiTexture::Flag::TextureArray));
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ for (int i = 0; i < LAYERS; ++i) {
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(i * 2, 0, 0));
+ QRhiTextureUploadEntry layerUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), layerUpload);
+ }
+
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ }
+
+ // Copy from 2D texture to 1D texture
+ {
+ const int WIDTH = 256;
+ const int HEIGHT = 256;
+
+ QScopedPointer<QRhiTexture> srcTexture(rhi->newTexture(
+ QRhiTexture::RGBA8, WIDTH, HEIGHT, 0, 1, QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(srcTexture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, HEIGHT, QImage::Format_RGBA8888);
+ for (int x = 0; x < WIDTH; ++x) {
+ for (int y = 0; y < HEIGHT; ++y) {
+ img.setPixelColor(x, y, QColor::fromRgb(x, y, 0));
+ }
+ }
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(srcTexture.data(), upload);
+
+ QScopedPointer<QRhiTexture> dstTexture(rhi->newTexture(
+ QRhiTexture::RGBA8, WIDTH, 0, 0, 1, QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(dstTexture->create());
+
+ QRhiTextureCopyDescription copy;
+ copy.setPixelSize(QSize(WIDTH / 2, 1));
+ copy.setDestinationTopLeft(QPoint(WIDTH / 2, 0));
+ copy.setSourceTopLeft(QPoint(33, 67));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ copy.setDestinationTopLeft(QPoint(0, 0));
+ copy.setSourceTopLeft(QPoint(99, 12));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+
+ QRhiReadbackDescription readbackDescription(dstTexture.data());
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ for (int i = 0; i < WIDTH / 2; ++i) {
+ referenceImage.setPixelColor(i, 0, img.pixelColor(99 + i, 12));
+ referenceImage.setPixelColor(WIDTH / 2 + i, 0, img.pixelColor(33 + i, 67));
+ }
+
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+
+ // Copy from 2D texture to 1D texture array
+ {
+ const int WIDTH = 256;
+ const int HEIGHT = 256;
+ const int LAYERS = 64;
+
+ QScopedPointer<QRhiTexture> srcTexture(rhi->newTexture(
+ QRhiTexture::RGBA8, WIDTH, HEIGHT, 0, 1, QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(srcTexture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, HEIGHT, QImage::Format_RGBA8888);
+ for (int x = 0; x < WIDTH; ++x) {
+ for (int y = 0; y < HEIGHT; ++y) {
+ img.setPixelColor(x, y, QColor::fromRgb(x, y, 0));
+ }
+ }
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(srcTexture.data(), upload);
+
+ QScopedPointer<QRhiTexture> dstTexture(
+ rhi->newTextureArray(QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0), 1,
+ QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(dstTexture->create());
+
+ QRhiTextureCopyDescription copy;
+ copy.setPixelSize(QSize(WIDTH / 2, 1));
+ copy.setDestinationTopLeft(QPoint(WIDTH / 2, 0));
+ copy.setSourceTopLeft(QPoint(33, 67));
+ copy.setDestinationLayer(12);
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ copy.setDestinationTopLeft(QPoint(0, 0));
+ copy.setSourceTopLeft(QPoint(99, 12));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+
+ QRhiReadbackDescription readbackDescription(dstTexture.data());
+ readbackDescription.setLayer(12);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ for (int i = 0; i < WIDTH / 2; ++i) {
+ referenceImage.setPixelColor(i, 0, img.pixelColor(99 + i, 12));
+ referenceImage.setPixelColor(WIDTH / 2 + i, 0, img.pixelColor(33 + i, 67));
+ }
+
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+
+ // Copy from 1D texture array to 1D texture
+ {
+ const int WIDTH = 256;
+ const int LAYERS = 256;
+
+ QScopedPointer<QRhiTexture> srcTexture(
+ rhi->newTextureArray(QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0), 1,
+ QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(srcTexture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ for (int y = 0; y < LAYERS; ++y) {
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ for (int x = 0; x < WIDTH; ++x) {
+ img.setPixelColor(x, 0, QColor::fromRgb(x, y, 0));
+ }
+ QRhiTextureUploadEntry upload(y, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(srcTexture.data(), upload);
+ }
+
+ QScopedPointer<QRhiTexture> dstTexture(rhi->newTexture(
+ QRhiTexture::RGBA8, WIDTH, 0, 0, 1, QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(dstTexture->create());
+
+ QRhiTextureCopyDescription copy;
+ copy.setPixelSize(QSize(WIDTH / 2, 1));
+ copy.setDestinationTopLeft(QPoint(WIDTH / 2, 0));
+ copy.setSourceLayer(67);
+ copy.setSourceTopLeft(QPoint(33, 0));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ copy.setDestinationTopLeft(QPoint(0, 0));
+ copy.setSourceLayer(12);
+ copy.setSourceTopLeft(QPoint(99, 0));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+
+ QRhiReadbackDescription readbackDescription(dstTexture.data());
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ for (int i = 0; i < WIDTH / 2; ++i) {
+ referenceImage.setPixelColor(i, 0, QColor::fromRgb(99 + i, 12, 0));
+ referenceImage.setPixelColor(WIDTH / 2 + i, 0, QColor::fromRgb(33 + i, 67, 0));
+ }
+
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+
+ // Copy from 1D texture to 1D texture array
+ {
+ const int WIDTH = 256;
+ const int LAYERS = 256;
+
+ QScopedPointer<QRhiTexture> srcTexture(rhi->newTexture(
+ QRhiTexture::RGBA8, WIDTH, 0, 0, 1, QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(srcTexture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ for (int x = 0; x < WIDTH; ++x) {
+ img.setPixelColor(x, 0, QColor::fromRgb(x, 0, 0));
+ }
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(srcTexture.data(), upload);
+
+ QScopedPointer<QRhiTexture> dstTexture(
+ rhi->newTextureArray(QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0), 1,
+ QRhiTexture::Flag::UsedAsTransferSource));
+ QVERIFY(dstTexture->create());
+
+ QRhiTextureCopyDescription copy;
+ copy.setPixelSize(QSize(WIDTH / 2, 1));
+ copy.setDestinationTopLeft(QPoint(WIDTH / 2, 0));
+ copy.setDestinationLayer(67);
+ copy.setSourceTopLeft(QPoint(33, 0));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ copy.setDestinationTopLeft(QPoint(0, 0));
+ copy.setSourceTopLeft(QPoint(99, 0));
+ batch->copyTexture(dstTexture.data(), srcTexture.data(), copy);
+
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+
+ QRhiReadbackDescription readbackDescription(dstTexture.data());
+ readbackDescription.setLayer(67);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ for (int i = 0; i < WIDTH / 2; ++i) {
+ referenceImage.setPixelColor(i, 0, QColor::fromRgb(99 + i, 0, 0));
+ referenceImage.setPixelColor(WIDTH / 2 + i, 0, QColor::fromRgb(33 + i, 0, 0));
+ }
+
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+
+ // mipmaps and 1D render target
+ if (!rhi->isFeatureSupported(QRhi::OneDimensionalTextureMipmaps))
+ QSKIP("Skipping testing 1D texture mipmaps and 1D render target because they are reported "
+ "as unsupported");
+
+ {
+ QScopedPointer<QRhiTexture> texture(
+ rhi->newTexture(QRhiTexture::RGBA8, WIDTH, 0, 0, 1,
+ QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
+ QVERIFY(texture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(128, 0, 0));
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), upload);
+
+ batch->generateMips(texture.data());
+
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+
+ // read back level 1 (256x1, #800000ff)
+ batch = rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+ QRhiReadbackDescription readbackDescription(texture.data());
+ readbackDescription.setLevel(1);
+ readbackDescription.setLayer(0);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH / 2, 1, result.format());
+ referenceImage.fill(QColor::fromRgb(128, 0, 0));
+
+ QVERIFY(imageRGBAEquals(result, referenceImage, 2));
+ }
+
+ {
+ QScopedPointer<QRhiTexture> texture(
+ rhi->newTextureArray(QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0), 1,
+ QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
+ QVERIFY(texture->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ for (int i = 0; i < LAYERS; ++i) {
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(i * 2, 0, 0));
+ QRhiTextureUploadEntry sliceUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), sliceUpload);
+ }
+
+ batch->generateMips(texture.data());
+
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+
+ // read back slice 63 of level 1 (256x1, #7E0000FF)
+ batch = rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+ QRhiReadbackDescription readbackDescription(texture.data());
+ readbackDescription.setLevel(1);
+ readbackDescription.setLayer(63);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH / 2, 1, result.format());
+ referenceImage.fill(QColor::fromRgb(126, 0, 0));
+
+ // Now restrict the test a bit. The Null QRhi backend has broken support for
+ // mipmap generation of 1D texture arrays.
+ if (impl != QRhi::Null)
+ QVERIFY(imageRGBAEquals(result, referenceImage, 2));
+ }
+
+ // 1D texture render target
+ // NB with Vulkan we require Vulkan 1.1 for this to work.
+ // Metal does not allow 1D texture render targets
+ {
+ QScopedPointer<QRhiTexture> texture(
+ rhi->newTexture(QRhiTexture::RGBA8, WIDTH, 0, 0, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ QVERIFY(texture->create());
+
+ QRhiColorAttachment att(texture.data());
+ QRhiTextureRenderTargetDescription rtDesc(att);
+ QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ QScopedPointer<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.data());
+ QVERIFY(rt->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(128, 0, 0));
+ QRhiTextureUploadEntry upload(0, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), upload);
+
+ QRhiCommandBuffer *cb = nullptr;
+ QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
+ QVERIFY(cb);
+ cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, batch);
+ // texture is now blue
+ cb->endPass();
+ rhi->endOffscreenFrame();
+
+ // read back texture (blue)
+ batch = rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+ QRhiReadbackDescription readbackDescription(texture.data());
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ referenceImage.fill(QColor::fromRgbF(0.0f, 0.0f, 1.0f));
+ // the Null backend does not render so skip the verification for that
+ if (impl != QRhi::Null)
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+
+ // 1D array texture render target (one slice)
+ // NB with Vulkan we require Vulkan 1.1 for this to work.
+ // Metal does not allow 1D texture render targets
+ {
+ const int SLICE = 23;
+ QScopedPointer<QRhiTexture> texture(rhi->newTextureArray(
+ QRhiTexture::RGBA8, LAYERS, QSize(WIDTH, 0), 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ QVERIFY(texture->create());
+
+ QRhiColorAttachment att(texture.data());
+ att.setLayer(SLICE);
+ QRhiTextureRenderTargetDescription rtDesc(att);
+ QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ QScopedPointer<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.data());
+ QVERIFY(rt->create());
+
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QVERIFY(batch);
+
+ for (int i = 0; i < LAYERS; ++i) {
+ QImage img(WIDTH, 1, QImage::Format_RGBA8888);
+ img.fill(QColor::fromRgb(i * 2, 0, 0));
+ QRhiTextureUploadEntry sliceUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
+ batch->uploadTexture(texture.data(), sliceUpload);
+ }
+
+ QRhiCommandBuffer *cb = nullptr;
+ QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
+ QVERIFY(cb);
+ cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, batch);
+ // slice 23 is now blue
+ cb->endPass();
+ rhi->endOffscreenFrame();
+
+ // read back slice 23 (blue)
+ batch = rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+ QRhiReadbackDescription readbackDescription(texture.data());
+ readbackDescription.setLayer(23);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ QImage referenceImage(WIDTH, 1, result.format());
+ referenceImage.fill(QColor::fromRgbF(0.0f, 0.0f, 1.0f));
+ // the Null backend does not render so skip the verification for that
+ if (impl != QRhi::Null)
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+
+ // read back slice 0 (black)
+ batch = rhi->nextResourceUpdateBatch();
+ result = QImage();
+ readbackDescription.setLayer(0);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ referenceImage.fill(QColor::fromRgbF(0.0f, 0.0f, 0.0f));
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+
+ // read back slice 127 (almost red)
+ batch = rhi->nextResourceUpdateBatch();
+ result = QImage();
+ readbackDescription.setLayer(127);
+ batch->readBackTexture(readbackDescription, &readResult);
+ QVERIFY(submitResourceUpdates(rhi.data(), batch));
+ QVERIFY(!result.isNull());
+ referenceImage.fill(QColor::fromRgb(254, 0, 0));
+ QVERIFY(imageRGBAEquals(result, referenceImage));
+ }
+}
void tst_QRhi::leakedResourceDestroy_data()
{
diff --git a/tests/manual/rhi/CMakeLists.txt b/tests/manual/rhi/CMakeLists.txt
index 1ad52f548c..189183ab2e 100644
--- a/tests/manual/rhi/CMakeLists.txt
+++ b/tests/manual/rhi/CMakeLists.txt
@@ -31,6 +31,7 @@ add_subdirectory(tessellation)
add_subdirectory(geometryshader)
add_subdirectory(stenciloutline)
add_subdirectory(stereo)
+add_subdirectory(tex1d)
if(QT_FEATURE_widgets)
add_subdirectory(rhiwidget)
endif()
diff --git a/tests/manual/rhi/tex1d/CMakeLists.txt b/tests/manual/rhi/tex1d/CMakeLists.txt
new file mode 100644
index 0000000000..b27627a13a
--- /dev/null
+++ b/tests/manual/rhi/tex1d/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(tex1d
+ GUI
+ SOURCES
+ tex1d.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+set(tex1d_resource_files
+ "texture1d.vert.qsb"
+ "texture1d.frag.qsb"
+)
+
+qt_internal_add_resource(tex1d "tex1d"
+ PREFIX
+ "/"
+ FILES
+ ${tex1d_resource_files}
+)
diff --git a/tests/manual/rhi/tex1d/buildshaders.bat b/tests/manual/rhi/tex1d/buildshaders.bat
new file mode 100644
index 0000000000..1ea24939a3
--- /dev/null
+++ b/tests/manual/rhi/tex1d/buildshaders.bat
@@ -0,0 +1,2 @@
+qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture1d.vert -o texture1d.vert.qsb
+qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture1d.frag -o texture1d.frag.qsb
diff --git a/tests/manual/rhi/tex1d/tex1d.cpp b/tests/manual/rhi/tex1d/tex1d.cpp
new file mode 100644
index 0000000000..66e0186b0f
--- /dev/null
+++ b/tests/manual/rhi/tex1d/tex1d.cpp
@@ -0,0 +1,625 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "../shared/examplefw.h"
+#include <QImage>
+#include <QPainter>
+#include <functional>
+
+static float quadVertexData[] = { // Y up, CCW
+ -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 0.0f
+};
+
+static quint16 quadIndexData[] = { 0, 1, 2, 0, 2, 3 };
+
+struct
+{
+ QList<QRhiResource *> releasePool;
+ QRhiResourceUpdateBatch *initialUpdates = nullptr;
+
+ QRhiBuffer *vertexBuffer = nullptr;
+ QRhiBuffer *indexBuffer = nullptr;
+ QRhiBuffer *uniformBuffer = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ int index = 0;
+} d;
+
+void readBackCompleted(QRhiReadbackResult *result, const QByteArray &expected)
+{
+ if (result->data != expected) {
+ qFatal("texture readback data did not match expected values");
+ }
+
+ delete result;
+}
+
+void Window::customInit()
+{
+ if (!m_r->isFeatureSupported(QRhi::Feature::OneDimensionalTextures))
+ qFatal("1D textures are not supported");
+
+ const bool mipmaps = m_r->isFeatureSupported(QRhi::Feature::OneDimensionalTextureMipmaps);
+
+ d.initialUpdates = m_r->nextResourceUpdateBatch();
+
+ QRhiTexture *texture = nullptr;
+ QRhiReadbackResult *readbackResult = nullptr;
+ QRhiReadbackDescription readbackDescription;
+ QByteArray data;
+
+ QList<QRhiShaderResourceBinding> shaderResouceBindings;
+
+ //
+ // Create vertex buffer
+ //
+ d.vertexBuffer =
+ m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData));
+ d.vertexBuffer->create();
+ d.releasePool << d.vertexBuffer;
+
+ d.initialUpdates->uploadStaticBuffer(d.vertexBuffer, 0, sizeof(quadVertexData), quadVertexData);
+
+ //
+ // Create index buffer
+ //
+ d.indexBuffer =
+ m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
+ d.indexBuffer->create();
+ d.releasePool << d.indexBuffer;
+
+ d.initialUpdates->uploadStaticBuffer(d.indexBuffer, quadIndexData);
+
+ //
+ // Create uniform buffer
+ //
+ d.uniformBuffer =
+ m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, m_r->ubufAligned(4));
+ d.uniformBuffer->create();
+ d.releasePool << d.uniformBuffer;
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::uniformBuffer(
+ 0, QRhiShaderResourceBinding::StageFlag::FragmentStage, d.uniformBuffer, 0, 4));
+
+ //
+ // Create samplers
+ //
+ QRhiSampler *samplerNearest = m_r->newSampler(
+ QRhiSampler::Filter::Nearest, QRhiSampler::Filter::Nearest,
+ QRhiSampler::Filter::Nearest, QRhiSampler::AddressMode::ClampToEdge,
+ QRhiSampler::AddressMode::ClampToEdge, QRhiSampler::AddressMode::ClampToEdge);
+ d.releasePool << samplerNearest;
+ samplerNearest->create();
+
+ QRhiSampler *samplerNone = m_r->newSampler(
+ QRhiSampler::Filter::Nearest, QRhiSampler::Filter::Nearest, QRhiSampler::Filter::None,
+ QRhiSampler::AddressMode::ClampToEdge, QRhiSampler::AddressMode::ClampToEdge,
+ QRhiSampler::AddressMode::ClampToEdge);
+ d.releasePool << samplerNone;
+ samplerNone->create();
+
+ //
+ // 1D texture with generated mipmaps. Contains grey colormap
+ //
+ texture = m_r->newTexture(QRhiTexture::Format::RGBA8, 8, 0, 0, 1,
+ QRhiTexture::Flag::OneDimensional
+ | QRhiTexture::Flag::UsedAsTransferSource
+ | (mipmaps ? (QRhiTexture::Flag::MipMapped
+ | QRhiTexture::Flag::UsedWithGenerateMips)
+ : QRhiTexture::Flag(0)));
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 1, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(i) * 32);
+ data.append(char(i) * 32);
+ data.append(char(i) * 32);
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(0, 0, QRhiTextureSubresourceUploadDescription(data)));
+ if (mipmaps)
+ d.initialUpdates->generateMips(texture);
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ //
+ // 1D texture array with generated mipmaps. layer 0 contains red colormap, layer 1 contains
+ // green colormap
+ //
+ texture =
+ m_r->newTextureArray(QRhiTexture::RGBA8, 2, QSize(8, 0), 1,
+ QRhiTexture::Flag::TextureArray | QRhiTexture::Flag::OneDimensional
+ | QRhiTexture::Flag::UsedAsTransferSource
+ | (mipmaps ? (QRhiTexture::Flag::MipMapped
+ | QRhiTexture::Flag::UsedWithGenerateMips)
+ : QRhiTexture::Flag(0)));
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 2, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ data.clear();
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(i * 32));
+ data.append(char(0));
+ data.append(char(0));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(0, 0, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(0));
+ data.append(char(i * 32));
+ data.append(char(0));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(1, 0, QRhiTextureSubresourceUploadDescription(data)));
+ if (mipmaps)
+ d.initialUpdates->generateMips(texture);
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ //
+ // 1D texture with uploaded mipmaps. Contains yellow colormap
+ //
+ texture = m_r->newTexture(
+ QRhiTexture::Format::RGBA8, 8, 0, 0, 1,
+ QRhiTexture::Flag::OneDimensional | QRhiTexture::Flag::UsedAsTransferSource
+ | (mipmaps ? QRhiTexture::Flag::MipMapped : QRhiTexture::Flag(0)));
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 3, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ data.clear();
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(i * 32));
+ data.append(char(i * 32));
+ data.append(char(0));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(0, 0, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ QRhiTexture *textureSource = texture;
+ QByteArray textureCopyData = data.mid(data.size() / 2).append(data.mid(0, data.size() / 2));
+
+ if (mipmaps) {
+
+ data.clear();
+ for (int i = 0; i < 4; ++i) {
+ data.append(char(i * 64));
+ data.append(char(i * 64));
+ data.append(char(0));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 1, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(1);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ for (int i = 0; i < 2; ++i) {
+ data.append(char(i * 128));
+ data.append(char(i * 128));
+ data.append(char(0));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 2, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(2);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ data.append(char(128));
+ data.append(char(128));
+ data.append(char(0));
+ data.append(char(255));
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 3, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(3);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+ }
+
+ //
+ // 1D texture array with uploaded mipmaps. Layer 0 contains blue colormap, layer 1 contains
+ // magenta colormap
+ //
+ texture = m_r->newTextureArray(
+ QRhiTexture::Format::RGBA8, 2, QSize(8, 0), 1,
+ QRhiTexture::Flag::TextureArray | QRhiTexture::Flag::OneDimensional
+ | QRhiTexture::Flag::UsedAsTransferSource
+ | (mipmaps ? QRhiTexture::Flag::MipMapped : QRhiTexture::Flag(0)));
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 4, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ data.clear();
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(0));
+ data.append(char(0));
+ data.append(char(i * 32));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(0, 0, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ QRhiTexture *textureArraySource = texture;
+ QList<QByteArray> textureArrayCopyData;
+ textureArrayCopyData.append(data.mid(data.size() / 2).append(data.mid(0, data.size() / 2)));
+
+ if (mipmaps) {
+
+ data.clear();
+ for (int i = 0; i < 4; ++i) {
+ data.append(char(0));
+ data.append(char(0));
+ data.append(char(i * 64));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 1, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(1);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ for (int i = 0; i < 2; ++i) {
+ data.append(char(0));
+ data.append(char(0));
+ data.append(char(i * 128));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 2, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(2);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ data.append(char(0));
+ data.append(char(0));
+ data.append(char(128));
+ data.append(char(255));
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(0, 3, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(3);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+ }
+
+ data.clear();
+ for (int i = 0; i < 8; ++i) {
+ data.append(char(i * 32));
+ data.append(char(0));
+ data.append(char(i * 32));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture, QRhiTextureUploadEntry(1, 0, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ textureArrayCopyData.append(data.mid(data.size() / 2).append(data.mid(0, data.size() / 2)));
+
+ if (mipmaps) {
+
+ data.clear();
+ for (int i = 0; i < 4; ++i) {
+ data.append(char(i * 64));
+ data.append(char(0));
+ data.append(char(i * 64));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(1, 1, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(1);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ for (int i = 0; i < 2; ++i) {
+ data.append(char(i * 128));
+ data.append(char(0));
+ data.append(char(i * 128));
+ data.append(char(255));
+ }
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(1, 2, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(2);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ data.clear();
+ data.append(char(128));
+ data.append(char(0));
+ data.append(char(128));
+ data.append(char(255));
+ d.initialUpdates->uploadTexture(
+ texture,
+ QRhiTextureUploadEntry(1, 3, QRhiTextureSubresourceUploadDescription(data)));
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, data);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(3);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+ }
+
+ //
+ // 1D Texture loaded from image - contains rainbow colormap
+ //
+ QImage image(256, 1, QImage::Format_RGBA8888);
+ for (int i = 0; i < image.width(); ++i) {
+ float x = float(i) / float(image.width() - 1);
+ if (x < 2.0f / 5.0f) {
+ image.setPixelColor(i, 0, QColor::fromRgbF(1.0f, 5.0f / 2.0f * x, 0.0f));
+ } else if (x < 3.0f / 5.0f) {
+ image.setPixelColor(i, 0, QColor::fromRgbF(-5.0f * x + 3.0f, 1.0f, 0.0f));
+ } else if (x < 4.0f / 5.0f) {
+ image.setPixelColor(i, 0, QColor::fromRgbF(0.0f, -5.0f * x + 4.0f, 5.0f * x - 3.0f));
+ } else {
+ image.setPixelColor(i, 0, QColor::fromRgbF(10.0f / 3.0f * x - 8.0f / 3.0f, 0.0f, 1.0f));
+ }
+ }
+
+ texture = m_r->newTexture(QRhiTexture::Format::RGBA8, image.width(), 0, 0, 1,
+ QRhiTexture::Flag::OneDimensional);
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 5, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ d.initialUpdates->uploadTexture(texture, image);
+
+ //
+ // 1D Texture copied
+ //
+ texture = m_r->newTexture(QRhiTexture::Format::RGBA8, 8, 0, 0, 1,
+ QRhiTexture::Flag::OneDimensional
+ | QRhiTexture::Flag::UsedAsTransferSource);
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 6, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ QRhiTextureCopyDescription copyDescription;
+ copyDescription.setSourceLayer(0);
+ copyDescription.setSourceLevel(0);
+ copyDescription.setSourceTopLeft(QPoint(4, 0));
+ copyDescription.setDestinationLayer(0);
+ copyDescription.setDestinationLevel(0);
+ copyDescription.setDestinationTopLeft(QPoint(0, 0));
+ copyDescription.setPixelSize(QSize(4, 1));
+
+ d.initialUpdates->copyTexture(texture, textureSource, copyDescription);
+
+ copyDescription.setSourceTopLeft(QPoint(0, 0));
+ copyDescription.setDestinationTopLeft(QPoint(4, 0));
+
+ d.initialUpdates->copyTexture(texture, textureSource, copyDescription);
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed = std::bind(readBackCompleted, readbackResult, textureCopyData);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ //
+ // 1D Texture array copied
+ //
+ texture = m_r->newTextureArray(QRhiTexture::Format::RGBA8, 2, QSize(8, 0), 1,
+ QRhiTexture::Flag::OneDimensional
+ | QRhiTexture::Flag::UsedAsTransferSource);
+ d.releasePool << texture;
+ texture->create();
+
+ shaderResouceBindings.append(QRhiShaderResourceBinding::sampledTexture(
+ 7, QRhiShaderResourceBinding::FragmentStage, texture,
+ texture->flags().testFlag(QRhiTexture::Flag::MipMapped) ? samplerNearest
+ : samplerNone));
+
+ copyDescription.setSourceLayer(1);
+ copyDescription.setSourceLevel(0);
+ copyDescription.setSourceTopLeft(QPoint(4, 0));
+ copyDescription.setDestinationLayer(0);
+ copyDescription.setDestinationLevel(0);
+ copyDescription.setDestinationTopLeft(QPoint(0, 0));
+ copyDescription.setPixelSize(QSize(4, 1));
+ d.initialUpdates->copyTexture(texture, textureArraySource, copyDescription);
+
+ copyDescription.setSourceTopLeft(QPoint(0, 0));
+ copyDescription.setDestinationTopLeft(QPoint(4, 0));
+ d.initialUpdates->copyTexture(texture, textureArraySource, copyDescription);
+
+ copyDescription.setSourceLayer(0);
+ copyDescription.setDestinationLayer(1);
+ d.initialUpdates->copyTexture(texture, textureArraySource, copyDescription);
+
+ copyDescription.setSourceTopLeft(QPoint(4, 0));
+ copyDescription.setDestinationTopLeft(QPoint(0, 0));
+ d.initialUpdates->copyTexture(texture, textureArraySource, copyDescription);
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed =
+ std::bind(readBackCompleted, readbackResult, textureArrayCopyData[0]);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(1);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ readbackResult = new QRhiReadbackResult;
+ readbackResult->completed =
+ std::bind(readBackCompleted, readbackResult, textureArrayCopyData[1]);
+ readbackDescription.setTexture(texture);
+ readbackDescription.setLayer(0);
+ readbackDescription.setLevel(0);
+ d.initialUpdates->readBackTexture(readbackDescription, readbackResult);
+
+ //
+ // Shader resource bindings
+ //
+ d.srb = m_r->newShaderResourceBindings();
+ d.releasePool << d.srb;
+ d.srb->setBindings(shaderResouceBindings.cbegin(), shaderResouceBindings.cend());
+ d.srb->create();
+
+ //
+ // Pipeline
+ //
+ d.ps = m_r->newGraphicsPipeline();
+ d.releasePool << d.ps;
+ d.ps->setShaderStages(
+ { { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture1d.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture1d.frag.qsb")) } });
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 4 * sizeof(float) } });
+ inputLayout.setAttributes(
+ { { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) } });
+
+ d.ps->setVertexInputLayout(inputLayout);
+ d.ps->setShaderResourceBindings(d.srb);
+ d.ps->setRenderPassDescriptor(m_rp);
+ d.ps->create();
+}
+
+void Window::customRelease()
+{
+ qDeleteAll(d.releasePool);
+ d.releasePool.clear();
+}
+
+void Window::customRender()
+{
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
+
+ if (d.initialUpdates) {
+ u->merge(d.initialUpdates);
+ d.initialUpdates->release();
+ d.initialUpdates = nullptr;
+ }
+
+ u->updateDynamicBuffer(d.uniformBuffer, 0, 4, &d.index);
+ d.index++;
+
+ cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(d.ps);
+ cb->setShaderResources(d.srb);
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+ cb->setViewport(
+ { 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ QRhiCommandBuffer::VertexInput vbufBinding(d.vertexBuffer, 0);
+ cb->setVertexInput(0, 1, &vbufBinding, d.indexBuffer, 0, QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(6);
+ cb->endPass();
+}
diff --git a/tests/manual/rhi/tex1d/texture1d.frag b/tests/manual/rhi/tex1d/texture1d.frag
new file mode 100644
index 0000000000..e5e0f3a6ca
--- /dev/null
+++ b/tests/manual/rhi/tex1d/texture1d.frag
@@ -0,0 +1,51 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ int idx;
+} ubuf;
+
+layout(binding = 1) uniform sampler1D tex;
+layout(binding = 2) uniform sampler1DArray texArray;
+layout(binding = 3) uniform sampler1D texA;
+layout(binding = 4) uniform sampler1DArray texArrayA;
+layout(binding = 5) uniform sampler1D texB;
+layout(binding = 6) uniform sampler1D texC;
+layout(binding = 7) uniform sampler1DArray texArrayB;
+
+void main()
+{
+ vec4 c = vec4(v_texcoord, 0, 1);
+
+ vec2 coord = vec2(v_texcoord.x, floor(v_texcoord.y*2));
+
+ switch((ubuf.idx/(60*2))%7) {
+ case 0:
+ c = textureLod(tex, v_texcoord.x, float((ubuf.idx/30)%4));
+ break;
+ case 1:
+ c = textureLod(texArray, coord, float((ubuf.idx/30)%4));
+ break;
+ case 2:
+ c = textureLod(texA, v_texcoord.x, float((ubuf.idx/30)%4));
+ break;
+ case 3:
+ c = textureLod(texArrayA, coord, float((ubuf.idx/30)%4));
+ break;
+ case 4:
+ c = texture(texB, v_texcoord.x);
+ break;
+ case 5:
+ c = texture(texC, v_texcoord.x);
+ break;
+ case 6:
+ c = texture(texArrayB, coord);
+ break;
+ }
+
+ fragColor = vec4(c.rgb*c.a, c.a);
+
+}
diff --git a/tests/manual/rhi/tex1d/texture1d.frag.qsb b/tests/manual/rhi/tex1d/texture1d.frag.qsb
new file mode 100644
index 0000000000..dd389c48c2
--- /dev/null
+++ b/tests/manual/rhi/tex1d/texture1d.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/tex1d/texture1d.vert b/tests/manual/rhi/tex1d/texture1d.vert
new file mode 100644
index 0000000000..97cccf8610
--- /dev/null
+++ b/tests/manual/rhi/tex1d/texture1d.vert
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_texcoord = texcoord;
+ gl_Position = position;
+}
diff --git a/tests/manual/rhi/tex1d/texture1d.vert.qsb b/tests/manual/rhi/tex1d/texture1d.vert.qsb
new file mode 100644
index 0000000000..99636ce3aa
--- /dev/null
+++ b/tests/manual/rhi/tex1d/texture1d.vert.qsb
Binary files differ