QtBase  v6.3.1
float16texture_with_compute.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 // An advanced version of floattexture. Instead of RGBA32F, we use RGBA16F, and
52 // also generate the floating point data from rgba with compute. Then there's a
53 // compute pass using the BSDF prefiltering taken from Qt Quick 3D, which
54 // generates all the mip levels.
55 
56 // Why do we animate the scale of the quad rendered to the window? To have
57 // different mip levels used, to prove that all of them are generated
58 // correctly, without artifacts (which would occur if memory barriers were not
59 // correctly generated by QRhi). For full verification use RenderDoc or similar.
60 
61 #include "../shared/examplefw.h"
62 #include <qmath.h>
63 
64 static float vertexData[] =
65 { // Y up, CCW
66  -0.5f, 0.5f, 0.0f, 0.0f,
67  -0.5f, -0.5f, 0.0f, 1.0f,
68  0.5f, -0.5f, 1.0f, 1.0f,
69  0.5f, 0.5f, 1.0f, 0.0f
70 };
71 
72 static quint16 indexData[] =
73 {
74  0, 1, 2, 0, 2, 3
75 };
76 
77 static const int MAX_MIP_LEVELS = 20;
78 
79 struct {
81 
82  QRhiBuffer *vbuf = nullptr;
83  QRhiBuffer *ibuf = nullptr;
84  QRhiBuffer *ubuf = nullptr;
85  QRhiTexture *texRgba = nullptr;
86  QRhiTexture *texFloat16 = nullptr;
87  QRhiSampler *sampler = nullptr;
90 
97 
99  bool computeDone = false;
100  int mipCount;
102  quint32 prefilterNumWorkGroups[MAX_MIP_LEVELS][3];
103  float scale = 2.5f;
104  int scale_dir = -1;
105 } d;
106 
108 {
109  const int w = d.texRgba->pixelSize().width() / 16;
110  const int h = d.texRgba->pixelSize().height() / 16;
111 
112  cb->beginComputePass();
113 
114  cb->setComputePipeline(d.computePipeline_load);
115  cb->setShaderResources();
116  cb->dispatch(w, h, 1);
117 
118  cb->setComputePipeline(d.computePipeline_prefilter);
119  for (int level = 1; level < d.mipCount; ++level) {
120  const int i = level - 1;
121  const int mipW = d.prefilterNumWorkGroups[i][0];
122  const int mipH = d.prefilterNumWorkGroups[i][1];
123  QPair<int, quint32> dynamicOffset = { 0, quint32(d.prefilterUBufElemSize * i) };
124  cb->setShaderResources(d.computeBindings_prefilter[i], 1, &dynamicOffset);
125  cb->dispatch(mipW, mipH, 1);
126  }
127 
128  cb->endComputePass();
129 }
130 
131 void Window::customInit()
132 {
134  qFatal("Compute is not supported");
135 
137  qFatal("RGBA16F texture format is not supported");
138 
139  d.initialUpdates = m_r->nextResourceUpdateBatch();
140 
141  // load rgba8 image data
142 
143  QImage image;
144  image.load(QLatin1String(":/qt256.png"));
145  image = image.convertToFormat(QImage::Format_RGBA8888);
146  Q_ASSERT(!image.isNull());
148  d.texRgba->create();
149  d.releasePool << d.texRgba;
150 
151  d.initialUpdates->uploadTexture(d.texRgba, image);
152 
153  d.mipCount = m_r->mipLevelsForSize(image.size());
154  Q_ASSERT(d.mipCount <= MAX_MIP_LEVELS);
155 
157  d.releasePool << d.texFloat16;
158  d.texFloat16->create();
159 
160  // compute
161 
162  d.computeUBuf_load = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 12);
163  d.computeUBuf_load->create();
164  d.releasePool << d.computeUBuf_load;
165 
166  quint32 numWorkGroups[3] = { quint32(image.width()), quint32(image.height()), 0 };
167  d.initialUpdates->updateDynamicBuffer(d.computeUBuf_load, 0, 12, numWorkGroups);
168 
169  d.computeBindings_load = m_r->newShaderResourceBindings();
170  d.computeBindings_load->setBindings({
174  });
175  d.computeBindings_load->create();
176  d.releasePool << d.computeBindings_load;
177 
178  d.computePipeline_load = m_r->newComputePipeline();
179  d.computePipeline_load->setShaderResourceBindings(d.computeBindings_load);
180  d.computePipeline_load->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/load.comp.qsb")) });
181  d.computePipeline_load->create();
182  d.releasePool << d.computePipeline_load;
183 
184  d.prefilterUBufElemSize = m_r->ubufAligned(12);
185  d.computeUBuf_prefilter = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.prefilterUBufElemSize * d.mipCount);
186  d.computeUBuf_prefilter->create();
187  d.releasePool << d.computeUBuf_prefilter;
188 
189  int mipW = image.width() >> 1;
190  int mipH = image.height() >> 1;
191  for (int level = 1; level < d.mipCount; ++level) {
192  const int i = level - 1;
193  d.prefilterNumWorkGroups[i][0] = quint32(mipW);
194  d.prefilterNumWorkGroups[i][1] = quint32(mipH);
195  d.prefilterNumWorkGroups[i][2] = 0;
196  d.initialUpdates->updateDynamicBuffer(d.computeUBuf_prefilter, d.prefilterUBufElemSize * i, 12, d.prefilterNumWorkGroups[i]);
197  mipW = mipW > 2 ? mipW >> 1 : 1;
198  mipH = mipH > 2 ? mipH >> 1 : 1;
199 
200  d.computeBindings_prefilter[i] = m_r->newShaderResourceBindings();
201  d.computeBindings_prefilter[i]->setBindings({
205  });
206  d.computeBindings_prefilter[i]->create();
207  d.releasePool << d.computeBindings_prefilter[i];
208  }
209 
210  d.computePipeline_prefilter = m_r->newComputePipeline();
211  d.computePipeline_prefilter->setShaderResourceBindings(d.computeBindings_prefilter[0]); // just need a layout compatible one
212  d.computePipeline_prefilter->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/prefilter.comp.qsb")) });
213  d.computePipeline_prefilter->create();
214  d.releasePool << d.computePipeline_prefilter;
215 
216  // graphics
217 
218  d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
219  d.vbuf->create();
220  d.releasePool << d.vbuf;
221 
222  d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
223  d.ibuf->create();
224  d.releasePool << d.ibuf;
225 
227  d.ubuf->create();
228  d.releasePool << d.ubuf;
229 
230  // enable mipmaps
233  d.releasePool << d.sampler;
234  d.sampler->create();
235 
237  d.releasePool << d.srb;
238  d.srb->setBindings({
241  });
242  d.srb->create();
243 
244  d.ps = m_r->newGraphicsPipeline();
245  d.releasePool << d.ps;
246  d.ps->setShaderStages({
247  { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
248  { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
249  });
250  QRhiVertexInputLayout inputLayout;
251  inputLayout.setBindings({
252  { 4 * sizeof(float) }
253  });
254  inputLayout.setAttributes({
256  { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
257  });
258  d.ps->setVertexInputLayout(inputLayout);
259  d.ps->setShaderResourceBindings(d.srb);
260  d.ps->setRenderPassDescriptor(m_rp);
261  d.ps->create();
262 
263  d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
264  d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
265 
266  qint32 flip = 0;
267  d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
268 }
269 
271 {
272  qDeleteAll(d.releasePool);
273  d.releasePool.clear();
274 }
275 
277 {
278  QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
280  if (d.initialUpdates) {
281  u->merge(d.initialUpdates);
282  d.initialUpdates->release();
283  d.initialUpdates = nullptr;
284  }
285 
286  QMatrix4x4 mvp = m_proj;
287  mvp.scale(d.scale);
288  d.scale += d.scale_dir * 0.01f;
289  if (qFuzzyIsNull(d.scale) || d.scale >= 2.5f)
290  d.scale_dir *= -1;
291  u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
292 
293  cb->resourceUpdate(u);
294 
295  // If not yet done, then do a compute pass that uploads level 0, doing an
296  // rgba8 -> float16 conversion. Follow that with another compute pass to do
297  // the filtering and generate all the mip levels.
298  if (!d.computeDone) {
300  d.computeDone = true;
301  }
302 
303  const QSize outputSizeInPixels = m_sc->currentPixelSize();
304  cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
305  cb->setGraphicsPipeline(d.ps);
306  cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
307  cb->setShaderResources();
308  const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
309  cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
310  cb->drawIndexed(6);
311  cb->endPass();
312 }
small capitals from c petite p scientific f u
Definition: afcover.h:88
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
The QImage class provides a hardware-independent image representation that allows direct access to th...
Definition: qimage.h:73
@ Format_RGBA8888
Definition: qimage.h:95
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition: qmatrix4x4.h:61
void scale(const QVector3D &vector)
Definition: qmatrix4x4.cpp:803
const float * constData() const
Definition: qmatrix4x4.h:183
@ Immutable
Definition: qrhi_p.h:717
@ Dynamic
Definition: qrhi_p.h:719
@ IndexBuffer
Definition: qrhi_p.h:724
@ VertexBuffer
Definition: qrhi_p.h:723
@ UniformBuffer
Definition: qrhi_p.h:725
QPair< QRhiBuffer *, quint32 > VertexInput
Definition: qrhi_p.h:1426
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition: qrhi.cpp:6310
int ubufAligned(int v) const
Definition: qrhi.cpp:6207
bool isFeatureSupported(QRhi::Feature feature) const
Definition: qrhi.cpp:6318
QRhiShaderResourceBindings * newShaderResourceBindings()
Definition: qrhi.cpp:6543
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
Definition: qrhi.cpp:6562
QRhiComputePipeline * newComputePipeline()
Definition: qrhi.cpp:6533
QRhiSampler * newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode addressU, QRhiSampler::AddressMode addressV, QRhiSampler::AddressMode addressW=QRhiSampler::Repeat)
Definition: qrhi.cpp:6679
QRhiGraphicsPipeline * newGraphicsPipeline()
Definition: qrhi.cpp:6520
int mipLevelsForSize(const QSize &size) const
Definition: qrhi.cpp:6216
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition: qrhi.cpp:6609
@ Compute
Definition: qrhi_p.h:1574
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition: qrhi.cpp:5568
@ ClampToEdge
Definition: qrhi_p.h:884
static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, int size)
Definition: qrhi.cpp:3342
static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
Definition: qrhi.cpp:3374
static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level)
Definition: qrhi.cpp:3455
static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level)
Definition: qrhi.cpp:3483
static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf)
Definition: qrhi.cpp:3268
@ UsedWithLoadStore
Definition: qrhi_p.h:772
@ MipMapped
Definition: qrhi_p.h:768
void setBindings(std::initializer_list< QRhiVertexInputBinding > list)
Definition: qrhi_p.h:257
void setAttributes(std::initializer_list< QRhiVertexInputAttribute > list)
Definition: qrhi_p.h:268
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:55
constexpr int height() const noexcept
Definition: qsize.h:160
constexpr int width() const noexcept
Definition: qsize.h:157
std::unique_ptr< QRhiSwapChain > m_sc
Definition: window.h:89
std::unique_ptr< QRhiRenderPassDescriptor > m_rp
Definition: window.h:91
virtual void customRender()
QColor m_clearColor
Definition: examplefw.h:176
QMatrix4x4 m_proj
Definition: window.h:94
virtual void customInit()
QRhi * m_r
Definition: examplefw.h:161
qDeleteAll(list.begin(), list.end())
QShader getShader(const QString &name)
Definition: examplefw.h:86
QRhiBuffer * ibuf
quint32 prefilterNumWorkGroups[MAX_MIP_LEVELS][3]
QRhiBuffer * computeUBuf_prefilter
QRhiBuffer * ubuf
QList< QRhiResource * > releasePool
void recordUploadThenFilterFloat16TextureWithCompute(QRhiCommandBuffer *cb)
struct @910 d
QRhiComputePipeline * computePipeline_load
QRhiTexture * texFloat16
QRhiShaderResourceBindings * computeBindings_prefilter[MAX_MIP_LEVELS]
QRhiComputePipeline * computePipeline_prefilter
QRhiTexture * texRgba
QRhiShaderResourceBindings * srb
QRhiBuffer * vbuf
QRhiBuffer * computeUBuf_load
QRhiShaderResourceBindings * computeBindings_load
QRhiGraphicsPipeline * ps
QRhiResourceUpdateBatch * initialUpdates
Definition: image.cpp:51
std::pair< T1, T2 > QPair
Definition: qcontainerfwd.h:56
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition: qfloat16.h:249
unsigned int quint32
Definition: qglobal.h:288
unsigned short quint16
Definition: qglobal.h:286
int qint32
Definition: qglobal.h:287
#define qFatal
Definition: qlogging.h:181
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLuint sampler
GLfloat GLfloat GLfloat GLfloat h
GLeglImageOES image
GLenum GLenum GLenum GLenum GLenum scale
Definition: qopenglext.h:10817
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)