diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 82c1de31f3..1585b4bbc0 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -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; diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 8d5d21278b..e19ebbfe26 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -806,6 +806,8 @@ public: R16F, R32F, + RGB10A2, + D16, D24, D24S8, diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index eaaf0c6991..04544bc3b2 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -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; } diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 01d4f927d7..b698ee369b 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -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()); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 3fbbf71362..750626f9c5 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -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; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index ff136debe9..e825f56943 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -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; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 2a9e0f8c69..3f6119bb97 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -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; diff --git a/tests/auto/gui/rhi/qrhi/BLACKLIST b/tests/auto/gui/rhi/qrhi/BLACKLIST index 4e7b7dbc29..dc2bc57077 100644 --- a/tests/auto/gui/rhi/qrhi/BLACKLIST +++ b/tests/auto/gui/rhi/qrhi/BLACKLIST @@ -13,3 +13,7 @@ android # Ditto [renderToTextureSampleWithSeparateTextureAndSampler] android +[renderToFloatTexture] +android +[renderToRgb10Texture] +android diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 6a06fc4ef5..5c3b2d2ddd 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer ibuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indices))); QVERIFY(ibuf->create()); @@ -3350,20 +3309,8 @@ void tst_QRhi::renderToTextureIndexedDraw() QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); QVERIFY(vbuf->create()); - updates->uploadStaticBuffer(vbuf.data(), vertices); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); QScopedPointer srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 srb(rhi->newShaderResourceBindings()); QVERIFY(srb->create()); - QScopedPointer 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 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices))); + QScopedPointer 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 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 texture(rhi->newTexture(QRhiTexture::RGBA16F, outputSize, 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + QVERIFY(texture->create()); + + QScopedPointer rt(rhi->newTextureRenderTarget({ texture.data() })); + QScopedPointer 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); + QVERIFY(vbuf->create()); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); + + QScopedPointer srb(rhi->newShaderResourceBindings()); + QVERIFY(srb->create()); + + QScopedPointer 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(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(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 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 texture(rhi->newTexture(QRhiTexture::RGB10A2, outputSize, 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + QVERIFY(texture->create()); + + QScopedPointer rt(rhi->newTextureRenderTarget({ texture.data() })); + QScopedPointer 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 vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); + QVERIFY(vbuf->create()); + updates->uploadStaticBuffer(vbuf.data(), triangleVertices); + + QScopedPointer srb(rhi->newShaderResourceBindings()); + QVERIFY(srb->create()); + + QScopedPointer 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(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 QTEST_MAIN(tst_QRhi)