Split into header and implementation
This commit is contained in:
parent
77322190ea
commit
3b117fd2dc
2 changed files with 932 additions and 872 deletions
|
|
@ -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()
|
||||||
|
|
|
||||||
141
examples/texturesparseresidency/texturesparseresidency.h
Normal file
141
examples/texturesparseresidency/texturesparseresidency.h
Normal 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);
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue