rhi: Add support for half precision vertex atttributes
Runtime support is indicated via QRhi::Feature::HalfAttributes. OpenGL support is available in OpenGL 3.0+, OpenGL ES 3.0+, and in implementations that support the extension GL_ARB_half_float_vertex. Other RHI backends (Vulkan, Metal, D3D11, and D3D12) all support this feature. Note that D3D does not support the half3 type. D3D backends pass half3 as half4. tst_qrhi auto unit test included. Change-Id: Ide05d7f62f6102ad5cae1b3681fdda98d52bca31 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
parent
742e79312f
commit
9ffa16baf0
@ -58,7 +58,8 @@ public:
|
||||
TextureSwizzle = 0x01000000,
|
||||
StandardDerivatives = 0x02000000,
|
||||
ASTCTextureCompression = 0x04000000,
|
||||
ETC2TextureCompression = 0x08000000
|
||||
ETC2TextureCompression = 0x08000000,
|
||||
HalfFloatVertex = 0x10000000
|
||||
};
|
||||
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
|
||||
|
||||
|
@ -346,6 +346,8 @@ static int qt_gl_resolve_extensions()
|
||||
extensions |= QOpenGLExtensions::TextureSwizzle;
|
||||
if (extensionMatcher.match("GL_OES_standard_derivatives"))
|
||||
extensions |= QOpenGLExtensions::StandardDerivatives;
|
||||
if (extensionMatcher.match("GL_ARB_half_float_vertex"))
|
||||
extensions |= QOpenGLExtensions::HalfFloatVertex;
|
||||
|
||||
if (ctx->isOpenGLES()) {
|
||||
if (format.majorVersion() >= 2)
|
||||
@ -360,7 +362,8 @@ static int qt_gl_resolve_extensions()
|
||||
| QOpenGLExtensions::FramebufferMultisample
|
||||
| QOpenGLExtensions::Sized8Formats
|
||||
| QOpenGLExtensions::StandardDerivatives
|
||||
| QOpenGLExtensions::ETC2TextureCompression;
|
||||
| QOpenGLExtensions::ETC2TextureCompression
|
||||
| QOpenGLExtensions::HalfFloatVertex;
|
||||
#ifndef Q_OS_WASM
|
||||
// WebGL 2.0 specification explicitly does not support texture swizzles
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.19
|
||||
|
@ -740,6 +740,16 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
unsupported on backends that do not report support for
|
||||
\l{OneDimensionalTextures}, and Metal.
|
||||
|
||||
\value HalfAttributes Indicates that specifying input attributes with half
|
||||
precision (16bit) floating point types for a shader pipeline is supported.
|
||||
When not supported, build() will succeed but just show a warning message
|
||||
and the values of the target attributes will be broken. In practice this
|
||||
feature will be unsupported in some OpenGL ES 2.0 and OpenGL 2.x
|
||||
implementations. Note that while D3D does support half precision input
|
||||
attributes, it does not support the half3 type. The D3D backends pass
|
||||
half3 attributes as half4. To ensure cross platform compatibility, half3
|
||||
inputs should be padded to 8 bytes.
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -1287,6 +1297,16 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
|
||||
\value SInt3 Three component signed integer vector
|
||||
\value SInt2 Two component signed integer vector
|
||||
\value SInt Signed integer
|
||||
\value Half4 Four component half precision (16bit) float vector
|
||||
\value Half3 Three component half precision (16bit) float vector
|
||||
\value Half2 Two component half precision (16bit) float vector
|
||||
\value Half half precision (16bit) float
|
||||
|
||||
\note Support for half precision floating point attributes is indicated at
|
||||
run time by the QRhi::Feature::HalfAttributes feature flag. Note that D3D
|
||||
supports half input attributes, but does not support the Half3 type. The
|
||||
D3D backends pass through Half3 as Half4. To ensure cross platform
|
||||
compatibility, Half3 inputs should be padded to 8 bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -1419,6 +1439,15 @@ quint32 QRhiImplementation::byteSizePerVertexForVertexInputFormat(QRhiVertexInpu
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return sizeof(qint32);
|
||||
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
return 4 * sizeof(qfloat16);
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
return 4 * sizeof(qfloat16); // half3 still takes 8 bytes
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
return 2 * sizeof(qfloat16);
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
return sizeof(qfloat16);
|
||||
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(1);
|
||||
}
|
||||
|
@ -247,7 +247,11 @@ public:
|
||||
SInt4,
|
||||
SInt3,
|
||||
SInt2,
|
||||
SInt
|
||||
SInt,
|
||||
Half4,
|
||||
Half3,
|
||||
Half2,
|
||||
Half
|
||||
};
|
||||
|
||||
QRhiVertexInputAttribute() = default;
|
||||
@ -1819,7 +1823,8 @@ public:
|
||||
TextureArrayRange,
|
||||
NonFillPolygonMode,
|
||||
OneDimensionalTextures,
|
||||
OneDimensionalTextureMipmaps
|
||||
OneDimensionalTextureMipmaps,
|
||||
HalfAttributes
|
||||
};
|
||||
|
||||
enum BeginFrameFlag {
|
||||
|
@ -528,6 +528,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::OneDimensionalTextureMipmaps:
|
||||
return true;
|
||||
case QRhi::HalfAttributes:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
@ -4038,6 +4040,14 @@ static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format
|
||||
return DXGI_FORMAT_R32G32_SINT;
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return DXGI_FORMAT_R32_SINT;
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
// Note: D3D does not support half3. Pass through half3 as half4.
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
return DXGI_FORMAT_R16G16_FLOAT;
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
return DXGI_FORMAT_R16_FLOAT;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
|
@ -583,6 +583,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::OneDimensionalTextureMipmaps:
|
||||
return false;
|
||||
case QRhi::HalfAttributes:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -5053,6 +5055,14 @@ static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format
|
||||
return DXGI_FORMAT_R32G32_SINT;
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return DXGI_FORMAT_R32_SINT;
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
// Note: D3D does not support half3. Pass through half3 as half4.
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
return DXGI_FORMAT_R16G16_FLOAT;
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
return DXGI_FORMAT_R16_FLOAT;
|
||||
}
|
||||
Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32G32B32A32_FLOAT);
|
||||
}
|
||||
|
@ -450,6 +450,10 @@ QT_BEGIN_NAMESPACE
|
||||
# define GL_TEXTURE_1D_ARRAY 0x8C18
|
||||
#endif
|
||||
|
||||
#ifndef GL_HALF_FLOAT
|
||||
#define GL_HALF_FLOAT 0x140B
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Constructs a new QRhiGles2InitParams.
|
||||
|
||||
@ -959,6 +963,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
||||
if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
|
||||
f->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
|
||||
caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
|
||||
|
||||
nativeHandlesStruct.context = ctx;
|
||||
|
||||
contextLost = false;
|
||||
@ -1315,6 +1321,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
||||
return caps.texture1D;
|
||||
case QRhi::OneDimensionalTextureMipmaps:
|
||||
return caps.texture1D;
|
||||
case QRhi::HalfAttributes:
|
||||
return caps.halfAttributes;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
@ -2939,6 +2947,22 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
||||
type = GL_INT;
|
||||
size = 1;
|
||||
break;
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
type = GL_HALF_FLOAT;
|
||||
size = 4;
|
||||
break;
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
type = GL_HALF_FLOAT;
|
||||
size = 3;
|
||||
break;
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
type = GL_HALF_FLOAT;
|
||||
size = 2;
|
||||
break;
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
type = GL_HALF_FLOAT;
|
||||
size = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -961,7 +961,8 @@ public:
|
||||
tessellation(false),
|
||||
geometryShader(false),
|
||||
texture1D(false),
|
||||
hasDrawBuffersFunc(false)
|
||||
hasDrawBuffersFunc(false),
|
||||
halfAttributes(false)
|
||||
{ }
|
||||
int ctxMajor;
|
||||
int ctxMinor;
|
||||
@ -1014,6 +1015,7 @@ public:
|
||||
uint geometryShader : 1;
|
||||
uint texture1D : 1;
|
||||
uint hasDrawBuffersFunc : 1;
|
||||
uint halfAttributes : 1;
|
||||
} caps;
|
||||
QGles2SwapChain *currentSwapChain = nullptr;
|
||||
QSet<GLint> supportedCompressedFormats;
|
||||
|
@ -794,6 +794,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::OneDimensionalTextureMipmaps:
|
||||
return false;
|
||||
case QRhi::HalfAttributes:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
@ -4297,6 +4299,14 @@ static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::F
|
||||
return MTLVertexFormatInt2;
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return MTLVertexFormatInt;
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
return MTLVertexFormatHalf4;
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
return MTLVertexFormatHalf3;
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
return MTLVertexFormatHalf2;
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
return MTLVertexFormatHalf;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return MTLVertexFormatFloat4;
|
||||
|
@ -4265,6 +4265,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::OneDimensionalTextureMipmaps:
|
||||
return true;
|
||||
case QRhi::HalfAttributes:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
@ -5279,6 +5281,14 @@ static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format form
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case QRhiVertexInputAttribute::Half4:
|
||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
case QRhiVertexInputAttribute::Half3:
|
||||
return VK_FORMAT_R16G16B16_SFLOAT;
|
||||
case QRhiVertexInputAttribute::Half2:
|
||||
return VK_FORMAT_R16G16_SFLOAT;
|
||||
case QRhiVertexInputAttribute::Half:
|
||||
return VK_FORMAT_R16_SFLOAT;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
}
|
||||
|
@ -21,3 +21,4 @@ qsb --glsl 320es,430 --msl 12 --tess-mode triangles storagebuffer_runtime.tesc -
|
||||
qsb --glsl 320es,430 --msl 12 --tess-vertex-count 3 storagebuffer_runtime.tese -o storagebuffer_runtime.tese.qsb
|
||||
qsb --glsl 320es,430 --msl 12 storagebuffer_runtime.frag -o storagebuffer_runtime.frag.qsb
|
||||
qsb --glsl 320es,430 --hlsl 50 -c --msl 12 storagebuffer_runtime.comp -o storagebuffer_runtime.comp.qsb
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o half.vert.qsb half.vert
|
||||
|
10
tests/auto/gui/rhi/qrhi/data/half.vert
Normal file
10
tests/auto/gui/rhi/qrhi/data/half.vert
Normal file
@ -0,0 +1,10 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/half.vert.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/half.vert.qsb
Normal file
Binary file not shown.
@ -152,6 +152,9 @@ private slots:
|
||||
void storageBufferRuntimeSizeGraphics_data();
|
||||
void storageBufferRuntimeSizeGraphics();
|
||||
|
||||
void halfPrecisionAttributes_data();
|
||||
void halfPrecisionAttributes();
|
||||
|
||||
private:
|
||||
void setWindowType(QWindow *window, QRhi::Implementation impl);
|
||||
|
||||
@ -6190,5 +6193,151 @@ void tst_QRhi::storageBufferRuntimeSizeGraphics()
|
||||
QCOMPARE(result.pixel(32, 32), qRgb(red, green, blue));
|
||||
}
|
||||
|
||||
void tst_QRhi::halfPrecisionAttributes_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::halfPrecisionAttributes()
|
||||
{
|
||||
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->isFeatureSupported(QRhi::HalfAttributes)) {
|
||||
QVERIFY(rhi->backend() != QRhi::Vulkan);
|
||||
QVERIFY(rhi->backend() != QRhi::Metal);
|
||||
QVERIFY(rhi->backend() != QRhi::D3D11);
|
||||
QVERIFY(rhi->backend() != QRhi::D3D12);
|
||||
QSKIP("Half precision vertex attributes are not supported with this graphics API, skipping test");
|
||||
}
|
||||
|
||||
const QSize outputSize(1920, 1080);
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, 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();
|
||||
|
||||
//
|
||||
// This test uses half3 vertices
|
||||
//
|
||||
// Note: D3D does not support half3 - rhi passes it through as half4. Because of this, D3D will
|
||||
// report the following warning and error if we don't take precautions:
|
||||
//
|
||||
// D3D11 WARNING: ID3D11DeviceContext::Draw: Input vertex slot 0 has stride 6 which is less than
|
||||
// the minimum stride logically expected from the current Input Layout (8 bytes). This is OK, as
|
||||
// hardware is perfectly capable of reading overlapping data. However the developer probably did
|
||||
// not intend to make use of this behavior. [ EXECUTION WARNING #355:
|
||||
// DEVICE_DRAW_VERTEX_BUFFER_STRIDE_TOO_SMALL]
|
||||
//
|
||||
// D3D11 ERROR: ID3D11DeviceContext::Draw: Vertex Buffer Stride (6) at the input vertex slot 0
|
||||
// is not aligned properly. The current Input Layout imposes an alignment of (4) because of the
|
||||
// Formats used with this slot. [ EXECUTION ERROR #367: DEVICE_DRAW_VERTEX_STRIDE_UNALIGNED]
|
||||
//
|
||||
// The same warning and error are produced for D3D12. The rendered output is correct despite
|
||||
// the warning and error.
|
||||
//
|
||||
// To avoid these errors, we pad the vertices to 8 byte stride.
|
||||
//
|
||||
static const qfloat16 vertices[] = {
|
||||
-1.0, -1.0, 0.0, 0.0,
|
||||
1.0, -1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
};
|
||||
|
||||
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
|
||||
QVERIFY(vbuf->create());
|
||||
updates->uploadStaticBuffer(vbuf.data(), vertices);
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||
QVERIFY(srb->create());
|
||||
|
||||
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
|
||||
QShader vs = loadShader(":/data/half.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({ { 4 * sizeof(qfloat16) } }); // 8 byte vertex stride for D3D
|
||||
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Half3, 0 } });
|
||||
pipeline->setVertexInputLayout(inputLayout);
|
||||
pipeline->setShaderResourceBindings(srb.data());
|
||||
pipeline->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(pipeline->create());
|
||||
|
||||
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_RGBA8888_Premultiplied); // non-owning, no copy needed because readResult outlives result
|
||||
};
|
||||
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
|
||||
readbackBatch->readBackTexture({ texture.data() }, &readResult);
|
||||
cb->endPass(readbackBatch);
|
||||
|
||||
rhi->endOffscreenFrame();
|
||||
// Offscreen frames are synchronous, so the readback is guaranteed to
|
||||
// complete at this point. This would not be the case with swapchain-based
|
||||
// frames.
|
||||
QCOMPARE(result.size(), texture->pixelSize());
|
||||
|
||||
if (impl == QRhi::Null)
|
||||
return;
|
||||
|
||||
// Now we have a red rectangle on blue background.
|
||||
const int y = 100;
|
||||
const quint32 *p = reinterpret_cast<const quint32 *>(result.constScanLine(y));
|
||||
int x = result.width() - 1;
|
||||
int redCount = 0;
|
||||
int blueCount = 0;
|
||||
const int maxFuzz = 1;
|
||||
while (x-- >= 0) {
|
||||
const QRgb c(*p++);
|
||||
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 != 0);
|
||||
QVERIFY(blueCount != 0);
|
||||
|
||||
// The triangle is "pointing up" in the resulting image with OpenGL
|
||||
// (because Y is up both in normalized device coordinates and in images)
|
||||
// and Vulkan (because Y is down in both and the vertex data was specified
|
||||
// with Y up in mind), but "pointing down" with D3D (because Y is up in NDC
|
||||
// but down in images).
|
||||
if (rhi->isYUpInFramebuffer() == rhi->isYUpInNDC())
|
||||
QVERIFY(redCount < blueCount);
|
||||
else
|
||||
QVERIFY(redCount > blueCount);
|
||||
|
||||
}
|
||||
|
||||
#include <tst_qrhi.moc>
|
||||
QTEST_MAIN(tst_QRhi)
|
||||
|
Loading…
Reference in New Issue
Block a user