33*
44* Note: Requires a device that supports VK_KHR_MAINTENANCE1
55*
6- * Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
6+ * Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
77*
88* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
99*/
@@ -20,9 +20,9 @@ class VulkanExample : public VulkanExampleBase
2020 int32_t cullMode = (int32_t )VK_CULL_MODE_BACK_BIT;
2121 int32_t quadType = 0 ;
2222
23- VkPipelineLayout pipelineLayout;
24- VkPipeline pipeline = VK_NULL_HANDLE;
25- VkDescriptorSetLayout descriptorSetLayout;
23+ VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE } ;
24+ VkPipeline pipeline{ VK_NULL_HANDLE } ;
25+ VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE } ;
2626 struct DescriptorSets {
2727 VkDescriptorSet CW;
2828 VkDescriptorSet CCW;
@@ -31,7 +31,7 @@ class VulkanExample : public VulkanExampleBase
3131 struct Textures {
3232 vks::Texture2D CW;
3333 vks::Texture2D CCW;
34- } textures;
34+ } textures{} ;
3535
3636 struct Quad {
3737 vks::Buffer verticesYUp;
@@ -49,6 +49,7 @@ class VulkanExample : public VulkanExampleBase
4949
5050 VulkanExample () : VulkanExampleBase()
5151 {
52+ useNewSync = true ;
5253 title = " Negative Viewport height" ;
5354 // [POI] VK_KHR_MAINTENANCE1 is required for using negative viewport heights
5455 // Note: This is core as of Vulkan 1.1. So if you target 1.1 you don't have to explicitly enable this
@@ -65,76 +66,6 @@ class VulkanExample : public VulkanExampleBase
6566 quad.destroy ();
6667 }
6768
68- void buildCommandBuffers ()
69- {
70- VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
71-
72- VkClearValue clearValues[2 ];
73- clearValues[0 ].color = defaultClearColor;
74- clearValues[1 ].depthStencil = { 1 .0f , 0 };
75-
76- VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
77- renderPassBeginInfo.renderPass = renderPass;
78- renderPassBeginInfo.renderArea .offset .x = 0 ;
79- renderPassBeginInfo.renderArea .offset .y = 0 ;
80- renderPassBeginInfo.renderArea .extent .width = width;
81- renderPassBeginInfo.renderArea .extent .height = height;
82- renderPassBeginInfo.clearValueCount = 2 ;
83- renderPassBeginInfo.pClearValues = clearValues;
84-
85- for (int32_t i = 0 ; i < drawCmdBuffers.size (); ++i) {
86- renderPassBeginInfo.framebuffer = frameBuffers[i];
87-
88- VK_CHECK_RESULT (vkBeginCommandBuffer (drawCmdBuffers[i], &cmdBufInfo));
89-
90- vkCmdBeginRenderPass (drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
91-
92- vkCmdBindPipeline (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
93-
94- // [POI] Viewport setup
95- VkViewport viewport{};
96- if (negativeViewport) {
97- viewport.x = offsetx;
98- // [POI] When using a negative viewport height, the origin needs to be adjusted too
99- viewport.y = (float )height - offsety;
100- viewport.width = (float )width;
101- // [POI] Flip the sign of the viewport's height
102- viewport.height = -(float )height;
103- }
104- else {
105- viewport.x = offsetx;
106- viewport.y = offsety;
107- viewport.width = (float )width;
108- viewport.height = (float )height;
109- }
110- viewport.minDepth = 0 .0f ;
111- viewport.maxDepth = 1 .0f ;
112- vkCmdSetViewport (drawCmdBuffers[i], 0 , 1 , &viewport);
113-
114- VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
115- vkCmdSetScissor (drawCmdBuffers[i], 0 , 1 , &scissor);
116-
117- VkDeviceSize offsets[1 ] = { 0 };
118-
119- // Render the quad with clock wise and counter clock wise indices, visibility is determined by pipeline settings
120-
121- vkCmdBindDescriptorSets (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSets.CW , 0 , nullptr );
122- vkCmdBindIndexBuffer (drawCmdBuffers[i], quad.indicesCW .buffer , 0 , VK_INDEX_TYPE_UINT32);
123- vkCmdBindVertexBuffers (drawCmdBuffers[i], 0 , 1 , quadType == 0 ? &quad.verticesYDown .buffer : &quad.verticesYUp .buffer , offsets);
124- vkCmdDrawIndexed (drawCmdBuffers[i], 6 , 1 , 0 , 0 , 0 );
125-
126- vkCmdBindDescriptorSets (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSets.CCW , 0 , nullptr );
127- vkCmdBindIndexBuffer (drawCmdBuffers[i], quad.indicesCCW .buffer , 0 , VK_INDEX_TYPE_UINT32);
128- vkCmdDrawIndexed (drawCmdBuffers[i], 6 , 1 , 0 , 0 , 0 );
129-
130- drawUI (drawCmdBuffers[i]);
131-
132- vkCmdEndRenderPass (drawCmdBuffers[i]);
133-
134- VK_CHECK_RESULT (vkEndCommandBuffer (drawCmdBuffers[i]));
135- }
136- }
137-
13869 void loadAssets ()
13970 {
14071 textures.CW .loadFromFile (getAssetPath () + " textures/texture_orientation_cw_rgba.ktx" , VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
@@ -262,51 +193,106 @@ class VulkanExample : public VulkanExampleBase
262193 VK_CHECK_RESULT (vkCreateGraphicsPipelines (device, pipelineCache, 1 , &pipelineCreateInfoCI, nullptr , &pipeline));
263194 }
264195
265- void draw ()
266- {
267- VulkanExampleBase::prepareFrame ();
268- submitInfo.commandBufferCount = 1 ;
269- submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
270- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, VK_NULL_HANDLE));
271- VulkanExampleBase::submitFrame ();
272- }
273-
274196 void prepare ()
275197 {
276198 VulkanExampleBase::prepare ();
277199 loadAssets ();
278200 setupDescriptors ();
279201 preparePipelines ();
280- buildCommandBuffers ();
281202 prepared = true ;
282203 }
283204
205+ void buildCommandBuffer ()
206+ {
207+ VkCommandBuffer cmdBuffer = drawCmdBuffers[currentBuffer];
208+ vkResetCommandBuffer (cmdBuffer, 0 );
209+
210+ VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
211+
212+ VkClearValue clearValues[2 ]{};
213+ clearValues[0 ].color = defaultClearColor;
214+ clearValues[1 ].depthStencil = { 1 .0f , 0 };
215+
216+ VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
217+ renderPassBeginInfo.renderPass = renderPass;
218+ renderPassBeginInfo.renderArea .offset .x = 0 ;
219+ renderPassBeginInfo.renderArea .offset .y = 0 ;
220+ renderPassBeginInfo.renderArea .extent .width = width;
221+ renderPassBeginInfo.renderArea .extent .height = height;
222+ renderPassBeginInfo.clearValueCount = 2 ;
223+ renderPassBeginInfo.pClearValues = clearValues;
224+ renderPassBeginInfo.framebuffer = frameBuffers[currentImageIndex];
225+
226+ VK_CHECK_RESULT (vkBeginCommandBuffer (cmdBuffer, &cmdBufInfo));
227+
228+ vkCmdBeginRenderPass (cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
229+
230+ vkCmdBindPipeline (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
231+
232+ // [POI] Viewport setup
233+ VkViewport viewport{};
234+ if (negativeViewport) {
235+ viewport.x = offsetx;
236+ // [POI] When using a negative viewport height, the origin needs to be adjusted too
237+ viewport.y = (float )height - offsety;
238+ viewport.width = (float )width;
239+ // [POI] Flip the sign of the viewport's height
240+ viewport.height = -(float )height;
241+ }
242+ else {
243+ viewport.x = offsetx;
244+ viewport.y = offsety;
245+ viewport.width = (float )width;
246+ viewport.height = (float )height;
247+ }
248+ viewport.minDepth = 0 .0f ;
249+ viewport.maxDepth = 1 .0f ;
250+ vkCmdSetViewport (cmdBuffer, 0 , 1 , &viewport);
251+
252+ VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
253+ vkCmdSetScissor (cmdBuffer, 0 , 1 , &scissor);
254+
255+ VkDeviceSize offsets[1 ] = { 0 };
256+
257+ // Render the quad with clock wise and counter clock wise indices, visibility is determined by pipeline settings
258+
259+ vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSets.CW , 0 , nullptr );
260+ vkCmdBindIndexBuffer (cmdBuffer, quad.indicesCW .buffer , 0 , VK_INDEX_TYPE_UINT32);
261+ vkCmdBindVertexBuffers (cmdBuffer, 0 , 1 , quadType == 0 ? &quad.verticesYDown .buffer : &quad.verticesYUp .buffer , offsets);
262+ vkCmdDrawIndexed (cmdBuffer, 6 , 1 , 0 , 0 , 0 );
263+
264+ vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSets.CCW , 0 , nullptr );
265+ vkCmdBindIndexBuffer (cmdBuffer, quad.indicesCCW .buffer , 0 , VK_INDEX_TYPE_UINT32);
266+ vkCmdDrawIndexed (cmdBuffer, 6 , 1 , 0 , 0 , 0 );
267+
268+ drawUI (cmdBuffer);
269+
270+ vkCmdEndRenderPass (cmdBuffer);
271+
272+ VK_CHECK_RESULT (vkEndCommandBuffer (cmdBuffer));
273+ }
274+
275+
284276 virtual void render ()
285277 {
286278 if (!prepared)
287279 return ;
288- draw ();
280+ VulkanExampleBase::prepareFrame ();
281+ buildCommandBuffer ();
282+ VulkanExampleBase::submitFrame ();
289283 }
290284
291285 virtual void OnUpdateUIOverlay (vks::UIOverlay *overlay)
292286 {
293287 if (overlay->header (" Scene" )) {
294288 overlay->text (" Quad type" );
295- if (overlay->comboBox (" ##quadtype" , &quadType, { " VK (y negative)" , " GL (y positive)" })) {
296- buildCommandBuffers ();
297- }
289+ overlay->comboBox (" ##quadtype" , &quadType, { " VK (y negative)" , " GL (y positive)" });
298290 }
299291
300292 if (overlay->header (" Viewport" )) {
301- if (overlay->checkBox (" Negative viewport height" , &negativeViewport)) {
302- buildCommandBuffers ();
303- }
304- if (overlay->sliderFloat (" offset x" , &offsetx, -(float )width, (float )width)) {
305- buildCommandBuffers ();
306- }
307- if (overlay->sliderFloat (" offset y" , &offsety, -(float )height, (float )height)) {
308- buildCommandBuffers ();
309- }
293+ overlay->checkBox (" Negative viewport height" , &negativeViewport);
294+ overlay->sliderFloat (" offset x" , &offsetx, -(float )width, (float )width);
295+ overlay->sliderFloat (" offset y" , &offsety, -(float )height, (float )height);
310296 }
311297 if (overlay->header (" Pipeline" )) {
312298 overlay->text (" Winding order" );
0 commit comments