qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,35 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(hellominimalcrossgfxtriangle)
add_subdirectory(compressedtexture_bc1)
add_subdirectory(compressedtexture_bc1_subupload)
add_subdirectory(texuploads)
add_subdirectory(msaatexture)
add_subdirectory(msaarenderbuffer)
add_subdirectory(cubemap)
add_subdirectory(cubemap_scissor)
add_subdirectory(cubemap_render)
add_subdirectory(multiwindow)
add_subdirectory(multiwindow_threaded)
add_subdirectory(triquadcube)
add_subdirectory(offscreen)
add_subdirectory(floattexture)
add_subdirectory(float16texture_with_compute)
add_subdirectory(mrt)
add_subdirectory(shadowmap)
add_subdirectory(computebuffer)
add_subdirectory(computeimage)
add_subdirectory(instancing)
add_subdirectory(noninstanced)
add_subdirectory(tex3d)
add_subdirectory(texturearray)
add_subdirectory(polygonmode)
add_subdirectory(tessellation)
add_subdirectory(geometryshader)
add_subdirectory(stenciloutline)
add_subdirectory(stereo)
add_subdirectory(tex1d)
if(QT_FEATURE_widgets)
add_subdirectory(rhiwidget)
endif()

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## compressedtexture_bc1 Binary:
#####################################################################
qt_internal_add_manual_test(compressedtexture_bc1
GUI
SOURCES
compressedtexture_bc1.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/qt256_bc1_9mips.dds"
PROPERTIES QT_RESOURCE_ALIAS "qt256_bc1_9mips.dds"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(compressedtexture_bc1_resource_files
"../shared/qt256_bc1_9mips.dds"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
)
qt_internal_add_resource(compressedtexture_bc1 "compressedtexture_bc1"
PREFIX
"/"
FILES
${compressedtexture_bc1_resource_files}
)

View File

@ -0,0 +1,153 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include "../shared/cube.h"
#include "../shared/dds_bc1.h"
struct {
QRhiBuffer *vbuf = nullptr;
bool vbufReady = false;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
float rotation = 0;
QByteArrayList compressedData;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::BC1))
qFatal("This backend does not support BC1");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.vbufReady = false;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
QSize imageSize;
d.compressedData = loadBC1(QLatin1String(":/qt256_bc1_9mips.dds"), &imageSize);
qDebug() << d.compressedData.count() << imageSize << m_r->mipLevelsForSize(imageSize);
d.tex = m_r->newTexture(QRhiTexture::BC1, imageSize, 1, QRhiTexture::MipMapped);
d.tex->create();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::Less);
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
const QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
}
void Window::customRelease()
{
delete d.ps;
d.ps = nullptr;
delete d.srb;
d.srb = nullptr;
delete d.ubuf;
d.ubuf = nullptr;
delete d.vbuf;
d.vbuf = nullptr;
delete d.sampler;
d.sampler = nullptr;
delete d.tex;
d.tex = nullptr;
}
void Window::customRender()
{
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (!d.vbufReady) {
d.vbufReady = true;
u->uploadStaticBuffer(d.vbuf, cube);
qint32 flip = 0;
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
if (!d.compressedData.isEmpty()) {
QVarLengthArray<QRhiTextureUploadEntry, 16> descEntries;
for (int i = 0; i < d.compressedData.count(); ++i) {
QRhiTextureSubresourceUploadDescription image(d.compressedData[i].constData(), d.compressedData[i].size());
descEntries.append({ 0, i, image });
}
QRhiTextureUploadDescription desc;
desc.setEntries(descEntries.cbegin(), descEntries.cend());
u->uploadTexture(d.tex, desc);
d.compressedData.clear();
}
d.rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.5f);
mvp.rotate(d.rotation, 0, 1, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ d.vbuf, 0 },
{ d.vbuf, quint32(36 * 3 * sizeof(float)) }
};
cb->setVertexInput(0, 2, vbufBindings);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
compressedtexture_bc1.cpp
RESOURCES = compressedtexture_bc1.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="qt256_bc1_9mips.dds">../shared/qt256_bc1_9mips.dds</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,42 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## compressedtexture_bc1_subupload Binary:
#####################################################################
qt_internal_add_manual_test(compressedtexture_bc1_subupload
GUI
SOURCES
compressedtexture_bc1_subupload.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/bwqt224_64_nomips.dds"
PROPERTIES QT_RESOURCE_ALIAS "bwqt224_64_nomips.dds"
)
set_source_files_properties("../shared/qt256_bc1_9mips.dds"
PROPERTIES QT_RESOURCE_ALIAS "qt256_bc1_9mips.dds"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(compressedtexture_bc1_subupload_resource_files
"../shared/bwqt224_64_nomips.dds"
"../shared/qt256_bc1_9mips.dds"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
)
qt_internal_add_resource(compressedtexture_bc1_subupload "compressedtexture_bc1_subupload"
PREFIX
"/"
FILES
${compressedtexture_bc1_subupload_resource_files}
)

View File

@ -0,0 +1,166 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include "../shared/cube.h"
#include "../shared/dds_bc1.h"
struct {
QRhiBuffer *vbuf = nullptr;
bool vbufReady = false;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
float rotation = 0;
QByteArrayList compressedData;
QByteArrayList compressedData2;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::BC1))
qFatal("This backend does not support BC1");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.vbufReady = false;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
QSize imageSize;
d.compressedData = loadBC1(QLatin1String(":/qt256_bc1_9mips.dds"), &imageSize);
Q_ASSERT(imageSize == QSize(256, 256));
d.tex = m_r->newTexture(QRhiTexture::BC1, imageSize);
d.tex->create();
d.compressedData2 = loadBC1(QLatin1String(":/bwqt224_64_nomips.dds"), &imageSize);
Q_ASSERT(imageSize == QSize(224, 64));
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, // no mipmapping here
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::Less);
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
const QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
}
void Window::customRelease()
{
delete d.ps;
d.ps = nullptr;
delete d.srb;
d.srb = nullptr;
delete d.ubuf;
d.ubuf = nullptr;
delete d.vbuf;
d.vbuf = nullptr;
delete d.sampler;
d.sampler = nullptr;
delete d.tex;
d.tex = nullptr;
}
void Window::customRender()
{
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (!d.vbufReady) {
d.vbufReady = true;
u->uploadStaticBuffer(d.vbuf, cube);
qint32 flip = 0;
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
if (!d.compressedData.isEmpty()) {
{
QRhiTextureUploadDescription desc({ 0, 0, { d.compressedData[0].constData(), quint32(d.compressedData[0].size()) } });
u->uploadTexture(d.tex, desc);
d.compressedData.clear();
}
// now exercise uploading a smaller compressed image into the same texture
{
QRhiTextureSubresourceUploadDescription image(d.compressedData2[0].constData(), d.compressedData2[0].size());
// positions and sizes must be multiples of 4 here (for BC1)
image.setDestinationTopLeft(QPoint(16, 32));
// the image is smaller than the subresource size (224x64 vs
// 256x256) so the size must be specified manually
image.setSourceSize(QSize(224, 64));
QRhiTextureUploadDescription desc({ 0, 0, image });
u->uploadTexture(d.tex, desc);
d.compressedData2.clear();
}
}
d.rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.5f);
mvp.rotate(d.rotation, 0, 1, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ d.vbuf, 0 },
{ d.vbuf, quint32(36 * 3 * sizeof(float)) }
};
cb->setVertexInput(0, 2, vbufBindings);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
compressedtexture_bc1_subupload.cpp
RESOURCES = compressedtexture_bc1_subupload.qrc

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="qt256_bc1_9mips.dds">../shared/qt256_bc1_9mips.dds</file>
<file alias="bwqt224_64_nomips.dds">../shared/bwqt224_64_nomips.dds</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,32 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## computebuffer Binary:
#####################################################################
qt_internal_add_manual_test(computebuffer
GUI
SOURCES
computebuffer.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set(computebuffer_resource_files
"buffer.comp.qsb"
"main.frag.qsb"
"main.vert.qsb"
)
qt_internal_add_resource(computebuffer "computebuffer"
PREFIX
"/"
FILES
${computebuffer_resource_files}
)
# TEMPLATE = "app"

View File

@ -0,0 +1,41 @@
#version 440
layout (local_size_x = 256) in;
struct Data
{
vec2 pos;
float dir;
};
layout(std140, binding = 0) buffer StorageBuffer
{
Data d[];
} buf;
layout(std140, binding = 1) uniform UniformBuffer
{
float step;
uint count;
} ubuf;
void main()
{
uint index = gl_GlobalInvocationID.x;
if (index < ubuf.count) {
vec2 p = buf.d[index].pos;
float dir = buf.d[index].dir;
p.x += dir * ubuf.step * 0.01;
if (p.x > 1.0) {
p.x = 1.0;
buf.d[index].dir *= -1.0;
}
if (p.x < -1.0) {
p.x = -1.0;
buf.d[index].dir *= -1.0;
}
buf.d[index].pos = p;
}
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
qsb --glsl "310 es,430" --hlsl 50 --msl 12 buffer.comp -o buffer.comp.qsb
qsb --glsl "310 es,430" --hlsl 50 --msl 12 main.vert -o main.vert.qsb
qsb --glsl "310 es,430" --hlsl 50 --msl 12 main.frag -o main.frag.qsb

View File

@ -0,0 +1,158 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include <QRandomGenerator>
// Compute shader example. Writes to a storage buffer from a compute shader,
// then uses the same buffer as vertex buffer in the vertex stage. This would
// be typical when implementing particles for example. Here we just simply move
// the positions back and forth along the X axis.
// Note that the example relies on gl_PointSize which is not supported
// everywhere. So in some cases the points will be of size 1.
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *sbuf = nullptr;
QRhiBuffer *computeUniBuf = nullptr;
QRhiShaderResourceBindings *computeBindings = nullptr;
QRhiComputePipeline *computePipeline = nullptr;
QRhiShaderResourceBindings *graphicsBindings = nullptr;
QRhiGraphicsPipeline *graphicsPipeline = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
float step = 0.2f;
} d;
// these struct must match the std140 packing rules
struct Data {
float pos[2];
float dir;
quint32 pad[1];
};
struct ComputeUBuf {
float step;
quint32 count;
};
const int DATA_COUNT = 256 * 128;
const int COMPUTE_UBUF_SIZE = 8;
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::Compute))
qFatal("Compute is not supported");
if (!m_r->isFeatureSupported(QRhi::VertexShaderPointSize))
qWarning("Point sizes other than 1 not supported");
// compute pass
d.sbuf = m_r->newBuffer(QRhiBuffer::Immutable,
QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer,
sizeof(Data) * DATA_COUNT);
d.sbuf->create();
d.releasePool << d.sbuf;
d.computeUniBuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, COMPUTE_UBUF_SIZE);
d.computeUniBuf->create();
d.releasePool << d.computeUniBuf;
d.initialUpdates = m_r->nextResourceUpdateBatch();
QByteArray data;
data.resize(sizeof(Data) * DATA_COUNT);
Data *p = reinterpret_cast<Data *>(data.data());
QRandomGenerator *rgen = QRandomGenerator::global();
for (int i = 0; i < DATA_COUNT; ++i) {
p->pos[0] = rgen->bounded(1000) / 500.0f - 1.0f;
p->pos[1] = rgen->bounded(1000) / 500.0f - 1.0f;
p->dir = rgen->bounded(2) ? 1 : -1;
++p;
}
d.initialUpdates->uploadStaticBuffer(d.sbuf, data.constData());
ComputeUBuf ud;
ud.step = d.step;
ud.count = DATA_COUNT;
d.initialUpdates->updateDynamicBuffer(d.computeUniBuf, 0, COMPUTE_UBUF_SIZE, &ud);
d.computeBindings = m_r->newShaderResourceBindings();
d.computeBindings->setBindings({
QRhiShaderResourceBinding::bufferLoadStore(0, QRhiShaderResourceBinding::ComputeStage, d.sbuf),
QRhiShaderResourceBinding::uniformBuffer(1, QRhiShaderResourceBinding::ComputeStage, d.computeUniBuf)
});
d.computeBindings->create();
d.releasePool << d.computeBindings;
d.computePipeline = m_r->newComputePipeline();
d.computePipeline->setShaderResourceBindings(d.computeBindings);
d.computePipeline->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/buffer.comp.qsb")) });
d.computePipeline->create();
d.releasePool << d.computePipeline;
// graphics pass
d.graphicsBindings = m_r->newShaderResourceBindings();
d.graphicsBindings->create();
d.releasePool << d.graphicsBindings;
d.graphicsPipeline = m_r->newGraphicsPipeline();
d.graphicsPipeline->setTopology(QRhiGraphicsPipeline::Points);
d.graphicsPipeline->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/main.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/main.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
});
d.graphicsPipeline->setVertexInputLayout(inputLayout);
d.graphicsPipeline->setShaderResourceBindings(d.graphicsBindings);
d.graphicsPipeline->setRenderPassDescriptor(m_rp);
d.graphicsPipeline->create();
d.releasePool << d.graphicsPipeline;
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
#if 0
u->updateDynamicBuffer(d.computeUniBuf, 0, sizeof(float), &d.step);
d.step += 0.01f;
#endif
// compute pass
cb->beginComputePass(u);
cb->setComputePipeline(d.computePipeline);
cb->setShaderResources();
cb->dispatch(DATA_COUNT / 256, 1, 1);
cb->endComputePass();
// graphics pass
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.graphicsPipeline);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
QRhiCommandBuffer::VertexInput vbufBinding(d.sbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(DATA_COUNT);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
computebuffer.cpp
RESOURCES = computebuffer.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>buffer.comp.qsb</file>
<file>main.vert.qsb</file>
<file>main.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,8 @@
#version 440
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(1.0);
}

