From 3e55366434de65e8f1b0ac2729f363a031ed6f6d Mon Sep 17 00:00:00 2001
From: Sascha Willems <webmaster@saschawillems.de>
Date: Sat, 6 Nov 2021 19:45:22 +0100
Subject: [PATCH] Added sample fro dynamic rendering using
 VK_KHR_dynamic_rendering

---
 base/vulkanexamplebase.cpp                    |   5 +-
 base/vulkanexamplebase.h                      |   2 +-
 .../glsl/dynamicrendering/texture.frag        |  26 ++
 .../glsl/dynamicrendering/texture.frag.spv    | Bin 0 -> 1972 bytes
 .../glsl/dynamicrendering/texture.vert        |  33 ++
 .../glsl/dynamicrendering/texture.vert.spv    | Bin 0 -> 3224 bytes
 examples/CMakeLists.txt                       |   1 +
 .../dynamicrendering/dynamicrendering.cpp     | 281 ++++++++++++++++++
 8 files changed, 346 insertions(+), 2 deletions(-)
 create mode 100644 data/shaders/glsl/dynamicrendering/texture.frag
 create mode 100644 data/shaders/glsl/dynamicrendering/texture.frag.spv
 create mode 100644 data/shaders/glsl/dynamicrendering/texture.vert
 create mode 100644 data/shaders/glsl/dynamicrendering/texture.vert.spv
 create mode 100644 examples/dynamicrendering/dynamicrendering.cpp

diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp
index 19c51b28..106145a5 100644
--- a/base/vulkanexamplebase.cpp
+++ b/base/vulkanexamplebase.cpp
@@ -823,7 +823,10 @@ VulkanExampleBase::~VulkanExampleBase()
 		vkDestroyDescriptorPool(device, descriptorPool, nullptr);
 	}
 	destroyCommandBuffers();
