rhi: Improve the handling of HDR capable texture formats

Add some sort of autotest for both RGBA16F and the new RGB10A2. The
latter is introduced particularly because ideally we should have a
texture format that corresponds to the D3D/Vulkan swapchain color
buffer format with HDR10.

Change-Id: I1e1bbb7c7e32cb3db89275900811c0bcaeac39d6
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
bb10
Laszlo Agocs 2022-01-05 15:48:46 +01:00
parent 0be28d1030
commit cdfbe70923
9 changed files with 314 additions and 178 deletions

View File

@ -2525,6 +2525,19 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\value RGBA32F Four components, 32-bit float per component.
\value R16F One component, 16-bit float.
\value R32F One component, 32-bit float.
\value RGBA10A2 Four components, unsigned normalized 10 bit R, G, and B,
2-bit alpha. This is a packed format so native endianness applies. Note
that there is no BGR30A2: this format maps to DXGI_FORMAT_R10G10B10A2_UNORM
with D3D, MTLPixelFormatRGB10A2Unorm with Metal,
VK_FORMAT_A2B10G10R10_UNORM_PACK32 with Vulkan, and
GL_RGB10_A2/GL_RGB/GL_UNSIGNED_INT_2_10_10_10_REV on OpenGL (ES). This is
the only universally supported RGB30 option. The corresponding QImage
formats are QImage::Format_BGR30 and QImage::Format_A2BGR30_Premultiplied.
\value D16 16-bit depth (normalized unsigned integer)
\value D24 24-bit depth (normalized unsigned integer)
@ -5032,6 +5045,10 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
bpc = 4;
break;
case QRhiTexture::RGB10A2:
bpc = 4;
break;
case QRhiTexture::D16:
bpc = 2;
break;

View File

@ -806,6 +806,8 @@ public:
R16F,
R32F,
RGB10A2,
D16,
D24,
D24S8,

View File

@ -1237,6 +1237,9 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
case QRhiTexture::R32F:
return DXGI_FORMAT_R32_FLOAT;
case QRhiTexture::RGB10A2:
return DXGI_FORMAT_R10G10B10A2_UNORM;
case QRhiTexture::D16:
return DXGI_FORMAT_R16_TYPELESS;
case QRhiTexture::D24:
@ -1290,7 +1293,7 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
}
}
static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
static inline QRhiTexture::Format swapchainReadbackTextureFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
{
switch (format) {
case DXGI_FORMAT_R8G8B8A8_UNORM:
@ -1305,16 +1308,14 @@ static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT f
if (flags)
(*flags) |= QRhiTexture::sRGB;
return QRhiTexture::BGRA8;
case DXGI_FORMAT_R8_UNORM:
return QRhiTexture::R8;
case DXGI_FORMAT_R8G8_UNORM:
return QRhiTexture::RG8;
case DXGI_FORMAT_R16_UNORM:
return QRhiTexture::R16;
case DXGI_FORMAT_R16G16_UNORM:
return QRhiTexture::RG16;
default: // this cannot assert, must warn and return unknown
qWarning("DXGI_FORMAT %d is not a recognized uncompressed color format", format);
case DXGI_FORMAT_R16G16B16A16_FLOAT:
return QRhiTexture::RGBA16F;
case DXGI_FORMAT_R32G32B32A32_FLOAT:
return QRhiTexture::RGBA32F;
case DXGI_FORMAT_R10G10B10A2_UNORM:
return QRhiTexture::RGB10A2;
default:
qWarning("DXGI_FORMAT %d cannot be read back", format);
break;
}
return QRhiTexture::UnknownFormat;
@ -1594,7 +1595,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
src = swapChainD->backBufferTex;
dxgiFormat = swapChainD->colorFormat;
pixelSize = swapChainD->pixelSize;
format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
format = swapchainReadbackTextureFormat(dxgiFormat, nullptr);
if (format == QRhiTexture::UnknownFormat)
continue;
}

View File