Binary file not shown.

View File

@ -0,0 +1,11 @@
#version 440
layout(location = 0) in vec4 position;
out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };
void main()
{
gl_PointSize = 4.0; // required with Vulkan when drawing points
gl_Position = position;
}

Binary file not shown.

View File

@ -0,0 +1,39 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## computeimage Binary:
#####################################################################
qt_internal_add_manual_test(computeimage
GUI
SOURCES
computeimage.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/qt256.png"
PROPERTIES QT_RESOURCE_ALIAS "qt256.png"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(computeimage_resource_files
"../shared/qt256.png"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
"image.comp.qsb"
)
qt_internal_add_resource(computeimage "computeimage"
PREFIX
"/"
FILES
${computeimage_resource_files}
)

View File

@ -0,0 +1 @@
qsb --glsl "310 es,430" --hlsl 50 --msl 12 image.comp -o image.comp.qsb

View File

@ -0,0 +1,181 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
// Compute shader example with image load/store. The texture sampled in the
// fragment shader is generated by the compute shader.
struct {
QList<QRhiResource *> releasePool;
QRhiTexture *texIn = nullptr;
QRhiTexture *texOut = nullptr;
QRhiBuffer *computeUBuf = nullptr;
QRhiShaderResourceBindings *computeBindings = nullptr;
QRhiComputePipeline *computePipeline = nullptr;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QSize imageSize;
QMatrix4x4 winProj;
float factor = 1.0f;
} d;
static float quadVertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 quadIndexData[] =
{
0, 1, 2, 0, 2, 3
};
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::Compute))
qFatal("Compute is not supported");
d.initialUpdates = m_r->nextResourceUpdateBatch();
// compute pass
const QImage image = QImage(QLatin1String(":/qt256.png")).convertToFormat(QImage::Format_RGBA8888);
d.imageSize = image.size();
d.texIn = m_r->newTexture(QRhiTexture::RGBA8, d.imageSize, 1, QRhiTexture::UsedWithLoadStore);
d.texIn->create();
d.releasePool << d.texIn;
d.texOut = m_r->newTexture(QRhiTexture::RGBA8, d.imageSize, 1, QRhiTexture::UsedWithLoadStore);
d.texOut->create();
d.releasePool << d.texOut;
d.initialUpdates->uploadTexture(d.texIn, image);
d.computeUBuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 4);
d.computeUBuf->create();
d.releasePool << d.computeUBuf;
d.computeBindings = m_r->newShaderResourceBindings();
d.computeBindings->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf),
QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texIn, 0),
QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texOut, 0)
});
d.computeBindings->create();
d.releasePool << d.computeBindings;
d.computePipeline = m_r->newComputePipeline();
d.computePipeline->setShaderResourceBindings(d.computeBindings);
d.computePipeline->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/image.comp.qsb")) });
d.computePipeline->create();
d.releasePool << d.computePipeline;
// graphics pass
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData));
d.vbuf->create();
d.releasePool << d.vbuf;
d.initialUpdates->uploadStaticBuffer(d.vbuf, quadVertexData);
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
d.ibuf->create();
d.releasePool << d.ibuf;
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
d.releasePool << d.ubuf;
qint32 flip = 0; // regardless of isYUpInFramebuffer() since the input is not flipped so the end result is good for GL too
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.texOut, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2.5f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
u->updateDynamicBuffer(d.computeUBuf, 0, 4, &d.factor);
d.factor += 0.1f;
if (d.factor >= 50.0f)
d.factor = 1.0f;
cb->beginComputePass(u);
cb->setComputePipeline(d.computePipeline);
cb->setShaderResources();
cb->dispatch(d.imageSize.width() / 16, d.imageSize.height() / 16, 1);
cb->endComputePass();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
computeimage.cpp
RESOURCES = computeimage.qrc

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>image.comp.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="qt256.png">../shared/qt256.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,20 @@
#version 440
layout (local_size_x = 16, local_size_y = 16) in;
layout(std140, binding = 0) uniform UniformBuffer
{
float factor;
} ubuf;
layout (binding = 1, rgba8) uniform readonly image2D texIn;
layout (binding = 2, rgba8) uniform writeonly image2D texOut;
void main()
{
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
ivec2 d = ivec2(1, 1);
vec4 diff = imageLoad(texIn, pos + d) - imageLoad(texIn, pos - d);
float c = (diff.x + diff.y + diff.z) / ubuf.factor + 0.5f;
imageStore(texOut, pos, vec4(c, c, c, 1.0));
}

Binary file not shown.

View File

