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,52 +10,26 @@
* 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>
#include "texturesparseresidency.h"
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
/*
Virtual texture page
Contains all functions and objects for a single page of a virtual texture
*/
#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
VirtualTexturePage::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()
{
imageMemoryBind.memory = VK_NULL_HANDLE; // Page initially not backed up by memory
// Pages are initially not backed up by memory (non-resident)
imageMemoryBind.memory = VK_NULL_HANDLE;
}
bool resident()
bool VirtualTexturePage::resident()
{
return (imageMemoryBind.memory != VK_NULL_HANDLE);
}
// 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)
{
@ -81,7 +55,7 @@ struct VirtualTexturePage
}
// Release Vulkan memory allocated for this page
void release(VkDevice device)
void VirtualTexturePage::release(VkDevice device)
{
if (imageMemoryBind.memory != VK_NULL_HANDLE)
{
@ -89,31 +63,15 @@ struct VirtualTexturePage
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;
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;
VirtualTexturePage newPage{};
newPage.offset = offset;
newPage.extent = extent;
newPage.size = size;
@ -128,7 +86,7 @@ struct VirtualTexture
}
// Call before sparse binding to update memory bind list etc.
void updateSparseBindInfo()
void VirtualTexture::updateSparseBindInfo()
{
// Update list of memory-backed sparse image memory binds
//sparseImageMemoryBinds.resize(pages.size());
@ -160,7 +118,7 @@ struct VirtualTexture
}
// Release all Vulkan resources
void destroy()
void VirtualTexture::destroy()
{
for (auto page : pages)
{
@ -171,49 +129,11 @@ struct VirtualTexture
vkFreeMemory(device, bind.memory, nullptr);
}
}
};
uint32_t memoryTypeIndex;
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() : VulkanExampleBase(ENABLE_VALIDATION)
/*
Vulkan Example class
*/
VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{
title = "Sparse texture residency";
std::cout.imbue(std::locale(""));
@ -224,7 +144,7 @@ public:
settings.overlay = true;
}
~VulkanExample()
VulkanExample::~VulkanExample()
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
@ -237,7 +157,7 @@ public:
uniformBufferVS.destroy();
}
virtual void getEnabledFeatures()
void VulkanExample::getEnabledFeatures()
{
if (deviceFeatures.sparseBinding && deviceFeatures.sparseResidencyImage2D) {
enabledFeatures.shaderResourceResidency = VK_TRUE;
@ -250,7 +170,7 @@ public:
}
}
glm::uvec3 alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity)
glm::uvec3 VulkanExample::alignedDivision(const VkExtent3D& extent, const VkExtent3D& granularity)
{
glm::uvec3 res;
res.x = extent.width / granularity.width + ((extent.width % granularity.width) ? 1u : 0u);
@ -259,7 +179,7 @@ public:
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.width = width;
@ -380,10 +300,10 @@ public:
return;
}
// todo:
// @todo: proper comment
// Calculate number of required sparse memory bindings by alignment
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
uint32_t sparseBindsCount = static_cast<uint32_t>(sparseImageMemoryReqs.size / sparseImageMemoryReqs.alignment);
@ -458,7 +378,7 @@ public:
// Allocate memory for the mip tail
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex;
allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VkDeviceMemory deviceMemory;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory));
@ -483,7 +403,7 @@ public:
// Allocate memory for the mip tail
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = sparseMemoryReq.imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex;
allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VkDeviceMemory deviceMemory;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory));
@ -514,7 +434,7 @@ public:
VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
sampler.magFilter = 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.addressModeV = 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.maxAnisotropy = vulkanDevice->features.samplerAnisotropy ? vulkanDevice->properties.limits.maxSamplerAnisotropy : 1.0f;
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));
// Create image view
@ -548,7 +468,7 @@ public:
}
// Free all Vulkan resources used a texture object
void destroyTextureImage(SparseTexture texture)
void VulkanExample::destroyTextureImage(SparseTexture texture)
{
vkDestroyImageView(device, texture.view, nullptr);
vkDestroyImage(device, texture.image, nullptr);
@ -556,7 +476,7 @@ public:
texture.destroy();
}
void buildCommandBuffers()
void VulkanExample::buildCommandBuffers()
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
@ -603,7 +523,7 @@ public:
}
}
void draw()
void VulkanExample::draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
@ -612,12 +532,12 @@ public:
VulkanExampleBase::submitFrame();
}
void loadAssets()
void VulkanExample::loadAssets()
{
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
std::vector<VkDescriptorPoolSize> poolSizes =
@ -635,7 +555,7 @@ public:
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout()
void VulkanExample::setupDescriptorSetLayout()
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
{
@ -666,7 +586,7 @@ public:
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
void setupDescriptorSet()
void VulkanExample::setupDescriptorSet()
{
VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo(
@ -695,7 +615,7 @@ public:
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);
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
@ -741,7 +661,7 @@ public:
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers()
void VulkanExample::prepareUniformBuffers()
{
// Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer(
@ -754,7 +674,7 @@ public:
updateUniformBuffers();
}
void updateUniformBuffers()
void VulkanExample::updateUniformBuffers()
{
uboVS.projection = camera.matrices.perspective;
uboVS.model = camera.matrices.view;
@ -765,7 +685,7 @@ public:
uniformBufferVS.unmap();
}
void prepare()
void VulkanExample::prepare()
{
VulkanExampleBase::prepare();
// Check if the GPU supports sparse residency for 2D images
@ -784,7 +704,7 @@ public:
prepared = true;
}
virtual void render()
void VulkanExample::render()
{
if (!prepared)
return;
@ -794,7 +714,7 @@ public:
}
}
void uploadContent(VirtualTexturePage page, VkImage image)
void VulkanExample::uploadContent(VirtualTexturePage page, VkImage image)
{
// Generate some random image data and upload as a buffer
const size_t bufferSize = 4 * page.extent.width * page.extent.height;
@ -846,7 +766,7 @@ public:
imageBuffer.destroy();
}
void fillRandomPages()
void VulkanExample::fillRandomPages()
{
vkDeviceWaitIdle(device);
@ -858,7 +778,7 @@ public:
if (rndDist(rndEngine) < 0.5f) {
continue;
}
page.allocate(device, memoryTypeIndex);
page.allocate(device, texture.memoryTypeIndex);
updatedPages.push_back(page);
}
@ -875,7 +795,7 @@ public:
}
}
void fillMipTail()
void VulkanExample::fillMipTail()
{
//@todo: WIP
VkDeviceSize imageMipTailSize = texture.sparseImageMemoryRequirements.imageMipTailSize;
@ -887,7 +807,7 @@ public:
VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo();
allocInfo.allocationSize = imageMipTailSize;
allocInfo.memoryTypeIndex = memoryTypeIndex;
allocInfo.memoryTypeIndex = texture.memoryTypeIndex;
VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &mipTailimageMemoryBind.memory));
uint32_t mipLevel = texture.sparseImageMemoryRequirements.imageMipTailFirstLod;
@ -964,7 +884,7 @@ public:
}
}
void flushRandomPages()
void VulkanExample::flushRandomPages()
{
vkDeviceWaitIdle(device);
@ -989,7 +909,7 @@ public:
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->sliderFloat("LOD bias", &uboVS.lodBias, -(float)texture.mipLevels, (float)texture.mipLevels)) {
@ -1013,6 +933,5 @@ public:
}
}
};
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);
};