1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 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 | #include "renderer.h" |
52 | #include "qrandom.h" |
53 | #include <QVulkanFunctions> |
54 | #include <QtConcurrentRun> |
55 | #include <QTime> |
56 | |
57 | static float quadVert[] = { // Y up, front = CW |
58 | -1, -1, 0, |
59 | -1, 1, 0, |
60 | 1, -1, 0, |
61 | 1, 1, 0 |
62 | }; |
63 | |
64 | #define DBG Q_UNLIKELY(m_window->isDebugEnabled()) |
65 | |
66 | const int MAX_INSTANCES = 16384; |
67 | const VkDeviceSize PER_INSTANCE_DATA_SIZE = 6 * sizeof(float); // instTranslate, instDiffuseAdjust |
68 | |
69 | static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) |
70 | { |
71 | return (v + byteAlign - 1) & ~(byteAlign - 1); |
72 | } |
73 | |
74 | Renderer::Renderer(VulkanWindow *w, int initialCount) |
75 | : m_window(w), |
76 | // Have the light positioned just behind the default camera position, looking forward. |
77 | m_lightPos(0.0f, 0.0f, 25.0f), |
78 | m_cam(QVector3D(0.0f, 0.0f, 20.0f)), // starting camera position |
79 | m_instCount(initialCount) |
80 | { |
81 | m_floorModel.translate(x: 0, y: -5, z: 0); |
82 | m_floorModel.rotate(angle: -90, x: 1, y: 0, z: 0); |
83 | m_floorModel.scale(x: 20, y: 100, z: 1); |
84 | |
85 | m_blockMesh.load(QStringLiteral(":/block.buf" )); |
86 | m_logoMesh.load(QStringLiteral(":/qt_logo.buf" )); |
87 | |
88 | QObject::connect(sender: &m_frameWatcher, signal: &QFutureWatcherBase::finished, slot: [this] { |
89 | if (m_framePending) { |
90 | m_framePending = false; |
91 | m_window->frameReady(); |
92 | m_window->requestUpdate(); |
93 | } |
94 | }); |
95 | } |
96 | |
97 | void Renderer::preInitResources() |
98 | { |
99 | const QVector<int> sampleCounts = m_window->supportedSampleCounts(); |
100 | if (DBG) |
101 | qDebug() << "Supported sample counts:" << sampleCounts; |
102 | if (sampleCounts.contains(t: 4)) { |
103 | if (DBG) |
104 | qDebug(msg: "Requesting 4x MSAA" ); |
105 | m_window->setSampleCount(4); |
106 | } |
107 | } |
108 | |
109 | void Renderer::initResources() |
110 | { |
111 | if (DBG) |
112 | qDebug(msg: "Renderer init" ); |
113 | |
114 | m_animating = true; |
115 | m_framePending = false; |
116 | |
117 | QVulkanInstance *inst = m_window->vulkanInstance(); |
118 | VkDevice dev = m_window->device(); |
119 | const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits; |
120 | const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment; |
121 | |
122 | m_devFuncs = inst->deviceFunctions(device: dev); |
123 | |
124 | // Note the std140 packing rules. A vec3 still has an alignment of 16, |
125 | // while a mat3 is like 3 * vec3. |
126 | m_itemMaterial.vertUniSize = aligned(v: 2 * 64 + 48, byteAlign: uniAlign); // see color_phong.vert |
127 | m_itemMaterial.fragUniSize = aligned(v: 6 * 16 + 12 + 2 * 4, byteAlign: uniAlign); // see color_phong.frag |
128 | |
129 | if (!m_itemMaterial.vs.isValid()) |
130 | m_itemMaterial.vs.load(inst, dev, QStringLiteral(":/color_phong_vert.spv" )); |
131 | if (!m_itemMaterial.fs.isValid()) |
132 | m_itemMaterial.fs.load(inst, dev, QStringLiteral(":/color_phong_frag.spv" )); |
133 | |
134 | if (!m_floorMaterial.vs.isValid()) |
135 | m_floorMaterial.vs.load(inst, dev, QStringLiteral(":/color_vert.spv" )); |
136 | if (!m_floorMaterial.fs.isValid()) |
137 | m_floorMaterial.fs.load(inst, dev, QStringLiteral(":/color_frag.spv" )); |
138 | |
139 | m_pipelinesFuture = QtConcurrent::run(object: this, fn: &Renderer::createPipelines); |
140 | } |
141 | |
142 | void Renderer::createPipelines() |
143 | { |
144 | VkDevice dev = m_window->device(); |
145 | |
146 | VkPipelineCacheCreateInfo pipelineCacheInfo; |
147 | memset(s: &pipelineCacheInfo, c: 0, n: sizeof(pipelineCacheInfo)); |
148 | pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; |
149 | VkResult err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache); |
150 | if (err != VK_SUCCESS) |
151 | qFatal(msg: "Failed to create pipeline cache: %d" , err); |
152 | |
153 | createItemPipeline(); |
154 | createFloorPipeline(); |
155 | } |
156 | |
157 | void Renderer::createItemPipeline() |
158 | { |
159 | VkDevice dev = m_window->device(); |
160 | |
161 | // Vertex layout. |
162 | VkVertexInputBindingDescription vertexBindingDesc[] = { |
163 | { |
164 | .binding: 0, // binding |
165 | .stride: 8 * sizeof(float), |
166 | .inputRate: VK_VERTEX_INPUT_RATE_VERTEX |
167 | }, |
168 | { |
169 | .binding: 1, |
170 | .stride: 6 * sizeof(float), |
171 | .inputRate: VK_VERTEX_INPUT_RATE_INSTANCE |
172 | } |
173 | }; |
174 | VkVertexInputAttributeDescription vertexAttrDesc[] = { |
175 | { // position |
176 | .location: 0, // location |
177 | .binding: 0, // binding |
178 | .format: VK_FORMAT_R32G32B32_SFLOAT, |
179 | .offset: 0 // offset |
180 | }, |
181 | { // normal |
182 | .location: 1, |
183 | .binding: 0, |
184 | .format: VK_FORMAT_R32G32B32_SFLOAT, |
185 | .offset: 5 * sizeof(float) |
186 | }, |
187 | { // instTranslate |
188 | .location: 2, |
189 | .binding: 1, |
190 | .format: VK_FORMAT_R32G32B32_SFLOAT, |
191 | .offset: 0 |
192 | }, |
193 | { // instDiffuseAdjust |
194 | .location: 3, |
195 | .binding: 1, |
196 | .format: VK_FORMAT_R32G32B32_SFLOAT, |
197 | .offset: 3 * sizeof(float) |
198 | } |
199 | }; |
200 | |
201 | VkPipelineVertexInputStateCreateInfo vertexInputInfo; |
202 | vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
203 | vertexInputInfo.pNext = nullptr; |
204 | vertexInputInfo.flags = 0; |
205 | vertexInputInfo.vertexBindingDescriptionCount = sizeof(vertexBindingDesc) / sizeof(vertexBindingDesc[0]); |
206 | vertexInputInfo.pVertexBindingDescriptions = vertexBindingDesc; |
207 | vertexInputInfo.vertexAttributeDescriptionCount = sizeof(vertexAttrDesc) / sizeof(vertexAttrDesc[0]); |
208 | vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc; |
209 | |
210 | // Descriptor set layout. |
211 | VkDescriptorPoolSize descPoolSizes[] = { |
212 | { .type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, .descriptorCount: 2 } |
213 | }; |
214 | VkDescriptorPoolCreateInfo descPoolInfo; |
215 | memset(s: &descPoolInfo, c: 0, n: sizeof(descPoolInfo)); |
216 | descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
217 | descPoolInfo.maxSets = 1; // a single set is enough due to the dynamic uniform buffer |
218 | descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]); |
219 | descPoolInfo.pPoolSizes = descPoolSizes; |
220 | VkResult err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_itemMaterial.descPool); |
221 | if (err != VK_SUCCESS) |
222 | qFatal(msg: "Failed to create descriptor pool: %d" , err); |
223 | |
224 | VkDescriptorSetLayoutBinding layoutBindings[] = |
225 | { |
226 | { |
227 | .binding: 0, // binding |
228 | .descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, |
229 | .descriptorCount: 1, // descriptorCount |
230 | .stageFlags: VK_SHADER_STAGE_VERTEX_BIT, |
231 | .pImmutableSamplers: nullptr |
232 | }, |
233 | { |
234 | .binding: 1, |
235 | .descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, |
236 | .descriptorCount: 1, |
237 | .stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, |
238 | .pImmutableSamplers: nullptr |
239 | } |
240 | }; |
241 | VkDescriptorSetLayoutCreateInfo descLayoutInfo = { |
242 | .sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, |
243 | .pNext: nullptr, |
244 | .flags: 0, |
245 | .bindingCount: sizeof(layoutBindings) / sizeof(layoutBindings[0]), |
246 | .pBindings: layoutBindings |
247 | }; |
248 | err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_itemMaterial.descSetLayout); |
249 | if (err != VK_SUCCESS) |
250 | qFatal(msg: "Failed to create descriptor set layout: %d" , err); |
251 | |
252 | VkDescriptorSetAllocateInfo descSetAllocInfo = { |
253 | .sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, |
254 | .pNext: nullptr, |
255 | .descriptorPool: m_itemMaterial.descPool, |
256 | .descriptorSetCount: 1, |
257 | .pSetLayouts: &m_itemMaterial.descSetLayout |
258 | }; |
259 | err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_itemMaterial.descSet); |
260 | if (err != VK_SUCCESS) |
261 | qFatal(msg: "Failed to allocate descriptor set: %d" , err); |
262 | |
263 | // Graphics pipeline. |
264 | VkPipelineLayoutCreateInfo pipelineLayoutInfo; |
265 | memset(s: &pipelineLayoutInfo, c: 0, n: sizeof(pipelineLayoutInfo)); |
266 | pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
267 | pipelineLayoutInfo.setLayoutCount = 1; |
268 | pipelineLayoutInfo.pSetLayouts = &m_itemMaterial.descSetLayout; |
269 | |
270 | err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_itemMaterial.pipelineLayout); |
271 | if (err != VK_SUCCESS) |
272 | qFatal(msg: "Failed to create pipeline layout: %d" , err); |
273 | |
274 | VkGraphicsPipelineCreateInfo pipelineInfo; |
275 | memset(s: &pipelineInfo, c: 0, n: sizeof(pipelineInfo)); |
276 | pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
277 | |
278 | VkPipelineShaderStageCreateInfo shaderStages[2] = { |
279 | { |
280 | .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
281 | .pNext: nullptr, |
282 | .flags: 0, |
283 | .stage: VK_SHADER_STAGE_VERTEX_BIT, |
284 | .module: m_itemMaterial.vs.data()->shaderModule, |
285 | .pName: "main" , |
286 | .pSpecializationInfo: nullptr |
287 | }, |
288 | { |
289 | .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
290 | .pNext: nullptr, |
291 | .flags: 0, |
292 | .stage: VK_SHADER_STAGE_FRAGMENT_BIT, |
293 | .module: m_itemMaterial.fs.data()->shaderModule, |
294 | .pName: "main" , |
295 | .pSpecializationInfo: nullptr |
296 | } |
297 | }; |
298 | pipelineInfo.stageCount = 2; |
299 | pipelineInfo.pStages = shaderStages; |
300 | |
301 | pipelineInfo.pVertexInputState = &vertexInputInfo; |
302 | |
303 | VkPipelineInputAssemblyStateCreateInfo ia; |
304 | memset(s: &ia, c: 0, n: sizeof(ia)); |
305 | ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
306 | ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
307 | pipelineInfo.pInputAssemblyState = &ia; |
308 | |
309 | VkPipelineViewportStateCreateInfo vp; |
310 | memset(s: &vp, c: 0, n: sizeof(vp)); |
311 | vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
312 | vp.viewportCount = 1; |
313 | vp.scissorCount = 1; |
314 | pipelineInfo.pViewportState = &vp; |
315 | |
316 | VkPipelineRasterizationStateCreateInfo rs; |
317 | memset(s: &rs, c: 0, n: sizeof(rs)); |
318 | rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
319 | rs.polygonMode = VK_POLYGON_MODE_FILL; |
320 | rs.cullMode = VK_CULL_MODE_BACK_BIT; |
321 | rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; |
322 | rs.lineWidth = 1.0f; |
323 | pipelineInfo.pRasterizationState = &rs; |
324 | |
325 | VkPipelineMultisampleStateCreateInfo ms; |
326 | memset(s: &ms, c: 0, n: sizeof(ms)); |
327 | ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
328 | ms.rasterizationSamples = m_window->sampleCountFlagBits(); |
329 | pipelineInfo.pMultisampleState = &ms; |
330 | |
331 | VkPipelineDepthStencilStateCreateInfo ds; |
332 | memset(s: &ds, c: 0, n: sizeof(ds)); |
333 | ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
334 | ds.depthTestEnable = VK_TRUE; |
335 | ds.depthWriteEnable = VK_TRUE; |
336 | ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; |
337 | pipelineInfo.pDepthStencilState = &ds; |
338 | |
339 | VkPipelineColorBlendStateCreateInfo cb; |
340 | memset(s: &cb, c: 0, n: sizeof(cb)); |
341 | cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
342 | VkPipelineColorBlendAttachmentState att; |
343 | memset(s: &att, c: 0, n: sizeof(att)); |
344 | att.colorWriteMask = 0xF; |
345 | cb.attachmentCount = 1; |
346 | cb.pAttachments = &att; |
347 | pipelineInfo.pColorBlendState = &cb; |
348 | |
349 | VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; |
350 | VkPipelineDynamicStateCreateInfo dyn; |
351 | memset(s: &dyn, c: 0, n: sizeof(dyn)); |
352 | dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
353 | dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState); |
354 | dyn.pDynamicStates = dynEnable; |
355 | pipelineInfo.pDynamicState = &dyn; |
356 | |
357 | pipelineInfo.layout = m_itemMaterial.pipelineLayout; |
358 | pipelineInfo.renderPass = m_window->defaultRenderPass(); |
359 | |
360 | err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_itemMaterial.pipeline); |
361 | if (err != VK_SUCCESS) |
362 | qFatal(msg: "Failed to create graphics pipeline: %d" , err); |
363 | } |
364 | |
365 | void Renderer::createFloorPipeline() |
366 | { |
367 | VkDevice dev = m_window->device(); |
368 | |
369 | // Vertex layout. |
370 | VkVertexInputBindingDescription vertexBindingDesc = { |
371 | .binding: 0, // binding |
372 | .stride: 3 * sizeof(float), |
373 | .inputRate: VK_VERTEX_INPUT_RATE_VERTEX |
374 | }; |
375 | VkVertexInputAttributeDescription vertexAttrDesc[] = { |
376 | { // position |
377 | .location: 0, // location |
378 | .binding: 0, // binding |
379 | .format: VK_FORMAT_R32G32B32_SFLOAT, |
380 | .offset: 0 // offset |
381 | }, |
382 | }; |
383 | |
384 | VkPipelineVertexInputStateCreateInfo vertexInputInfo; |
385 | vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
386 | vertexInputInfo.pNext = nullptr; |
387 | vertexInputInfo.flags = 0; |
388 | vertexInputInfo.vertexBindingDescriptionCount = 1; |
389 | vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc; |
390 | vertexInputInfo.vertexAttributeDescriptionCount = sizeof(vertexAttrDesc) / sizeof(vertexAttrDesc[0]); |
391 | vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc; |
392 | |
393 | // Do not bother with uniform buffers and descriptors, all the data fits |
394 | // into the spec mandated minimum of 128 bytes for push constants. |
395 | VkPushConstantRange pcr[] = { |
396 | // mvp |
397 | { |
398 | .stageFlags: VK_SHADER_STAGE_VERTEX_BIT, |
399 | .offset: 0, |
400 | .size: 64 |
401 | }, |
402 | // color |
403 | { |
404 | .stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, |
405 | .offset: 64, |
406 | .size: 12 |
407 | } |
408 | }; |
409 | |
410 | VkPipelineLayoutCreateInfo pipelineLayoutInfo; |
411 | memset(s: &pipelineLayoutInfo, c: 0, n: sizeof(pipelineLayoutInfo)); |
412 | pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
413 | pipelineLayoutInfo.pushConstantRangeCount = sizeof(pcr) / sizeof(pcr[0]); |
414 | pipelineLayoutInfo.pPushConstantRanges = pcr; |
415 | |
416 | VkResult err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_floorMaterial.pipelineLayout); |
417 | if (err != VK_SUCCESS) |
418 | qFatal(msg: "Failed to create pipeline layout: %d" , err); |
419 | |
420 | VkGraphicsPipelineCreateInfo pipelineInfo; |
421 | memset(s: &pipelineInfo, c: 0, n: sizeof(pipelineInfo)); |
422 | pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
423 | |
424 | VkPipelineShaderStageCreateInfo shaderStages[2] = { |
425 | { |
426 | .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
427 | .pNext: nullptr, |
428 | .flags: 0, |
429 | .stage: VK_SHADER_STAGE_VERTEX_BIT, |
430 | .module: m_floorMaterial.vs.data()->shaderModule, |
431 | .pName: "main" , |
432 | .pSpecializationInfo: nullptr |
433 | }, |
434 | { |
435 | .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
436 | .pNext: nullptr, |
437 | .flags: 0, |
438 | .stage: VK_SHADER_STAGE_FRAGMENT_BIT, |
439 | .module: m_floorMaterial.fs.data()->shaderModule, |
440 | .pName: "main" , |
441 | .pSpecializationInfo: nullptr |
442 | } |
443 | }; |
444 | pipelineInfo.stageCount = 2; |
445 | pipelineInfo.pStages = shaderStages; |
446 | |
447 | pipelineInfo.pVertexInputState = &vertexInputInfo; |
448 | |
449 | VkPipelineInputAssemblyStateCreateInfo ia; |
450 | memset(s: &ia, c: 0, n: sizeof(ia)); |
451 | ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
452 | ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; |
453 | pipelineInfo.pInputAssemblyState = &ia; |
454 | |
455 | VkPipelineViewportStateCreateInfo vp; |
456 | memset(s: &vp, c: 0, n: sizeof(vp)); |
457 | vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
458 | vp.viewportCount = 1; |
459 | vp.scissorCount = 1; |
460 | pipelineInfo.pViewportState = &vp; |
461 | |
462 | VkPipelineRasterizationStateCreateInfo rs; |
463 | memset(s: &rs, c: 0, n: sizeof(rs)); |
464 | rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
465 | rs.polygonMode = VK_POLYGON_MODE_FILL; |
466 | rs.cullMode = VK_CULL_MODE_BACK_BIT; |
467 | rs.frontFace = VK_FRONT_FACE_CLOCKWISE; |
468 | rs.lineWidth = 1.0f; |
469 | pipelineInfo.pRasterizationState = &rs; |
470 | |
471 | VkPipelineMultisampleStateCreateInfo ms; |
472 | memset(s: &ms, c: 0, n: sizeof(ms)); |
473 | ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
474 | ms.rasterizationSamples = m_window->sampleCountFlagBits(); |
475 | pipelineInfo.pMultisampleState = &ms; |
476 | |
477 | VkPipelineDepthStencilStateCreateInfo ds; |
478 | memset(s: &ds, c: 0, n: sizeof(ds)); |
479 | ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
480 | ds.depthTestEnable = VK_TRUE; |
481 | ds.depthWriteEnable = VK_TRUE; |
482 | ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; |
483 | pipelineInfo.pDepthStencilState = &ds; |
484 | |
485 | VkPipelineColorBlendStateCreateInfo cb; |
486 | memset(s: &cb, c: 0, n: sizeof(cb)); |
487 | cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
488 | VkPipelineColorBlendAttachmentState att; |
489 | memset(s: &att, c: 0, n: sizeof(att)); |
490 | att.colorWriteMask = 0xF; |
491 | cb.attachmentCount = 1; |
492 | cb.pAttachments = &att; |
493 | pipelineInfo.pColorBlendState = &cb; |
494 | |
495 | VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; |
496 | VkPipelineDynamicStateCreateInfo dyn; |
497 | memset(s: &dyn, c: 0, n: sizeof(dyn)); |
498 | dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
499 | dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState); |
500 | dyn.pDynamicStates = dynEnable; |
501 | pipelineInfo.pDynamicState = &dyn; |
502 | |
503 | pipelineInfo.layout = m_floorMaterial.pipelineLayout; |
504 | pipelineInfo.renderPass = m_window->defaultRenderPass(); |
505 | |
506 | err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_floorMaterial.pipeline); |
507 | if (err != VK_SUCCESS) |
508 | qFatal(msg: "Failed to create graphics pipeline: %d" , err); |
509 | } |
510 | |
511 | void Renderer::initSwapChainResources() |
512 | { |
513 | m_proj = m_window->clipCorrectionMatrix(); |
514 | const QSize sz = m_window->swapChainImageSize(); |
515 | m_proj.perspective(verticalAngle: 45.0f, aspectRatio: sz.width() / (float) sz.height(), nearPlane: 0.01f, farPlane: 1000.0f); |
516 | markViewProjDirty(); |
517 | } |
518 | |
519 | void Renderer::releaseSwapChainResources() |
520 | { |
521 | // It is important to finish the pending frame right here since this is the |
522 | // last opportunity to act with all resources intact. |
523 | m_frameWatcher.waitForFinished(); |
524 | // Cannot count on the finished() signal being emitted before returning |
525 | // from here. |
526 | if (m_framePending) { |
527 | m_framePending = false; |
528 | m_window->frameReady(); |
529 | } |
530 | } |
531 | |
532 | void Renderer::releaseResources() |
533 | { |
534 | if (DBG) |
535 | qDebug(msg: "Renderer release" ); |
536 | |
537 | m_pipelinesFuture.waitForFinished(); |
538 | |
539 | VkDevice dev = m_window->device(); |
540 | |
541 | if (m_itemMaterial.descSetLayout) { |
542 | m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_itemMaterial.descSetLayout, nullptr); |
543 | m_itemMaterial.descSetLayout = VK_NULL_HANDLE; |
544 | } |
545 | |
546 | if (m_itemMaterial.descPool) { |
547 | m_devFuncs->vkDestroyDescriptorPool(dev, m_itemMaterial.descPool, nullptr); |
548 | m_itemMaterial.descPool = VK_NULL_HANDLE; |
549 | } |
550 | |
551 | if (m_itemMaterial.pipeline) { |
552 | m_devFuncs->vkDestroyPipeline(dev, m_itemMaterial.pipeline, nullptr); |
553 | m_itemMaterial.pipeline = VK_NULL_HANDLE; |
554 | } |
555 | |
556 | if (m_itemMaterial.pipelineLayout) { |
557 | m_devFuncs->vkDestroyPipelineLayout(dev, m_itemMaterial.pipelineLayout, nullptr); |
558 | m_itemMaterial.pipelineLayout = VK_NULL_HANDLE; |
559 | } |
560 | |
561 | if (m_floorMaterial.pipeline) { |
562 | m_devFuncs->vkDestroyPipeline(dev, m_floorMaterial.pipeline, nullptr); |
563 | m_floorMaterial.pipeline = VK_NULL_HANDLE; |
564 | } |
565 | |
566 | if (m_floorMaterial.pipelineLayout) { |
567 | m_devFuncs->vkDestroyPipelineLayout(dev, m_floorMaterial.pipelineLayout, nullptr); |
568 | m_floorMaterial.pipelineLayout = VK_NULL_HANDLE; |
569 | } |
570 | |
571 | if (m_pipelineCache) { |
572 | m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr); |
573 | m_pipelineCache = VK_NULL_HANDLE; |
574 | } |
575 | |
576 | if (m_blockVertexBuf) { |
577 | m_devFuncs->vkDestroyBuffer(dev, m_blockVertexBuf, nullptr); |
578 | m_blockVertexBuf = VK_NULL_HANDLE; |
579 | } |
580 | |
581 | if (m_logoVertexBuf) { |
582 | m_devFuncs->vkDestroyBuffer(dev, m_logoVertexBuf, nullptr); |
583 | m_logoVertexBuf = VK_NULL_HANDLE; |
584 | } |
585 | |
586 | if (m_floorVertexBuf) { |
587 | m_devFuncs->vkDestroyBuffer(dev, m_floorVertexBuf, nullptr); |
588 | m_floorVertexBuf = VK_NULL_HANDLE; |
589 | } |
590 | |
591 | if (m_uniBuf) { |
592 | m_devFuncs->vkDestroyBuffer(dev, m_uniBuf, nullptr); |
593 | m_uniBuf = VK_NULL_HANDLE; |
594 | } |
595 | |
596 | if (m_bufMem) { |
597 | m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr); |
598 | m_bufMem = VK_NULL_HANDLE; |
599 | } |
600 | |
601 | if (m_instBuf) { |
602 | m_devFuncs->vkDestroyBuffer(dev, m_instBuf, nullptr); |
603 | m_instBuf = VK_NULL_HANDLE; |
604 | } |
605 | |
606 | if (m_instBufMem) { |
607 | m_devFuncs->vkFreeMemory(dev, m_instBufMem, nullptr); |
608 | m_instBufMem = VK_NULL_HANDLE; |
609 | } |
610 | |
611 | if (m_itemMaterial.vs.isValid()) { |
612 | m_devFuncs->vkDestroyShaderModule(dev, m_itemMaterial.vs.data()->shaderModule, nullptr); |
613 | m_itemMaterial.vs.reset(); |
614 | } |
615 | if (m_itemMaterial.fs.isValid()) { |
616 | m_devFuncs->vkDestroyShaderModule(dev, m_itemMaterial.fs.data()->shaderModule, nullptr); |
617 | m_itemMaterial.fs.reset(); |
618 | } |
619 | |
620 | if (m_floorMaterial.vs.isValid()) { |
621 | m_devFuncs->vkDestroyShaderModule(dev, m_floorMaterial.vs.data()->shaderModule, nullptr); |
622 | m_floorMaterial.vs.reset(); |
623 | } |
624 | if (m_floorMaterial.fs.isValid()) { |
625 | m_devFuncs->vkDestroyShaderModule(dev, m_floorMaterial.fs.data()->shaderModule, nullptr); |
626 | m_floorMaterial.fs.reset(); |
627 | } |
628 | } |
629 | |
630 | void Renderer::ensureBuffers() |
631 | { |
632 | if (m_blockVertexBuf) |
633 | return; |
634 | |
635 | VkDevice dev = m_window->device(); |
636 | const int concurrentFrameCount = m_window->concurrentFrameCount(); |
637 | |
638 | // Vertex buffer for the block. |
639 | VkBufferCreateInfo bufInfo; |
640 | memset(s: &bufInfo, c: 0, n: sizeof(bufInfo)); |
641 | bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
642 | const int blockMeshByteCount = m_blockMesh.data()->vertexCount * 8 * sizeof(float); |
643 | bufInfo.size = blockMeshByteCount; |
644 | bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
645 | VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_blockVertexBuf); |
646 | if (err != VK_SUCCESS) |
647 | qFatal(msg: "Failed to create vertex buffer: %d" , err); |
648 | |
649 | VkMemoryRequirements blockVertMemReq; |
650 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_blockVertexBuf, &blockVertMemReq); |
651 | |
652 | // Vertex buffer for the logo. |
653 | const int logoMeshByteCount = m_logoMesh.data()->vertexCount * 8 * sizeof(float); |
654 | bufInfo.size = logoMeshByteCount; |
655 | bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
656 | err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_logoVertexBuf); |
657 | if (err != VK_SUCCESS) |
658 | qFatal(msg: "Failed to create vertex buffer: %d" , err); |
659 | |
660 | VkMemoryRequirements logoVertMemReq; |
661 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_logoVertexBuf, &logoVertMemReq); |
662 | |
663 | // Vertex buffer for the floor. |
664 | bufInfo.size = sizeof(quadVert); |
665 | err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_floorVertexBuf); |
666 | if (err != VK_SUCCESS) |
667 | qFatal(msg: "Failed to create vertex buffer: %d" , err); |
668 | |
669 | VkMemoryRequirements floorVertMemReq; |
670 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_floorVertexBuf, &floorVertMemReq); |
671 | |
672 | // Uniform buffer. Instead of using multiple descriptor sets, we take a |
673 | // different approach: have a single dynamic uniform buffer and specify the |
674 | // active-frame-specific offset at the time of binding the descriptor set. |
675 | bufInfo.size = (m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize) * concurrentFrameCount; |
676 | bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
677 | err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_uniBuf); |
678 | if (err != VK_SUCCESS) |
679 | qFatal(msg: "Failed to create uniform buffer: %d" , err); |
680 | |
681 | VkMemoryRequirements uniMemReq; |
682 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_uniBuf, &uniMemReq); |
683 | |
684 | // Allocate memory for everything at once. |
685 | VkDeviceSize logoVertStartOffset = aligned(v: 0 + blockVertMemReq.size, byteAlign: logoVertMemReq.alignment); |
686 | VkDeviceSize floorVertStartOffset = aligned(v: logoVertStartOffset + logoVertMemReq.size, byteAlign: floorVertMemReq.alignment); |
687 | m_itemMaterial.uniMemStartOffset = aligned(v: floorVertStartOffset + floorVertMemReq.size, byteAlign: uniMemReq.alignment); |
688 | VkMemoryAllocateInfo memAllocInfo = { |
689 | .sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
690 | .pNext: nullptr, |
691 | .allocationSize: m_itemMaterial.uniMemStartOffset + uniMemReq.size, |
692 | .memoryTypeIndex: m_window->hostVisibleMemoryIndex() |
693 | }; |
694 | err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem); |
695 | if (err != VK_SUCCESS) |
696 | qFatal(msg: "Failed to allocate memory: %d" , err); |
697 | |
698 | err = m_devFuncs->vkBindBufferMemory(dev, m_blockVertexBuf, m_bufMem, 0); |
699 | if (err != VK_SUCCESS) |
700 | qFatal(msg: "Failed to bind vertex buffer memory: %d" , err); |
701 | err = m_devFuncs->vkBindBufferMemory(dev, m_logoVertexBuf, m_bufMem, logoVertStartOffset); |
702 | if (err != VK_SUCCESS) |
703 | qFatal(msg: "Failed to bind vertex buffer memory: %d" , err); |
704 | err = m_devFuncs->vkBindBufferMemory(dev, m_floorVertexBuf, m_bufMem, floorVertStartOffset); |
705 | if (err != VK_SUCCESS) |
706 | qFatal(msg: "Failed to bind vertex buffer memory: %d" , err); |
707 | err = m_devFuncs->vkBindBufferMemory(dev, m_uniBuf, m_bufMem, m_itemMaterial.uniMemStartOffset); |
708 | if (err != VK_SUCCESS) |
709 | qFatal(msg: "Failed to bind uniform buffer memory: %d" , err); |
710 | |
711 | // Copy vertex data. |
712 | quint8 *p; |
713 | err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, m_itemMaterial.uniMemStartOffset, 0, reinterpret_cast<void **>(&p)); |
714 | if (err != VK_SUCCESS) |
715 | qFatal(msg: "Failed to map memory: %d" , err); |
716 | memcpy(dest: p, src: m_blockMesh.data()->geom.constData(), n: blockMeshByteCount); |
717 | memcpy(dest: p + logoVertStartOffset, src: m_logoMesh.data()->geom.constData(), n: logoMeshByteCount); |
718 | memcpy(dest: p + floorVertStartOffset, src: quadVert, n: sizeof(quadVert)); |
719 | m_devFuncs->vkUnmapMemory(dev, m_bufMem); |
720 | |
721 | // Write descriptors for the uniform buffers in the vertex and fragment shaders. |
722 | VkDescriptorBufferInfo vertUni = { .buffer: m_uniBuf, .offset: 0, .range: m_itemMaterial.vertUniSize }; |
723 | VkDescriptorBufferInfo fragUni = { .buffer: m_uniBuf, .offset: m_itemMaterial.vertUniSize, .range: m_itemMaterial.fragUniSize }; |
724 | |
725 | VkWriteDescriptorSet descWrite[2]; |
726 | memset(s: descWrite, c: 0, n: sizeof(descWrite)); |
727 | descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
728 | descWrite[0].dstSet = m_itemMaterial.descSet; |
729 | descWrite[0].dstBinding = 0; |
730 | descWrite[0].descriptorCount = 1; |
731 | descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; |
732 | descWrite[0].pBufferInfo = &vertUni; |
733 | |
734 | descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
735 | descWrite[1].dstSet = m_itemMaterial.descSet; |
736 | descWrite[1].dstBinding = 1; |
737 | descWrite[1].descriptorCount = 1; |
738 | descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; |
739 | descWrite[1].pBufferInfo = &fragUni; |
740 | |
741 | m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr); |
742 | } |
743 | |
744 | void Renderer::ensureInstanceBuffer() |
745 | { |
746 | if (m_instCount == m_preparedInstCount && m_instBuf) |
747 | return; |
748 | |
749 | Q_ASSERT(m_instCount <= MAX_INSTANCES); |
750 | |
751 | VkDevice dev = m_window->device(); |
752 | |
753 | // allocate only once, for the maximum instance count |
754 | if (!m_instBuf) { |
755 | VkBufferCreateInfo bufInfo; |
756 | memset(s: &bufInfo, c: 0, n: sizeof(bufInfo)); |
757 | bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
758 | bufInfo.size = MAX_INSTANCES * PER_INSTANCE_DATA_SIZE; |
759 | bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
760 | |
761 | // Keep a copy of the data since we may lose all graphics resources on |
762 | // unexpose, and reinitializing to new random positions afterwards |
763 | // would not be nice. |
764 | m_instData.resize(size: bufInfo.size); |
765 | |
766 | VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_instBuf); |
767 | if (err != VK_SUCCESS) |
768 | qFatal(msg: "Failed to create instance buffer: %d" , err); |
769 | |
770 | VkMemoryRequirements memReq; |
771 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_instBuf, &memReq); |
772 | if (DBG) |
773 | qDebug(msg: "Allocating %u bytes for instance data" , uint32_t(memReq.size)); |
774 | |
775 | VkMemoryAllocateInfo memAllocInfo = { |
776 | .sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
777 | .pNext: nullptr, |
778 | .allocationSize: memReq.size, |
779 | .memoryTypeIndex: m_window->hostVisibleMemoryIndex() |
780 | }; |
781 | err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_instBufMem); |
782 | if (err != VK_SUCCESS) |
783 | qFatal(msg: "Failed to allocate memory: %d" , err); |
784 | |
785 | err = m_devFuncs->vkBindBufferMemory(dev, m_instBuf, m_instBufMem, 0); |
786 | if (err != VK_SUCCESS) |
787 | qFatal(msg: "Failed to bind instance buffer memory: %d" , err); |
788 | } |
789 | |
790 | if (m_instCount != m_preparedInstCount) { |
791 | if (DBG) |
792 | qDebug(msg: "Preparing instances %d..%d" , m_preparedInstCount, m_instCount - 1); |
793 | char *p = m_instData.data(); |
794 | p += m_preparedInstCount * PER_INSTANCE_DATA_SIZE; |
795 | auto gen = [](int a, int b) { |
796 | return float(QRandomGenerator::global()->bounded(highest: double(b - a)) + a); |
797 | }; |
798 | for (int i = m_preparedInstCount; i < m_instCount; ++i) { |
799 | // Apply a random translation to each instance of the mesh. |
800 | float t[] = { gen(-5, 5), gen(-4, 6), gen(-30, 5) }; |
801 | memcpy(dest: p, src: t, n: 12); |
802 | // Apply a random adjustment to the diffuse color for each instance. (default is 0.7) |
803 | float d[] = { gen(-6, 3) / 10.0f, gen(-6, 3) / 10.0f, gen(-6, 3) / 10.0f }; |
804 | memcpy(dest: p + 12, src: d, n: 12); |
805 | p += PER_INSTANCE_DATA_SIZE; |
806 | } |
807 | m_preparedInstCount = m_instCount; |
808 | } |
809 | |
810 | quint8 *p; |
811 | VkResult err = m_devFuncs->vkMapMemory(dev, m_instBufMem, 0, m_instCount * PER_INSTANCE_DATA_SIZE, 0, |
812 | reinterpret_cast<void **>(&p)); |
813 | if (err != VK_SUCCESS) |
814 | qFatal(msg: "Failed to map memory: %d" , err); |
815 | memcpy(dest: p, src: m_instData.constData(), n: m_instData.size()); |
816 | m_devFuncs->vkUnmapMemory(dev, m_instBufMem); |
817 | } |
818 | |
819 | void Renderer::getMatrices(QMatrix4x4 *vp, QMatrix4x4 *model, QMatrix3x3 *modelNormal, QVector3D *eyePos) |
820 | { |
821 | model->setToIdentity(); |
822 | if (m_useLogo) |
823 | model->rotate(angle: 90, x: 1, y: 0, z: 0); |
824 | model->rotate(angle: m_rotation, x: 1, y: 1, z: 0); |
825 | |
826 | *modelNormal = model->normalMatrix(); |
827 | |
828 | QMatrix4x4 view = m_cam.viewMatrix(); |
829 | *vp = m_proj * view; |
830 | |
831 | *eyePos = view.inverted().column(index: 3).toVector3D(); |
832 | } |
833 | |
834 | void Renderer::writeFragUni(quint8 *p, const QVector3D &eyePos) |
835 | { |
836 | float ECCameraPosition[] = { eyePos.x(), eyePos.y(), eyePos.z() }; |
837 | memcpy(dest: p, src: ECCameraPosition, n: 12); |
838 | p += 16; |
839 | |
840 | // Material |
841 | float ka[] = { 0.05f, 0.05f, 0.05f }; |
842 | memcpy(dest: p, src: ka, n: 12); |
843 | p += 16; |
844 | |
845 | float kd[] = { 0.7f, 0.7f, 0.7f }; |
846 | memcpy(dest: p, src: kd, n: 12); |
847 | p += 16; |
848 | |
849 | float ks[] = { 0.66f, 0.66f, 0.66f }; |
850 | memcpy(dest: p, src: ks, n: 12); |
851 | p += 16; |
852 | |
853 | // Light parameters |
854 | float ECLightPosition[] = { m_lightPos.x(), m_lightPos.y(), m_lightPos.z() }; |
855 | memcpy(dest: p, src: ECLightPosition, n: 12); |
856 | p += 16; |
857 | |
858 | float att[] = { 1, 0, 0 }; |
859 | memcpy(dest: p, src: att, n: 12); |
860 | p += 16; |
861 | |
862 | float color[] = { 1.0f, 1.0f, 1.0f }; |
863 | memcpy(dest: p, src: color, n: 12); |
864 | p += 12; // next we have two floats which have an alignment of 4, hence 12 only |
865 | |
866 | float intensity = 0.8f; |
867 | memcpy(dest: p, src: &intensity, n: 4); |
868 | p += 4; |
869 | |
870 | float specularExp = 150.0f; |
871 | memcpy(dest: p, src: &specularExp, n: 4); |
872 | p += 4; |
873 | } |
874 | |
875 | void Renderer::startNextFrame() |
876 | { |
877 | // For demonstration purposes offload the command buffer generation onto a |
878 | // worker thread and continue with the frame submission only when it has |
879 | // finished. |
880 | Q_ASSERT(!m_framePending); |
881 | m_framePending = true; |
882 | QFuture<void> future = QtConcurrent::run(object: this, fn: &Renderer::buildFrame); |
883 | m_frameWatcher.setFuture(future); |
884 | } |
885 | |
886 | void Renderer::buildFrame() |
887 | { |
888 | QMutexLocker locker(&m_guiMutex); |
889 | |
890 | ensureBuffers(); |
891 | ensureInstanceBuffer(); |
892 | m_pipelinesFuture.waitForFinished(); |
893 | |
894 | VkCommandBuffer cb = m_window->currentCommandBuffer(); |
895 | const QSize sz = m_window->swapChainImageSize(); |
896 | |
897 | VkClearColorValue clearColor = {.float32: { 0.67f, 0.84f, 0.9f, 1.0f }}; |
898 | VkClearDepthStencilValue clearDS = { .depth: 1, .stencil: 0 }; |
899 | VkClearValue clearValues[3]; |
900 | memset(s: clearValues, c: 0, n: sizeof(clearValues)); |
901 | clearValues[0].color = clearValues[2].color = clearColor; |
902 | clearValues[1].depthStencil = clearDS; |
903 | |
904 | VkRenderPassBeginInfo rpBeginInfo; |
905 | memset(s: &rpBeginInfo, c: 0, n: sizeof(rpBeginInfo)); |
906 | rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
907 | rpBeginInfo.renderPass = m_window->defaultRenderPass(); |
908 | rpBeginInfo.framebuffer = m_window->currentFramebuffer(); |
909 | rpBeginInfo.renderArea.extent.width = sz.width(); |
910 | rpBeginInfo.renderArea.extent.height = sz.height(); |
911 | rpBeginInfo.clearValueCount = m_window->sampleCountFlagBits() > VK_SAMPLE_COUNT_1_BIT ? 3 : 2; |
912 | rpBeginInfo.pClearValues = clearValues; |
913 | VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); |
914 | m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); |
915 | |
916 | VkViewport viewport = { |
917 | .x: 0, .y: 0, |
918 | .width: float(sz.width()), .height: float(sz.height()), |
919 | .minDepth: 0, .maxDepth: 1 |
920 | }; |
921 | m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport); |
922 | |
923 | VkRect2D scissor = { |
924 | .offset: { .x: 0, .y: 0 }, |
925 | .extent: { .width: uint32_t(sz.width()), .height: uint32_t(sz.height()) } |
926 | }; |
927 | m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor); |
928 | |
929 | buildDrawCallsForFloor(); |
930 | buildDrawCallsForItems(); |
931 | |
932 | m_devFuncs->vkCmdEndRenderPass(cmdBuf); |
933 | } |
934 | |
935 | void Renderer::buildDrawCallsForItems() |
936 | { |
937 | VkDevice dev = m_window->device(); |
938 | VkCommandBuffer cb = m_window->currentCommandBuffer(); |
939 | |
940 | m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_itemMaterial.pipeline); |
941 | |
942 | VkDeviceSize vbOffset = 0; |
943 | m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, m_useLogo ? &m_logoVertexBuf : &m_blockVertexBuf, &vbOffset); |
944 | m_devFuncs->vkCmdBindVertexBuffers(cb, 1, 1, &m_instBuf, &vbOffset); |
945 | |
946 | // Now provide offsets so that the two dynamic buffers point to the |
947 | // beginning of the vertex and fragment uniform data for the current frame. |
948 | uint32_t frameUniOffset = m_window->currentFrame() * (m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize); |
949 | uint32_t frameUniOffsets[] = { frameUniOffset, frameUniOffset }; |
950 | m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_itemMaterial.pipelineLayout, 0, 1, |
951 | &m_itemMaterial.descSet, 2, frameUniOffsets); |
952 | |
953 | if (m_animating) |
954 | m_rotation += 0.5; |
955 | |
956 | if (m_animating || m_vpDirty) { |
957 | if (m_vpDirty) |
958 | --m_vpDirty; |
959 | QMatrix4x4 vp, model; |
960 | QMatrix3x3 modelNormal; |
961 | QVector3D eyePos; |
962 | getMatrices(vp: &vp, model: &model, modelNormal: &modelNormal, eyePos: &eyePos); |
963 | |
964 | // Map the uniform data for the current frame, ignore the geometry data at |
965 | // the beginning and the uniforms for other frames. |
966 | quint8 *p; |
967 | VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, |
968 | m_itemMaterial.uniMemStartOffset + frameUniOffset, |
969 | m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize, |
970 | 0, reinterpret_cast<void **>(&p)); |
971 | if (err != VK_SUCCESS) |
972 | qFatal(msg: "Failed to map memory: %d" , err); |
973 | |
974 | // Vertex shader uniforms |
975 | memcpy(dest: p, src: vp.constData(), n: 64); |
976 | memcpy(dest: p + 64, src: model.constData(), n: 64); |
977 | const float *mnp = modelNormal.constData(); |
978 | memcpy(dest: p + 128, src: mnp, n: 12); |
979 | memcpy(dest: p + 128 + 16, src: mnp + 3, n: 12); |
980 | memcpy(dest: p + 128 + 32, src: mnp + 6, n: 12); |
981 | |
982 | // Fragment shader uniforms |
983 | p += m_itemMaterial.vertUniSize; |
984 | writeFragUni(p, eyePos); |
985 | |
986 | m_devFuncs->vkUnmapMemory(dev, m_bufMem); |
987 | } |
988 | |
989 | m_devFuncs->vkCmdDraw(cb, (m_useLogo ? m_logoMesh.data() : m_blockMesh.data())->vertexCount, m_instCount, 0, 0); |
990 | } |
991 | |
992 | void Renderer::buildDrawCallsForFloor() |
993 | { |
994 | VkCommandBuffer cb = m_window->currentCommandBuffer(); |
995 | |
996 | m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_floorMaterial.pipeline); |
997 | |
998 | VkDeviceSize vbOffset = 0; |
999 | m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_floorVertexBuf, &vbOffset); |
1000 | |
1001 | QMatrix4x4 mvp = m_proj * m_cam.viewMatrix() * m_floorModel; |
1002 | m_devFuncs->vkCmdPushConstants(cb, m_floorMaterial.pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, 64, mvp.constData()); |
1003 | float color[] = { 0.67f, 1.0f, 0.2f }; |
1004 | m_devFuncs->vkCmdPushConstants(cb, m_floorMaterial.pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 64, 12, color); |
1005 | |
1006 | m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0); |
1007 | } |
1008 | |
1009 | void Renderer::addNew() |
1010 | { |
1011 | QMutexLocker locker(&m_guiMutex); |
1012 | m_instCount = qMin(a: m_instCount + 16, b: MAX_INSTANCES); |
1013 | } |
1014 | |
1015 | void Renderer::yaw(float degrees) |
1016 | { |
1017 | QMutexLocker locker(&m_guiMutex); |
1018 | m_cam.yaw(degrees); |
1019 | markViewProjDirty(); |
1020 | } |
1021 | |
1022 | void Renderer::pitch(float degrees) |
1023 | { |
1024 | QMutexLocker locker(&m_guiMutex); |
1025 | m_cam.pitch(degrees); |
1026 | markViewProjDirty(); |
1027 | } |
1028 | |
1029 | void Renderer::walk(float amount) |
1030 | { |
1031 | QMutexLocker locker(&m_guiMutex); |
1032 | m_cam.walk(amount); |
1033 | markViewProjDirty(); |
1034 | } |
1035 | |
1036 | void Renderer::strafe(float amount) |
1037 | { |
1038 | QMutexLocker locker(&m_guiMutex); |
1039 | m_cam.strafe(amount); |
1040 | markViewProjDirty(); |
1041 | } |
1042 | |
1043 | void Renderer::setUseLogo(bool b) |
1044 | { |
1045 | QMutexLocker locker(&m_guiMutex); |
1046 | m_useLogo = b; |
1047 | if (!m_animating) |
1048 | m_window->requestUpdate(); |
1049 | } |
1050 | |