@ -0,0 +1,29 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## cubemap Binary:
#####################################################################
qt_internal_add_manual_test(cubemap
GUI
SOURCES
cubemap.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set(cubemap_resource_files
"c.png"
"cubemap.frag.qsb"
"cubemap.vert.qsb"
)
qt_internal_add_resource(cubemap "cubemap"
PREFIX
"/"
FILES
${cubemap_resource_files}
)

View File

@ -0,0 +1,2 @@
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.vert -o cubemap.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.frag -o cubemap.frag.qsb

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,135 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include "../shared/cube.h"
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
} d;
void Window::customInit()
{
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
d.ubuf->create();
d.releasePool << d.ubuf;
const QSize cubeMapSize(512, 512);
d.tex = m_r->newTexture(QRhiTexture::RGBA8, cubeMapSize, 1, QRhiTexture::CubeMap
| QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips); // exercise mipmap generation as well
d.releasePool << d.tex;
d.tex->create();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
QImage img = QImage(":/c.png").mirrored().convertToFormat(QImage::Format_RGBA8888);
// just use the same image for all faces for now
QRhiTextureSubresourceUploadDescription subresDesc(img);
QRhiTextureUploadDescription desc({
{ 0, 0, subresDesc }, // +X
{ 1, 0, subresDesc }, // -X
{ 2, 0, subresDesc }, // +Y
{ 3, 0, subresDesc }, // -Y
{ 4, 0, subresDesc }, // +Z
{ 5, 0, subresDesc } // -Z
});
d.initialUpdates->uploadTexture(d.tex, desc);
d.initialUpdates->generateMips(d.tex);
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::Repeat, QRhiSampler::Repeat);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
QShader vs = getShader(QLatin1String(":/cubemap.vert.qsb"));
Q_ASSERT(vs.isValid());
QShader fs = getShader(QLatin1String(":/cubemap.frag.qsb"));
Q_ASSERT(fs.isValid());
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix();
mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
// cube vertices go from -1..1
mvp.scale(10);
static float rx = 0;
mvp.rotate(rx, 1, 0, 0);
rx += 0.5f;
// no translation
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform samplerCube tex;
void main()
{
fragColor = vec4(texture(tex, v_coord).rgb, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
cubemap.cpp
RESOURCES = cubemap.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>cubemap.vert.qsb</file>
<file>cubemap.frag.qsb</file>
<file>c.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,15 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 0) out vec3 v_coord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_coord = position.xyz;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,32 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## cubemap_render Binary:
#####################################################################
qt_internal_add_manual_test(cubemap_render
GUI
SOURCES
cubemap_render.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set(cubemap_render_resource_files
"cubemap_mrt.frag.qsb"
"cubemap_mrt.vert.qsb"
"cubemap_oneface.frag.qsb"
"cubemap_oneface.vert.qsb"
"cubemap_sample.frag.qsb"
"cubemap_sample.vert.qsb"
)
qt_internal_add_resource(cubemap_render "cubemap_render"
PREFIX
"/"
FILES
${cubemap_render_resource_files}
)

View File

@ -0,0 +1,6 @@
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.vert -o cubemap_oneface.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.frag -o cubemap_oneface.frag.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.vert -o cubemap_mrt.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.frag -o cubemap_mrt.frag.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.vert -o cubemap_sample.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.frag -o cubemap_sample.frag.qsb

View File

@ -0,0 +1,28 @@
#version 440
layout(location = 0) out vec4 c0;
layout(location = 1) out vec4 c1;
layout(location = 2) out vec4 c2;
layout(location = 3) out vec4 c3;
layout(location = 4) out vec4 c4;
layout(location = 5) out vec4 c5;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color0;
vec3 color1;
vec3 color2;
vec3 color3;
vec3 color4;
vec3 color5;
} ubuf;
void main()
{
c0 = vec4(ubuf.color0, 1.0);
c1 = vec4(ubuf.color1, 1.0);
c2 = vec4(ubuf.color2, 1.0);
c3 = vec4(ubuf.color3, 1.0);
c4 = vec4(ubuf.color4, 1.0);
c5 = vec4(ubuf.color5, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
#version 440
layout(location = 0) in vec4 position;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color0;
vec3 color1;
vec3 color2;
vec3 color3;
vec3 color4;
vec3 color5;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
#version 440
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color;
} ubuf;
void main()
{
fragColor = vec4(ubuf.color, 1.0);
}

View File

@ -0,0 +1,15 @@
#version 440
layout(location = 0) in vec4 position;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
gl_Position = ubuf.mvp * position;
}

View File

@ -0,0 +1,419 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Demonstrates rendering to two cubemaps in two different ways:
// - one by one, to each face,
// - if the supported max number of color attachments is greater than 4: in
// one go with all 6 faces attached as render targets.
//
// Finally, show what we got in a skybox-ish thing. Press the arrow keys to
// switch between the two cubemaps. (the only difference should be their
// background clear color)
#define EXAMPLEFW_KEYPRESS_EVENTS
#include "../shared/examplefw.h"
#include "../shared/cube.h"
// each face is 512x512
static const QSize cubemapSize(512, 512);
// each cubemap face gets a 256x256 quad in the center
static float halfQuadVertexData[] =
{ // Y up, CCW
-0.5f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
};
static quint16 halfQuadIndexData[] =
{
0, 1, 2, 0, 2, 3
};
struct {
QList<QRhiResource *> releasePool;
QRhiTexture *cubemap1 = nullptr;
QRhiTexture *cubemap2 = nullptr;
bool canDoMrt = false;
QRhiBuffer *half_quad_vbuf = nullptr;
QRhiBuffer *half_quad_ibuf = nullptr;
QRhiBuffer *oneface_ubuf = nullptr;
int ubufSizePerFace;
QRhiTextureRenderTarget *oneface_rt[6];
QRhiRenderPassDescriptor *oneface_rp = nullptr;
QRhiShaderResourceBindings *oneface_srb = nullptr;
QRhiGraphicsPipeline *oneface_ps = nullptr;
QRhiBuffer *mrt_ubuf = nullptr;
QRhiTextureRenderTarget *mrt_rt = nullptr;
QRhiRenderPassDescriptor *mrt_rp = nullptr;
QRhiShaderResourceBindings *mrt_srb = nullptr;
QRhiGraphicsPipeline *mrt_ps = nullptr;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
float rx = 0;
} d;
void initializePerFaceRendering(QRhi *rhi)
{
d.cubemap1 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget);
d.cubemap1->create();
d.releasePool << d.cubemap1;
d.ubufSizePerFace = rhi->ubufAligned(64 + 12);
d.oneface_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.ubufSizePerFace * 6);
d.oneface_ubuf->create();
d.releasePool << d.oneface_ubuf;
for (int face = 0; face < 6; ++face) {
QRhiColorAttachment att(d.cubemap1);
att.setLayer(face);
QRhiTextureRenderTargetDescription rtDesc(att);
d.oneface_rt[face] = rhi->newTextureRenderTarget(rtDesc);
if (face == 0) {
d.oneface_rp = d.oneface_rt[0]->newCompatibleRenderPassDescriptor();
d.releasePool << d.oneface_rp;
}
d.oneface_rt[face]->setRenderPassDescriptor(d.oneface_rp);
d.oneface_rt[face]->create();
d.releasePool << d.oneface_rt[face];
}
d.oneface_srb = rhi->newShaderResourceBindings();
const QRhiShaderResourceBinding::StageFlags visibility =
QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
d.oneface_srb->setBindings({
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, visibility, d.oneface_ubuf, 64 + 12)
});
d.oneface_srb->create();
d.releasePool << d.oneface_srb;
d.oneface_ps = rhi->newGraphicsPipeline();
d.oneface_ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_oneface.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_oneface.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
});
d.oneface_ps->setVertexInputLayout(inputLayout);
d.oneface_ps->setShaderResourceBindings(d.oneface_srb);
d.oneface_ps->setRenderPassDescriptor(d.oneface_rp);
d.oneface_ps->create();
d.releasePool << d.oneface_ps;
// wasteful to duplicate the mvp as well but will do for now
for (int face = 0; face < 6; ++face) {
const int offset = d.ubufSizePerFace * face;
QMatrix4x4 identity;
d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset, 64, identity.constData());
// will use a different color for each face
QColor c;
switch (face) {
case 0:
c = Qt::red;
break;
case 1:
c = Qt::green;
break;
case 2:
c = Qt::blue;
break;
case 3:
c = Qt::yellow;
break;
case 4:
c = Qt::lightGray;
break;
case 5:
c = Qt::cyan;
break;
}
float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset + 64, 12, color);
}
}
// 6 render passes, 1 draw call each, targeting one cubemap face at a time
void renderPerFace(QRhiCommandBuffer *cb)
{
for (int face = 0; face < 6; ++face) {
cb->beginPass(d.oneface_rt[face], Qt::black, { 1.0f, 0 });
cb->setGraphicsPipeline(d.oneface_ps);
cb->setViewport({ 0, 0,
float(d.oneface_rt[face]->pixelSize().width()),
float(d.oneface_rt[face]->pixelSize().height()) });
const QRhiCommandBuffer::DynamicOffset dynamicOffset(0, face * d.ubufSizePerFace);
cb->setShaderResources(nullptr, 1, &dynamicOffset);
QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}
}
void initializeMrtRendering(QRhi *rhi)
{
d.cubemap2 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget);
d.cubemap2->create();
d.releasePool << d.cubemap2;
d.mrt_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 6 * 16); // note that vec3 is aligned to 16 bytes
d.mrt_ubuf->create();
d.releasePool << d.mrt_ubuf;
QVarLengthArray<QRhiColorAttachment, 6> attachments;
for (int face = 0; face < 6; ++face) {
QRhiColorAttachment att(d.cubemap2);
att.setLayer(face);
attachments.append(att);
}
QRhiTextureRenderTargetDescription rtDesc;
rtDesc.setColorAttachments(attachments.cbegin(), attachments.cend());
d.mrt_rt = rhi->newTextureRenderTarget(rtDesc);
d.mrt_rp = d.mrt_rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.mrt_rp;
d.mrt_rt->setRenderPassDescriptor(d.mrt_rp);
d.mrt_rt->create();
d.releasePool << d.mrt_rt;
d.mrt_srb = rhi->newShaderResourceBindings();
const QRhiShaderResourceBinding::StageFlags visibility =
QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
d.mrt_srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, visibility, d.mrt_ubuf)
});
d.mrt_srb->create();
d.releasePool << d.mrt_srb;
d.mrt_ps = rhi->newGraphicsPipeline();
d.mrt_ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_mrt.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_mrt.frag.qsb")) }
});
QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 6> targetBlends;
for (int face = 0; face < 6; ++face)
targetBlends.append({}); // default to blend = false, color write = all, which is good
d.mrt_ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
});
d.mrt_ps->setVertexInputLayout(inputLayout);
d.mrt_ps->setShaderResourceBindings(d.mrt_srb);
d.mrt_ps->setRenderPassDescriptor(d.mrt_rp);
d.mrt_ps->create();
d.releasePool << d.mrt_ps;
QMatrix4x4 identity;
d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, 0, 64, identity.constData());
for (int face = 0; face < 6; ++face) {
const int offset = 64 + face * 16;
// will use a different color for each face
QColor c;
switch (face) {
case 0:
c = Qt::red;
break;
case 1:
c = Qt::green;
break;
case 2:
c = Qt::blue;
break;
case 3:
c = Qt::yellow;
break;
case 4:
c = Qt::lightGray;
break;
case 5:
c = Qt::cyan;
break;
}
float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, offset, 12, color);
}
}
// 1 render pass, 1 draw call, with all 6 faces attached and written to
void renderWithMrt(QRhiCommandBuffer *cb)
{
// use a different clear color to differentiate from cubemap1 (because the
// results are expected to be identical otherwise)
cb->beginPass(d.mrt_rt, Qt::magenta, { 1.0f, 0 });
cb->setGraphicsPipeline(d.mrt_ps);
cb->setViewport({ 0, 0,
float(d.mrt_rt->pixelSize().width()),
float(d.mrt_rt->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}
void Window::customInit()
{
d.half_quad_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(halfQuadVertexData));
d.half_quad_vbuf->create();
d.releasePool << d.half_quad_vbuf;
d.half_quad_ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(halfQuadIndexData));
d.half_quad_ibuf->create();
d.releasePool << d.half_quad_ibuf;
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.half_quad_vbuf, 0, sizeof(halfQuadVertexData), halfQuadVertexData);
d.initialUpdates->uploadStaticBuffer(d.half_quad_ibuf, halfQuadIndexData);
initializePerFaceRendering(m_r);
d.canDoMrt = m_r->resourceLimit(QRhi::MaxColorAttachments) >= 6;
if (d.canDoMrt)
initializeMrtRendering(m_r);
else
qWarning("Not enough color attachments (need 6, supports %d)", m_r->resourceLimit(QRhi::MaxColorAttachments));
// onscreen stuff
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.releasePool << d.vbuf;
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
d.ubuf->create();
d.releasePool << d.ubuf;
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::Repeat, QRhiSampler::Repeat);
d.sampler->create();
d.releasePool << d.sampler;
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler)
});
d.srb->create();
d.releasePool << d.srb;
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
QShader vs = getShader(QLatin1String(":/cubemap_sample.vert.qsb"));
Q_ASSERT(vs.isValid());
QShader fs = getShader(QLatin1String(":/cubemap_sample.frag.qsb"));
Q_ASSERT(fs.isValid());
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.releasePool << d.ps;
if (d.canDoMrt)
qDebug("Use the arrow keys to switch between the two generated cubemaps");
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix();
mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
mvp.scale(10);
mvp.rotate(d.rx, 1, 0, 0);
d.rx += 0.5f;
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->resourceUpdate(u);
renderPerFace(cb);
if (d.canDoMrt)
renderWithMrt(cb);
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
cb->endPass();
}
void Window::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Left:
case Qt::Key_Up:
qDebug("Showing first cubemap (generated by rendering to the faces one by one; black background)");
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler)
});
d.srb->create();
break;
case Qt::Key_Right:
case Qt::Key_Down:
if (d.canDoMrt) {
qDebug("Showing second cubemap (generated with multiple render targets; magenta background)");
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap2, d.sampler)
});
d.srb->create();
}
break;
default:
e->ignore();
break;
}
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
cubemap_render.cpp
RESOURCES = cubemap_render.qrc

