Add bindless texture support

This commit is contained in:
Magnus Norddahl 2023-09-19 23:58:17 +02:00 committed by Christoph Oelckers
parent adc9fb3421
commit ebdef188ee
11 changed files with 114 additions and 7 deletions

View file

@ -1852,6 +1852,7 @@ std::vector<VulkanCompatibleDevice> VulkanDeviceBuilder::FindDevices(const std::
enabledFeatures.DescriptorIndexing.descriptorBindingPartiallyBound = deviceFeatures.DescriptorIndexing.descriptorBindingPartiallyBound;
enabledFeatures.DescriptorIndexing.descriptorBindingSampledImageUpdateAfterBind = deviceFeatures.DescriptorIndexing.descriptorBindingSampledImageUpdateAfterBind;
enabledFeatures.DescriptorIndexing.descriptorBindingVariableDescriptorCount = deviceFeatures.DescriptorIndexing.descriptorBindingVariableDescriptorCount;
enabledFeatures.DescriptorIndexing.shaderSampledImageArrayNonUniformIndexing = deviceFeatures.DescriptorIndexing.shaderSampledImageArrayNonUniformIndexing;
// Figure out which queue can present
if (surface)

View file

@ -249,6 +249,9 @@ public:
virtual void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect&)> renderFunc) {}
virtual void SetActiveRenderTarget() {}
// Get the array index for the material in the textures array accessible from shaders
virtual int GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation) { return -1; }
// Screen wiping
virtual FTexture *WipeStartScreen();
virtual FTexture *WipeEndScreen();

View file

@ -3,6 +3,7 @@
#include "vulkan/vk_renderdevice.h"
#include "vulkan/textures/vk_texture.h"
#include "vulkan/commands/vk_commandbuffer.h"
#include "vulkan/descriptorsets/vk_descriptorset.h"
#include "vk_raytrace.h"
#include "zvulkan/vulkanbuilders.h"
#include "halffloat.h"
@ -141,6 +142,7 @@ void VkLightmap::RenderBakeImage()
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 2, fb->GetDescriptorSetManager()->GetBindlessDescriptorSet());
VkViewport viewport = {};
viewport.maxDepth = 1;
@ -498,6 +500,7 @@ void VkLightmap::CreateRaytracePipeline()
raytrace.pipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(raytrace.descriptorSetLayout0.get())
.AddSetLayout(raytrace.descriptorSetLayout1.get())
.AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessSetLayout())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapPushConstants))
.DebugName("raytrace.pipelineLayout")
.Create(fb->GetDevice());

View file

@ -25,6 +25,7 @@
#include "vulkan/vk_renderstate.h"
#include "vulkan/vk_postprocess.h"
#include "vulkan/framebuffers/vk_framebuffer.h"
#include "vulkan/descriptorsets/vk_descriptorset.h"
#include <zvulkan/vulkanswapchain.h>
#include <zvulkan/vulkanbuilders.h>
#include "hw_clock.h"
@ -150,6 +151,8 @@ void VkCommandBufferManager::FlushCommands(VulkanCommandBuffer** commands, size_
void VkCommandBufferManager::FlushCommands(bool finish, bool lastsubmit, bool uploadOnly)
{
fb->GetDescriptorSetManager()->UpdateBindlessDescriptorSet();
if (!uploadOnly)
fb->GetRenderState(0)->EndRenderPass();

View file

@ -44,6 +44,7 @@ VkDescriptorSetManager::VkDescriptorSetManager(VulkanRenderDevice* fb) : fb(fb)
CreateFixedSetLayout();
CreateRSBufferPool();
CreateFixedSetPool();
CreateBindlessDescriptorSet();
}
VkDescriptorSetManager::~VkDescriptorSetManager()
@ -126,6 +127,9 @@ void VkDescriptorSetManager::ResetHWTextureSets()
TextureDescriptorPools.clear();
TextureDescriptorSetsLeft = 0;
TextureDescriptorsLeft = 0;
WriteBindless = WriteDescriptors();
NextBindlessIndex = 0;
}
VulkanDescriptorSet* VkDescriptorSetManager::GetNullTextureDescriptorSet()
@ -306,3 +310,38 @@ void VkDescriptorSetManager::CreateFixedSetPool()
poolbuilder.DebugName("VkDescriptorSetManager.FixedDescriptorPool");
FixedDescriptorPool = poolbuilder.Create(fb->GetDevice());
}
void VkDescriptorSetManager::CreateBindlessDescriptorSet()
{
BindlessDescriptorPool = DescriptorPoolBuilder()
.Flags(VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT)
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MaxBindlessTextures)
.MaxSets(MaxBindlessTextures)
.DebugName("BindlessDescriptorPool")
.Create(fb->GetDevice());
BindlessDescriptorSetLayout = DescriptorSetLayoutBuilder()
.Flags(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT)
.AddBinding(
0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
MaxBindlessTextures,
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT)
.DebugName("BindlessDescriptorSetLayout")
.Create(fb->GetDevice());
BindlessDescriptorSet = BindlessDescriptorPool->allocate(BindlessDescriptorSetLayout.get(), MaxBindlessTextures);
}
void VkDescriptorSetManager::UpdateBindlessDescriptorSet()
{
WriteBindless.Execute(fb->GetDevice());
WriteBindless = WriteDescriptors();
}
int VkDescriptorSetManager::AddBindlessTextureIndex(VulkanImageView* imageview, VulkanSampler* sampler)
{
int index = NextBindlessIndex++;
WriteBindless.AddCombinedImageSampler(BindlessDescriptorSet.get(), 0, index, imageview, sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return index;
}

View file

@ -2,6 +2,7 @@
#pragma once
#include "zvulkan/vulkanobjects.h"
#include "zvulkan/vulkanbuilders.h"
#include <list>
#include "tarray.h"
@ -24,10 +25,12 @@ public:
VulkanDescriptorSetLayout* GetRSBufferSetLayout() { return RSBufferSetLayout.get(); }
VulkanDescriptorSetLayout* GetFixedSetLayout() { return FixedSetLayout.get(); }
VulkanDescriptorSetLayout* GetTextureSetLayout(int numLayers);
VulkanDescriptorSetLayout* GetBindlessSetLayout() { return BindlessDescriptorSetLayout.get(); }
VulkanDescriptorSet* GetRSBufferDescriptorSet(int threadIndex) { return RSBufferSets[threadIndex].get(); }
VulkanDescriptorSet* GetFixedDescriptorSet() { return FixedSet.get(); }
VulkanDescriptorSet* GetNullTextureDescriptorSet();
VulkanDescriptorSet* GetBindlessDescriptorSet() { return BindlessDescriptorSet.get(); }
std::unique_ptr<VulkanDescriptorSet> AllocateTextureDescriptorSet(int numLayers);
@ -36,11 +39,15 @@ public:
void AddMaterial(VkMaterial* texture);
void RemoveMaterial(VkMaterial* texture);
void UpdateBindlessDescriptorSet();
int AddBindlessTextureIndex(VulkanImageView* imageview, VulkanSampler* sampler);
private:
void CreateRSBufferSetLayout();
void CreateFixedSetLayout();
void CreateRSBufferPool();
void CreateFixedSetPool();
void CreateBindlessDescriptorSet();
void UpdateFixedSet();
std::unique_ptr<VulkanDescriptorSet> AllocatePPDescriptorSet(VulkanDescriptorSetLayout* layout);
@ -66,5 +73,12 @@ private:
std::list<VkMaterial*> Materials;
std::unique_ptr<VulkanDescriptorPool> BindlessDescriptorPool;
std::unique_ptr<VulkanDescriptorSet> BindlessDescriptorSet;
std::unique_ptr<VulkanDescriptorSetLayout> BindlessDescriptorSetLayout;
WriteDescriptors WriteBindless;
int NextBindlessIndex = 0;
static const int maxSets = 100;
static const int MaxBindlessTextures = 16536;
};

View file

@ -331,6 +331,16 @@ void VkMaterial::DeleteDescriptors()
}
VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
{
return GetDescriptorEntry(state).descriptor.get();
}
int VkMaterial::GetBindlessIndex(const FMaterialState& state)
{
return GetDescriptorEntry(state).bindlessIndex;
}
VkMaterial::DescriptorEntry& VkMaterial::GetDescriptorEntry(const FMaterialState& state)
{
auto base = Source();
int clampmode = state.mClampMode;
@ -341,12 +351,13 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
for (auto& set : mDescriptorSets)
{
if (set.descriptor && set.clampmode == clampmode && set.remap == translationp) return set.descriptor.get();
if (set.descriptor && set.clampmode == clampmode && set.remap == translationp) return set;
}
int numLayers = NumLayers();
auto descriptor = fb->GetDescriptorSetManager()->AllocateTextureDescriptorSet(max(numLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS));
auto descriptors = fb->GetDescriptorSetManager();
auto descriptor = descriptors->AllocateTextureDescriptorSet(max(numLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS));
descriptor->SetDebugName("VkHardwareTexture.mDescriptorSets");
@ -358,6 +369,8 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
auto systeximage = systex->GetImage(layer->layerTexture, state.mTranslation, layer->scaleFlags);
update.AddCombinedImageSampler(descriptor.get(), 0, systeximage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(0), clampmode), systeximage->Layout);
int bindlessIndex = descriptors->AddBindlessTextureIndex(systeximage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(0), clampmode));
if (!(layer->scaleFlags & CTF_Indexed))
{
for (int i = 1; i < numLayers; i++)
@ -365,6 +378,8 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
auto syslayer = static_cast<VkHardwareTexture*>(GetLayer(i, 0, &layer));
auto syslayerimage = syslayer->GetImage(layer->layerTexture, 0, layer->scaleFlags);
update.AddCombinedImageSampler(descriptor.get(), i, syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode), syslayerimage->Layout);
descriptors->AddBindlessTextureIndex(syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode));
}
}
else
@ -374,6 +389,8 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
auto syslayer = static_cast<VkHardwareTexture*>(GetLayer(i, translation, &layer));
auto syslayerimage = syslayer->GetImage(layer->layerTexture, 0, layer->scaleFlags);
update.AddCombinedImageSampler(descriptor.get(), i, syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode), syslayerimage->Layout);
descriptors->AddBindlessTextureIndex(syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode));
}
numLayers = 3;
}
@ -385,8 +402,8 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
}
update.Execute(fb->GetDevice());
mDescriptorSets.emplace_back(clampmode, translationp, std::move(descriptor));
return mDescriptorSets.back().descriptor.get();
mDescriptorSets.emplace_back(clampmode, translationp, std::move(descriptor), bindlessIndex);
return mDescriptorSets.back();
}
VulkanDescriptorSet* VkMaterial::GetDescriptorSet(int threadIndex, const FMaterialState& state)

View file

@ -71,21 +71,22 @@ public:
std::list<VkMaterial*>::iterator it;
VulkanDescriptorSet* GetDescriptorSet(int threadIndex, const FMaterialState& state);
int GetBindlessIndex(const FMaterialState& state);
private:
VulkanDescriptorSet* GetDescriptorSet(const FMaterialState& state);
struct DescriptorEntry
{
int clampmode;
intptr_t remap;
std::unique_ptr<VulkanDescriptorSet> descriptor;
int bindlessIndex;
DescriptorEntry(int cm, intptr_t f, std::unique_ptr<VulkanDescriptorSet>&& d)
DescriptorEntry(int cm, intptr_t f, std::unique_ptr<VulkanDescriptorSet>&& d, int index)
{
clampmode = cm;
remap = f;
descriptor = std::move(d);
bindlessIndex = index;
}
};
@ -98,6 +99,9 @@ private:
ThreadDescriptorEntry(int cm, intptr_t f, VulkanDescriptorSet* d) : clampmode(cm), remap(f), descriptor(d) { }
};
VulkanDescriptorSet* GetDescriptorSet(const FMaterialState& state);
DescriptorEntry& GetDescriptorEntry(const FMaterialState& state);
std::vector<DescriptorEntry> mDescriptorSets;
std::vector<std::vector<ThreadDescriptorEntry>> mThreadDescriptorSets;
};

View file

@ -120,10 +120,20 @@ VulkanRenderDevice::VulkanRenderDevice(void *hMonitor, bool fullscreen, std::sha
{
VulkanDeviceBuilder builder;
builder.OptionalRayQuery();
builder.RequireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
builder.Surface(surface);
builder.SelectDevice(vk_device);
SupportedDevices = builder.FindDevices(surface->Instance);
mDevice = builder.Create(surface->Instance);
bool supportsBindless =
mDevice->EnabledFeatures.DescriptorIndexing.descriptorBindingPartiallyBound &&
mDevice->EnabledFeatures.DescriptorIndexing.runtimeDescriptorArray &&
mDevice->EnabledFeatures.DescriptorIndexing.shaderSampledImageArrayNonUniformIndexing;
if (!supportsBindless)
{
I_FatalError("This GPU does not support the minimum requirements of this application");
}
}
VulkanRenderDevice::~VulkanRenderDevice()
@ -607,3 +617,12 @@ void VulkanRenderDevice::SetSceneRenderTarget(bool useSSAO)
renderstate->SetRenderTarget(&GetBuffers()->SceneColor, GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
}
}
int VulkanRenderDevice::GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation)
{
FMaterialState materialState;
materialState.mMaterial = material;
materialState.mClampMode = clampmode;
materialState.mTranslation = translation;
return static_cast<VkMaterial*>(material)->GetBindlessIndex(materialState);
}

View file

@ -87,6 +87,8 @@ public:
void WaitForCommands(bool finish) override;
int GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation) override;
void ResetRenderStateCache();
std::mutex ThreadMutex;

View file

@ -68,6 +68,8 @@ layout(set = 0, binding = 2) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
layout(set = 0, binding = 3) buffer LightBuffer { LightInfo lights[]; };
layout(set = 0, binding = 4) buffer PortalBuffer { PortalInfo portals[]; };
layout(set = 2, binding = 0) uniform sampler2D textures[];
layout(push_constant) uniform PushConstants
{
uint LightStart;