QtBase  v6.3.1
cubemap_render.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 // Demonstrates rendering to two cubemaps in two different ways:
52 // - one by one, to each face,
53 // - if the supported max number of color attachments is greater than 4: in
54 // one go with all 6 faces attached as render targets.
55 //
56 // Finally, show what we got in a skybox-ish thing. Press the arrow keys to
57 // switch between the two cubemaps. (the only difference should be their
58 // background clear color)
59 
60 #define EXAMPLEFW_KEYPRESS_EVENTS
61 #include "../shared/examplefw.h"
62 #include "../shared/cube.h"
63 
64 // each face is 512x512
65 static const QSize cubemapSize(512, 512);
66 
67 // each cubemap face gets a 256x256 quad in the center
68 static float halfQuadVertexData[] =
69 { // Y up, CCW
70  -0.5f, 0.5f,
71  -0.5f, -0.5f,
72  0.5f, -0.5f,
73  0.5f, 0.5f,
74 };
75 
76 static quint16 halfQuadIndexData[] =
77 {
78  0, 1, 2, 0, 2, 3
79 };
80 
81 struct {
83 
84  QRhiTexture *cubemap1 = nullptr;
85  QRhiTexture *cubemap2 = nullptr;
86  bool canDoMrt = false;
87 
90 
97 
98  QRhiBuffer *mrt_ubuf = nullptr;
103 
104  QRhiBuffer *vbuf = nullptr;
105  QRhiBuffer *ubuf = nullptr;
106  QRhiSampler *sampler = nullptr;
109 
112  float rx = 0;
113 } d;
114 
116 {
118  d.cubemap1->create();
119  d.releasePool << d.cubemap1;
120 
121  d.ubufSizePerFace = rhi->ubufAligned(64 + 12);
122  d.oneface_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.ubufSizePerFace * 6);
123  d.oneface_ubuf->create();
124  d.releasePool << d.oneface_ubuf;
125 
126  for (int face = 0; face < 6; ++face) {
127  QRhiColorAttachment att(d.cubemap1);
128  att.setLayer(face);
130  d.oneface_rt[face] = rhi->newTextureRenderTarget(rtDesc);
131  if (face == 0) {
132  d.oneface_rp = d.oneface_rt[0]->newCompatibleRenderPassDescriptor();
133  d.releasePool << d.oneface_rp;
134  }
135  d.oneface_rt[face]->setRenderPassDescriptor(d.oneface_rp);
136  d.oneface_rt[face]->create();
137  d.releasePool << d.oneface_rt[face];
138  }
139 
140  d.oneface_srb = rhi->newShaderResourceBindings();
141  const QRhiShaderResourceBinding::StageFlags visibility =
143  d.oneface_srb->setBindings({
144  QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, visibility, d.oneface_ubuf, 64 + 12)
145  });
146  d.oneface_srb->create();
147  d.releasePool << d.oneface_srb;
148 
149  d.oneface_ps = rhi->newGraphicsPipeline();
150  d.oneface_ps->setShaderStages({
151  { QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_oneface.vert.qsb")) },
152  { QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_oneface.frag.qsb")) }
153  });
154  QRhiVertexInputLayout inputLayout;
155  inputLayout.setBindings({
156  { 2 * sizeof(float) }
157  });
158  inputLayout.setAttributes({
160  });
161  d.oneface_ps->setVertexInputLayout(inputLayout);
162  d.oneface_ps->setShaderResourceBindings(d.oneface_srb);
163  d.oneface_ps->setRenderPassDescriptor(d.oneface_rp);
164  d.oneface_ps->create();
165  d.releasePool << d.oneface_ps;
166 
167  // wasteful to duplicate the mvp as well but will do for now
168  for (int face = 0; face < 6; ++face) {
169  const int offset = d.ubufSizePerFace * face;
170  QMatrix4x4 identity;
171  d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset, 64, identity.constData());
172  // will use a different color for each face
173  QColor c;
174  switch (face) {
175  case 0:
176  c = Qt::red;
177  break;
178  case 1:
179  c = Qt::green;
180  break;
181  case 2:
182  c = Qt::blue;
183  break;
184  case 3:
185  c = Qt::yellow;
186  break;
187  case 4:
188  c = Qt::lightGray;
189  break;
190  case 5:
191  c = Qt::cyan;
192  break;
193  }
194  float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
195  d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset + 64, 12, color);
196  }
197 }
198 
199 // 6 render passes, 1 draw call each, targeting one cubemap face at a time
201 {
202  for (int face = 0; face < 6; ++face) {
203  cb->beginPass(d.oneface_rt[face], Qt::black, { 1.0f, 0 });
204  cb->setGraphicsPipeline(d.oneface_ps);
205  cb->setViewport({ 0, 0,
206  float(d.oneface_rt[face]->pixelSize().width()),
207  float(d.oneface_rt[face]->pixelSize().height()) });
208  const QRhiCommandBuffer::DynamicOffset dynamicOffset(0, face * d.ubufSizePerFace);
209  cb->setShaderResources(nullptr, 1, &dynamicOffset);
210  QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
211  cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
212  cb->drawIndexed(6);
213  cb->endPass();
214  }
215 }
216 
218 {
220  d.cubemap2->create();
221  d.releasePool << d.cubemap2;
222 
223  d.mrt_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 6 * 16); // note that vec3 is aligned to 16 bytes
224  d.mrt_ubuf->create();
225  d.releasePool << d.mrt_ubuf;
226 
228  for (int face = 0; face < 6; ++face) {
229  QRhiColorAttachment att(d.cubemap2);
230  att.setLayer(face);
231  attachments.append(att);
232  }
234  rtDesc.setColorAttachments(attachments.cbegin(), attachments.cend());
235  d.mrt_rt = rhi->newTextureRenderTarget(rtDesc);
236  d.mrt_rp = d.mrt_rt->newCompatibleRenderPassDescriptor();
237  d.releasePool << d.mrt_rp;
238  d.mrt_rt->setRenderPassDescriptor(d.mrt_rp);
239  d.mrt_rt->create();
240  d.releasePool << d.mrt_rt;
241 
242  d.mrt_srb = rhi->newShaderResourceBindings();
243  const QRhiShaderResourceBinding::StageFlags visibility =
245  d.mrt_srb->setBindings({
246  QRhiShaderResourceBinding::uniformBuffer(0, visibility, d.mrt_ubuf)
247  });
248  d.mrt_srb->create();
249  d.releasePool << d.mrt_srb;
250 
251  d.mrt_ps = rhi->newGraphicsPipeline();
252  d.mrt_ps->setShaderStages({
253  { QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_mrt.vert.qsb")) },
254  { QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_mrt.frag.qsb")) }
255  });
257  for (int face = 0; face < 6; ++face)
258  targetBlends.append({}); // default to blend = false, color write = all, which is good
259  d.mrt_ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
260  QRhiVertexInputLayout inputLayout;
261  inputLayout.setBindings({
262  { 2 * sizeof(float) }
263  });
264  inputLayout.setAttributes({
266  });
267  d.mrt_ps->setVertexInputLayout(inputLayout);
268  d.mrt_ps->setShaderResourceBindings(d.mrt_srb);
269  d.mrt_ps->setRenderPassDescriptor(d.mrt_rp);
270  d.mrt_ps->create();
271  d.releasePool << d.mrt_ps;
272 
273  QMatrix4x4 identity;
274  d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, 0, 64, identity.constData());
275  for (int face = 0; face < 6; ++face) {
276  const int offset = 64 + face * 16;
277  // will use a different color for each face
278  QColor c;
279  switch (face) {
280  case 0:
281  c = Qt::red;
282  break;
283  case 1:
284  c = Qt::green;
285  break;
286  case 2:
287  c = Qt::blue;
288  break;
289  case 3:
290  c = Qt::yellow;
291  break;
292  case 4:
293  c = Qt::lightGray;
294  break;
295  case 5:
296  c = Qt::cyan;
297  break;
298  }
299  float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
300  d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, offset, 12, color);
301  }
302 }
303 
304 // 1 render pass, 1 draw call, with all 6 faces attached and written to
306 {
307  // use a different clear color to differentiate from cubemap1 (because the
308  // results are expected to be identical otherwise)
309  cb->beginPass(d.mrt_rt, Qt::magenta, { 1.0f, 0 });
310  cb->setGraphicsPipeline(d.mrt_ps);
311  cb->setViewport({ 0, 0,
312  float(d.mrt_rt->pixelSize().width()),
313  float(d.mrt_rt->pixelSize().height()) });
314  cb->setShaderResources();
315  QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
316  cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
317  cb->drawIndexed(6);
318  cb->endPass();
319 }
320 
321 void Window::customInit()
322 {
323  d.half_quad_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(halfQuadVertexData));
324  d.half_quad_vbuf->create();
325  d.releasePool << d.half_quad_vbuf;
326 
327  d.half_quad_ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(halfQuadIndexData));
328  d.half_quad_ibuf->create();
329  d.releasePool << d.half_quad_ibuf;
330 
331  d.initialUpdates = m_r->nextResourceUpdateBatch();
332  d.initialUpdates->uploadStaticBuffer(d.half_quad_vbuf, 0, sizeof(halfQuadVertexData), halfQuadVertexData);
333  d.initialUpdates->uploadStaticBuffer(d.half_quad_ibuf, halfQuadIndexData);
334 
336 
337  d.canDoMrt = m_r->resourceLimit(QRhi::MaxColorAttachments) >= 6;
338  if (d.canDoMrt)
340  else
341  qWarning("Not enough color attachments (need 6, supports %d)", m_r->resourceLimit(QRhi::MaxColorAttachments));
342 
343 
344  // onscreen stuff
346  d.vbuf->create();
347  d.releasePool << d.vbuf;
348  d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
349 
351  d.ubuf->create();
352  d.releasePool << d.ubuf;
353 
356  d.sampler->create();
357  d.releasePool << d.sampler;
358 
360  d.srb->setBindings({
363  });
364  d.srb->create();
365  d.releasePool << d.srb;
366 
367  d.ps = m_r->newGraphicsPipeline();
368  d.ps->setDepthTest(true);
369  d.ps->setDepthWrite(true);
370  d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
371  d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
372  d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
373  QShader vs = getShader(QLatin1String(":/cubemap_sample.vert.qsb"));
374  Q_ASSERT(vs.isValid());
375  QShader fs = getShader(QLatin1String(":/cubemap_sample.frag.qsb"));
376  Q_ASSERT(fs.isValid());
377  d.ps->setShaderStages({
378  { QRhiShaderStage::Vertex, vs },
380  });
381  QRhiVertexInputLayout inputLayout;
382  inputLayout.setBindings({
383  { 3 * sizeof(float) }
384  });
385  inputLayout.setAttributes({
387  });
388  d.ps->setVertexInputLayout(inputLayout);
389  d.ps->setShaderResourceBindings(d.srb);
390  d.ps->setRenderPassDescriptor(m_rp);
391  d.ps->create();
392  d.releasePool << d.ps;
393 
394  if (d.canDoMrt)
395  qDebug("Use the arrow keys to switch between the two generated cubemaps");
396 }
397 
399 {
400  qDeleteAll(d.releasePool);
401  d.releasePool.clear();
402 }
403 
405 {
406  const QSize outputSizeInPixels = m_sc->currentPixelSize();
407  QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
409  if (d.initialUpdates) {
410  u->merge(d.initialUpdates);
411  d.initialUpdates->release();
412  d.initialUpdates = nullptr;
413  }
414 
416  mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
417  mvp.scale(10);
418  mvp.rotate(d.rx, 1, 0, 0);
419  d.rx += 0.5f;
420  u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
421 
422  cb->resourceUpdate(u);
423 
424  renderPerFace(cb);
425 
426  if (d.canDoMrt)
427  renderWithMrt(cb);
428 
429  cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
430  cb->setGraphicsPipeline(d.ps);
431  cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
432  cb->setShaderResources();
433  const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
434  cb->setVertexInput(0, 1, &vbufBinding);
435  cb->draw(36);
436  cb->endPass();
437 }
438 
440 {
441  switch (e->key()) {
442  case Qt::Key_Left:
443  case Qt::Key_Up:
444  qDebug("Showing first cubemap (generated by rendering to the faces one by one; black background)");
445  d.srb->setBindings({
448  });
449  d.srb->create();
450  break;
451  case Qt::Key_Right:
452  case Qt::Key_Down:
453  if (d.canDoMrt) {
454  qDebug("Showing second cubemap (generated with multiple render targets; magenta background)");
455  d.srb->setBindings({
458  });
459  d.srb->create();
460  }
461  break;
462  default:
463  e->ignore();
464  break;
465  }
466 }
small capitals from c petite p scientific f u
Definition: afcover.h:88
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition: qcolor.h:67
The QKeyEvent class describes a key event.
Definition: qevent.h:471
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 rotate(float angle, const QVector3D &vector)
void scale(const QVector3D &vector)
Definition: qmatrix4x4.cpp:803
void perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane)
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
void setLayer(int layer)
Definition: qrhi_p.h:470
QPair< QRhiBuffer *, quint32 > VertexInput
Definition: qrhi_p.h:1426
QPair< int, quint32 > DynamicOffset
Definition: qrhi_p.h:1422
Definition: qrhi_p.h:1536
int ubufAligned(int v) const
Definition: qrhi.cpp:6207
int resourceLimit(ResourceLimit limit) const
Definition: qrhi.cpp:6329
QMatrix4x4 clipSpaceCorrMatrix() const
Definition: qrhi.cpp:6299
QRhiShaderResourceBindings * newShaderResourceBindings()
Definition: qrhi.cpp:6543
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
Definition: qrhi.cpp:6562
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
QRhiTextureRenderTarget * newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags={})
Definition: qrhi.cpp:6696
QRhiGraphicsPipeline * newGraphicsPipeline()
Definition: qrhi.cpp:6520
@ MaxColorAttachments
Definition: qrhi_p.h:1607
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition: qrhi.cpp:6609
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition: qrhi.cpp:5568
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 uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf)
Definition: qrhi.cpp:3268
@ RenderTarget
Definition: qrhi_p.h:766
void setColorAttachments(std::initializer_list< QRhiColorAttachment > list)
Definition: qrhi_p.h:504
void setBindings(std::initializer_list< QRhiVertexInputBinding > list)
Definition: qrhi_p.h:257
void setAttributes(std::initializer_list< QRhiVertexInputAttribute > list)
Definition: qrhi_p.h:268
bool isValid() const
Definition: qshader.cpp:266
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
void append(const T &t)
std::unique_ptr< QRhiSwapChain > m_sc
Definition: window.h:89
std::unique_ptr< QRhiRenderPassDescriptor > m_rp
Definition: window.h:91
void keyPressEvent(QKeyEvent *event) override
Definition: window.cpp:112
virtual void customRender()
QColor m_clearColor
Definition: examplefw.h:176
virtual void customInit()
QRhi * m_r
Definition: examplefw.h:161
void initializePerFaceRendering(QRhi *rhi)
float rx
QRhiBuffer * mrt_ubuf
QRhiRenderPassDescriptor * oneface_rp
bool canDoMrt
void renderPerFace(QRhiCommandBuffer *cb)
QRhiBuffer * half_quad_vbuf
QRhiGraphicsPipeline * mrt_ps
QRhiBuffer * ubuf
QRhiShaderResourceBindings * mrt_srb
QList< QRhiResource * > releasePool
QRhiBuffer * half_quad_ibuf
QRhiTexture * cubemap1
QRhiTextureRenderTarget * mrt_rt
QRhiTextureRenderTarget * oneface_rt[6]
QRhiRenderPassDescriptor * mrt_rp
QRhiGraphicsPipeline * oneface_ps
void initializeMrtRendering(QRhi *rhi)
void renderWithMrt(QRhiCommandBuffer *cb)
QRhiShaderResourceBindings * srb
QRhiBuffer * vbuf
QRhiShaderResourceBindings * oneface_srb
QRhiBuffer * oneface_ubuf
QMatrix4x4 winProj
struct @908 d
QRhiGraphicsPipeline * ps
QRhiResourceUpdateBatch * initialUpdates
QRhiTexture * cubemap2
int ubufSizePerFace
qDeleteAll(list.begin(), list.end())
double e
QShader getShader(const QString &name)
Definition: examplefw.h:86
@ cyan
Definition: qnamespace.h:69
@ blue
Definition: qnamespace.h:68
@ magenta
Definition: qnamespace.h:70
@ yellow
Definition: qnamespace.h:71
@ black
Definition: qnamespace.h:61
@ lightGray
Definition: qnamespace.h:65
@ green
Definition: qnamespace.h:67
@ red
Definition: qnamespace.h:66
@ Key_Right
Definition: qnamespace.h:700
@ Key_Left
Definition: qnamespace.h:698
@ Key_Up
Definition: qnamespace.h:699
@ Key_Down
Definition: qnamespace.h:701
unsigned short quint16
Definition: qglobal.h:286
#define qDebug
[1]
Definition: qlogging.h:177
#define qWarning
Definition: qlogging.h:179
GLuint sampler
GLenum face
GLuint color
[2]
GLsizei const GLenum * attachments
GLenum GLuint GLintptr offset
const GLubyte * c
Definition: qopenglext.h:12701
#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)