View File

@ -0,0 +1,10 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>cubemap_oneface.vert.qsb</file>
<file>cubemap_oneface.frag.qsb</file>
<file>cubemap_mrt.vert.qsb</file>
<file>cubemap_mrt.frag.qsb</file>
<file>cubemap_sample.vert.qsb</file>
<file>cubemap_sample.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform samplerCube tex;
void main()
{
fragColor = vec4(texture(tex, v_coord).rgb, 1.0);
}

View File

@ -0,0 +1,16 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 0) out vec3 v_coord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_coord = position.xyz;
gl_Position = ubuf.mvp * position;
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## cubemap_scissor Binary:
#####################################################################
qt_internal_add_manual_test(cubemap_scissor
GUI
SOURCES
cubemap_scissor.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../cubemap/c.png"
PROPERTIES QT_RESOURCE_ALIAS "c.png"
)
set_source_files_properties("../cubemap/cubemap.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "cubemap.frag.qsb"
)
set_source_files_properties("../cubemap/cubemap.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "cubemap.vert.qsb"
)
set(cubemap_scissor_resource_files
"../cubemap/c.png"
"../cubemap/cubemap.frag.qsb"
"../cubemap/cubemap.vert.qsb"
)
qt_internal_add_resource(cubemap_scissor "cubemap_scissor"
PREFIX
"/"
FILES
${cubemap_scissor_resource_files}
)

View File