-	vkDestroyRenderPass(device, renderPass, nullptr);
+	if (renderPass != VK_NULL_HANDLE)
+	{
+		vkDestroyRenderPass(device, renderPass, nullptr);
+	}
 	for (uint32_t i = 0; i < frameBuffers.size(); i++)
 	{
 		vkDestroyFramebuffer(device, frameBuffers[i], nullptr);
diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h
index 6f7e2bf8..14644f6b 100644
--- a/base/vulkanexamplebase.h
+++ b/base/vulkanexamplebase.h
@@ -153,7 +153,7 @@ protected:
 	// Command buffers used for rendering
 	std::vector<VkCommandBuffer> drawCmdBuffers;
 	// Global render pass for frame buffer writes
-	VkRenderPass renderPass;
+	VkRenderPass renderPass = VK_NULL_HANDLE;
 	// List of available frame buffers (same as number of swap chain images)
 	std::vector<VkFramebuffer>frameBuffers;
 	// Active frame buffer index
diff --git a/data/shaders/glsl/dynamicrendering/texture.frag b/data/shaders/glsl/dynamicrendering/texture.frag
new file mode 100644
index 00000000..c86d35de
--- /dev/null
+++ b/data/shaders/glsl/dynamicrendering/texture.frag
@@ -0,0 +1,26 @@
+#version 450
+
+layout (set = 1, binding = 0) uniform sampler2D samplerColor;
+
+layout (location = 0) in vec2 inUV;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec3 inViewVec;
+layout (location = 3) in vec3 inLightVec;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+	vec4 color = texture(samplerColor, inUV);
+
+	vec3 N = normalize(inNormal);
+	vec3 L = normalize(inLightVec);
+	vec3 V = normalize(inViewVec);
+	vec3 R = reflect(-L, N);
+	vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
+	float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a;
+
+	outFragColor = vec4(diffuse * color.rgb + specular, 1.0);	
+
+	outFragColor = texture(samplerColor, inUV);
+}
\ No newline at end of file
diff --git a/data/shaders/glsl/dynamicrendering/texture.frag.spv b/data/shaders/glsl/dynamicrendering/texture.frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..78f2d155dff88159b2ff6e3d00399acbe14bf3c8
GIT binary patch
literal 1972
zcmZQ(Qf6mhU}WH8;9&4$fB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z3|tH>3=CkLo0ypgk`QEIV31&7V31*8U{GLSV6bOkU=Uzn2CHFbU|?WoU}j)qU|=}J
z$iTqLzycPB=>_pQ85kIn^K<fx7(nuD47>~s48@7L1v#lj&QM{nK9D_`d7)t-J6Rc+
z8N?VEz<yw5U<KQsndg^Zl$)3Xlb2>-VDJH($qIH`W}Z)GdPYfDYBI<kW(Ii%1_qE}
zApIbBXXb@vrj~=H89?SMGcYg&f%UU6XfQA^q-3V0l@_NmfYcj-+*pvBT$+;zayA1i
z0~^@i`K2XpMTzNfce^vNfc>My019;m1{hxv<VXexALK7NBt9rCL3~i0fYgJ+6~qU{
z0*DWCD~JyYQxG2%-XK262w?^$a9AsXL!AN47Gq#$V1veuI0Fj<C{93pP&_IyurP2i
zFo4BC;*t!U3|tHhVD%vTL1w}*j4#c=%)rgS09FUm&jVEe60>GtVF2+#aR?Fy*~bqx
z59D5udI4xyfW$%SL3~gcfXoM}7lEn=`4^;K6zV>ZIEXI})equ>^n>`IFazm_@j+pR
z%m;-Th_B7S0`{8@$Z-q|3^2YPnBQQp!@$M>GFP90fdM22!ytPNq3!^M9Za7&M4zK5
z0}F#CG>kz)AbAkq3Y`8y`GSSP4$20FGsvAFJ}B-${xoA?1;-UAPC)uWVm}y|7+4t?
z7(i+{7#J8p`2?mH6eghbkIV<9cVs>&eIxTh=^2?1O4Bet$Zw!D4dUA}uz=&2kAVT4
zc0qhlTnRBSFo5DIoPiabUW6GK7(i-3;&u$oVE2K<K<YtZC<!eWc%We_1&udX1_lO@
z97qqy4<K=v9$9EP0@DLZ*PwU<=>f@s^nm;c5{Kyl<t0%3!t}uM8b}XF4x|SZ79eq$
z9u)=#29P=R3@qUIR%Kvd0Exlm)fgDS<qHo3J6K*F>UMVq1_qE?kUYp-P?&<`VEQzn
z`e1x5s6A7_B?KtFF))DCfW$#&gVK^N)ND|EXfrS{fYN3k11s2GPyq}|iy$>1`#@?y
zVleZK85kHqZh-Mk7#P6i8q7XZ1_lO@8jv{5J~JfyK=L5_U~(2vIZ&K{)Pd5sBLgb~
zD1BH%(;z5KgX{yj-3FXK85mlj{<CFZU;vo`5(l{#l%_ypAoZX$;lRKEE=xgtP+D_@
zn(5EL0rsyG0|Ns{41_`X02KG2asy;ONWU`!0|UqmkUEeVAUC)$Fff4Z34o?EP`rc0
zV7L()mTn9T3?Tg=`9P?9LGcL^2iXM^^I%|L0EvO@hKYGX#XxQeU|<KQe=l(P&A<Rs
y50VF!6(B!@@-#>e<UUZD=#7?-Ky?ywKJsN?U;wEBiNo}R#QricGFUJ$G5`SW8fM`D

literal 0
HcmV?d00001

diff --git a/data/shaders/glsl/dynamicrendering/texture.vert b/data/shaders/glsl/dynamicrendering/texture.vert
new file mode 100644
index 00000000..3c7c17eb
--- /dev/null
+++ b/data/shaders/glsl/dynamicrendering/texture.vert
@@ -0,0 +1,33 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV;
+
+layout (binding = 0) uniform UBO 
+{
+	mat4 projection;
+	mat4 model;
+	vec4 viewPos;
+} ubo;
+
+layout (location = 0) out vec2 outUV;
+layout (location = 1) out vec3 outNormal;
+layout (location = 2) out vec3 outViewVec;
+layout (location = 3) out vec3 outLightVec;
+
+void main() 
+{
+	outUV = inUV;
+
+	vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0));
+
+	gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
+
+    vec4 pos = ubo.model * vec4(inPos, 1.0);
+	outNormal = mat3(inverse(transpose(ubo.model))) * inNormal;
+	vec3 lightPos = vec3(0.0);
+	vec3 lPos = mat3(ubo.model) * lightPos.xyz;
+    outLightVec = lPos - pos.xyz;
+    outViewVec = ubo.viewPos.xyz - pos.xyz;		
+}
diff --git a/data/shaders/glsl/dynamicrendering/texture.vert.spv b/data/shaders/glsl/dynamicrendering/texture.vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..1d7d8865ae153715719443f3e464e7251c70eac0
GIT binary patch
literal 3224
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

literal 0
HcmV?d00001

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index b2db1356..1a4e49c5 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -79,6 +79,7 @@ set(EXAMPLES
 	descriptorsets
 	displacement
 	distancefieldfonts
+	dynamicrendering
 	dynamicuniformbuffer
 	gears
 	geometryshader
diff --git a/examples/dynamicrendering/dynamicrendering.cpp b/examples/dynamicrendering/dynamicrendering.cpp
new file mode 100644
index 00000000..d1a01672
--- /dev/null
+++ b/examples/dynamicrendering/dynamicrendering.cpp
@@ -0,0 +1,281 @@
+/*
+ * Vulkan Example - Using VK_KHR_dynamic_rendering for rendering without framebuffers and render passes (wip)
+ *
+ * Copyright (C) 2021 by Sascha Willems - www.saschawillems.de
+ *
+ * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+ */
+
+#include "vulkanexamplebase.h"
+#include "VulkanglTFModel.h"
+
+#define ENABLE_VALIDATION false
+
+class VulkanExample : public VulkanExampleBase
+{
+public:
+	PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHR;
+	PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHR;
+
+	VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeaturesKHR{};
+
+	vks::Texture2D texture;
+	vkglTF::Model model;
+
+	struct UniformData {
+		glm::mat4 projection;
+		glm::mat4 modelView;
+		glm::vec4 viewPos;
+	} uniformData;
+	vks::Buffer uniformBuffer;
+
+	VkPipeline pipeline;
+	VkPipelineLayout pipelineLayout;
+	VkDescriptorSet descriptorSet;
+	VkDescriptorSetLayout descriptorSetLayout;
+
+	VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
+	{
+		title = "Dynamic rendering";
+		camera.type = Camera::CameraType::lookat;
+		camera.setPosition(glm::vec3(0.0f, 0.0f, -10.0f));
+		camera.setRotation(glm::vec3(-7.5f, 72.0f, 0.0f));
+		camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
+
+		enabledDeviceExtensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
+	}
+
+	~VulkanExample()
+	{
+		if (device) {
+			vkDestroyPipeline(device, pipeline, nullptr);
+			vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+			vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
+			uniformBuffer.destroy();
+		}
+	}
+
+	void setupRenderPass()
+	{
+		// With VK_KHR_dynamic_rendering we no longer need a render pass, so skip the sample base render pass setup
+		renderPass = VK_NULL_HANDLE;
+	}
+
+	// Enable physical device features required for this example
+	virtual void getEnabledFeatures()
+	{
+		// Enable anisotropic filtering if supported
+		if (deviceFeatures.samplerAnisotropy) {
+			enabledFeatures.samplerAnisotropy = VK_TRUE;
+		};
+
+		dynamicRenderingFeaturesKHR.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR;
+		dynamicRenderingFeaturesKHR.dynamicRendering = VK_TRUE;
+
+		deviceCreatepNextChain = &dynamicRenderingFeaturesKHR;
+	}
+
+	void loadAssets()
+	{
+		const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY;
+		model.loadFromFile(getAssetPath() + "models/voyager.gltf", vulkanDevice, queue, glTFLoadingFlags);
+		texture.loadFromFile(getAssetPath() + "textures/vulkan_11_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
+	}
+
+	void buildCommandBuffers()
+	{
+		VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
+
+		for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
+		{
+			VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
+
+			// @todo: comment
+			VkRenderingAttachmentInfoKHR colorAttachment{};
+			colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
+			colorAttachment.imageView = swapChain.buffers[i].view;
+			colorAttachment.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
+			colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE_KHR;
+			colorAttachment.clearValue.color = { 0.0f,0.0f,0.0f,0.0f };
+
+			// A single depth stencil attachment info can be used, but they can also be specified separately.
+			// When both are specified separately, the only requirement is that the image view is identical.			
+			VkRenderingAttachmentInfoKHR depthStencilAttachment{};
+			depthStencilAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
+			depthStencilAttachment.imageView = depthStencil.view;
+			depthStencilAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR;
+			depthStencilAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			depthStencilAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+			depthStencilAttachment.clearValue.depthStencil = { 1.0f,  0 };
+
+			VkRenderingInfoKHR renderingInfo{};
+			renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
+			renderingInfo.renderArea = { 0, 0, width, height };
+			renderingInfo.layerCount = 1;
+			renderingInfo.colorAttachmentCount = 1;
+			renderingInfo.pColorAttachments = &colorAttachment;
+			renderingInfo.pDepthAttachment = &depthStencilAttachment;
+			renderingInfo.pStencilAttachment = &depthStencilAttachment;
+
+			// Begin dynamic rendering
+			vkCmdBeginRenderingKHR(drawCmdBuffers[i], &renderingInfo);
+
+			VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
+			vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
+
+			VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
+			vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
+
+			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
+			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+
+			model.draw(drawCmdBuffers[i], vkglTF::RenderFlags::BindImages, pipelineLayout);
+
+			// End dynamic rendering
+			vkCmdEndRenderingKHR(drawCmdBuffers[i]);
+
+			VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
+		}
+	}
+
+	void draw()
+	{
+		VulkanExampleBase::prepareFrame();
+		submitInfo.commandBufferCount = 1;
+		submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
+		VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
+		VulkanExampleBase::submitFrame();
+	}
+
+	void setupDescriptorPool()
+	{
+		// Example uses one ubo and one image sampler
+		std::vector<VkDescriptorPoolSize> poolSizes = {
+			vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
+			vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
+		};
+		VkDescriptorPoolCreateInfo descriptorPoolInfo =
+			vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
+		VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
+	}
+
+	void setupDescriptorSetLayout()
+	{
+		const std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
+			// Binding 0 : Vertex shader uniform buffer
+			vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
+		};
+		VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
+		VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
+
+		// Layout uses set 0 for passing vertex shader ubo and set 1 for fragment shader images (taken from glTF model)
+		const std::vector<VkDescriptorSetLayout> setLayouts = {
+			descriptorSetLayout,
+			vkglTF::descriptorSetLayoutImage,
+		};
+		VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), 2);
+		VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
+	}
+
+	void setupDescriptorSet()
+	{
+		VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
+		VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
+		std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
+			// Binding 0 : Vertex shader uniform buffer
+			vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
+		};
+		vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);
+	}
+
+	void preparePipelines()
+	{
+		VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
+		VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
+		VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
+		VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
+		VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
+		VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
+		VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
+		std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+		VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
+		std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages{};
+
+		// We no longer need to set a renderpass for the pipeline create info
+		VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo();
+		pipelineCI.layout = pipelineLayout;
+		pipelineCI.pInputAssemblyState = &inputAssemblyState;
+		pipelineCI.pRasterizationState = &rasterizationState;
+		pipelineCI.pColorBlendState = &colorBlendState;
+		pipelineCI.pMultisampleState = &multisampleState;
+		pipelineCI.pViewportState = &viewportState;
+		pipelineCI.pDepthStencilState = &depthStencilState;
+		pipelineCI.pDynamicState = &dynamicState;
+		pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
+		pipelineCI.pStages = shaderStages.data();
+		pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV });
+
+		// New create info to define color, depth and stencil attachments at pipeline create time
+		VkPipelineRenderingCreateInfoKHR pipelineRenderingCreateInfo{};
+		pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
+		pipelineRenderingCreateInfo.colorAttachmentCount = 1;
+		pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChain.colorFormat;
+		pipelineRenderingCreateInfo.depthAttachmentFormat = depthFormat;
+		pipelineRenderingCreateInfo.stencilAttachmentFormat = depthFormat;
+		// Chain into the pipeline creat einfo
+		pipelineCI.pNext = &pipelineRenderingCreateInfo;
+
+		shaderStages[0] = loadShader(getShadersPath() + "dynamicrendering/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
+		shaderStages[1] = loadShader(getShadersPath() + "dynamicrendering/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
+		VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
+	}
+
+	// Prepare and initialize uniform buffer containing shader uniforms
+	void prepareUniformBuffers()
+	{
+		VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(uniformData), &uniformData));
+		VK_CHECK_RESULT(uniformBuffer.map());
+
+		updateUniformBuffers();
+	}
+
+	void updateUniformBuffers()
+	{
+		uniformData.projection = camera.matrices.perspective;
+		uniformData.modelView = camera.matrices.view;
+		uniformData.viewPos = camera.viewPos;
+		memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData));
+	}
+
+	void prepare()
+	{
+		VulkanExampleBase::prepare();
+
+		vkCmdBeginRenderingKHR = reinterpret_cast<PFN_vkCmdBeginRenderingKHR>(vkGetDeviceProcAddr(device, "vkCmdBeginRenderingKHR"));
+		vkCmdEndRenderingKHR = reinterpret_cast<PFN_vkCmdEndRenderingKHR>(vkGetDeviceProcAddr(device, "vkCmdEndRenderingKHR"));
+
+		loadAssets();
+		prepareUniformBuffers();
+		setupDescriptorSetLayout();
+		preparePipelines();
+		setupDescriptorPool();
+		setupDescriptorSet();
+		buildCommandBuffers();
+		prepared = true;
+	}
+
+	virtual void render()
+	{
+		if (!prepared)
+			return;
+		draw();
+	}
+
+	virtual void viewChanged()
+	{
+		updateUniformBuffers();
+	}
+};
+
+VULKAN_EXAMPLE_MAIN()
-- 
GitLab