Split into header and implementation

This commit is contained in:
Sascha Willems 2020-07-14 20:17:48 +02:00
parent 77322190ea
commit 3b117fd2dc
2 changed files with 932 additions and 872 deletions

View file

@ -10,53 +10,27 @@
* Note : This sample is work-in-progress and works basically, but it's not yet finished * Note : This sample is work-in-progress and works basically, but it's not yet finished
*/ */
#include <stdio.h> #include "texturesparseresidency.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
#define GLM_FORCE_RADIANS /*
#define GLM_FORCE_DEPTH_ZERO_TO_ONE Virtual texture page
#include <glm/glm.hpp> Contains all functions and objects for a single page of a virtual texture
#include <glm/gtc/matrix_transform.hpp> */
#include <vulkan/vulkan.h> VirtualTexturePage::VirtualTexturePage()
#include "vulkanexamplebase.h"
#include "VulkanDevice.hpp"
#include "VulkanBuffer.hpp"
#include "VulkanModel.hpp"
#define ENABLE_VALIDATION false
// Virtual texture page as a part of the partially resident texture
// Contains memory bindings, offsets and status information
struct VirtualTexturePage
{ {
VkOffset3D offset; // Pages are initially not backed up by memory (non-resident)
VkExtent3D extent; imageMemoryBind.memory = VK_NULL_HANDLE;
VkSparseImageMemoryBind imageMemoryBind; // Sparse image memory bind for this page }
VkDeviceSize size; // Page (memory) size in bytes
uint32_t mipLevel; // Mip level that this page belongs to
uint32_t layer; // Array layer that this page belongs to
uint32_t index;
VirtualTexturePage() bool VirtualTexturePage::resident()
{ {
imageMemoryBind.memory = VK_NULL_HANDLE; // Page initially not backed up by memory
}
bool resident()
{
return (imageMemoryBind.memory != VK_NULL_HANDLE); return (imageMemoryBind.memory != VK_NULL_HANDLE);
} }
// Allocate Vulkan memory for the virtual page // Allocate Vulkan memory for the virtual page
void allocate(VkDevice device, uint32_t memoryTypeIndex) void VirtualTexturePage::allocate(VkDevice device, uint32_t memoryTypeIndex)
{ {
if (imageMemoryBind.memory != VK_NULL_HANDLE) if (imageMemoryBind.memory != VK_NULL_HANDLE)
{ {
return; return;
@ -78,42 +52,26 @@ struct VirtualTexturePage
imageMemoryBind.subresource = subResource; imageMemoryBind.subresource = subResource;
imageMemoryBind.extent = extent; imageMemoryBind.extent = extent;
imageMemoryBind.offset = offset; imageMemoryBind.offset = offset;
} }
// Release Vulkan memory allocated for this page // Release Vulkan memory allocated for this page
void release(VkDevice device) void VirtualTexturePage::release(VkDevice device)
{ {
if (imageMemoryBind.memory != VK_NULL_HANDLE) if (imageMemoryBind.memory != VK_NULL_HANDLE)
{ {
vkFreeMemory(device, imageMemoryBind.memory, nullptr); vkFreeMemory(device, imageMemoryBind.memory, nullptr);
imageMemoryBind.memory = VK_NULL_HANDLE; imageMemoryBind.memory = VK_NULL_HANDLE;
} }
} }
};
// Virtual texture object containing all pages /*
struct VirtualTexture Virtual texture
Contains the virtual pages and memory binding information for a whole virtual texture
*/
VirtualTexturePage* VirtualTexture::addPage(VkOffset3D offset, VkExtent3D extent, const VkDeviceSize size, const uint32_t mipLevel, uint32_t layer)
{ {
VkDevice device; VirtualTexturePage newPage{};
VkImage image; // Texture image handle
VkBindSparseInfo bindSparseInfo; // Sparse queue binding information
std::vector<VirtualTexturePage> pages; // Contains all virtual pages of the texture
std::vector<VkSparseImageMemoryBind> sparseImageMemoryBinds; // Sparse image memory bindings of all memory-backed virtual tables
std::vector<VkSparseMemoryBind> opaqueMemoryBinds; // Sparse ópaque memory bindings for the mip tail (if present)
VkSparseImageMemoryBindInfo imageMemoryBindInfo; // Sparse image memory bind info
VkSparseImageOpaqueMemoryBindInfo opaqueMemoryBindInfo; // Sparse image opaque memory bind info (mip tail)
uint32_t mipTailStart; // First mip level in mip tail
VkSparseImageMemoryRequirements sparseImageMemoryRequirements; // @todo: Comment
// @todo: comment
struct MipTailInfo {
bool singleMipTail;
bool alingedMipSize;
} mipTailInfo;
VirtualTexturePage* addPage(VkOffset3D offset, VkExtent3D extent, const VkDeviceSize size, const uint32_t mipLevel, uint32_t layer)
{
VirtualTexturePage newPage;
newPage.offset = offset; newPage.offset = offset;
newPage.extent = extent; newPage.extent = extent;
newPage.size = size; newPage.size = size;
@ -125,11 +83,11 @@ struct VirtualTexture
newPage.imageMemoryBind.extent = extent; newPage.imageMemoryBind.extent = extent;
pages.push_back(newPage); pages.push_back(newPage);
return &pages.back(); return &pages.back();
} }
// Call before sparse binding to update memory bind list etc. // Call before sparse binding to update memory bind list etc.
void updateSparseBindInfo() void VirtualTexture::updateSparseBindInfo()
{ {
// Update list of memory-backed sparse image memory binds // Update list of memory-backed sparse image memory binds
//sparseImageMemoryBinds.resize(pages.size()); //sparseImageMemoryBinds.resize(pages.size());
sparseImageMemoryBinds.clear(); sparseImageMemoryBinds.clear();
@ -157,11 +115,11 @@ struct VirtualTexture
opaqueMemoryBindInfo.pBinds = opaqueMemoryBinds.data(); opaqueMemoryBindInfo.pBinds = opaqueMemoryBinds.data();
bindSparseInfo.imageOpaqueBindCount = (opaqueMemoryBindInfo.bindCount > 0) ? 1 : 0; bindSparseInfo.imageOpaqueBindCount = (opaqueMemoryBindInfo.bindCount > 0) ? 1 : 0;
bindSparseInfo.pImageOpaqueBinds = &opaqueMemoryBindInfo; bindSparseInfo.pImageOpaqueBinds = &opaqueMemoryBindInfo;
} }
// Release all Vulkan resources // Release all Vulkan resources
void destroy() void VirtualTexture::destroy()
{ {
for (auto page : pages) for (auto page : pages)
{ {
page.release(device); page.release(device);
@ -170,51 +128,13 @@ struct VirtualTexture
{ {
vkFreeMemory(device, bind.memory, nullptr); vkFreeMemory(device, bind.memory, nullptr);
} }
} }
};
uint32_t memoryTypeIndex; /*
Vulkan Example class
class VulkanExample : public VulkanExampleBase */
VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{ {
public:
//todo: comments
struct SparseTexture : VirtualTexture {
VkSampler sampler;
VkImageLayout imageLayout;
VkImageView view;
VkDescriptorImageInfo descriptor;
VkFormat format;
uint32_t width, height;
uint32_t mipLevels;
uint32_t layerCount;
} texture;
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;
glm::mat4 model;
glm::vec4 viewPos;
float lodBias = 0.0f;
} uboVS;
vks::Buffer uniformBufferVS;
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
//todo: comment
VkSemaphore bindSparseSemaphore = VK_NULL_HANDLE;
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{
title = "Sparse texture residency"; title = "Sparse texture residency";
std::cout.imbue(std::locale("")); std::cout.imbue(std::locale(""));
camera.type = Camera::CameraType::lookat; camera.type = Camera::CameraType::lookat;
@ -222,10 +142,10 @@ public:
camera.setRotation(glm::vec3(0.0f, 180.0f, 0.0f)); camera.setRotation(glm::vec3(0.0f, 180.0f, 0.0f));
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
settings.overlay = true; settings.overlay = true;
} }
~VulkanExample() VulkanExample::~VulkanExample()
{ {
// Clean up used Vulkan resources // Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class // Note : Inherited destructor cleans up resources stored in base class
destroyTextureImage(texture); destroyTextureImage(texture);
@ -235,10 +155,10 @@ public:
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
plane.destroy(); plane.destroy();
uniformBufferVS.destroy(); uniformBufferVS.destroy();
} }
virtual void getEnabledFeatures() void VulkanExample::getEnabledFeatures()
{ {
if (deviceFeatures.sparseBinding && deviceFeatures.sparseResidencyImage2D) { if (deviceFeatures.sparseBinding && deviceFeatures.sparseResidencyImage2D) {
enabledFeatures.shaderResourceResidency = VK_TRUE; enabledFeatures.shaderResourceResidency = VK_TRUE;
enabledFeatures.shaderResourceMinLod = VK_TRUE; enabledFeatures.shaderResourceMinLod = VK_TRUE;
@ -248,19 +168,19 @@ public:
else { else {
std::cout << "Sparse binding not supported" << std::endl; std::cout << "Sparse binding not supported" << std::endl;
} }
} }
glm::uvec3 alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity) glm::uvec3 VulkanExample::alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity)
{ {
glm::uvec3 res; 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.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; return res;
} }
void prepareSparseTexture(uint32_t width, uint32_t height, uint32_t layerCount, VkFormat format) void VulkanExample::prepareSparseTexture(uint32_t width, uint32_t height, uint32_t layerCount, VkFormat format)
{ {
texture.device = vulkanDevice->logicalDevice; texture.device = vulkanDevice->logicalDevice;
texture.width = width; texture.width = width;
texture.height = height; texture.height = height;
@ -380,10 +300,10 @@ public:
return; return;
} }
// todo: // @todo: proper comment
// Calculate number of required sparse memory bindings by alignment // Calculate number of required sparse memory bindings by alignment
assert((sparseImageMemoryReqs.size % sparseImageMemoryReqs.alignment) == 0); assert((sparseImageMemoryReqs.size % sparseImageMemoryReqs.alignment) == 0);
memoryTypeIndex = vulkanDevice->getMemoryType(sparseImageMemoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); texture.memoryTypeIndex = vulkanDevice->getMemoryType(sparseImageMemoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
// Get sparse bindings // Get sparse bindings
uint32_t sparseBindsCount = static_cast<uint32_t>(sparseImageMemoryReqs.size / sparseImageMemoryReqs.alignment); uint32_t sparseBindsCount = static_cast<uint32_t>(sparseImageMemoryReqs.size / sparseImageMemoryReqs.alignment);
@ -458,7 +378,7 @@ public:
// Allocate memory for the mip tail // Allocate memory for the mip tail
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize; allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex; allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VkDeviceMemory deviceMemory; VkDeviceMemory deviceMemory;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory));
@ -483,7 +403,7 @@ public:
// Allocate memory for the mip tail // Allocate memory for the mip tail
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize; allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex; allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VkDeviceMemory deviceMemory; VkDeviceMemory deviceMemory;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory));
@ -514,7 +434,7 @@ public:
VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
sampler.magFilter = VK_FILTER_LINEAR; sampler.magFilter = VK_FILTER_LINEAR;
sampler.minFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR;
sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler.addressModeV = 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.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@ -524,7 +444,7 @@ public:
sampler.maxLod = static_cast<float>(texture.mipLevels); sampler.maxLod = static_cast<float>(texture.mipLevels);
sampler.maxAnisotropy = vulkanDevice->features.samplerAnisotropy ? vulkanDevice->properties.limits.maxSamplerAnisotropy : 1.0f; sampler.maxAnisotropy = vulkanDevice->features.samplerAnisotropy ? vulkanDevice->properties.limits.maxSamplerAnisotropy : 1.0f;
sampler.anisotropyEnable = false; sampler.anisotropyEnable = false;
sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sampler.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &texture.sampler)); VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &texture.sampler));
// Create image view // Create image view
@ -545,19 +465,19 @@ public:
texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
texture.descriptor.imageView = texture.view; texture.descriptor.imageView = texture.view;
texture.descriptor.sampler = texture.sampler; texture.descriptor.sampler = texture.sampler;
} }
// Free all Vulkan resources used a texture object // Free all Vulkan resources used a texture object
void destroyTextureImage(SparseTexture texture) void VulkanExample::destroyTextureImage(SparseTexture texture)
{ {
vkDestroyImageView(device, texture.view, nullptr); vkDestroyImageView(device, texture.view, nullptr);
vkDestroyImage(device, texture.image, nullptr); vkDestroyImage(device, texture.image, nullptr);
vkDestroySampler(device, texture.sampler, nullptr); vkDestroySampler(device, texture.sampler, nullptr);
texture.destroy(); texture.destroy();
} }
void buildCommandBuffers() void VulkanExample::buildCommandBuffers()
{ {
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2]; VkClearValue clearValues[2];
@ -601,24 +521,24 @@ public:
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
} }
} }
void draw() void VulkanExample::draw()
{ {
VulkanExampleBase::prepareFrame(); VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame(); VulkanExampleBase::submitFrame();
} }
void loadAssets() void VulkanExample::loadAssets()
{ {
plane.loadFromFile(getAssetPath() + "models/plane_z.obj", vertexLayout, 1.0f, vulkanDevice, queue); plane.loadFromFile(getAssetPath() + "models/plane_z.obj", vertexLayout, 1.0f, vulkanDevice, queue);
} }
void setupDescriptorPool() void VulkanExample::setupDescriptorPool()
{ {
// Example uses one ubo and one image sampler // Example uses one ubo and one image sampler
std::vector<VkDescriptorPoolSize> poolSizes = std::vector<VkDescriptorPoolSize> poolSizes =
{ {
@ -633,10 +553,10 @@ public:
2); 2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
} }
void setupDescriptorSetLayout() void VulkanExample::setupDescriptorSetLayout()
{ {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
{ {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
@ -664,10 +584,10 @@ public:
1); 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
} }
void setupDescriptorSet() void VulkanExample::setupDescriptorSet()
{ {
VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo( vks::initializers::descriptorSetAllocateInfo(
descriptorPool, descriptorPool,
@ -693,10 +613,10 @@ public:
}; };
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
} }
void preparePipelines() void VulkanExample::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); 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); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
@ -738,11 +658,11 @@ public:
shaderStages[0] = loadShader(getShadersPath() + "texturesparseresidency/sparseresidency.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); 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); shaderStages[1] = loadShader(getShadersPath() + "texturesparseresidency/sparseresidency.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
} }
// Prepare and initialize uniform buffer containing shader uniforms // Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers() void VulkanExample::prepareUniformBuffers()
{ {
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
@ -752,10 +672,10 @@ public:
&uboVS)); &uboVS));
updateUniformBuffers(); updateUniformBuffers();
} }
void updateUniformBuffers() void VulkanExample::updateUniformBuffers()
{ {
uboVS.projection = camera.matrices.perspective; uboVS.projection = camera.matrices.perspective;
uboVS.model = camera.matrices.view; uboVS.model = camera.matrices.view;
uboVS.viewPos = camera.viewPos; uboVS.viewPos = camera.viewPos;
@ -763,10 +683,10 @@ public:
VK_CHECK_RESULT(uniformBufferVS.map()); VK_CHECK_RESULT(uniformBufferVS.map());
memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS)); memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS));
uniformBufferVS.unmap(); uniformBufferVS.unmap();
} }
void prepare() void VulkanExample::prepare()
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
// Check if the GPU supports sparse residency for 2D images // Check if the GPU supports sparse residency for 2D images
if (!vulkanDevice->features.sparseResidencyImage2D) { if (!vulkanDevice->features.sparseResidencyImage2D) {
@ -782,20 +702,20 @@ public:
setupDescriptorSet(); setupDescriptorSet();
buildCommandBuffers(); buildCommandBuffers();
prepared = true; prepared = true;
} }
virtual void render() void VulkanExample::render()
{ {
if (!prepared) if (!prepared)
return; return;
draw(); draw();
if (camera.updated) { if (camera.updated) {
updateUniformBuffers(); updateUniformBuffers();
} }
} }
void uploadContent(VirtualTexturePage page, VkImage image) void VulkanExample::uploadContent(VirtualTexturePage page, VkImage image)
{ {
// Generate some random image data and upload as a buffer // Generate some random image data and upload as a buffer
const size_t bufferSize = 4 * page.extent.width * page.extent.height; const size_t bufferSize = 4 * page.extent.width * page.extent.height;
@ -844,10 +764,10 @@ public:
vulkanDevice->flushCommandBuffer(copyCmd, queue); vulkanDevice->flushCommandBuffer(copyCmd, queue);
imageBuffer.destroy(); imageBuffer.destroy();
} }
void fillRandomPages() void VulkanExample::fillRandomPages()
{ {
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
std::default_random_engine rndEngine(std::random_device{}()); std::default_random_engine rndEngine(std::random_device{}());
@ -858,7 +778,7 @@ public:
if (rndDist(rndEngine) < 0.5f) { if (rndDist(rndEngine) < 0.5f) {
continue; continue;
} }
page.allocate(device, memoryTypeIndex); page.allocate(device, texture.memoryTypeIndex);
updatedPages.push_back(page); updatedPages.push_back(page);
} }
@ -873,10 +793,10 @@ public:
for (auto &page: updatedPages) { for (auto &page: updatedPages) {
uploadContent(page, texture.image); uploadContent(page, texture.image);
} }
} }
void fillMipTail() void VulkanExample::fillMipTail()
{ {
//@todo: WIP //@todo: WIP
VkDeviceSize imageMipTailSize = texture.sparseImageMemoryRequirements.imageMipTailSize; VkDeviceSize imageMipTailSize = texture.sparseImageMemoryRequirements.imageMipTailSize;
VkDeviceSize imageMipTailOffset = texture.sparseImageMemoryRequirements.imageMipTailOffset; VkDeviceSize imageMipTailOffset = texture.sparseImageMemoryRequirements.imageMipTailOffset;
@ -887,7 +807,7 @@ public:
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = imageMipTailSize; allocInfo.allocationSize = imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex; allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &mipTailimageMemoryBind.memory)); VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &mipTailimageMemoryBind.memory));
uint32_t mipLevel = texture.sparseImageMemoryRequirements.imageMipTailFirstLod; uint32_t mipLevel = texture.sparseImageMemoryRequirements.imageMipTailFirstLod;
@ -962,10 +882,10 @@ public:
imageBuffer.destroy(); imageBuffer.destroy();
} }
} }
void flushRandomPages() void VulkanExample::flushRandomPages()
{ {
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
std::default_random_engine rndEngine(std::random_device{}()); std::default_random_engine rndEngine(std::random_device{}());
@ -987,10 +907,10 @@ public:
VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence));
vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, fence); vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, fence);
vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
} }
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay) void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay)
{ {
if (overlay->header("Settings")) { if (overlay->header("Settings")) {
if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, -(float)texture.mipLevels, (float)texture.mipLevels)) { if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, -(float)texture.mipLevels, (float)texture.mipLevels)) {
updateUniformBuffers(); updateUniformBuffers();
@ -1012,7 +932,6 @@ public:
overlay->text("Mip tail starts at: %d", texture.mipTailStart); overlay->text("Mip tail starts at: %d", texture.mipTailStart);
} }
} }
};
VULKAN_EXAMPLE_MAIN() VULKAN_EXAMPLE_MAIN()

