mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-04 00:05:25 +08:00
qt 6.5.1 original
This commit is contained in:
35
tests/manual/rhi/CMakeLists.txt
Normal file
35
tests/manual/rhi/CMakeLists.txt
Normal 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()
|
38
tests/manual/rhi/compressedtexture_bc1/CMakeLists.txt
Normal file
38
tests/manual/rhi/compressedtexture_bc1/CMakeLists.txt
Normal 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}
|
||||
)
|
153
tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp
Normal file
153
tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp
Normal 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();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
compressedtexture_bc1.cpp
|
||||
|
||||
RESOURCES = compressedtexture_bc1.qrc
|
@ -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>
|
@ -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}
|
||||
)
|
@ -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();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
compressedtexture_bc1_subupload.cpp
|
||||
|
||||
RESOURCES = compressedtexture_bc1_subupload.qrc
|
@ -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>
|
32
tests/manual/rhi/computebuffer/CMakeLists.txt
Normal file
32
tests/manual/rhi/computebuffer/CMakeLists.txt
Normal 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"
|
41
tests/manual/rhi/computebuffer/buffer.comp
Normal file
41
tests/manual/rhi/computebuffer/buffer.comp
Normal 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;
|
||||
}
|
||||
}
|
BIN
tests/manual/rhi/computebuffer/buffer.comp.qsb
Normal file
BIN
tests/manual/rhi/computebuffer/buffer.comp.qsb
Normal file
Binary file not shown.
3
tests/manual/rhi/computebuffer/buildshaders.bat
Normal file
3
tests/manual/rhi/computebuffer/buildshaders.bat
Normal 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
|
158
tests/manual/rhi/computebuffer/computebuffer.cpp
Normal file
158
tests/manual/rhi/computebuffer/computebuffer.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/computebuffer/computebuffer.pro
Normal file
8
tests/manual/rhi/computebuffer/computebuffer.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
computebuffer.cpp
|
||||
|
||||
RESOURCES = computebuffer.qrc
|
7
tests/manual/rhi/computebuffer/computebuffer.qrc
Normal file
7
tests/manual/rhi/computebuffer/computebuffer.qrc
Normal 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>
|
8
tests/manual/rhi/computebuffer/main.frag
Normal file
8
tests/manual/rhi/computebuffer/main.frag
Normal file
@ -0,0 +1,8 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(1.0);
|
||||
}
|
BIN
tests/manual/rhi/computebuffer/main.frag.qsb
Normal file
BIN
tests/manual/rhi/computebuffer/main.frag.qsb
Normal file
Binary file not shown.
11
tests/manual/rhi/computebuffer/main.vert
Normal file
11
tests/manual/rhi/computebuffer/main.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/computebuffer/main.vert.qsb
Normal file
BIN
tests/manual/rhi/computebuffer/main.vert.qsb
Normal file
Binary file not shown.
39
tests/manual/rhi/computeimage/CMakeLists.txt
Normal file
39
tests/manual/rhi/computeimage/CMakeLists.txt
Normal 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}
|
||||
)
|
1
tests/manual/rhi/computeimage/buildshaders.bat
Normal file
1
tests/manual/rhi/computeimage/buildshaders.bat
Normal file
@ -0,0 +1 @@
|
||||
qsb --glsl "310 es,430" --hlsl 50 --msl 12 image.comp -o image.comp.qsb
|
181
tests/manual/rhi/computeimage/computeimage.cpp
Normal file
181
tests/manual/rhi/computeimage/computeimage.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/computeimage/computeimage.pro
Normal file
8
tests/manual/rhi/computeimage/computeimage.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
computeimage.cpp
|
||||
|
||||
RESOURCES = computeimage.qrc
|
8
tests/manual/rhi/computeimage/computeimage.qrc
Normal file
8
tests/manual/rhi/computeimage/computeimage.qrc
Normal 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>
|
20
tests/manual/rhi/computeimage/image.comp
Normal file
20
tests/manual/rhi/computeimage/image.comp
Normal 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));
|
||||
}
|
BIN
tests/manual/rhi/computeimage/image.comp.qsb
Normal file
BIN
tests/manual/rhi/computeimage/image.comp.qsb
Normal file
Binary file not shown.
29
tests/manual/rhi/cubemap/CMakeLists.txt
Normal file
29
tests/manual/rhi/cubemap/CMakeLists.txt
Normal 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}
|
||||
)
|
2
tests/manual/rhi/cubemap/buildshaders.bat
Normal file
2
tests/manual/rhi/cubemap/buildshaders.bat
Normal 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
|
BIN
tests/manual/rhi/cubemap/c.png
Normal file
BIN
tests/manual/rhi/cubemap/c.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
135
tests/manual/rhi/cubemap/cubemap.cpp
Normal file
135
tests/manual/rhi/cubemap/cubemap.cpp
Normal 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();
|
||||
}
|
10
tests/manual/rhi/cubemap/cubemap.frag
Normal file
10
tests/manual/rhi/cubemap/cubemap.frag
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/cubemap/cubemap.frag.qsb
Normal file
BIN
tests/manual/rhi/cubemap/cubemap.frag.qsb
Normal file
Binary file not shown.
8
tests/manual/rhi/cubemap/cubemap.pro
Normal file
8
tests/manual/rhi/cubemap/cubemap.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
cubemap.cpp
|
||||
|
||||
RESOURCES = cubemap.qrc
|
7
tests/manual/rhi/cubemap/cubemap.qrc
Normal file
7
tests/manual/rhi/cubemap/cubemap.qrc
Normal 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>
|
15
tests/manual/rhi/cubemap/cubemap.vert
Normal file
15
tests/manual/rhi/cubemap/cubemap.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/cubemap/cubemap.vert.qsb
Normal file
BIN
tests/manual/rhi/cubemap/cubemap.vert.qsb
Normal file
Binary file not shown.
32
tests/manual/rhi/cubemap_render/CMakeLists.txt
Normal file
32
tests/manual/rhi/cubemap_render/CMakeLists.txt
Normal 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}
|
||||
)
|
6
tests/manual/rhi/cubemap_render/buildshaders.bat
Normal file
6
tests/manual/rhi/cubemap_render/buildshaders.bat
Normal 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
|
28
tests/manual/rhi/cubemap_render/cubemap_mrt.frag
Normal file
28
tests/manual/rhi/cubemap_render/cubemap_mrt.frag
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_mrt.frag.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_mrt.frag.qsb
Normal file
Binary file not shown.
20
tests/manual/rhi/cubemap_render/cubemap_mrt.vert
Normal file
20
tests/manual/rhi/cubemap_render/cubemap_mrt.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_mrt.vert.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_mrt.vert.qsb
Normal file
Binary file not shown.
13
tests/manual/rhi/cubemap_render/cubemap_oneface.frag
Normal file
13
tests/manual/rhi/cubemap_render/cubemap_oneface.frag
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_oneface.frag.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_oneface.frag.qsb
Normal file
Binary file not shown.
15
tests/manual/rhi/cubemap_render/cubemap_oneface.vert
Normal file
15
tests/manual/rhi/cubemap_render/cubemap_oneface.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_oneface.vert.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_oneface.vert.qsb
Normal file
Binary file not shown.
419
tests/manual/rhi/cubemap_render/cubemap_render.cpp
Normal file
419
tests/manual/rhi/cubemap_render/cubemap_render.cpp
Normal 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;
|
||||
}
|
||||
}
|
8
tests/manual/rhi/cubemap_render/cubemap_render.pro
Normal file
8
tests/manual/rhi/cubemap_render/cubemap_render.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
cubemap_render.cpp
|
||||
|
||||
RESOURCES = cubemap_render.qrc
|
10
tests/manual/rhi/cubemap_render/cubemap_render.qrc
Normal file
10
tests/manual/rhi/cubemap_render/cubemap_render.qrc
Normal 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>
|
10
tests/manual/rhi/cubemap_render/cubemap_sample.frag
Normal file
10
tests/manual/rhi/cubemap_render/cubemap_sample.frag
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_sample.frag.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_sample.frag.qsb
Normal file
Binary file not shown.
16
tests/manual/rhi/cubemap_render/cubemap_sample.vert
Normal file
16
tests/manual/rhi/cubemap_render/cubemap_sample.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/cubemap_render/cubemap_sample.vert.qsb
Normal file
BIN
tests/manual/rhi/cubemap_render/cubemap_sample.vert.qsb
Normal file
Binary file not shown.
38
tests/manual/rhi/cubemap_scissor/CMakeLists.txt
Normal file
38
tests/manual/rhi/cubemap_scissor/CMakeLists.txt
Normal 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}
|
||||
)
|
194
tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp
Normal file
194
tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/cubemap_scissor/cubemap_scissor.pro
Normal file
8
tests/manual/rhi/cubemap_scissor/cubemap_scissor.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
cubemap_scissor.cpp
|
||||
|
||||
RESOURCES = cubemap_scissor.qrc
|
7
tests/manual/rhi/cubemap_scissor/cubemap_scissor.qrc
Normal file
7
tests/manual/rhi/cubemap_scissor/cubemap_scissor.qrc
Normal 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>
|
40
tests/manual/rhi/float16texture_with_compute/CMakeLists.txt
Normal file
40
tests/manual/rhi/float16texture_with_compute/CMakeLists.txt
Normal 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}
|
||||
)
|
@ -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
|
@ -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();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
float16texture_with_compute.cpp
|
||||
|
||||
RESOURCES = float16texture_with_compute.qrc
|
@ -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>
|
19
tests/manual/rhi/float16texture_with_compute/load.comp
Normal file
19
tests/manual/rhi/float16texture_with_compute/load.comp
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/float16texture_with_compute/load.comp.qsb
Normal file
BIN
tests/manual/rhi/float16texture_with_compute/load.comp.qsb
Normal file
Binary file not shown.
50
tests/manual/rhi/float16texture_with_compute/prefilter.comp
Normal file
50
tests/manual/rhi/float16texture_with_compute/prefilter.comp
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/float16texture_with_compute/prefilter.comp.qsb
Normal file
BIN
tests/manual/rhi/float16texture_with_compute/prefilter.comp.qsb
Normal file
Binary file not shown.
41
tests/manual/rhi/floattexture/CMakeLists.txt
Normal file
41
tests/manual/rhi/floattexture/CMakeLists.txt
Normal 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"
|
281
tests/manual/rhi/floattexture/floattexture.cpp
Normal file
281
tests/manual/rhi/floattexture/floattexture.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/floattexture/floattexture.pro
Normal file
8
tests/manual/rhi/floattexture/floattexture.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
floattexture.cpp
|
||||
|
||||
RESOURCES = floattexture.qrc
|
7
tests/manual/rhi/floattexture/floattexture.qrc
Normal file
7
tests/manual/rhi/floattexture/floattexture.qrc
Normal 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>
|
24
tests/manual/rhi/geometryshader/CMakeLists.txt
Normal file
24
tests/manual/rhi/geometryshader/CMakeLists.txt
Normal 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}
|
||||
)
|
4
tests/manual/rhi/geometryshader/buildshaders.bat
Normal file
4
tests/manual/rhi/geometryshader/buildshaders.bat
Normal 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
|
94
tests/manual/rhi/geometryshader/geometryshader.cpp
Normal file
94
tests/manual/rhi/geometryshader/geometryshader.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/geometryshader/test.frag
Normal file
8
tests/manual/rhi/geometryshader/test.frag
Normal file
@ -0,0 +1,8 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(1.0);
|
||||
}
|
BIN
tests/manual/rhi/geometryshader/test.frag.qsb
Normal file
BIN
tests/manual/rhi/geometryshader/test.frag.qsb
Normal file
Binary file not shown.
26
tests/manual/rhi/geometryshader/test.geom
Normal file
26
tests/manual/rhi/geometryshader/test.geom
Normal 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();
|
||||
|
||||
}
|
BIN
tests/manual/rhi/geometryshader/test.geom.qsb
Normal file
BIN
tests/manual/rhi/geometryshader/test.geom.qsb
Normal file
Binary file not shown.
8
tests/manual/rhi/geometryshader/test.vert
Normal file
8
tests/manual/rhi/geometryshader/test.vert
Normal file
@ -0,0 +1,8 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
BIN
tests/manual/rhi/geometryshader/test.vert.qsb
Normal file
BIN
tests/manual/rhi/geometryshader/test.vert.qsb
Normal file
Binary file not shown.
26
tests/manual/rhi/geometryshader/test_geom.hlsl
Normal file
26
tests/manual/rhi/geometryshader/test_geom.hlsl
Normal 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);
|
||||
}
|
||||
}
|
35
tests/manual/rhi/hellominimalcrossgfxtriangle/CMakeLists.txt
Normal file
35
tests/manual/rhi/hellominimalcrossgfxtriangle/CMakeLists.txt
Normal 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}
|
||||
)
|
@ -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
|
@ -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>
|
112
tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp
Normal file
112
tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp
Normal 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();
|
||||
}
|
31
tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h
Normal file
31
tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h
Normal 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
|
112
tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp
Normal file
112
tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp
Normal 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;
|
||||
}
|
219
tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp
Normal file
219
tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp
Normal 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, ¶ms, 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, ¶ms, 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, ¶ms, rhiFlags));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_graphicsApi == QRhi::D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
params.enableDebugLayer = true;
|
||||
m_rhi.reset(QRhi::create(QRhi::D3D11, ¶ms, 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, ¶ms, 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()
|
||||
{
|
||||
}
|
64
tests/manual/rhi/hellominimalcrossgfxtriangle/window.h
Normal file
64
tests/manual/rhi/hellominimalcrossgfxtriangle/window.h
Normal 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
|
28
tests/manual/rhi/instancing/CMakeLists.txt
Normal file
28
tests/manual/rhi/instancing/CMakeLists.txt
Normal 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}
|
||||
)
|
2
tests/manual/rhi/instancing/buildshaders.bat
Normal file
2
tests/manual/rhi/instancing/buildshaders.bat
Normal 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
|
9
tests/manual/rhi/instancing/inst.frag
Normal file
9
tests/manual/rhi/instancing/inst.frag
Normal 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);
|
||||
}
|
BIN
tests/manual/rhi/instancing/inst.frag.qsb
Normal file
BIN
tests/manual/rhi/instancing/inst.frag.qsb
Normal file
Binary file not shown.
21
tests/manual/rhi/instancing/inst.vert
Normal file
21
tests/manual/rhi/instancing/inst.vert
Normal 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;
|
||||
}
|
BIN
tests/manual/rhi/instancing/inst.vert.qsb
Normal file
BIN
tests/manual/rhi/instancing/inst.vert.qsb
Normal file
Binary file not shown.
133
tests/manual/rhi/instancing/instancing.cpp
Normal file
133
tests/manual/rhi/instancing/instancing.cpp
Normal 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();
|
||||
}
|
8
tests/manual/rhi/instancing/instancing.pro
Normal file
8
tests/manual/rhi/instancing/instancing.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
instancing.cpp
|
||||
|
||||
RESOURCES = instancing.qrc
|
6
tests/manual/rhi/instancing/instancing.qrc
Normal file
6
tests/manual/rhi/instancing/instancing.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>inst.vert.qsb</file>
|
||||
<file>inst.frag.qsb</file>
|
||||
</qresource>
|
||||
</RCC>
|
36
tests/manual/rhi/mrt/CMakeLists.txt
Normal file
36
tests/manual/rhi/mrt/CMakeLists.txt
Normal 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}
|
||||
)
|
2
tests/manual/rhi/mrt/buildshaders.bat
Normal file
2
tests/manual/rhi/mrt/buildshaders.bat
Normal 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
Reference in New Issue
Block a user