@ -0,0 +1,194 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// This is a test for scissoring. Based on the cubemap test (because there the
// rendering covers the entire viewport which is what we need here). The
// scissor rectangle moves first up, then down, then from the center to the
// left and then to right. The important part is to ensure that the behavior
// identical between all backends, especially when the rectangle is partly or
// fully off window.
#include "../shared/examplefw.h"
#include "../shared/cube.h"
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QPoint scissorBottomLeft;
QSize scissorSize;
int scissorAnimState = 0;
QSize outputSize;
} d;
void Window::customInit()
{
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
d.ubuf->create();
d.releasePool << d.ubuf;
const QSize cubeMapSize(512, 512);
d.tex = m_r->newTexture(QRhiTexture::RGBA8, cubeMapSize, 1, QRhiTexture::CubeMap);
d.releasePool << d.tex;
d.tex->create();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
QImage img = QImage(":/c.png").mirrored().convertToFormat(QImage::Format_RGBA8888);
// just use the same image for all faces for now
QRhiTextureSubresourceUploadDescription subresDesc(img);
QRhiTextureUploadDescription desc({
{ 0, 0, subresDesc }, // +X
{ 1, 0, subresDesc }, // -X
{ 2, 0, subresDesc }, // +Y
{ 3, 0, subresDesc }, // -Y
{ 4, 0, subresDesc }, // +Z
{ 5, 0, subresDesc } // -Z
});
d.initialUpdates->uploadTexture(d.tex, desc);
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::Repeat, QRhiSampler::Repeat);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setFlags(QRhiGraphicsPipeline::UsesScissor);
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
QShader vs = getShader(QLatin1String(":/cubemap.vert.qsb"));
Q_ASSERT(vs.isValid());
QShader fs = getShader(QLatin1String(":/cubemap.frag.qsb"));
Q_ASSERT(fs.isValid());
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.scissorAnimState = 0;
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
static void advanceScissor()
{
switch (d.scissorAnimState) {
case 1: // up
d.scissorBottomLeft.setX(d.outputSize.width() / 4);
d.scissorBottomLeft.ry() += 1;
if (d.scissorBottomLeft.y() > d.outputSize.height() + 100)
d.scissorAnimState = 2;
break;
case 2: // down
d.scissorBottomLeft.ry() -= 1;
if (d.scissorBottomLeft.y() < -d.scissorSize.height() - 100)
d.scissorAnimState = 3;
break;
case 3: // left
d.scissorBottomLeft.setY(d.outputSize.height() / 4);
d.scissorBottomLeft.rx() += 1;
if (d.scissorBottomLeft.x() > d.outputSize.width() + 100)
d.scissorAnimState = 4;
break;
case 4: // right
d.scissorBottomLeft.rx() -= 1;
if (d.scissorBottomLeft.x() < -d.scissorSize.width() - 100)
d.scissorAnimState = 1;
break;
}
qDebug() << "scissor bottom-left" << d.scissorBottomLeft << "size" << d.scissorSize;
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
d.outputSize = outputSizeInPixels;
if (d.scissorAnimState == 0) {
d.scissorBottomLeft = QPoint(outputSizeInPixels.width() / 4, 0);
d.scissorSize = QSize(outputSizeInPixels.width() / 2, outputSizeInPixels.height() / 2);
d.scissorAnimState = 1;
}
QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix();
mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
// cube vertices go from -1..1
mvp.scale(10);
static float rx = 0;
mvp.rotate(rx, 1, 0, 0);
rx += 0.5f;
// no translation
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
// Apply a scissor rectangle that moves around on the screen, also
// exercising the out of screen (negative x or y) case.
cb->setScissor(QRhiScissor(d.scissorBottomLeft.x(), d.scissorBottomLeft.y(),
d.scissorSize.width(), d.scissorSize.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
cb->endPass();
advanceScissor();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
cubemap_scissor.cpp
RESOURCES = cubemap_scissor.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="cubemap.vert.qsb">../cubemap/cubemap.vert.qsb</file>
<file alias="cubemap.frag.qsb">../cubemap/cubemap.frag.qsb</file>
<file alias="c.png">../cubemap/c.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,40 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## float16texture_with_compute Binary:
#####################################################################
qt_internal_add_manual_test(float16texture_with_compute
GUI
SOURCES
float16texture_with_compute.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/qt256.png"
PROPERTIES QT_RESOURCE_ALIAS "qt256.png"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(float16texture_with_compute_resource_files
"../shared/qt256.png"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
"load.comp.qsb"
"prefilter.comp.qsb"
)
qt_internal_add_resource(float16texture_with_compute "float16texture_with_compute"
PREFIX
"/"
FILES
${float16texture_with_compute_resource_files}
)

View File

@ -0,0 +1,2 @@
qsb --glsl "430,310 es" --hlsl 50 --msl 12 load.comp -o load.comp.qsb
qsb --glsl "430,310 es" --hlsl 50 --msl 12 prefilter.comp -o prefilter.comp.qsb

View File

@ -0,0 +1,265 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// An advanced version of floattexture. Instead of RGBA32F, we use RGBA16F, and
// also generate the floating point data from rgba with compute. Then there's a
// compute pass using the BSDF prefiltering taken from Qt Quick 3D, which
// generates all the mip levels.
// Why do we animate the scale of the quad rendered to the window? To have
// different mip levels used, to prove that all of them are generated
// correctly, without artifacts (which would occur if memory barriers were not
// correctly generated by QRhi). For full verification use RenderDoc or similar.
#include "../shared/examplefw.h"
#include <qmath.h>
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
static const int MAX_MIP_LEVELS = 20;
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *texRgba = nullptr;
QRhiTexture *texFloat16 = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiBuffer *computeUBuf_load = nullptr;
QRhiShaderResourceBindings *computeBindings_load = nullptr;
QRhiComputePipeline *computePipeline_load = nullptr;
QRhiBuffer *computeUBuf_prefilter = nullptr;
QRhiShaderResourceBindings *computeBindings_prefilter[MAX_MIP_LEVELS];
QRhiComputePipeline *computePipeline_prefilter = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
bool computeDone = false;
int mipCount;
int prefilterUBufElemSize;
quint32 prefilterNumWorkGroups[MAX_MIP_LEVELS][3];
float scale = 2.5f;
int scale_dir = -1;
} d;
void recordUploadThenFilterFloat16TextureWithCompute(QRhiCommandBuffer *cb)
{
const int w = d.texRgba->pixelSize().width() / 16;
const int h = d.texRgba->pixelSize().height() / 16;
cb->beginComputePass();
cb->setComputePipeline(d.computePipeline_load);
cb->setShaderResources();
cb->dispatch(w, h, 1);
cb->setComputePipeline(d.computePipeline_prefilter);
for (int level = 1; level < d.mipCount; ++level) {
const int i = level - 1;
const int mipW = d.prefilterNumWorkGroups[i][0];
const int mipH = d.prefilterNumWorkGroups[i][1];
QPair<int, quint32> dynamicOffset = { 0, quint32(d.prefilterUBufElemSize * i) };
cb->setShaderResources(d.computeBindings_prefilter[i], 1, &dynamicOffset);
cb->dispatch(mipW, mipH, 1);
}
cb->endComputePass();
}
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::Compute))
qFatal("Compute is not supported");
if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA16F))
qFatal("RGBA16F texture format is not supported");
d.initialUpdates = m_r->nextResourceUpdateBatch();
// load rgba8 image data
QImage image;
image.load(QLatin1String(":/qt256.png"));
image = image.convertToFormat(QImage::Format_RGBA8888);
Q_ASSERT(!image.isNull());
d.texRgba = m_r->newTexture(QRhiTexture::RGBA8, image.size(), 1, QRhiTexture::UsedWithLoadStore);
d.texRgba->create();
d.releasePool << d.texRgba;
d.initialUpdates->uploadTexture(d.texRgba, image);
d.mipCount = m_r->mipLevelsForSize(image.size());
Q_ASSERT(d.mipCount <= MAX_MIP_LEVELS);
d.texFloat16 = m_r->newTexture(QRhiTexture::RGBA16F, image.size(), 1, QRhiTexture::UsedWithLoadStore | QRhiTexture::MipMapped);
d.releasePool << d.texFloat16;
d.texFloat16->create();
// compute
d.computeUBuf_load = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 12);
d.computeUBuf_load->create();
d.releasePool << d.computeUBuf_load;
quint32 numWorkGroups[3] = { quint32(image.width()), quint32(image.height()), 0 };
d.initialUpdates->updateDynamicBuffer(d.computeUBuf_load, 0, 12, numWorkGroups);
d.computeBindings_load = m_r->newShaderResourceBindings();
d.computeBindings_load->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_load),
QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texRgba, 0),
QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, 0)
});
d.computeBindings_load->create();
d.releasePool << d.computeBindings_load;
d.computePipeline_load = m_r->newComputePipeline();
d.computePipeline_load->setShaderResourceBindings(d.computeBindings_load);
d.computePipeline_load->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/load.comp.qsb")) });
d.computePipeline_load->create();
d.releasePool << d.computePipeline_load;
d.prefilterUBufElemSize = m_r->ubufAligned(12);
d.computeUBuf_prefilter = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.prefilterUBufElemSize * d.mipCount);
d.computeUBuf_prefilter->create();
d.releasePool << d.computeUBuf_prefilter;
int mipW = image.width() >> 1;
int mipH = image.height() >> 1;
for (int level = 1; level < d.mipCount; ++level) {
const int i = level - 1;
d.prefilterNumWorkGroups[i][0] = quint32(mipW);
d.prefilterNumWorkGroups[i][1] = quint32(mipH);
d.prefilterNumWorkGroups[i][2] = 0;
d.initialUpdates->updateDynamicBuffer(d.computeUBuf_prefilter, d.prefilterUBufElemSize * i, 12, d.prefilterNumWorkGroups[i]);
mipW = mipW > 2 ? mipW >> 1 : 1;
mipH = mipH > 2 ? mipH >> 1 : 1;
d.computeBindings_prefilter[i] = m_r->newShaderResourceBindings();
d.computeBindings_prefilter[i]->setBindings({
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_prefilter, 12),
QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level - 1),
QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level)
});
d.computeBindings_prefilter[i]->create();
d.releasePool << d.computeBindings_prefilter[i];
}
d.computePipeline_prefilter = m_r->newComputePipeline();
d.computePipeline_prefilter->setShaderResourceBindings(d.computeBindings_prefilter[0]); // just need a layout compatible one
d.computePipeline_prefilter->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/prefilter.comp.qsb")) });
d.computePipeline_prefilter->create();
d.releasePool << d.computePipeline_prefilter;
// graphics
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->create();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
d.releasePool << d.ubuf;
// enable mipmaps
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.texFloat16, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
qint32 flip = 0;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_proj;
mvp.scale(d.scale);
d.scale += d.scale_dir * 0.01f;
if (qFuzzyIsNull(d.scale) || d.scale >= 2.5f)
d.scale_dir *= -1;
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->resourceUpdate(u);
// If not yet done, then do a compute pass that uploads level 0, doing an
// rgba8 -> float16 conversion. Follow that with another compute pass to do
// the filtering and generate all the mip levels.
if (!d.computeDone) {
recordUploadThenFilterFloat16TextureWithCompute(cb);
d.computeDone = true;
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
float16texture_with_compute.cpp
RESOURCES = float16texture_with_compute.qrc

View File

@ -0,0 +1,9 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>load.comp.qsb</file>
<file>prefilter.comp.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="qt256.png">../shared/qt256.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,19 @@
#version 440
layout(local_size_x = 16, local_size_y = 16) in;
layout(rgba8, binding = 1) readonly uniform image2D inputImage;
layout(rgba16f, binding = 2) writeonly uniform image2D outputImage;
// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the
// values in in a uniform buffer.
layout(std140, binding = 0) uniform numWorkGroupsBuf {
uvec3 numWorkGroups;
};
void main()
{
if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y)
return;
vec4 value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy));
imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), value);
}