@ -422,6 +422,14 @@ QT_BEGIN_NAMESPACE
#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
#endif
#ifndef GL_RGB10_A2
#define GL_RGB10_A2 0x8059
#endif
#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
#endif
/*!
Constructs a new QRhiGles2InitParams.
@ -733,6 +741,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
caps.rgb10Formats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
#ifdef Q_OS_WASM
@ -1041,6 +1050,12 @@ static inline void toGlTextureFormat(QRhiTexture::Format format, const QRhiGles2
*glformat = GL_RED;
*gltype = GL_FLOAT;
break;
case QRhiTexture::RGB10A2:
*glintformat = GL_RGB10_A2;
*glsizedintformat = *glintformat;
*glformat = GL_RGBA;
*gltype = GL_UNSIGNED_INT_2_10_10_10_REV;
break;
case QRhiTexture::D16:
*glintformat = GL_DEPTH_COMPONENT16;
*glsizedintformat = *glintformat;
@ -1114,6 +1129,9 @@ bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
case QRhiTexture::R32F:
return caps.floatFormats;
case QRhiTexture::RGB10A2:
return caps.rgb10Formats;
default:
break;
}
@ -3051,6 +3069,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
result->data.resize(w * h * 16);
f->glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, result->data.data());
break;
case QRhiTexture::RGB10A2:
result->data.resize(w * h * 4);
f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, result->data.data());
break;
default:
result->data.resize(w * h * 4);
f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, result->data.data());

View File

@ -940,6 +940,7 @@ public:
r8Format(false),
r16Format(false),
floatFormats(false),
rgb10Formats(false),
depthTexture(false),
packedDepthStencil(false),
needsDepthStencilCombinedAttach(false),
@ -985,6 +986,7 @@ public:
uint r8Format : 1;
uint r16Format : 1;
uint floatFormats : 1;
uint rgb10Formats : 1;
uint depthTexture : 1;
uint packedDepthStencil : 1;
uint needsDepthStencilCombinedAttach : 1;

View File

@ -2411,6 +2411,9 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
case QRhiTexture::R32F:
return MTLPixelFormatR32Float;
case QRhiTexture::RGB10A2:
return MTLPixelFormatRGB10A2Unorm;
#ifdef Q_OS_MACOS
case QRhiTexture::D16:
return MTLPixelFormatDepth16Unorm;

View File

@ -918,6 +918,10 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
case QRhiTexture::R32F:
return VK_FORMAT_R32_SFLOAT;
case QRhiTexture::RGB10A2:
// intentionally A2B10G10R10, not A2R10G10B10
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
case QRhiTexture::D16:
return VK_FORMAT_D16_UNORM;
case QRhiTexture::D24:
@ -984,7 +988,7 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
}
}
static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags)
static inline QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
{
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
@ -999,24 +1003,14 @@ static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format
if (flags)
(*flags) |= QRhiTexture::sRGB;
return QRhiTexture::BGRA8;
case VK_FORMAT_R8_UNORM:
return QRhiTexture::R8;
case VK_FORMAT_R8G8_UNORM:
return QRhiTexture::RG8;
case VK_FORMAT_R8_SRGB:
if (flags)
(*flags) |= QRhiTexture::sRGB;
return QRhiTexture::R8;
case VK_FORMAT_R8G8_SRGB:
if (flags)
(*flags) |= QRhiTexture::sRGB;
return QRhiTexture::RG8;
case VK_FORMAT_R16_UNORM:
return QRhiTexture::R16;
case VK_FORMAT_R16G16_UNORM:
return QRhiTexture::RG16;
default: // this cannot assert, must warn and return unknown
qWarning("VkFormat %d is not a recognized uncompressed color format", format);
case VK_FORMAT_R16G16B16A16_SFLOAT:
return QRhiTexture::RGBA16F;
case VK_FORMAT_R32G32B32A32_SFLOAT:
return QRhiTexture::RGBA32F;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return QRhiTexture::RGB10A2;
default:
qWarning("VkFormat %d cannot be read back", format);
break;
}
return QRhiTexture::UnknownFormat;
@ -3409,7 +3403,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
readback.pixelSize = swapChainD->pixelSize;
readback.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
if (readback.format == QRhiTexture::UnknownFormat)
continue;

View File

@ -13,3 +13,7 @@ android
# Ditto
[renderToTextureSampleWithSeparateTextureAndSampler]
android
[renderToFloatTexture]
android
[renderToRgb10Texture]
android

View File

@ -31,6 +31,8 @@
#include <QFile>
#include <QOffscreenSurface>
#include <QPainter>
#include <qrgbafloat.h>
#include <qrgba64.h>
#include <QtGui/private/qrhi_p.h>
#include <QtGui/private/qrhi_p_p.h>
@ -153,6 +155,11 @@ private slots:
void leakedResourceDestroy_data();
void leakedResourceDestroy();
void renderToFloatTexture_data();
void renderToFloatTexture();
void renderToRgb10Texture_data();
void renderToRgb10Texture();
private:
void setWindowType(QWindow *window, QRhi::Implementation impl);
@ -1532,6 +1539,31 @@ void tst_QRhi::renderToTextureSimple_data()
rhiTestData();
}
static QRhiGraphicsPipeline *createSimplePipeline(QRhi *rhi, QRhiShaderResourceBindings *srb, QRhiRenderPassDescriptor *rpDesc)
{
std::unique_ptr<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
if (!vs.isValid())
return nullptr;
QShader fs = loadShader(":/data/simple.frag.qsb");
if (!fs.isValid())
return nullptr;
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb);
pipeline->setRenderPassDescriptor(rpDesc);
return pipeline->create() ? pipeline.release() : nullptr;
}
static const float triangleVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
void tst_QRhi::renderToTextureSimple()
{
QFETCH(QRhi::Implementation, impl);
@ -1557,32 +1589,15 @@ void tst_QRhi::renderToTextureSimple()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
@ -1684,32 +1699,15 @@ void tst_QRhi::renderToTextureMip()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
@ -1806,32 +1804,15 @@ void tst_QRhi::renderToTextureCubemapFace()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
@ -1949,32 +1930,15 @@ void tst_QRhi::renderToTextureTextureArray()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
@ -3330,18 +3294,13 @@ void tst_QRhi::renderToTextureIndexedDraw()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
static const quint16 indices[] = {
0, 1, 2
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiBuffer> ibuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indices)));
QVERIFY(ibuf->create());
@ -3350,20 +3309,8 @@ void tst_QRhi::renderToTextureIndexedDraw()
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
@ -3470,32 +3417,15 @@ void tst_QRhi::renderToWindowSimple()
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
const int asyncReadbackFrames = rhi->resourceLimit(QRhi::MaxAsyncReadbackFrames);
// one frame issues the readback, then we do MaxAsyncReadbackFrames more to ensure the readback completes
@ -3609,26 +3539,10 @@ void tst_QRhi::finishWithinSwapchainFrame()
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
QShader vs = loadShader(":/data/simple.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/simple.frag.qsb");
QVERIFY(fs.isValid());
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
pipeline->setVertexInputLayout(inputLayout);
pipeline->setShaderResourceBindings(srb.data());
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
static const float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
// exercise begin/endExternal() just a little bit, note ExternalContent for beginPass()
@ -3641,7 +3555,7 @@ void tst_QRhi::finishWithinSwapchainFrame()
// times within the same frame
for (int i = 0; i < 5; ++i) {
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
updates->uploadStaticBuffer(vbuf.data(), vertices);
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
cb->beginPass(rt, Qt::blue, { 1.0f, 0 }, updates, QRhiCommandBuffer::ExternalContent);
@ -4783,5 +4697,182 @@ void tst_QRhi::leakedResourceDestroy()
// let the scoped ptr do its job with the resources
}
void tst_QRhi::renderToFloatTexture_data()
{
rhiTestData();
}
void tst_QRhi::renderToFloatTexture()
{
QFETCH(QRhi::Implementation, impl);
QFETCH(QRhiInitParams *, initParams);
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi)
QSKIP("QRhi could not be created, skipping testing rendering");
if (!rhi->isTextureFormatSupported(QRhiTexture::RGBA16F))
QSKIP("RGBA16F is not supported, skipping test");
const QSize outputSize(1920, 1080);
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA16F, outputSize, 1,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
QVERIFY(texture->create());
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rpDesc.data());
QVERIFY(rt->create());
QRhiCommandBuffer *cb = nullptr;
QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
QVERIFY(cb);
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
cb->setViewport({ 0, 0, float(outputSize.width()), float(outputSize.height()) });
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
cb->setVertexInput(0, 1, &vbindings);
cb->draw(3);
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_RGBA16FPx4);
};
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
readbackBatch->readBackTexture({ texture.data() }, &readResult);
cb->endPass(readbackBatch);
rhi->endOffscreenFrame();
QCOMPARE(result.size(), texture->pixelSize());
if (impl == QRhi::Null)
return;
if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
result = std::move(result).mirrored();
// Now we have a red rectangle on blue background.
const int y = 100;
const QRgbaFloat16 *p = reinterpret_cast<const QRgbaFloat16 *>(result.constScanLine(y));
int redCount = 0;
int blueCount = 0;
int x = result.width() - 1;
while (x-- >= 0) {
QRgbaFloat16 c = *p++;
if (c.red() >= 0.95f && qFuzzyIsNull(c.green()) && qFuzzyIsNull(c.blue()))
++redCount;
else if (qFuzzyIsNull(c.red()) && qFuzzyIsNull(c.green()) && c.blue() >= 0.95f)
++blueCount;
else
QFAIL("Encountered a pixel that is neither red or blue");
}
QCOMPARE(redCount + blueCount, texture->pixelSize().width());
QVERIFY(redCount > blueCount); // 1742 > 178
}
void tst_QRhi::renderToRgb10Texture_data()
{
rhiTestData();
}
void tst_QRhi::renderToRgb10Texture()
{
QFETCH(QRhi::Implementation, impl);
QFETCH(QRhiInitParams *, initParams);
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi)
QSKIP("QRhi could not be created, skipping testing rendering");
if (!rhi->isTextureFormatSupported(QRhiTexture::RGB10A2))
QSKIP("RGB10A2 is not supported, skipping test");
const QSize outputSize(1920, 1080);
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGB10A2, outputSize, 1,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
QVERIFY(texture->create());
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rpDesc.data());
QVERIFY(rt->create());
QRhiCommandBuffer *cb = nullptr;
QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
QVERIFY(cb);
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), triangleVertices);
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(createSimplePipeline(rhi.data(), srb.data(), rpDesc.data()));
QVERIFY(pipeline);
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
cb->setViewport({ 0, 0, float(outputSize.width()), float(outputSize.height()) });
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
cb->setVertexInput(0, 1, &vbindings);
cb->draw(3);
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_A2BGR30_Premultiplied);
};
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
readbackBatch->readBackTexture({ texture.data() }, &readResult);
cb->endPass(readbackBatch);
rhi->endOffscreenFrame();
QCOMPARE(result.size(), texture->pixelSize());
if (impl == QRhi::Null)
return;
if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
result = std::move(result).mirrored();
// Now we have a red rectangle on blue background.
const int y = 100;
int redCount = 0;
int blueCount = 0;
const int maxFuzz = 1;
for (int x = 0; x < result.width(); ++x) {
QRgb c = result.pixel(x, y);
if (qRed(c) >= (255 - maxFuzz) && qGreen(c) == 0 && qBlue(c) == 0)
++redCount;
else if (qRed(c) == 0 && qGreen(c) == 0 && qBlue(c) >= (255 - maxFuzz))
++blueCount;
else
QFAIL("Encountered a pixel that is neither red or blue");
}
QCOMPARE(redCount + blueCount, texture->pixelSize().width());
QVERIFY(redCount > blueCount); // 1742 > 178
}
#include <tst_qrhi.moc>
QTEST_MAIN(tst_QRhi)