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
parent
0be28d1030
commit
cdfbe70923
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -806,6 +806,8 @@ public:
|
|||
R16F,
|
||||
R32F,
|
||||
|
||||
RGB10A2,
|
||||
|
||||
D16,
|
||||
D24,
|
||||
D24S8,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,3 +13,7 @@ android
|
|||
# Ditto
|
||||
[renderToTextureSampleWithSeparateTextureAndSampler]
|
||||
android
|
||||
[renderToFloatTexture]
|
||||
android
|
||||
[renderToRgb10Texture]
|
||||
android
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue