RHI: Add support for 1D textures

Support for 1D textures on Vulkan, OpenGL, Metal, and D3D.

Change-Id: Ie74ec103da9cfcbf83fa78588cf8cfc1bd6e104f
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
bb10
Ben Fletcher 2022-10-30 12:19:38 -07:00
parent a47c7a9826
commit 85a1663eb1
18 changed files with 1559 additions and 41 deletions

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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));
@ -847,6 +885,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
@ -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()),

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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()
{

View File

@ -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()

View File

@ -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}
)

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}

Binary file not shown.

View File

@ -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;
}

Binary file not shown.