View File

@ -0,0 +1,50 @@
#version 440
layout(local_size_x = 16, local_size_y = 16) in;
layout(rgba16f, binding = 1) readonly uniform image2D inputImage;
layout(rgba16f, binding = 2) writeonly uniform image2D outputImage;
// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the
// values in in a uniform buffer.
layout(std140, binding = 0) uniform numWorkGroupsBuf {
uvec3 numWorkGroups;
};
int wrapMod( in int a, in int base )
{
return ( a >= 0 ) ? a % base : -(a % base) + base;
}
void getWrappedCoords( inout int sX, inout int sY, in int width, in int height )
{
if (sY < 0) { sX -= width >> 1; sY = -sY; }
if (sY >= height) { sX += width >> 1; sY = height - sY; }
sX = wrapMod( sX, width );
}
void main()
{
int prevWidth = int(numWorkGroups.x) << 1;
int prevHeight = int(numWorkGroups.y) << 1;
if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y)
return;
vec4 accumVal = vec4(0.0);
for (int sy = -2; sy <= 2; ++sy) {
for (int sx = -2; sx <= 2; ++sx) {
int sampleX = sx + (int(gl_GlobalInvocationID.x) << 1);
int sampleY = sy + (int(gl_GlobalInvocationID.y) << 1);
getWrappedCoords(sampleX, sampleY, prevWidth, prevHeight);
if ((sampleY * prevWidth + sampleX) < 0 )
sampleY = prevHeight + sampleY;
ivec2 pos = ivec2(sampleX, sampleY);
vec4 value = imageLoad(inputImage, pos);
float filterPdf = 1.0 / ( 1.0 + float(sx*sx + sy*sy)*2.0 );
filterPdf /= 4.71238898;
accumVal[0] += filterPdf * value.r;
accumVal[1] += filterPdf * value.g;
accumVal[2] += filterPdf * value.b;
accumVal[3] += filterPdf * value.a;
}
}
imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), accumVal);
}

View File

@ -0,0 +1,41 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## floattexture Binary:
#####################################################################
qt_internal_add_manual_test(floattexture
GUI
SOURCES
floattexture.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/OpenfootageNET_fieldairport-512.hdr"
PROPERTIES QT_RESOURCE_ALIAS "OpenfootageNET_fieldairport-512.hdr"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(floattexture_resource_files
"../shared/OpenfootageNET_fieldairport-512.hdr"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
)
qt_internal_add_resource(floattexture "floattexture"
PREFIX
"/"
FILES
${floattexture_resource_files}
)
# TEMPLATE = "app"

View File

@ -0,0 +1,281 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include <qmath.h>
QByteArray loadHdr(const QString &fn, QSize *size)
{
QFile f(fn);
if (!f.open(QIODevice::ReadOnly)) {
qWarning("Failed to open %s", qPrintable(fn));
return QByteArray();
}
char sig[256];
f.read(sig, 11);
if (strncmp(sig, "#?RADIANCE\n", 11))
return QByteArray();
QByteArray buf = f.readAll();
const char *p = buf.constData();
const char *pEnd = p + buf.size();
// Process lines until the empty one.
QByteArray line;
while (p < pEnd) {
char c = *p++;
if (c == '\n') {
if (line.isEmpty())
break;
if (line.startsWith(QByteArrayLiteral("FORMAT="))) {
const QByteArray format = line.mid(7).trimmed();
if (format != QByteArrayLiteral("32-bit_rle_rgbe")) {
qWarning("HDR format '%s' is not supported", format.constData());
return QByteArray();
}
}
line.clear();
} else {
line.append(c);
}
}
if (p == pEnd) {
qWarning("Malformed HDR image data at property strings");
return QByteArray();
}
// Get the resolution string.
while (p < pEnd) {
char c = *p++;
if (c == '\n')
break;
line.append(c);
}
if (p == pEnd) {
qWarning("Malformed HDR image data at resolution string");
return QByteArray();
}
int w = 0, h = 0;
// We only care about the standard orientation.
if (!sscanf(line.constData(), "-Y %d +X %d", &h, &w)) {
qWarning("Unsupported HDR resolution string '%s'", line.constData());
return QByteArray();
}
if (w <= 0 || h <= 0) {
qWarning("Invalid HDR resolution");
return QByteArray();
}
// output is RGBA32F
const int blockSize = 4 * sizeof(float);
QByteArray data;
data.resize(w * h * blockSize);
typedef unsigned char RGBE[4];
RGBE *scanline = new RGBE[w];
for (int y = 0; y < h; ++y) {
if (pEnd - p < 4) {
qWarning("Unexpected end of HDR data");
delete[] scanline;
return QByteArray();
}
scanline[0][0] = *p++;
scanline[0][1] = *p++;
scanline[0][2] = *p++;
scanline[0][3] = *p++;
if (scanline[0][0] == 2 && scanline[0][1] == 2 && scanline[0][2] < 128) {
// new rle, the first pixel was a dummy
for (int channel = 0; channel < 4; ++channel) {
for (int x = 0; x < w && p < pEnd; ) {
unsigned char c = *p++;
if (c > 128) { // run
if (p < pEnd) {
int repCount = c & 127;
c = *p++;
while (repCount--)
scanline[x++][channel] = c;
}
} else { // not a run
while (c-- && p < pEnd)
scanline[x++][channel] = *p++;
}
}
}
} else {
// old rle
scanline[0][0] = 2;
int bitshift = 0;
int x = 1;
while (x < w && pEnd - p >= 4) {
scanline[x][0] = *p++;
scanline[x][1] = *p++;
scanline[x][2] = *p++;
scanline[x][3] = *p++;
if (scanline[x][0] == 1 && scanline[x][1] == 1 && scanline[x][2] == 1) { // run
int repCount = scanline[x][3] << bitshift;
while (repCount--) {
memcpy(scanline[x], scanline[x - 1], 4);
++x;
}
bitshift += 8;
} else { // not a run
++x;
bitshift = 0;
}
}
}
// adjust for -Y orientation
float *fp = reinterpret_cast<float *>(data.data() + (h - 1 - y) * blockSize * w);
for (int x = 0; x < w; ++x) {
float d = qPow(2.0f, float(scanline[x][3]) - 128.0f);
// r, g, b, a
*fp++ = scanline[x][0] / 256.0f * d;
*fp++ = scanline[x][1] / 256.0f * d;
*fp++ = scanline[x][2] / 256.0f * d;
*fp++ = 1.0f;
}
}
delete[] scanline;
if (size)
*size = QSize(w, h);
return data;
}
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA32F))
qWarning("RGBA32F texture format is not supported");
QSize size;
QByteArray floatData = loadHdr(QLatin1String(":/OpenfootageNET_fieldairport-512.hdr"), &size);
Q_ASSERT(!floatData.isEmpty());
qDebug() << size;
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->create();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
d.releasePool << d.ubuf;
d.tex = m_r->newTexture(QRhiTexture::RGBA32F, size);
d.releasePool << d.tex;
d.tex->create();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
qint32 flip = 1;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
QRhiTextureUploadDescription desc({ 0, 0, { floatData.constData(), quint32(floatData.size()) } });
d.initialUpdates->uploadTexture(d.tex, desc);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2.5f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
floattexture.cpp
RESOURCES = floattexture.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="OpenfootageNET_fieldairport-512.hdr">../shared/OpenfootageNET_fieldairport-512.hdr</file>
</qresource>
</RCC>

View File

@ -0,0 +1,24 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(geometryshader
GUI
SOURCES
geometryshader.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
set(geometryshader_resource_files
"test.vert.qsb"
"test.geom.qsb"
"test.frag.qsb"
)
qt_internal_add_resource(geometryshader "geometryshader"
PREFIX
"/"
FILES
${geometryshader_resource_files}
)

View File

@ -0,0 +1,4 @@
qsb --glsl 320es,410 --hlsl 50 test.vert -o test.vert.qsb
qsb --glsl 320es,410 test.geom -o test.geom.qsb
qsb -r hlsl,50,test_geom.hlsl test.geom.qsb
qsb --glsl 320es,410 --hlsl 50 test.frag -o test.frag.qsb

View File

@ -0,0 +1,94 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
static const float points[] = { 0.0f, 0.0f, 0.0f };
struct
{
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
float radius = 0.0f;
} d;
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::GeometryShader))
qFatal("Geometry shaders are not supported");
m_clearColor.setRgb(0, 0, 0);
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(points));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 4);
d.ubuf->create();
d.releasePool << d.ubuf;
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
const QRhiShaderResourceBinding::StageFlags geom = QRhiShaderResourceBinding::GeometryStage;
d.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, geom, d.ubuf) });
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setTopology(QRhiGraphicsPipeline::Points);
d.ps->setShaderStages(
{ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/test.vert.qsb")) },
{ QRhiShaderStage::Geometry, getShader(QLatin1String(":/test.geom.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/test.frag.qsb")) } });
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 3 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, points);
d.initialUpdates->updateDynamicBuffer(d.ubuf, 0, 4, &d.radius);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
u->updateDynamicBuffer(d.ubuf, 0, 4, &d.radius);
d.radius = std::fmod(d.radius + 0.01f, 1.0f);
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(1);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
#version 440
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(1.0);
}

Binary file not shown.

View File