View file

@ -0,0 +1,141 @@
/*
* Vulkan Example - Sparse texture residency example
*
* Copyright (C) 2016-2020 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
/*
* Note : This sample is work-in-progress and works basically, but it's not yet finished
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vulkan/vulkan.h>
#include "vulkanexamplebase.h"
#include "VulkanDevice.hpp"
#include "VulkanBuffer.hpp"
#include "VulkanModel.hpp"
#define ENABLE_VALIDATION false
// Virtual texture page as a part of the partially resident texture
// Contains memory bindings, offsets and status information
struct VirtualTexturePage
{
VkOffset3D offset;
VkExtent3D extent;
VkSparseImageMemoryBind imageMemoryBind; // Sparse image memory bind for this page
VkDeviceSize size; // Page (memory) size in bytes
uint32_t mipLevel; // Mip level that this page belongs to
uint32_t layer; // Array layer that this page belongs to
uint32_t index;
VirtualTexturePage();
bool resident();
void allocate(VkDevice device, uint32_t memoryTypeIndex);
void release(VkDevice device);
};
// Virtual texture object containing all pages
struct VirtualTexture
{
VkDevice device;
VkImage image; // Texture image handle
VkBindSparseInfo bindSparseInfo; // Sparse queue binding information
std::vector<VirtualTexturePage> pages; // Contains all virtual pages of the texture
std::vector<VkSparseImageMemoryBind> sparseImageMemoryBinds; // Sparse image memory bindings of all memory-backed virtual tables
std::vector<VkSparseMemoryBind> opaqueMemoryBinds; // Sparse ópaque memory bindings for the mip tail (if present)
VkSparseImageMemoryBindInfo imageMemoryBindInfo; // Sparse image memory bind info
VkSparseImageOpaqueMemoryBindInfo opaqueMemoryBindInfo; // Sparse image opaque memory bind info (mip tail)
uint32_t mipTailStart; // First mip level in mip tail
VkSparseImageMemoryRequirements sparseImageMemoryRequirements; // @todo: Comment
uint32_t memoryTypeIndex; // @todo: Comment
// @todo: comment
struct MipTailInfo {
bool singleMipTail;
bool alingedMipSize;
} mipTailInfo;
VirtualTexturePage *addPage(VkOffset3D offset, VkExtent3D extent, const VkDeviceSize size, const uint32_t mipLevel, uint32_t layer);
void updateSparseBindInfo();
// @todo: replace with dtor?
void destroy();
};
class VulkanExample : public VulkanExampleBase
{
public:
//todo: comments
struct SparseTexture : VirtualTexture {
VkSampler sampler;
VkImageLayout imageLayout;
VkImageView view;
VkDescriptorImageInfo descriptor;
VkFormat format;
uint32_t width, height;
uint32_t mipLevels;
uint32_t layerCount;
} texture;
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;
glm::mat4 model;
glm::vec4 viewPos;
float lodBias = 0.0f;
} uboVS;
vks::Buffer uniformBufferVS;
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
//todo: comment
VkSemaphore bindSparseSemaphore = VK_NULL_HANDLE;
VulkanExample();
~VulkanExample();
virtual void getEnabledFeatures();
glm::uvec3 alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity);
void prepareSparseTexture(uint32_t width, uint32_t height, uint32_t layerCount, VkFormat format);
// @todo: move to dtor of texture
void destroyTextureImage(SparseTexture texture);
void buildCommandBuffers();
void draw();
void loadAssets();
void setupDescriptorPool();
void setupDescriptorSetLayout();
void setupDescriptorSet();
void preparePipelines();
void prepareUniformBuffers();
void updateUniformBuffers();
void prepare();
virtual void render();
void uploadContent(VirtualTexturePage page, VkImage image);
void fillRandomPages();
void fillMipTail();
void flushRandomPages();
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
};