stop hardcoding vulkan vertex layouts

This commit is contained in:
niki
2022-06-16 01:16:23 +02:00
parent 2bf7c35220
commit b5ba2b65b5
5 changed files with 284 additions and 58 deletions
+2
View File
@@ -606,6 +606,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN
src/modules/graphics/vulkan/Buffer.cpp
src/modules/graphics/vulkan/Texture.h
src/modules/graphics/vulkan/Texture.cpp
src/modules/graphics/vulkan/Vulkan.h
src/modules/graphics/vulkan/Vulkan.cpp
src/modules/graphics/vulkan/vk_mem_alloc.h
src/modules/graphics/vulkan/UseVMA.cpp
)
+159 -56
View File
@@ -5,6 +5,7 @@
#include "common/Exception.h"
#include "Shader.h"
#include "graphics/Texture.h"
#include "Vulkan.h"
#include <vector>
#include <cstring>
@@ -112,7 +113,7 @@ namespace love {
const auto& commandBuffer = commandBuffers.at(imageIndex);
vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
currentGraphicsPipeline = VK_NULL_HANDLE;
}
void Graphics::endRecordingGraphicsCommands() {
@@ -215,7 +216,6 @@ namespace love {
createRenderPass();
createDefaultShaders();
createDescriptorSetLayout();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createCommandBuffers();
@@ -313,17 +313,19 @@ namespace love {
std::vector<VkBuffer> buffers;
std::vector<VkDeviceSize> offsets;
// vertices
buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle());
offsets.push_back((VkDeviceSize)cmd.buffers->info[0].offset);
bool useConstantColorBuffer;
GraphicsPipelineConfiguration configuration;
createVulkanVertexFormat(*cmd.attributes, useConstantColorBuffer, configuration);
// tex coords
buffers.push_back((VkBuffer)cmd.buffers->info[1].buffer->getHandle());
offsets.push_back((VkDeviceSize)cmd.buffers->info[1].offset);
for (uint32_t i = 0; i < 2; i++) {
buffers.push_back((VkBuffer)cmd.buffers->info[i].buffer->getHandle());
offsets.push_back((VkDeviceSize)cmd.buffers->info[i].offset);
}
//constant color
buffers.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
offsets.push_back((VkDeviceSize)0);
if (useConstantColorBuffer) {
buffers.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
offsets.push_back((VkDeviceSize)0);
}
if (cmd.texture == nullptr) {
setTexture(standardTexture);
@@ -332,6 +334,8 @@ namespace love {
setTexture(cmd.texture);
}
ensureGraphicsPipelineConfiguration(configuration);
vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, buffers.size(), buffers.data(), offsets.data());
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), 0, getVulkanIndexBufferType(cmd.indexType));
@@ -930,7 +934,7 @@ namespace love {
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
@@ -970,6 +974,8 @@ namespace love {
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
// FIXME: When using more than 128 textures at once we will run out of memory.
// we probably want to reuse descriptors per flight image
// and use multiple pools in case of too many allocations
poolInfo.maxSets = 128 * static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
@@ -1039,53 +1045,85 @@ namespace love {
return newDescriptorSets;
}
void Graphics::createGraphicsPipeline() {
void Graphics::createVulkanVertexFormat(
VertexAttributes vertexAttributes,
bool& useConstantVertexColor,
GraphicsPipelineConfiguration& configuration) {
std::set<uint32_t> usedBuffers;
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
auto allBits = vertexAttributes.enableBits;
bool usesColor = false;
for (uint32_t i = 0; i < 32; i++) { // change to loop like in opengl implementation ?
uint32 bit = 1u << i;
if (allBits & bit) {
if (i == ATTRIB_COLOR) {
usesColor = true;
}
auto attrib = vertexAttributes.attribs[i];
auto bufferBinding = attrib.bufferIndex;
if (usedBuffers.find(bufferBinding) == usedBuffers.end()) { // use .contains() when c++20 is enabled
usedBuffers.insert(bufferBinding);
VkVertexInputBindingDescription bindingDescription{};
bindingDescription.binding = bufferBinding;
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
bindingDescription.stride = vertexAttributes.bufferLayouts[bufferBinding].stride;
bindingDescriptions.push_back(bindingDescription);
}
VkVertexInputAttributeDescription attributeDescription{};
attributeDescription.location = i;
attributeDescription.binding = bufferBinding;
attributeDescription.offset = attrib.offsetFromVertex;
attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format);
attributeDescriptions.push_back(attributeDescription);
}
}
// do we need to use a constant VertexColor?
if (!usesColor) {
constexpr uint32_t constantColorBufferBinding = 2;
VkVertexInputBindingDescription bindingDescription{};
bindingDescription.binding = constantColorBufferBinding;
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
bindingDescription.stride = 0; // no stride, will always read the same color multiple times.
bindingDescriptions.push_back(bindingDescription);
VkVertexInputAttributeDescription attributeDescription{};
attributeDescription.binding = constantColorBufferBinding;
attributeDescription.location = ATTRIB_COLOR;
attributeDescription.offset = 0;
attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
useConstantVertexColor = true;
}
else {
useConstantVertexColor = false;
}
configuration.vertexInputBindingDescriptions = bindingDescriptions;
configuration.vertexInputAttributeDescriptions = attributeDescriptions;
}
VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
auto shader = reinterpret_cast<love::graphics::vulkan::Shader*>(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]);
auto shaderStages = shader->getShaderStages();
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
VkVertexInputBindingDescription vertexBindingDescription;
vertexBindingDescription.binding = 0;
vertexBindingDescription.stride = 2 * sizeof(float); // just position for now
vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription positionInputAttributeDescription;
positionInputAttributeDescription.binding = 0;
positionInputAttributeDescription.location = 0;
positionInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT;
positionInputAttributeDescription.offset = 0;
VkVertexInputBindingDescription texCoordsBindingDescription;
texCoordsBindingDescription.binding = 1;
texCoordsBindingDescription.stride = sizeof(STf_RGBAub);
texCoordsBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription texCoordsInputAttributeDescription;
texCoordsInputAttributeDescription.binding = 1;
texCoordsInputAttributeDescription.location = 1;
texCoordsInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT;
texCoordsInputAttributeDescription.offset = 0;
VkVertexInputBindingDescription constantColorBindingDescription;
constantColorBindingDescription.binding = 2;
constantColorBindingDescription.stride = 0;
constantColorBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription constantColorInputAttributeDescription;
constantColorInputAttributeDescription.binding = 2;
constantColorInputAttributeDescription.location = 2;
constantColorInputAttributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
constantColorInputAttributeDescription.offset = 0;
std::vector<VkVertexInputBindingDescription> bindingDescriptions = { vertexBindingDescription, texCoordsBindingDescription, constantColorBindingDescription };
std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions = { positionInputAttributeDescription, texCoordsInputAttributeDescription, constantColorInputAttributeDescription };
vertexInputInfo.vertexBindingDescriptionCount = bindingDescriptions.size();
vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
vertexInputInfo.vertexAttributeDescriptionCount = vertexInputAttributeDescriptions.size();
vertexInputInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions.data();
vertexInputInfo.vertexBindingDescriptionCount = configuration.vertexInputBindingDescriptions.size();
vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data();
vertexInputInfo.vertexAttributeDescriptionCount = configuration.vertexInputAttributeDescriptions.size();
vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
@@ -1176,9 +1214,32 @@ namespace love {
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
VkPipeline graphicsPipeline;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
throw love::Exception("failed to create graphics pipeline");
}
return graphicsPipeline;
}
void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) {
VkPipeline pipeline = VK_NULL_HANDLE;
for (auto const& p : graphicsPipelines) {
if (p.first == configuration) {
pipeline = p.second;
break;
}
}
if (pipeline != VK_NULL_HANDLE) {
if (currentGraphicsPipeline != pipeline) {
vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
currentGraphicsPipeline = pipeline;
}
} else {
VkPipeline newPipeLine = createGraphicsPipeline(configuration);
graphicsPipelines.push_back(std::make_pair(configuration, newPipeLine));
vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, newPipeLine);
currentGraphicsPipeline = newPipeLine;
}
}
void Graphics::createFramebuffers() {
@@ -1257,6 +1318,45 @@ namespace love {
standardTexture = newTexture(settings);
}
bool operator==(const Graphics::GraphicsPipelineConfiguration& first, const Graphics::GraphicsPipelineConfiguration& other) {
if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) {
return false;
}
if (first.vertexInputBindingDescriptions.size() != other.vertexInputBindingDescriptions.size()) {
return false;
}
for (uint32_t i = 0; i < first.vertexInputAttributeDescriptions.size(); i++) {
const VkVertexInputAttributeDescription& x = first.vertexInputAttributeDescriptions[i];
const VkVertexInputAttributeDescription& y = other.vertexInputAttributeDescriptions[i];
if (x.binding != y.binding) {
return false;
}
if (x.location != y.location) {
return false;
}
if (x.offset != y.offset) {
return false;
}
if (x.format != y.format) {
return false;
}
}
for (uint32_t i = 0; i < first.vertexInputBindingDescriptions.size(); i++) {
const VkVertexInputBindingDescription& x = first.vertexInputBindingDescriptions[i];
const VkVertexInputBindingDescription& y = other.vertexInputBindingDescriptions[i];
if (x.binding != y.binding) {
return false;
}
if (x.inputRate != y.inputRate) {
return false;
}
if (x.stride != y.stride) {
return false;
}
}
return true;
}
void Graphics::cleanup() {
vkDeviceWaitIdle(device);
@@ -1284,8 +1384,12 @@ namespace love {
vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr);
}
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
for (auto const& p : graphicsPipelines) {
vkDestroyPipeline(device, p.second, nullptr);
}
graphicsPipelines.clear();
currentGraphicsPipeline = VK_NULL_HANDLE;
// vkDestroyPipelineLayout(device, pipelineLayout, nullptr); FIXME
vkDestroyRenderPass(device, renderPass, nullptr);
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
vkDestroyImageView(device, swapChainImageViews[i], nullptr);
@@ -1303,7 +1407,6 @@ namespace love {
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createUniformBuffers();
createDescriptorPool();
+17 -2
View File
@@ -80,6 +80,13 @@ namespace love {
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer);
struct GraphicsPipelineConfiguration {
std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
};
protected:
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
std::cout << "newShaderStageInternal ";
@@ -125,6 +132,12 @@ namespace love {
std::vector<BatchedDrawBuffers> batchedDrawBuffers;
void updatedBatchedDrawBuffers();
void createVulkanVertexFormat(
VertexAttributes vertexAttributes,
bool& useConstantVertexColor,
GraphicsPipelineConfiguration& configuration);
// vulkan specific member functions and variables
struct QueueFamilyIndices {
@@ -163,7 +176,7 @@ namespace love {
void createUniformBuffers();
void createDescriptorPool();
std::vector<VkDescriptorSet> createDescriptorSets(graphics::Texture* texture);
void createGraphicsPipeline();
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
void createFramebuffers();
void createCommandPool();
void createCommandBuffers();
@@ -175,6 +188,7 @@ namespace love {
void startRecordingGraphicsCommands();
void endRecordingGraphicsCommands();
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
void prepareDraw(uint32_t currentImage);
@@ -192,7 +206,8 @@ namespace love {
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
VkRenderPass renderPass;
VkPipeline graphicsPipeline;
VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
std::vector<VkFramebuffer> swapChainFramBuffers;
VkCommandPool commandPool;
std::vector<VkCommandBuffer> commandBuffers;
+88
View File
@@ -0,0 +1,88 @@
#include "Vulkan.h"
namespace love {
namespace graphics {
namespace vulkan {
VkFormat Vulkan::getVulkanVertexFormat(DataFormat format) {
switch (format) {
case DATAFORMAT_FLOAT:
return VK_FORMAT_R32_SFLOAT;
case DATAFORMAT_FLOAT_VEC2:
return VK_FORMAT_R32G32_SFLOAT;
case DATAFORMAT_FLOAT_VEC3:
return VK_FORMAT_R32G32B32_SFLOAT;
case DATAFORMAT_FLOAT_VEC4:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case DATAFORMAT_FLOAT_MAT2X2:
case DATAFORMAT_FLOAT_MAT2X3:
case DATAFORMAT_FLOAT_MAT2X4:
case DATAFORMAT_FLOAT_MAT3X2:
case DATAFORMAT_FLOAT_MAT3X3:
case DATAFORMAT_FLOAT_MAT3X4:
case DATAFORMAT_FLOAT_MAT4X2:
case DATAFORMAT_FLOAT_MAT4X3:
case DATAFORMAT_FLOAT_MAT4X4:
throw love::Exception("unimplemented data format (matnxm)");
case DATAFORMAT_INT32:
return VK_FORMAT_R32_SINT;
case DATAFORMAT_INT32_VEC2:
return VK_FORMAT_R32G32_SINT;
case DATAFORMAT_INT32_VEC3:
return VK_FORMAT_R32G32B32_SINT;
case DATAFORMAT_INT32_VEC4:
return VK_FORMAT_R32G32B32A32_SINT;
case DATAFORMAT_UINT32:
return VK_FORMAT_R32_UINT;
case DATAFORMAT_UINT32_VEC2:
return VK_FORMAT_R32G32_UINT;
case DATAFORMAT_UINT32_VEC3:
return VK_FORMAT_R32G32B32_UINT;
case DATAFORMAT_UINT32_VEC4:
return VK_FORMAT_R32G32B32A32_UINT;
case DATAFORMAT_SNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_SNORM;
case DATAFORMAT_UNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_UNORM;
case DATAFORMAT_INT8_VEC4:
return VK_FORMAT_R8G8B8A8_SINT;
case DATAFORMAT_UINT8_VEC4:
return VK_FORMAT_R8G8B8A8_UINT;
case DATAFORMAT_SNORM16_VEC2:
return VK_FORMAT_R16G16_SNORM;
case DATAFORMAT_SNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_SNORM;
case DATAFORMAT_UNORM16_VEC2:
return VK_FORMAT_R16G16_UNORM;
case DATAFORMAT_UNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_UNORM;
case DATAFORMAT_INT16_VEC2:
return VK_FORMAT_R16G16_SINT;
case DATAFORMAT_INT16_VEC4:
return VK_FORMAT_R16G16B16A16_SINT;
case DATAFORMAT_UINT16:
return VK_FORMAT_R16_UINT;
case DATAFORMAT_UINT16_VEC2:
return VK_FORMAT_R16G16_UINT;
case DATAFORMAT_UINT16_VEC4:
return VK_FORMAT_R16G16B16A16_UINT;
case DATAFORMAT_BOOL:
case DATAFORMAT_BOOL_VEC2:
case DATAFORMAT_BOOL_VEC3:
case DATAFORMAT_BOOL_VEC4:
throw love::Exception("unimplemented data format (bool)");
default:
throw love::Exception("unknown data format");
}
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef LOVE_GRAPHICS_VULKAN_VULKAN_H
#define LOVE_GRAPHICS_VULKAN_VULKAN_H
#include "Vulkan.h"
#include "Graphics.h"
namespace love {
namespace graphics {
namespace vulkan {
class Vulkan {
public:
static VkFormat getVulkanVertexFormat(DataFormat format);
};
}
}
}
#endif