@ -0,0 +1,26 @@
#version 430
# define M_PI 3.14159265358979323846
layout(points) in;
layout(line_strip, max_vertices = 7) out;
layout(std140, binding = 0) uniform buf {
float radius;
};
void main(void)
{
for(int i=0;i<7;++i)
{
float theta = float(i) / 6.0f * 2.0 * M_PI;
gl_Position = gl_in[0].gl_Position;
gl_Position.xy += radius * vec2(cos(theta), sin(theta));
EmitVertex();
}
EndPrimitive();
}

Binary file not shown.

View File

@ -0,0 +1,8 @@
#version 440
layout(location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,26 @@
struct VertexOutput
{
float4 position : SV_Position;
};
struct PixelInput
{
float4 position : SV_POSITION;
};
cbuffer buf : register(b0)
{
float radius : packoffset(c0);
};
[maxvertexcount(7)]
void main(point VertexOutput input[1], inout LineStream<PixelInput> OutputStream)
{
PixelInput output;
for (int i = 0; i < 7; ++i) {
float theta = float(i) / 6.0f * 2.0 * 3.14159265;
output.position = input[0].position;
output.position.xy += radius * float2(cos(theta), sin(theta));
OutputStream.Append(output);
}
}

View File

@ -0,0 +1,35 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## hellominimalcrossgfxtriangle Binary:
#####################################################################
qt_internal_add_manual_test(hellominimalcrossgfxtriangle
SOURCES
hellowindow.cpp hellowindow.h
main.cpp
window.cpp window.h
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/color.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "color.frag.qsb"
)
set_source_files_properties("../shared/color.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "color.vert.qsb"
)
set(hellominimalcrossgfxtriangle_resource_files
"../shared/color.frag.qsb"
"../shared/color.vert.qsb"
)
qt_internal_add_resource(hellominimalcrossgfxtriangle "hellominimalcrossgfxtriangle"
PREFIX
"/"
FILES
${hellominimalcrossgfxtriangle_resource_files}
)

View File

@ -0,0 +1,14 @@
TEMPLATE = app
CONFIG += console
QT += gui-private
SOURCES = \
main.cpp \
window.cpp \
hellowindow.cpp
HEADERS = \
window.h \
hellowindow.h
RESOURCES = hellominimalcrossgfxtriangle.qrc

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,112 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "hellowindow.h"
#include <QFile>
#include <QtGui/private/qshader_p.h>
static float vertexData[] = {
// Y up (note clipSpaceCorrMatrix in m_proj), CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
HelloWindow::HelloWindow(QRhi::Implementation graphicsApi)
: Window(graphicsApi)
{
}
QShader HelloWindow::getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
void HelloWindow::customInit()
{
m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
m_vbuf->create();
m_vbufReady = false;
m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
m_ubuf->create();
m_srb.reset(m_rhi->newShaderResourceBindings());
m_srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
m_ubuf.get())
});
m_srb->create();
m_ps.reset(m_rhi->newGraphicsPipeline());
QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
premulAlphaBlend.enable = true;
m_ps->setTargetBlends({ premulAlphaBlend });
const QShader vs = getShader(QLatin1String(":/color.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/color.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
m_ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
m_ps->setVertexInputLayout(inputLayout);
m_ps->setShaderResourceBindings(m_srb.get());
m_ps->setRenderPassDescriptor(m_rp.get());
m_ps->create();
}
// called once per frame
void HelloWindow::customRender()
{
QRhiResourceUpdateBatch *u = m_rhi->nextResourceUpdateBatch();
if (!m_vbufReady) {
m_vbufReady = true;
u->uploadStaticBuffer(m_vbuf.get(), vertexData);
}
m_rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.rotate(m_rotation, 0, 1, 0);
u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData());
m_opacity += m_opacityDir * 0.005f;
if (m_opacity < 0.0f || m_opacity > 1.0f) {
m_opacityDir *= -1;
m_opacity = qBound(0.0f, m_opacity, 1.0f);
}
u->updateDynamicBuffer(m_ubuf.get(), 64, 4, &m_opacity);
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(m_ps.get());
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef HELLOWINDOW_H
#define HELLOWINDOW_H
#include "window.h"
class HelloWindow : public Window
{
public:
HelloWindow(QRhi::Implementation graphicsApi);
void customInit() override;
void customRender() override;
private:
QShader getShader(const QString &name);
std::unique_ptr<QRhiBuffer> m_vbuf;
bool m_vbufReady = false;
std::unique_ptr<QRhiBuffer> m_ubuf;
std::unique_ptr<QRhiShaderResourceBindings> m_srb;
std::unique_ptr<QRhiGraphicsPipeline> m_ps;
float m_rotation = 0;
float m_opacity = 1;
int m_opacityDir = -1;
};
#endif

View File

@ -0,0 +1,112 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// This is a compact, minimal demo of deciding the backend at runtime while
// using the exact same shaders and rendering code without any branching
// whatsoever once the QWindow is up and the RHI is initialized.
#include <QGuiApplication>
#include <QCommandLineParser>
#include "hellowindow.h"
QString graphicsApiName(QRhi::Implementation graphicsApi)
{
switch (graphicsApi) {
case QRhi::Null:
return QLatin1String("Null (no output)");
case QRhi::OpenGLES2:
return QLatin1String("OpenGL 2.x");
case QRhi::Vulkan:
return QLatin1String("Vulkan");
case QRhi::D3D11:
return QLatin1String("Direct3D 11");
case QRhi::Metal:
return QLatin1String("Metal");
default:
break;
}
return QString();
}
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QRhi::Implementation graphicsApi;
#if defined(Q_OS_WIN)
graphicsApi = QRhi::D3D11;
#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
graphicsApi = QRhi::Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = QRhi::Vulkan;
#else
graphicsApi = QRhi::OpenGLES2;
#endif
QCommandLineParser cmdLineParser;
cmdLineParser.addHelpOption();
QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
cmdLineParser.addOption(nullOption);
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
cmdLineParser.process(app);
if (cmdLineParser.isSet(nullOption))
graphicsApi = QRhi::Null;
if (cmdLineParser.isSet(glOption))
graphicsApi = QRhi::OpenGLES2;
if (cmdLineParser.isSet(vkOption))
graphicsApi = QRhi::Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = QRhi::D3D11;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = QRhi::Metal;
qDebug("Selected graphics API is %s", qPrintable(graphicsApiName(graphicsApi)));
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
QSurfaceFormat::setDefaultFormat(fmt);
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
if (graphicsApi == QRhi::Vulkan) {
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (!inst.create()) {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = QRhi::OpenGLES2;
}
}
#endif
HelloWindow w(graphicsApi);
#if QT_CONFIG(vulkan)
if (graphicsApi == QRhi::Vulkan)
w.setVulkanInstance(&inst);
#endif
w.resize(1280, 720);
w.setTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName(graphicsApi));
w.show();
int ret = app.exec();
// Window::event() will not get invoked when the
// PlatformSurfaceAboutToBeDestroyed event is sent during the QWindow
// destruction. That happens only when exiting via app::quit() instead of
// the more common QWindow::close(). Take care of it: if the QPlatformWindow
// is still around (there was no close() yet), get rid of the swapchain
// while it's not too late.
if (w.handle())
w.releaseSwapChain();
return ret;
}

View File

