From 358ab9d550775dcfb3632b49e0b60a68ad3239ed Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 28 Mar 2019 23:51:50 +0100 Subject: [PATCH] Added new sample to demonstrate negative viewport height via VK_KHR_MAINTENANCE1 Refs #563 --- data/shaders/negativeviewportheight/quad.frag | 12 + .../negativeviewportheight/quad.frag.spv | Bin 0 -> 808 bytes data/shaders/negativeviewportheight/quad.vert | 14 + .../negativeviewportheight/quad.vert.spv | Bin 0 -> 1004 bytes examples/CMakeLists.txt | 1 + .../negativeviewportheight.cpp | 309 ++++++++++++++++++ 6 files changed, 336 insertions(+) create mode 100644 data/shaders/negativeviewportheight/quad.frag create mode 100644 data/shaders/negativeviewportheight/quad.frag.spv create mode 100644 data/shaders/negativeviewportheight/quad.vert create mode 100644 data/shaders/negativeviewportheight/quad.vert.spv create mode 100644 examples/negativeviewportheight/negativeviewportheight.cpp diff --git a/data/shaders/negativeviewportheight/quad.frag b/data/shaders/negativeviewportheight/quad.frag new file mode 100644 index 00000000..e0340e54 --- /dev/null +++ b/data/shaders/negativeviewportheight/quad.frag @@ -0,0 +1,12 @@ +#version 450 + +layout (binding = 0) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; +layout (location = 0) out vec4 outColor; + +void main() +{ + outColor = texture(samplerColor, inUV); + outColor.rgb *= vec3(inUV, 0.0f); +} \ No newline at end of file diff --git a/data/shaders/negativeviewportheight/quad.frag.spv b/data/shaders/negativeviewportheight/quad.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..836ff5b41a8edf8daf050f14307839b870ea3aae GIT binary patch literal 808 zcmY+BOD{uF6ot1|UrMX$RrM%_lEwg$2onR7CWv6%gh7*1Y558Mm|tZg@qM?wQ9Idp zpS{jH>#TilHnU%dn2ACZV<*N@iwsCaIr2%p>fCkOqxYxw(ea^-N@NqFoGK@0spdZTcH4%q_uO96{q?(3z1~uO9as9=O&mtDUS*vJYmsZ{>EB+@ExtvnFCcz4>GdGi zz|9<0KBNANv~R*Jvm;Nv`3Xx*J$q*`yuV6SPoMG^scMPKw|6G71A4hDq~ZBhs+j&x zcv*Fv#Sr@j$x+Wi^-v+b-`+cxNcA6KIdh$qBYuH`vx}wtW!Ca(_` z`384-Y>;x@)4zJ&w?TTxH7_r(Il;sYHJs^_``q6o<*1ixY_T3|loCz>#yo1==|0l zYnHor&bepio|#Ff4)VqS<;=e^v1B`z#9`3XP{|tDB^*WnEgA5O;{j(h8>!hY!Jg%{VmDi*#i z4QrU!HOnb`iiY%h>=;w?gbT`O;m;Gyd%#g&QO2LEgPmvgTN7q~yw&tCDSHaWVS>w= zvqw{ST^T)GOEb>m_Mk@4cS9Ku)bR!84dCdrsq87(mp)derSHS5t+$Md!J#jmH}j<^SWfYJZxlxK=xY{WYI literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 77119f5c..7745b90f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -73,6 +73,7 @@ set(EXAMPLES multisampling multithreading multiview + negativeviewportheight occlusionquery offscreen parallaxmapping diff --git a/examples/negativeviewportheight/negativeviewportheight.cpp b/examples/negativeviewportheight/negativeviewportheight.cpp new file mode 100644 index 00000000..7c3c9921 --- /dev/null +++ b/examples/negativeviewportheight/negativeviewportheight.cpp @@ -0,0 +1,309 @@ +/* +* Vulkan Example - Using VK_KHR_MAINTENANCE1 for negative viewport heights +* +* Copyright (C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include +#include + +#include +#include "vulkanexamplebase.h" +#include "VulkanTexture.hpp" + +#define ENABLE_VALIDATION false + +class VulkanExample : public VulkanExampleBase +{ +public: + bool negativeViewport; + int32_t offsety = 0; + int32_t offsetx = 0; + int32_t windingOrder = 1; + int32_t cullMode = 0; + int32_t quadType = 0; + + vks::Texture2D texture; + + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + + struct Quad { + vks::Buffer verticesYUp; + vks::Buffer verticesYDown; + vks::Buffer indices; + } quad; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + title = "Negative Viewport height"; + settings.overlay = true; + // VK_KHR_MAINTENANCE1 is required for using negative viewport heights + enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + texture.destroy(); + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + VkViewport viewport{}; + if (negativeViewport) { + // When using a negative viewport height, the origin needs to be adjusted too + viewport.x = offsetx; + viewport.y = (float)height - offsety; + viewport.width = (float)width; + viewport.height = -(float)height; + } + else { + viewport.x = offsetx; + viewport.y = offsety; + viewport.width = (float)width; + viewport.height = (float)height; + } + viewport.minDepth = 0.0f; + viewport.maxDepth = 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); + + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindIndexBuffer(drawCmdBuffers[i], quad.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, quadType == 0 ? &quad.verticesYDown.buffer : &quad.verticesYUp.buffer, offsets); + vkCmdDrawIndexed(drawCmdBuffers[i], 6, 1, 0, 0, 0); + + drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + texture.loadFromFile(getAssetPath() + "textures/texture_orientation_test_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + + // Create two quads with different Y orientations + + struct Vertex { + float pos[3]; + float uv[2]; + }; + + const float ar = (float)height / (float)width; + + // OpenGL style (y points upwards) + std::vector verticesYPos = { + { -1.0f * ar, 1.0f, 1.0f, 0.0f, 1.0f }, + { -1.0f * ar, -1.0f, 1.0f, 0.0f, 0.0f }, + { 1.0f * ar, -1.0f, 1.0f, 1.0f, 0.0f }, + { 1.0f * ar, 1.0f, 1.0f, 1.0f, 1.0f }, + }; + + // Vulkan style (y points downwards) + std::vector verticesYNeg = { + { -1.0f * ar, -1.0f, 1.0f, 0.0f, 1.0f }, + { -1.0f * ar, 1.0f, 1.0f, 0.0f, 0.0f }, + { 1.0f * ar, 1.0f, 1.0f, 1.0f, 0.0f }, + { 1.0f * ar, -1.0f, 1.0f, 1.0f, 1.0f }, + }; + std::vector indices = { 2,1,0, 0,3,2 }; + + const VkMemoryPropertyFlags memoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, memoryPropertyFlags, &quad.verticesYUp, verticesYPos.size() * sizeof(Vertex), verticesYPos.data())); + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, memoryPropertyFlags, &quad.verticesYDown, verticesYNeg.size() * sizeof(Vertex), verticesYNeg.data())); + + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, memoryPropertyFlags, &quad.indices, indices.size() * sizeof(uint32_t), indices.data())); + } + + void setupDescriptors() + { + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayout)); + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + VkDescriptorPoolSize poolSize = vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1); + VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo(1, &poolSize, 1); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); + + VkDescriptorSetAllocateInfo descriptorSetAI = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAI, &descriptorSet)); + + VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texture.descriptor); + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); + } + + void recreatePipeline() + { + vkDestroyPipeline(device, pipeline, nullptr); + preparePipelines(); + } + + void preparePipelines() + { + const std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast(dynamicStateEnables.size()), 0); + + //VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + + VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; + rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; + rasterizationStateCI.lineWidth = 1.0f; + rasterizationStateCI.cullMode = VK_CULL_MODE_NONE + cullMode; + rasterizationStateCI.frontFace = windingOrder == 0 ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE; + + // Vertex bindings and attributes + std::vector vertexInputBindings = { + vks::initializers::vertexInputBindingDescription(0, sizeof(float) * 5, VK_VERTEX_INPUT_RATE_VERTEX), + }; + std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 3), // uv + }; + VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputState.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size()); + vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputState.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + VkGraphicsPipelineCreateInfo pipelineCreateInfoCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + //pipelineCreateInfoCI.pVertexInputState = &emptyInputState; + pipelineCreateInfoCI.pVertexInputState = &vertexInputState; + pipelineCreateInfoCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCreateInfoCI.pRasterizationState = &rasterizationStateCI; + pipelineCreateInfoCI.pColorBlendState = &colorBlendStateCI; + pipelineCreateInfoCI.pMultisampleState = &multisampleStateCI; + pipelineCreateInfoCI.pViewportState = &viewportStateCI; + pipelineCreateInfoCI.pDepthStencilState = &depthStencilStateCI; + pipelineCreateInfoCI.pDynamicState = &dynamicStateCI; + + const std::array shaderStages = { + loadShader(getAssetPath() + "shaders/negativeviewportheight/quad.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), + loadShader(getAssetPath() + "shaders/negativeviewportheight/quad.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) + }; + + pipelineCreateInfoCI.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfoCI.pStages = shaderStages.data(); + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfoCI, nullptr, &pipeline)); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + { + if (overlay->header("Scene")) { + overlay->text("Quad type"); + if (overlay->comboBox("##quadtype", &quadType, { "VK (y negative)", "GL (y positive)" })) { + buildCommandBuffers(); + } + } + + if (overlay->header("Viewport")) { + if (overlay->checkBox("Negative viewport height", &negativeViewport)) { + buildCommandBuffers(); + } + if (overlay->sliderInt("offfset x", &offsetx, -(int32_t)width, (int32_t)width)) { + buildCommandBuffers(); + } + if (overlay->sliderInt("offfset y", &offsety, -(int32_t)height, (int32_t)height)) { + buildCommandBuffers(); + } + } + if (overlay->header("Pipeline")) { + overlay->text("Winding order"); + if (overlay->comboBox("##windingorder", &windingOrder, { "clock wise", "counter clock wise" })) { + recreatePipeline(); + } + overlay->text("Cull mode"); + if (overlay->comboBox("##cullmode", &cullMode, { "none", "front face", "back face" })) { + recreatePipeline(); + } + } + } +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file