mirror of
https://github.com/love2d/love.git
synced 2026-08-20 04:30:09 +02:00
vulkan: implement storage and texel buffers
This commit is contained in:
@@ -15,6 +15,7 @@ static VkBufferUsageFlags getUsageBit(BufferUsage mode)
|
|||||||
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||||
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
||||||
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||||
|
case BUFFERUSAGE_TEXEL: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||||
default:
|
default:
|
||||||
throw love::Exception("unsupported BufferUsage mode");
|
throw love::Exception("unsupported BufferUsage mode");
|
||||||
}
|
}
|
||||||
@@ -47,16 +48,27 @@ bool Buffer::loadVolatile()
|
|||||||
VkBufferCreateInfo bufferInfo{};
|
VkBufferCreateInfo bufferInfo{};
|
||||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
bufferInfo.size = getSize();
|
bufferInfo.size = getSize();
|
||||||
if (dataUsage == BUFFERDATAUSAGE_READBACK)
|
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | getVulkanUsageFlags(usageFlags);
|
||||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
||||||
else
|
|
||||||
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
|
|
||||||
|
|
||||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||||
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
|
||||||
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
|
auto result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
|
||||||
|
if (result != VK_SUCCESS)
|
||||||
|
throw love::Exception("failed to create buffer");
|
||||||
|
|
||||||
|
if (usageFlags & BUFFERUSAGE_TEXEL)
|
||||||
|
{
|
||||||
|
VkBufferViewCreateInfo bufferViewInfo{};
|
||||||
|
bufferViewInfo.buffer = buffer;
|
||||||
|
bufferViewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
|
||||||
|
bufferViewInfo.format = Vulkan::getVulkanVertexFormat(getDataMember(0).decl.format);
|
||||||
|
bufferViewInfo.range = VK_WHOLE_SIZE;
|
||||||
|
|
||||||
|
if (vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView) != VK_SUCCESS)
|
||||||
|
throw love::Exception("failed to create texel buffer view");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -69,12 +81,15 @@ void Buffer::unloadVolatile()
|
|||||||
auto device = vgfx->getDevice();
|
auto device = vgfx->getDevice();
|
||||||
|
|
||||||
vgfx->queueCleanUp(
|
vgfx->queueCleanUp(
|
||||||
[device=device, allocator=allocator, buffer=buffer, allocation=allocation](){
|
[device=device, allocator=allocator, buffer=buffer, allocation=allocation, bufferView=bufferView](){
|
||||||
vkDeviceWaitIdle(device);
|
vkDeviceWaitIdle(device);
|
||||||
vmaDestroyBuffer(allocator, buffer, allocation);
|
vmaDestroyBuffer(allocator, buffer, allocation);
|
||||||
|
if (bufferView)
|
||||||
|
vkDestroyBufferView(device, bufferView, nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
buffer = VK_NULL_HANDLE;
|
buffer = VK_NULL_HANDLE;
|
||||||
|
bufferView = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer::~Buffer()
|
Buffer::~Buffer()
|
||||||
@@ -89,8 +104,7 @@ ptrdiff_t Buffer::getHandle() const
|
|||||||
|
|
||||||
ptrdiff_t Buffer::getTexelBufferHandle() const
|
ptrdiff_t Buffer::getTexelBufferHandle() const
|
||||||
{
|
{
|
||||||
throw love::Exception("unimplemented Buffer::getTexelBufferHandle");
|
return (ptrdiff_t) bufferView;
|
||||||
return (ptrdiff_t) nullptr; // todo ?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Buffer::map(MapType map, size_t offset, size_t size)
|
void* Buffer::map(MapType map, size_t offset, size_t size)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
// todo use a staging buffer for improved performance
|
// todo use a staging buffer for improved performance
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
|
VkBufferView bufferView = VK_NULL_HANDLE;
|
||||||
Graphics *vgfx = nullptr;
|
Graphics *vgfx = nullptr;
|
||||||
VmaAllocator allocator;
|
VmaAllocator allocator;
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
|
|||||||
@@ -270,63 +270,7 @@ void Shader::newFrame(uint32_t frameIndex)
|
|||||||
|
|
||||||
currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
|
currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
|
||||||
|
|
||||||
// update everything other than uniform buffers
|
initDescriptorSet();
|
||||||
for (const auto &[key, val] : uniformInfos) {
|
|
||||||
// fixme: other types.
|
|
||||||
if (val.baseType == UNIFORM_SAMPLER) {
|
|
||||||
VkWriteDescriptorSet write{};
|
|
||||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
write.dstSet = currentDescriptorSet;
|
|
||||||
write.dstBinding = val.location;
|
|
||||||
write.dstArrayElement = 0;
|
|
||||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
||||||
write.descriptorCount = val.count;
|
|
||||||
|
|
||||||
std::vector<VkDescriptorImageInfo> imageInfos;
|
|
||||||
|
|
||||||
for (int i = 0; i < val.count; i++)
|
|
||||||
{
|
|
||||||
auto vkTexture = dynamic_cast<Texture*>(val.textures[i]);
|
|
||||||
|
|
||||||
VkDescriptorImageInfo imageInfo{};
|
|
||||||
imageInfo.imageLayout = vkTexture->getImageLayout();
|
|
||||||
imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
|
|
||||||
imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
|
|
||||||
|
|
||||||
imageInfos.push_back(imageInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
write.pImageInfo = imageInfos.data();
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
||||||
}
|
|
||||||
if (val.baseType == UNIFORM_STORAGETEXTURE) {
|
|
||||||
VkWriteDescriptorSet write{};
|
|
||||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
write.dstSet = currentDescriptorSet;
|
|
||||||
write.dstBinding = val.location;
|
|
||||||
write.dstArrayElement = 0;
|
|
||||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
||||||
write.descriptorCount = val.count;
|
|
||||||
|
|
||||||
std::vector<VkDescriptorImageInfo> imageInfos;
|
|
||||||
|
|
||||||
for (int i = 0; i < val.count; i++)
|
|
||||||
{
|
|
||||||
auto vkTexture = dynamic_cast<Texture*>(val.textures[i]);
|
|
||||||
|
|
||||||
VkDescriptorImageInfo imageInfo{};
|
|
||||||
imageInfo.imageLayout = vkTexture->getImageLayout();
|
|
||||||
imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
|
|
||||||
|
|
||||||
imageInfos.push_back(imageInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
write.pImageInfo = imageInfos.data();
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
|
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
|
||||||
@@ -409,6 +353,8 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
|
|||||||
descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
|
descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
|
||||||
|
|
||||||
currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
|
currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
|
||||||
|
|
||||||
|
initDescriptorSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader::~Shader()
|
Shader::~Shader()
|
||||||
@@ -465,12 +411,17 @@ static bool usesLocalUniformData(const graphics::Shader::UniformInfo *info)
|
|||||||
|
|
||||||
void Shader::updateUniform(const UniformInfo *info, int count)
|
void Shader::updateUniform(const UniformInfo *info, int count)
|
||||||
{
|
{
|
||||||
if (current == this)
|
updateUniform(info, count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
|
||||||
|
{
|
||||||
|
if (!internal && current == this)
|
||||||
Graphics::flushBatchedDrawsGlobal();
|
Graphics::flushBatchedDrawsGlobal();
|
||||||
|
|
||||||
if (usesLocalUniformData(info))
|
if (usesLocalUniformData(info))
|
||||||
memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
|
memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
|
||||||
else if (info->baseType == UNIFORM_SAMPLER || info->baseType == UNIFORM_STORAGETEXTURE)
|
if (info->baseType == UNIFORM_SAMPLER || info->baseType == UNIFORM_STORAGETEXTURE)
|
||||||
{
|
{
|
||||||
bool isSampler = info->baseType == UNIFORM_SAMPLER;
|
bool isSampler = info->baseType == UNIFORM_SAMPLER;
|
||||||
|
|
||||||
@@ -495,10 +446,58 @@ void Shader::updateUniform(const UniformInfo *info, int count)
|
|||||||
write.dstSet = currentDescriptorSet;
|
write.dstSet = currentDescriptorSet;
|
||||||
write.dstBinding = info->location;
|
write.dstBinding = info->location;
|
||||||
write.dstArrayElement = 0;
|
write.dstArrayElement = 0;
|
||||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
if (isSampler)
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
else
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||||
write.descriptorCount = static_cast<uint32_t>(count);
|
write.descriptorCount = static_cast<uint32_t>(count);
|
||||||
write.pImageInfo = imageInfos.data();
|
write.pImageInfo = imageInfos.data();
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||||
|
}
|
||||||
|
if (info->baseType == UNIFORM_STORAGEBUFFER)
|
||||||
|
{
|
||||||
|
VkWriteDescriptorSet write{};
|
||||||
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
write.dstSet = currentDescriptorSet;
|
||||||
|
write.dstBinding = info->location;
|
||||||
|
write.dstArrayElement = 0;
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||||
|
write.descriptorCount = info->count;
|
||||||
|
|
||||||
|
std::vector<VkDescriptorBufferInfo> bufferInfos;
|
||||||
|
|
||||||
|
for (int i = 0; i < info->count; i++)
|
||||||
|
{
|
||||||
|
VkDescriptorBufferInfo bufferInfo{};
|
||||||
|
bufferInfo.buffer = (VkBuffer)info->buffers[i]->getHandle();;
|
||||||
|
bufferInfo.offset = 0;
|
||||||
|
bufferInfo.range = info->buffers[i]->getSize();
|
||||||
|
|
||||||
|
bufferInfos.push_back(bufferInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
write.pBufferInfo = bufferInfos.data();
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||||
|
}
|
||||||
|
if (info->baseType == UNIFORM_TEXELBUFFER)
|
||||||
|
{
|
||||||
|
VkWriteDescriptorSet write{};
|
||||||
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
write.dstSet = currentDescriptorSet;
|
||||||
|
write.dstBinding = info->location;
|
||||||
|
write.dstArrayElement = 0;
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||||
|
write.descriptorCount = info->count;
|
||||||
|
|
||||||
|
std::vector<VkBufferView> bufferViews;
|
||||||
|
|
||||||
|
for (int i = 0; i < info->count; i++)
|
||||||
|
bufferViews.push_back((VkBufferView)info->buffers[i]->getTexelBufferHandle());
|
||||||
|
|
||||||
|
write.pTexelBufferView = bufferViews.data();
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,6 +514,18 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
auto oldBuffer = info->buffers[i];
|
||||||
|
info->buffers[i] = buffers[i];
|
||||||
|
info->buffers[i]->retain();
|
||||||
|
if (oldBuffer)
|
||||||
|
oldBuffer->release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Shader::calculateUniformBufferSizeAligned()
|
void Shader::calculateUniformBufferSizeAligned()
|
||||||
{
|
{
|
||||||
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
|
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
|
||||||
@@ -525,6 +536,13 @@ void Shader::calculateUniformBufferSizeAligned()
|
|||||||
uniformBufferSizeAligned = factor * minAlignment;
|
uniformBufferSizeAligned = factor * minAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::initDescriptorSet()
|
||||||
|
{
|
||||||
|
for (const auto &[key, val] : uniformInfos)
|
||||||
|
if (Vulkan::getDescriptorType(val.baseType) != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||||
|
updateUniform(&val, val.count, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
|
void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
|
||||||
{
|
{
|
||||||
using namespace spirv_cross;
|
using namespace spirv_cross;
|
||||||
@@ -880,6 +898,12 @@ void Shader::createDescriptorSetLayout()
|
|||||||
{
|
{
|
||||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||||
|
|
||||||
|
VkShaderStageFlags stageFlags;
|
||||||
|
if (isCompute)
|
||||||
|
stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
else
|
||||||
|
stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
|
||||||
for (auto const &[key, val] : uniformInfos)
|
for (auto const &[key, val] : uniformInfos)
|
||||||
{
|
{
|
||||||
auto type = Vulkan::getDescriptorType(val.baseType);
|
auto type = Vulkan::getDescriptorType(val.baseType);
|
||||||
@@ -890,10 +914,7 @@ void Shader::createDescriptorSetLayout()
|
|||||||
layoutBinding.binding = val.location;
|
layoutBinding.binding = val.location;
|
||||||
layoutBinding.descriptorType = type;
|
layoutBinding.descriptorType = type;
|
||||||
layoutBinding.descriptorCount = val.count;
|
layoutBinding.descriptorCount = val.count;
|
||||||
if (isCompute)
|
layoutBinding.stageFlags = stageFlags;
|
||||||
layoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
||||||
else
|
|
||||||
layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
||||||
|
|
||||||
bindings.push_back(layoutBinding);
|
bindings.push_back(layoutBinding);
|
||||||
}
|
}
|
||||||
@@ -905,10 +926,7 @@ void Shader::createDescriptorSetLayout()
|
|||||||
uniformBinding.binding = uniformLocation;
|
uniformBinding.binding = uniformLocation;
|
||||||
uniformBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
uniformBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
uniformBinding.descriptorCount = 1;
|
uniformBinding.descriptorCount = 1;
|
||||||
if (isCompute)
|
uniformBinding.stageFlags = stageFlags;
|
||||||
uniformBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
||||||
else
|
|
||||||
uniformBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
||||||
bindings.push_back(uniformBinding);
|
bindings.push_back(uniformBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public:
|
|||||||
void updateUniform(const UniformInfo *info, int count) override;
|
void updateUniform(const UniformInfo *info, int count) override;
|
||||||
|
|
||||||
void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
|
void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
|
||||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override {}
|
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
|
||||||
|
|
||||||
bool hasUniform(const std::string &name) const override;
|
bool hasUniform(const std::string &name) const override;
|
||||||
|
|
||||||
@@ -81,6 +81,8 @@ private:
|
|||||||
const spirv_cross::SPIRType &type,
|
const spirv_cross::SPIRType &type,
|
||||||
size_t baseoff,
|
size_t baseoff,
|
||||||
const std::string &basename);
|
const std::string &basename);
|
||||||
|
void initDescriptorSet();
|
||||||
|
void updateUniform(const UniformInfo* info, int count, bool internal);
|
||||||
|
|
||||||
VkDescriptorSet allocateDescriptorSet();
|
VkDescriptorSet allocateDescriptorSet();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user