From 62070aa9f4446c26563a3ce83ef23323731d255e Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 11 Jul 2020 13:01:02 +0200 Subject: [PATCH] Reworking sparse image residency sample Was broken with recent drivers --- .../sparseresidency.frag | 18 +- .../sparseresidency.frag.spv | Bin 2612 -> 1224 bytes .../sparseresidency.vert | 16 - .../sparseresidency.vert.spv | Bin 2364 -> 1732 bytes .../texturesparseresidency.cpp | 528 +++++++----------- 5 files changed, 202 insertions(+), 360 deletions(-) diff --git a/data/shaders/glsl/texturesparseresidency/sparseresidency.frag b/data/shaders/glsl/texturesparseresidency/sparseresidency.frag index c442a7a1..a55916b8 100644 --- a/data/shaders/glsl/texturesparseresidency/sparseresidency.frag +++ b/data/shaders/glsl/texturesparseresidency/sparseresidency.frag @@ -7,9 +7,6 @@ layout (binding = 1) uniform sampler2D samplerColor; layout (location = 0) in vec2 inUV; layout (location = 1) in float inLodBias; -layout (location = 2) in vec3 inNormal; -layout (location = 3) in vec3 inViewVec; -layout (location = 4) in vec3 inLightVec; layout (location = 0) out vec4 outFragColor; @@ -21,27 +18,22 @@ void main() int residencyCode = sparseTextureARB(samplerColor, inUV, color, inLodBias); // Fetch sparse until we get a valid texel + /* float minLod = 1.0; while (!sparseTexelsResidentARB(residencyCode)) { residencyCode = sparseTextureClampARB(samplerColor, inUV, minLod, color); minLod += 1.0f; - } + } + */ // Check if texel is resident bool texelResident = sparseTexelsResidentARB(residencyCode); if (!texelResident) { - color = vec4(1.0, 0.0, 0.0, 0.0); + color = vec4(0.0, 0.0, 0.0, 0.0); } - vec3 N = normalize(inNormal); - - N = normalize((inNormal - 0.5) * 2.0); - - vec3 L = normalize(inLightVec); - vec3 R = reflect(-L, N); - vec3 diffuse = max(dot(N, L), 0.25) * color.rgb; - outFragColor = vec4(diffuse, 1.0); + outFragColor = color; } \ No newline at end of file diff --git a/data/shaders/glsl/texturesparseresidency/sparseresidency.frag.spv b/data/shaders/glsl/texturesparseresidency/sparseresidency.frag.spv index a652e319226fdc14af869827a3962c6df6db7958..9f661cbe18011a29963e38c054600146478d5aa9 100644 GIT binary patch delta 364 zcmdlYa)OhWnMs+Qfq{{M0|>Pz@`_K?QIqCiU}0bYW6%WB`K2XpMTzOo`8oMT43im|+&Mv_azMcs0MWiC<^TWy literal 2612 zcmZvdYjaao6oyaRq^(l9$W25s0k42n5D=xbp+$lip%kchLre~snKbDn=_sG9<99## zBm5oyC`D(S@p(?q8qYB7nYU}LcU|`0Yp;_|O`e!Zl8Iy{nN2#$&f1qu!X%0Etz4c& z?n-Qh^XJaLYsT(mN|E{Y5LH4K8?A0%{t!3_!gLQMZ%!+mBWToiyj4M~*1cONT3YcvjEzcl-C&5!mJIF5P}(&{^)bMj2O1jw096 z(cMRzX_iyL-pGP>_B@BpEz%xAZal^=bLU0m%}g(6{pt2^4u%`89#rPPfo$Y{dq>Ic z!|%uIDGfB=+_lVKO70*#-3Jf0MxJ3h>+@i1d~MizSh!C)>wUMs-c5hV$|QAq#qr1A1cn3g?A>*+79u#a-Ky*P|B ze_tLXcPY2+r^~Y+Kw8gx8GjIIEqUX$?XR5v7joSk{o3|h|1oT5&9i!D<6p^j4@Pq8axd(j4vR(Is?ai#_tp3vkx+J9A&b8&{N*sP_n z>pONW;++v|g#WY5zrASvuhHGxJ&o6PhU6l@jc)!0rrq8;8!lrW!<3Q6IxGGLL@nRj z-+-{~U!`E%C%@P5d-ugB>>E$meuu?)zeVjuPUGIr;$_a_jO+)_hqg0t82J9$_WVWQ zd!Lz@$=>=UbaRaJzWQ%-JL|s;^vlOya{8SSzcKwT{oY$ny@ITQzvuIPtc^!^Nb1^i6^e2cR{&OSxY>KiJ$sh$DXqVN zyaH-qj+<#O1N-z5&}R+jTt0GE&}(2d&oTCD0l&~EYh44z>i;$8uIGFm8GC+=9((GK zJ>_H12D-luk@G2f`7NK1b#!Cot?kTw4y^5)>i2ie{2AmIz<0XEuaf8ZGUw!7=1qd=;aBL! zSkrs^yCN6o`4(Ny`F8InkaGohA6?Gc!L@SEy;eDyXZsGAe;??t0{8THSKq%>xCs6O D7}dHV diff --git a/data/shaders/glsl/texturesparseresidency/sparseresidency.vert b/data/shaders/glsl/texturesparseresidency/sparseresidency.vert index 8cd7095b..e613226f 100644 --- a/data/shaders/glsl/texturesparseresidency/sparseresidency.vert +++ b/data/shaders/glsl/texturesparseresidency/sparseresidency.vert @@ -14,26 +14,10 @@ layout (binding = 0) uniform UBO layout (location = 0) out vec2 outUV; layout (location = 1) out float outLodBias; -layout (location = 2) out vec3 outNormal; -layout (location = 3) out vec3 outViewVec; -layout (location = 4) out vec3 outLightVec; - -out gl_PerVertex -{ - vec4 gl_Position; -}; void main() { outUV = inUV; outLodBias = ubo.lodBias; - outNormal = inNormal; - - vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0)); - gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0); - - vec3 lightPos = vec3(0.0, 50.0f, 0.0f); - outLightVec = lightPos - inPos.xyz; - outViewVec = ubo.viewPos.xyz - worldPos.xyz; } diff --git a/data/shaders/glsl/texturesparseresidency/sparseresidency.vert.spv b/data/shaders/glsl/texturesparseresidency/sparseresidency.vert.spv index a0fa11922c63ae10e85c2e85075e0b9df69b9591..328462a05356aa287df8e9585f1b26220e8b77b2 100644 GIT binary patch literal 1732 zcmY+DYflqF6o$u^E_eeGxu}R+idVFvpfNGt(4>55w1mJ{Q|ZQx+3u3=R*j$d3F=Ss zSNX-n_nF-(b;#k&`=0llIdf)Ptu!W$Ic28ww`GboYbs)lIcoyvTkXAeGs}C;t;ZWE zrcKoe(acE2nvnIoHer8S20JZVk=>DP%BuRS$p2PiO&Q}kVDi#ozJDMOriM)%6t1~? zinr6=w(VwUs%BQ2{q6VaG;n)Cb2LalM~Ash)lDE38hY)gy(pIV9KC$8(brCzjW})= zmj_UPPI&k+Rh7UjYyTGYiIc$?&V_IrfR$2ecH zH?ci>YqPwY97dxUeGnVQ@hC<=%#+1@n5#|b9$a%Xw#mD6(C>0h(<*1`bHL0L%-LZ7 zM)bKO%&b^7*^d5u+;HswoIV_L!Rd-TYK-TB$$KU9wct+jiu&||?pWq^+#l|TtX00V z@0mAQm9e-3xb9Gq&pWY-FY|=sY&bmUpra-n=Xs7j9N#BrdmU#n^YM9ZmYAAzsz$xK za;U+6LAyXclr1WsyMu{^!c+2S;h&Y5-vJ!)GxF5-aj?Gw@$$&o_boiM}z;X4Wipf5h+`c`lYWJtyNmUnt7E zgI`O*{1#=*oVlXs>?IjIdfpj~K9sM^&~tzE+<8?7?=@@MqhSVKb3=RhU#dgSO&NLc zXqd%a8FSc`hCc3zXCU96Gu&b9x{nA3bsA`BQa?ZOAwu4Lvj8M>5`+ OdvR|ty1$z8Q`tX9O=fxk literal 2364 zcmZ9M+foxj5QYar0*Z==96VtF&mf`#qR1&Ki5ekjky{H%3fBr*D+%HyRlb1wNIsP} zR{4Ls({W{}&h-CxPxo~9EUC_kZs$5&x9f3lUA+2Trx@pYT_)P|#dpQ&W~(wiJ2!*J z8JCJ2@#zy{bxHPjf;##MNv{OUCCL@Zb;(1?y5yB4rN2(?zZ4{`)5-H-VuS~+?Xq?- z@q&5;h0{@N*^6N%7wk0gNx6RcZ09z0>rA8zy3dX9Q+2l$gt}QqUifhD{jgH4X*Z6$ zd8~M?5 zab=YVzxW(6lIOsjTa{QW zxPRy3_}m4*BZ>K?JojCD-p}3c%#6k*ENTFcMJUG1Em2Xi+# z?5DNkOH9s#nIjze`m|%?z9af&MsVO!S(quD@$p@|+&OXF5j%0g^cHSV_`LS4gnLYS zJSg4nJtQ4p;&G1)UN`$iVRU%ngPE1tFH0x3*}=>Xj=HaErzhAg|Cn^Mk4q;%brGNQ z%o;oX-0P;qxuca)Wo5?nh1d_Ya}PMqg82sFEZ3}b7XGW+v2osFKa|nN z=NA%u;MuJQ>(Ys3nD+&?ysxEG7kP_bC-1!8EhARHv4nH**0(3p zlYLthhGSWMPo={pYgzJKvTw`cIFCQ}WZzbV$&U|rUXAwnJE1P>Uek^rdQLlU-tL)~ z4rjO^oqK;%EZ)yc2|n7#Ms>Imz96XD9sn2qjq-Q0T Tb4xmB;K|9i2lq!O7bJfHLn(?1 diff --git a/examples/texturesparseresidency/texturesparseresidency.cpp b/examples/texturesparseresidency/texturesparseresidency.cpp index 7958176a..cd87e978 100644 --- a/examples/texturesparseresidency/texturesparseresidency.cpp +++ b/examples/texturesparseresidency/texturesparseresidency.cpp @@ -1,17 +1,13 @@ /* * Vulkan Example - Sparse texture residency example * -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016-2020 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ /* -todos: -- check sparse binding support on queue -- residencyNonResidentStrict -- meta data -- Run-time image data upload +* Note : This sample is work-in-progress and works basically, but it's not yet finished */ #include @@ -30,21 +26,12 @@ todos: #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanDevice.hpp" #include "VulkanBuffer.hpp" -#include "VulkanHeightmap.hpp" +#include "VulkanModel.hpp" -#define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false -// Vertex layout for this example -struct Vertex { - float pos[3]; - float normal[3]; - float uv[2]; -}; - // Virtual texture page as a part of the partially resident texture // Contains memory bindings, offsets and status information struct VirtualTexturePage @@ -62,6 +49,11 @@ struct VirtualTexturePage imageMemoryBind.memory = VK_NULL_HANDLE; // Page initially not backed up by memory } + bool resident() + { + return (imageMemoryBind.memory != VK_NULL_HANDLE); + } + // Allocate Vulkan memory for the virtual page void allocate(VkDevice device, uint32_t memoryTypeIndex) { @@ -96,7 +88,6 @@ struct VirtualTexturePage { vkFreeMemory(device, imageMemoryBind.memory, nullptr); imageMemoryBind.memory = VK_NULL_HANDLE; - //std::cout << "Page " << index << " released" << std::endl; } } }; @@ -123,6 +114,7 @@ struct VirtualTexture newPage.mipLevel = mipLevel; newPage.layer = layer; newPage.index = static_cast(pages.size()); + newPage.imageMemoryBind = {}; newPage.imageMemoryBind.offset = offset; newPage.imageMemoryBind.extent = extent; pages.push_back(newPage); @@ -133,12 +125,11 @@ struct VirtualTexture void updateSparseBindInfo() { // Update list of memory-backed sparse image memory binds - sparseImageMemoryBinds.resize(pages.size()); - uint32_t index = 0; + //sparseImageMemoryBinds.resize(pages.size()); + sparseImageMemoryBinds.clear(); for (auto page : pages) { - sparseImageMemoryBinds[index] = page.imageMemoryBind; - index++; + sparseImageMemoryBinds.push_back(page.imageMemoryBind); } // Update sparse bind info bindSparseInfo = vks::initializers::bindSparseInfo(); @@ -147,6 +138,7 @@ struct VirtualTexture // bindSparseInfo.pSignalSemaphores = &bindSparseSemaphore; // Image memory binds + imageMemoryBindInfo = {}; imageMemoryBindInfo.image = image; imageMemoryBindInfo.bindCount = static_cast(sparseImageMemoryBinds.size()); imageMemoryBindInfo.pBinds = sparseImageMemoryBinds.data(); @@ -154,11 +146,14 @@ struct VirtualTexture bindSparseInfo.pImageBinds = &imageMemoryBindInfo; // Opaque image memory binds (mip tail) + // @todo + /* opaqueMemoryBindInfo.image = image; opaqueMemoryBindInfo.bindCount = static_cast(opaqueMemoryBinds.size()); opaqueMemoryBindInfo.pBinds = opaqueMemoryBinds.data(); bindSparseInfo.imageOpaqueBindCount = (opaqueMemoryBindInfo.bindCount > 0) ? 1 : 0; bindSparseInfo.pImageOpaqueBinds = &opaqueMemoryBindInfo; + */ } // Release all Vulkan resources @@ -193,21 +188,12 @@ public: uint32_t layerCount; } texture; - struct { - vks::Texture2D source; - } textures; - - vks::HeightMap *heightMap = nullptr; - - struct { - VkPipelineVertexInputStateCreateInfo inputState; - std::vector bindingDescriptions; - std::vector attributeDescriptions; - } vertices; - - uint32_t indexCount; - - vks::Buffer uniformBufferVS; + vks::VertexLayout vertexLayout = vks::VertexLayout({ + vks::VERTEX_COMPONENT_POSITION, + vks::VERTEX_COMPONENT_NORMAL, + vks::VERTEX_COMPONENT_UV, + }); + vks::Model plane; struct UboVS { glm::mat4 projection; @@ -215,11 +201,9 @@ public: glm::vec4 viewPos; float lodBias = 0.0f; } uboVS; + vks::Buffer uniformBufferVS; - struct { - VkPipeline solid; - } pipelines; - + VkPipeline pipeline; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout; @@ -231,43 +215,31 @@ public: { title = "Sparse texture residency"; std::cout.imbue(std::locale("")); - camera.type = Camera::CameraType::firstperson; - camera.movementSpeed = 50.0f; -#ifndef __ANDROID__ - camera.rotationSpeed = 0.25f; -#endif - camera.position = { 84.5f, 40.5f, 225.0f }; - camera.setRotation(glm::vec3(-8.5f, -200.0f, 0.0f)); - camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 1024.0f); + camera.type = Camera::CameraType::lookat; + camera.setPosition(glm::vec3(0.0f, 0.0f, -20.0f)); + camera.setRotation(glm::vec3(0.0f, 180.0f, 0.0f)); + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); settings.overlay = true; - // Device features to be enabled for this example - enabledFeatures.shaderResourceResidency = VK_TRUE; - enabledFeatures.shaderResourceMinLod = VK_TRUE; } ~VulkanExample() { // Clean up used Vulkan resources // Note : Inherited destructor cleans up resources stored in base class - - if (heightMap) - delete heightMap; - destroyTextureImage(texture); - vkDestroySemaphore(device, bindSparseSemaphore, nullptr); - - vkDestroyPipeline(device, pipelines.solid, nullptr); - + vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - + plane.destroy(); uniformBufferVS.destroy(); } virtual void getEnabledFeatures() { if (deviceFeatures.sparseBinding && deviceFeatures.sparseResidencyImage2D) { + enabledFeatures.shaderResourceResidency = VK_TRUE; + enabledFeatures.shaderResourceMinLod = VK_TRUE; enabledFeatures.sparseBinding = VK_TRUE; enabledFeatures.sparseResidencyImage2D = VK_TRUE; } @@ -279,9 +251,9 @@ public: glm::uvec3 alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity) { glm::uvec3 res; - res.x = extent.width / granularity.width + ((extent.width % granularity.width) ? 1u : 0u); + res.x = extent.width / granularity.width + ((extent.width % granularity.width) ? 1u : 0u); res.y = extent.height / granularity.height + ((extent.height % granularity.height) ? 1u : 0u); - res.z = extent.depth / granularity.depth + ((extent.depth % granularity.depth) ? 1u : 0u); + res.z = extent.depth / granularity.depth + ((extent.depth % granularity.depth) ? 1u : 0u); return res; } @@ -291,6 +263,7 @@ public: texture.width = width; texture.height = height; texture.mipLevels = floor(log2(std::max(width, height))) + 1; + texture.mipLevels = 1; texture.layerCount = layerCount; texture.format = format; @@ -298,19 +271,16 @@ public: VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); + const VkImageType imageType = VK_IMAGE_TYPE_2D; + const VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; + const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + const VkImageTiling imageTiling = VK_IMAGE_TILING_OPTIMAL; + // Get sparse image properties std::vector sparseProperties; // Sparse properties count for the desired format uint32_t sparsePropertiesCount; - vkGetPhysicalDeviceSparseImageFormatProperties( - physicalDevice, - format, - VK_IMAGE_TYPE_2D, - VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_TILING_OPTIMAL, - &sparsePropertiesCount, - nullptr); + vkGetPhysicalDeviceSparseImageFormatProperties(physicalDevice, format, imageType, sampleCount, imageUsage, imageTiling, &sparsePropertiesCount, nullptr); // Check if sparse is supported for this format if (sparsePropertiesCount == 0) { @@ -320,15 +290,7 @@ public: // Get actual image format properties sparseProperties.resize(sparsePropertiesCount); - vkGetPhysicalDeviceSparseImageFormatProperties( - physicalDevice, - format, - VK_IMAGE_TYPE_2D, - VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_TILING_OPTIMAL, - &sparsePropertiesCount, - sparseProperties.data()); + vkGetPhysicalDeviceSparseImageFormatProperties(physicalDevice, format, imageType, sampleCount, imageUsage, imageTiling, &sparsePropertiesCount, sparseProperties.data()); std::cout << "Sparse image format properties: " << sparsePropertiesCount << std::endl; for (auto props : sparseProperties) @@ -340,19 +302,23 @@ public: // Create sparse image VkImageCreateInfo sparseImageCreateInfo = vks::initializers::imageCreateInfo(); - sparseImageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + sparseImageCreateInfo.imageType = imageType; sparseImageCreateInfo.format = texture.format; sparseImageCreateInfo.mipLevels = texture.mipLevels; sparseImageCreateInfo.arrayLayers = texture.layerCount; - sparseImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - sparseImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + sparseImageCreateInfo.samples = sampleCount; + sparseImageCreateInfo.tiling = imageTiling; sparseImageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; sparseImageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; sparseImageCreateInfo.extent = { texture.width, texture.height, 1 }; - sparseImageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + sparseImageCreateInfo.usage = imageUsage; sparseImageCreateInfo.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; VK_CHECK_RESULT(vkCreateImage(device, &sparseImageCreateInfo, nullptr, &texture.image)); + VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + vulkanDevice->flushCommandBuffer(copyCmd, queue); + // Get memory requirements VkMemoryRequirements sparseImageMemoryReqs; // Sparse image memory requirement counts @@ -452,7 +418,7 @@ public: lastBlockExtent.y = (extent.height % imageGranularity.height) ? extent.height % imageGranularity.height : imageGranularity.height; lastBlockExtent.z = (extent.depth % imageGranularity.depth) ? extent.depth % imageGranularity.depth : imageGranularity.depth; - // Alllocate memory for some blocks + // @todo: Comment uint32_t index = 0; for (uint32_t z = 0; z < sparseBindCounts.z; z++) { @@ -472,15 +438,9 @@ public: extent.depth = (z == sparseBindCounts.z - 1) ? lastBlockExtent.z : imageGranularity.depth; // Add new virtual page - VirtualTexturePage *newPage = texture.addPage(offset, extent, sparseImageMemoryReqs.alignment, mipLevel, layer); + VirtualTexturePage* newPage = texture.addPage(offset, extent, sparseImageMemoryReqs.alignment, mipLevel, layer); newPage->imageMemoryBind.subresource = subResource; - if ((x % 2 == 1) || (y % 2 == 1)) - { - // Allocate memory for this virtual page - //newPage->allocate(device, memoryTypeIndex); - } - index++; } } @@ -550,9 +510,9 @@ public: sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; @@ -580,9 +540,6 @@ public: texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; texture.descriptor.imageView = texture.view; texture.descriptor.sampler = texture.sampler; - - // Fill smallest (non-tail) mip map leve - fillVirtualTexture(lastFilledMip); } // Free all Vulkan resources used a texture object @@ -599,7 +556,7 @@ public: VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; - clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 1.0f } }; + clearValues[0].color = defaultClearColor; clearValues[1].depthStencil = { 1.0f, 0 }; VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); @@ -613,7 +570,6 @@ public: for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { - // Set target frame buffer renderPassBeginInfo.framebuffer = frameBuffers[i]; VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); @@ -627,12 +583,12 @@ public: vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &heightMap->vertexBuffer.buffer, offsets); - vkCmdBindIndexBuffer(drawCmdBuffers[i], heightMap->indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(drawCmdBuffers[i], heightMap->indexCount, 1, 0, 0, 0); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &plane.vertices.buffer, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], plane.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(drawCmdBuffers[i], plane.indexCount, 1, 0, 0, 0); drawUI(drawCmdBuffers[i]); @@ -645,78 +601,15 @@ public: void draw() { VulkanExampleBase::prepareFrame(); - - // Sparse bindings -// vkQueueBindSparse(queue, 1, &bindSparseInfo, VK_NULL_HANDLE); - //todo: use sparse bind semaphore -// vkQueueWaitIdle(queue); - - // Command buffer to be sumitted to the queue submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; - - // Submit to queue VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); - VulkanExampleBase::submitFrame(); } void loadAssets() { - textures.source.loadFromFile(getAssetPath() + "textures/ground_dry_bc3_unorm.ktx", VK_FORMAT_BC3_UNORM_BLOCK, vulkanDevice, queue, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); - } - - // Generate a terrain quad patch for feeding to the tessellation control shader - void generateTerrain() - { - heightMap = new vks::HeightMap(vulkanDevice, queue); -#if defined(__ANDROID__) - heightMap->loadFromFile(getAssetPath() + "textures/terrain_heightmap_r16.ktx", 128, glm::vec3(2.0f, 48.0f, 2.0f), vks::HeightMap::topologyTriangles, androidApp->activity->assetManager); -#else - heightMap->loadFromFile(getAssetPath() + "textures/terrain_heightmap_r16.ktx", 128, glm::vec3(2.0f, 48.0f, 2.0f), vks::HeightMap::topologyTriangles); -#endif - } - - void setupVertexDescriptions() - { - // Binding description - vertices.bindingDescriptions.resize(1); - vertices.bindingDescriptions[0] = - vks::initializers::vertexInputBindingDescription( - VERTEX_BUFFER_BIND_ID, - sizeof(Vertex), - VK_VERTEX_INPUT_RATE_VERTEX); - - // Attribute descriptions - // Describes memory layout and shader positions - vertices.attributeDescriptions.resize(3); - // Location 0 : Position - vertices.attributeDescriptions[0] = - vks::initializers::vertexInputAttributeDescription( - VERTEX_BUFFER_BIND_ID, - 0, - VK_FORMAT_R32G32B32_SFLOAT, - offsetof(Vertex, pos)); - // Location 1 : Vertex normal - vertices.attributeDescriptions[1] = - vks::initializers::vertexInputAttributeDescription( - VERTEX_BUFFER_BIND_ID, - 1, - VK_FORMAT_R32G32B32_SFLOAT, - offsetof(Vertex, normal)); - // Location 1 : Texture coordinates - vertices.attributeDescriptions[2] = - vks::initializers::vertexInputAttributeDescription( - VERTEX_BUFFER_BIND_ID, - 2, - VK_FORMAT_R32G32_SFLOAT, - offsetof(Vertex, uv)); - - vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo(); - vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.bindingDescriptions.size()); - vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); - vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); - vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); + plane.loadFromFile(getAssetPath() + "models/plane_z.obj", vertexLayout, 1.0f, vulkanDevice, queue); } void setupDescriptorPool() @@ -799,77 +692,47 @@ public: void preparePipelines() { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vks::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - 0, - VK_FALSE); + 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_BACK_BIT, 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 dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array shaderStages; - VkPipelineRasterizationStateCreateInfo rasterizationState = - vks::initializers::pipelineRasterizationStateCreateInfo( - VK_POLYGON_MODE_FILL, - VK_CULL_MODE_BACK_BIT, - VK_FRONT_FACE_COUNTER_CLOCKWISE, - 0); + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo( pipelineLayout, renderPass); + 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(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); - 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 dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR + // Vertex bindings an attributes + std::vector vertexInputBindings = { + vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX), }; - VkPipelineDynamicStateCreateInfo dynamicState = - vks::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - static_cast(dynamicStateEnables.size()), - 0); - - // Load shaders - std::array shaderStages; + std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Location 0: Position + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Location 1: Normal + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6), // Location 0: Texture coordinates + }; + 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(); + pipelineCI.pVertexInputState = &vertexInputState; shaderStages[0] = loadShader(getShadersPath() + "texturesparseresidency/sparseresidency.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getShadersPath() + "texturesparseresidency/sparseresidency.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - - VkGraphicsPipelineCreateInfo pipelineCreateInfo = - vks::initializers::pipelineCreateInfo( - pipelineLayout, - renderPass, - 0); - - pipelineCreateInfo.pVertexInputState = &vertices.inputState; - pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; - pipelineCreateInfo.pRasterizationState = &rasterizationState; - pipelineCreateInfo.pColorBlendState = &colorBlendState; - pipelineCreateInfo.pMultisampleState = &multisampleState; - pipelineCreateInfo.pViewportState = &viewportState; - pipelineCreateInfo.pDepthStencilState = &depthStencilState; - pipelineCreateInfo.pDynamicState = &dynamicState; - pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); - pipelineCreateInfo.pStages = shaderStages.data(); - - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid)); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); } // Prepare and initialize uniform buffer containing shader uniforms @@ -905,11 +768,9 @@ public: vks::tools::exitFatal("Device does not support sparse residency for 2D images!", VK_ERROR_FEATURE_NOT_PRESENT); } loadAssets(); - generateTerrain(); - setupVertexDescriptions(); prepareUniformBuffers(); // Create a virtual texture with max. possible dimension (does not take up any VRAM yet) - prepareSparseTexture(8192, 8192, 1, VK_FORMAT_R8G8B8A8_UNORM); + prepareSparseTexture(4096, 4096, 1, VK_FORMAT_R8G8B8A8_UNORM); setupDescriptorSetLayout(); preparePipelines(); setupDescriptorPool(); @@ -923,130 +784,135 @@ public: if (!prepared) return; draw(); - } - - virtual void viewChanged() - { - updateUniformBuffers(); - } - - // Clear all pages of the virtual texture - // todo: just for testing - void flushVirtualTexture() - { - vkDeviceWaitIdle(device); - for (auto& page : texture.pages) - { - page.release(device); + if (camera.updated) { + updateUniformBuffers(); } - texture.updateSparseBindInfo(); - vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, VK_NULL_HANDLE); - //todo: use sparse bind semaphore - vkQueueWaitIdle(queue); - lastFilledMip = texture.mipTailStart - 1; } - // Fill a complete mip level - void fillVirtualTexture(int32_t &mipLevel) + void uploadContent(VirtualTexturePage page, VkImage image) { - vkDeviceWaitIdle(device); - std::vector imageBlits; - for (auto& page : texture.pages) + // Generate some random image data and upload as a buffer + const size_t bufferSize = 4 * page.extent.width * page.extent.height; + + vks::Buffer imageBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &imageBuffer, + bufferSize)); + imageBuffer.map(); + + // Fill buffer with random colors + std::random_device rd; + std::mt19937 rndEngine(rd()); + std::uniform_int_distribution rndDist(0, 255); + uint8_t* data = (uint8_t*)imageBuffer.mapped; + uint8_t rndVal[4]; + ZeroMemory(&rndVal, sizeof(uint32_t)); + while (rndVal[0] + rndVal[1] + rndVal[2] < 10) { + rndVal[0] = (uint8_t)rndDist(rndEngine); + rndVal[1] = (uint8_t)rndDist(rndEngine); + rndVal[2] = (uint8_t)rndDist(rndEngine); + } + rndVal[3] = 0; + + for (uint32_t y = 0; y < page.extent.height; y++) { - if ((page.mipLevel == mipLevel) && /*(rndDist(rndEngine) < 0.5f) &&*/ (page.imageMemoryBind.memory == VK_NULL_HANDLE)) + for (uint32_t x = 0; x < page.extent.width; x++) { - // Allocate page memory - page.allocate(device, memoryTypeIndex); - - // Current mip level scaling - uint32_t scale = texture.width / (texture.width >> page.mipLevel); - - for (uint32_t x = 0; x < scale; x++) + for (uint32_t c = 0; c < 4; c++, ++data) { - for (uint32_t y = 0; y < scale; y++) - { - // Image blit - VkImageBlit blit{}; - // Source - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.srcSubresource.baseArrayLayer = 0; - blit.srcSubresource.layerCount = 1; - blit.srcSubresource.mipLevel = 0; - blit.srcOffsets[0] = { 0, 0, 0 }; - blit.srcOffsets[1] = { static_cast(textures.source.width), static_cast(textures.source.height), 1 }; - // Dest - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.baseArrayLayer = 0; - blit.dstSubresource.layerCount = 1; - blit.dstSubresource.mipLevel = page.mipLevel; - blit.dstOffsets[0].x = static_cast(page.offset.x + x * 128 / scale); - blit.dstOffsets[0].y = static_cast(page.offset.y + y * 128 / scale); - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = static_cast(blit.dstOffsets[0].x + page.extent.width / scale); - blit.dstOffsets[1].y = static_cast(blit.dstOffsets[0].y + page.extent.height / scale); - blit.dstOffsets[1].z = 1; - - imageBlits.push_back(blit); - } + *data = rndVal[c]; } } } - // Update sparse queue binding - texture.updateSparseBindInfo(); - vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, VK_NULL_HANDLE); - //todo: use sparse bind semaphore - vkQueueWaitIdle(queue); + VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VkBufferImageCopy region{}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageOffset = page.offset; + region.imageExtent = page.extent; + vkCmdCopyBufferToImage(copyCmd, imageBuffer.buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + vulkanDevice->flushCommandBuffer(copyCmd, queue); - // Issue blit commands - if (imageBlits.size() > 0) + imageBuffer.destroy(); + } + + void fillRandomPages() + { + vkDeviceWaitIdle(device); + + std::default_random_engine rndEngine(std::random_device{}()); + std::uniform_real_distribution rndDist(0.0f, 1.0f); + + std::vector updatedPages; + for (auto& page : texture.pages) { - auto tStart = std::chrono::high_resolution_clock::now(); - - VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - vkCmdBlitImage( - copyCmd, - textures.source.image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - texture.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(imageBlits.size()), - imageBlits.data(), - VK_FILTER_LINEAR - ); - - vulkanDevice->flushCommandBuffer(copyCmd, queue); - - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - std::cout << "Image blits took " << tDiff << " ms" << std::endl; + if (rndDist(rndEngine) < 0.5f) { + continue; + } + page.allocate(device, memoryTypeIndex); + updatedPages.push_back(page); } - vkQueueWaitIdle(queue); + // Update sparse queue binding + texture.updateSparseBindInfo(); + VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); + vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, fence); + vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); - mipLevel--; + for (auto &page: updatedPages) { + uploadContent(page, texture.image); + } } - virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + void flushRandomPages() + { + vkDeviceWaitIdle(device); + + std::default_random_engine rndEngine(std::random_device{}()); + std::uniform_real_distribution rndDist(0.0f, 1.0f); + + std::vector updatedPages; + for (auto& page : texture.pages) + { + if (rndDist(rndEngine) < 0.5f) { + continue; + } + page.release(device); + } + + // Update sparse queue binding + texture.updateSparseBindInfo(); + VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); + vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, fence); + vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay) { if (overlay->header("Settings")) { if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)texture.mipLevels)) { updateUniformBuffers(); } overlay->text("Last filled mip level: %d", lastFilledMip); - if (overlay->button("Fill next mip level")) { - if (lastFilledMip >= 0) { - fillVirtualTexture(lastFilledMip); - } + if (overlay->button("Fill random pages")) { + fillRandomPages(); } - if (overlay->button("Flush virtual texture")) { - flushVirtualTexture(); + if (overlay->button("Flush random pages")) { + flushRandomPages(); } } if (overlay->header("Statistics")) { uint32_t respages = 0; - std::for_each(texture.pages.begin(), texture.pages.end(), [&respages](VirtualTexturePage page) { respages += (page.imageMemoryBind.memory != VK_NULL_HANDLE) ? 1 : 0; }); + std::for_each(texture.pages.begin(), texture.pages.end(), [&respages](VirtualTexturePage page) { respages += (page.resident()) ? 1 : 0; }); overlay->text("Resident pages: %d of %d", respages, static_cast(texture.pages.size())); }