@ -0,0 +1,219 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "window.h"
#include <QPlatformSurfaceEvent>
#include <QTimer>
Window::Window(QRhi::Implementation graphicsApi)
: m_graphicsApi(graphicsApi)
{
switch (graphicsApi) {
case QRhi::OpenGLES2:
setSurfaceType(OpenGLSurface);
break;
case QRhi::Vulkan:
setSurfaceType(VulkanSurface);
break;
case QRhi::D3D11:
setSurfaceType(Direct3DSurface);
break;
case QRhi::Metal:
setSurfaceType(MetalSurface);
break;
default:
break;
}
}
void Window::exposeEvent(QExposeEvent *)
{
// initialize and start rendering when the window becomes usable for graphics purposes
if (isExposed() && !m_running) {
qDebug("init");
m_running = true;
init();
resizeSwapChain();
}
const QSize surfaceSize = m_hasSwapChain ? m_sc->surfacePixelSize() : QSize();
// stop pushing frames when not exposed (or size is 0)
if ((!isExposed() || (m_hasSwapChain && surfaceSize.isEmpty())) && m_running && !m_notExposed) {
qDebug("not exposed");
m_notExposed = true;
}
// Continue when exposed again and the surface has a valid size. Note that
// surfaceSize can be (0, 0) even though size() reports a valid one, hence
// trusting surfacePixelSize() and not QWindow.
if (isExposed() && m_running && m_notExposed && !surfaceSize.isEmpty()) {
qDebug("exposed again");
m_notExposed = false;
m_newlyExposed = true;
}
// always render a frame on exposeEvent() (when exposed) in order to update
// immediately on window resize.
if (isExposed() && !surfaceSize.isEmpty())
render();
}
bool Window::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
render();
break;
case QEvent::PlatformSurface:
// this is the proper time to tear down the swapchain (while the native window and surface are still around)
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
releaseSwapChain();
break;
default:
break;
}
return QWindow::event(e);
}
void Window::init()
{
QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling;
if (m_graphicsApi == QRhi::Null) {
QRhiNullInitParams params;
m_rhi.reset(QRhi::create(QRhi::Null, &params, rhiFlags));
}
#if QT_CONFIG(opengl)
if (m_graphicsApi == QRhi::OpenGLES2) {
m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
params.fallbackSurface = m_fallbackSurface.get();
params.window = this;
m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params, rhiFlags));
}
#endif
#if QT_CONFIG(vulkan)
if (m_graphicsApi == QRhi::Vulkan) {
QRhiVulkanInitParams params;
params.inst = vulkanInstance();
params.window = this;
m_rhi.reset(QRhi::create(QRhi::Vulkan, &params, rhiFlags));
}
#endif
#ifdef Q_OS_WIN
if (m_graphicsApi == QRhi::D3D11) {
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
m_rhi.reset(QRhi::create(QRhi::D3D11, &params, rhiFlags));
}
#endif
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
if (m_graphicsApi == QRhi::Metal) {
QRhiMetalInitParams params;
m_rhi.reset(QRhi::create(QRhi::Metal, &params, rhiFlags));
}
#endif
if (!m_rhi)
qFatal("Failed to create RHI backend");
m_sc.reset(m_rhi->newSwapChain());
m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
QSize(), // no need to set the size here, due to UsedWithSwapChainOnly
1,
QRhiRenderBuffer::UsedWithSwapChainOnly));
m_sc->setWindow(this);
m_sc->setDepthStencil(m_ds.get());
m_rp.reset(m_sc->newCompatibleRenderPassDescriptor());
m_sc->setRenderPassDescriptor(m_rp.get());
customInit();
}
void Window::resizeSwapChain()
{
m_hasSwapChain = m_sc->createOrResize(); // also handles m_ds
const QSize outputSize = m_sc->currentPixelSize();
m_proj = m_rhi->clipSpaceCorrMatrix();
m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
m_proj.translate(0, 0, -4);
}
void Window::releaseSwapChain()
{
if (m_hasSwapChain) {
m_hasSwapChain = false;
m_sc->destroy();
}
}
void Window::render()
{
if (!m_hasSwapChain || m_notExposed)
return;
// If the window got resized or newly exposed, resize the swapchain. (the
// newly-exposed case is not actually required by some platforms, but
// f.ex. Vulkan on Windows seems to need it)
//
// This (exposeEvent + the logic here) is the only safe way to perform
// resize handling. Note the usage of the RHI's surfacePixelSize(), and
// never QWindow::size(). (the two may or may not be the same under the hood,
// depending on the backend and platform)
//
if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
m_newlyExposed = false;
}
QRhi::FrameOpResult r = m_rhi->beginFrame(m_sc.get());
if (r == QRhi::FrameOpSwapChainOutOfDate) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
r = m_rhi->beginFrame(m_sc.get());
}
if (r != QRhi::FrameOpSuccess) {
qDebug("beginFrame failed with %d, retry", r);
requestUpdate();
return;
}
customRender();
m_rhi->endFrame(m_sc.get());
// Always request the next frame via requestUpdate(). On some platforms this is backed
// by a platform-specific solution, e.g. CVDisplayLink on macOS, which is potentially
// more efficient than a timer, queued metacalls, etc.
//
// However, the rendering behavior is identical no matter how the next round of
// rendering is triggered: the rendering thread is throttled to the presentation rate
// (either in beginFrame() or endFrame()) so the triangle should rotate at the exact
// same speed no matter which approach is taken here.
#if 1
requestUpdate();
#else
QTimer::singleShot(0, this, [this] { render(); });
#endif
}
void Window::customInit()
{
}
void Window::customRender()
{
}

View File

@ -0,0 +1,64 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef WINDOW_H
#define WINDOW_H
#include <QWindow>
#include <QtGui/private/qrhinull_p.h>
#if QT_CONFIG(opengl)
#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
#endif
#if QT_CONFIG(vulkan)
#include <QtGui/private/qrhivulkan_p.h>
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
#endif
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
#include <QtGui/private/qrhimetal_p.h>
#endif
class Window : public QWindow
{
public:
Window(QRhi::Implementation graphicsApi);
void releaseSwapChain();
protected:
virtual void customInit();
virtual void customRender();
// destruction order matters to a certain degree: the fallbackSurface must
// outlive the rhi, the rhi must outlive all other resources. The resources
// need no special order when destroying.
#if QT_CONFIG(opengl)
std::unique_ptr<QOffscreenSurface> m_fallbackSurface;
#endif
std::unique_ptr<QRhi> m_rhi;
std::unique_ptr<QRhiSwapChain> m_sc;
std::unique_ptr<QRhiRenderBuffer> m_ds;
std::unique_ptr<QRhiRenderPassDescriptor> m_rp;
bool m_hasSwapChain = false;
QMatrix4x4 m_proj;
private:
void init();
void resizeSwapChain();
void render();
void exposeEvent(QExposeEvent *) override;
bool event(QEvent *) override;
QRhi::Implementation m_graphicsApi;
bool m_running = false;
bool m_notExposed = false;
bool m_newlyExposed = false;
};
#endif

View File

@ -0,0 +1,28 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## instancing Binary:
#####################################################################
qt_internal_add_manual_test(tst_manual_instancing
GUI
SOURCES
instancing.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set(instancing_resource_files
"inst.frag.qsb"
"inst.vert.qsb"
)
qt_internal_add_resource(tst_manual_instancing "instancing"
PREFIX
"/"
FILES
${instancing_resource_files}
)

View File

@ -0,0 +1,2 @@
qsb --glsl "330,300 es" --hlsl 50 --msl 12 inst.vert -o inst.vert.qsb
qsb --glsl "330,300 es" --hlsl 50 --msl 12 inst.frag -o inst.frag.qsb

View File

@ -0,0 +1,9 @@
#version 440
layout(location = 0) in vec3 vColor;
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(vColor, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,21 @@
#version 440
layout(location = 0) in vec4 position;
// Instanced attributes to variate the transform and color of the cube
layout(location = 1) in mat4 instMat;
layout(location = 5) in vec3 instColor;
layout(location = 0) out vec3 vColor;
out gl_PerVertex { vec4 gl_Position; };
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
void main()
{
vColor = instColor;
gl_Position = ubuf.mvp * instMat * position;
}

Binary file not shown.

View File

@ -0,0 +1,133 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../shared/examplefw.h"
#include "../shared/cube.h"
#include <QRandomGenerator>
// Instanced draw example. When running with OpenGL, at least 3.3 or ES 3.0 is
// needed.
const int INSTANCE_COUNT = 1024;
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *instBuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
} d;
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::Instancing))
qFatal("Instanced drawing is not supported");
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->create();
d.releasePool << d.vbuf;
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
// transform + color (mat4 + vec3), interleaved, for each instance
d.instBuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, INSTANCE_COUNT * 19 * sizeof(float));
d.instBuf->create();
d.releasePool << d.instBuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 12);
d.ubuf->create();
d.releasePool << d.ubuf;
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, d.ubuf)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/inst.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/inst.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }, // cube vertices
{ 19 * sizeof(float), QRhiVertexInputBinding::PerInstance }, // per-instance transform and color
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }, // position
{ 1, 1, QRhiVertexInputAttribute::Float4, 0, 0 }, // instMat
{ 1, 2, QRhiVertexInputAttribute::Float4, 4 * sizeof(float), 1 },
{ 1, 3, QRhiVertexInputAttribute::Float4, 8 * sizeof(float), 2 },
{ 1, 4, QRhiVertexInputAttribute::Float4, 12 * sizeof(float), 3 },
{ 1, 5, QRhiVertexInputAttribute::Float3, 16 * sizeof(float) }, // instColor
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
QByteArray instData;
instData.resize(INSTANCE_COUNT * 19 * sizeof(float));
float *p = reinterpret_cast<float *>(instData.data());
QRandomGenerator *rgen = QRandomGenerator::global();
for (int i = 0; i < INSTANCE_COUNT; ++i) {
QMatrix4x4 m;
m.translate(rgen->bounded(8000) / 100.0f - 40.0f,
rgen->bounded(8000) / 100.0f - 40.0f,
0.0f);
memcpy(p, m.constData(), 16 * sizeof(float));
p += 16;
// color
*p++ = i / float(INSTANCE_COUNT);
*p++ = 0.0f;
*p++ = 0.0f;
}
d.initialUpdates->uploadStaticBuffer(d.instBuf, instData.constData());
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.05f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding[] = {
{ d.vbuf, 0 },
{ d.instBuf, 0 }
};
cb->setVertexInput(0, 2, vbufBinding);
cb->draw(36, INSTANCE_COUNT);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
instancing.cpp
RESOURCES = instancing.qrc

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>inst.vert.qsb</file>
<file>inst.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,36 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## mrt Binary:
#####################################################################
qt_internal_add_manual_test(mrt
GUI
SOURCES
mrt.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
set(mrt_resource_files
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
"mrt.frag.qsb"
"mrt.vert.qsb"
)
qt_internal_add_resource(mrt "mrt"
PREFIX
"/"
FILES
${mrt_resource_files}
)

View File

@ -0,0 +1,2 @@
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.vert -o mrt.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.frag -o mrt.frag.qsb

Some files were not shown because too many files have changed in this diff Show More