vulkan: move descriptor set logic into Shader

Using the extension VK_KHR_push_descriptor makes the code a lot easier.
Using it makes us able to set set descriptor sets dynamically.
This commit is contained in:
niki
2022-07-19 02:36:02 +02:00
parent 00974b5820
commit fb6457f57d
4 changed files with 275 additions and 224 deletions
+20 -172
View File
@@ -34,7 +34,10 @@ namespace love {
};
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_push_descriptor.html
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME
};
#ifdef NDEBUG
@@ -207,12 +210,10 @@ namespace love {
createSwapChain();
createImageViews();
createDefaultShaders();
createDescriptorSetLayout();
createCommandPool();
createCommandBuffers();
createDefaultTexture();
createQuadIndexBuffer();
createDescriptorPool();
createSyncObjects();
startRecordingGraphicsCommands();
currentFrame = 0;
@@ -520,10 +521,6 @@ namespace love {
}
}
void Graphics::setTexture(graphics::Texture* texture) {
currentTexture = texture;
}
void Graphics::updatedBatchedDrawBuffers() {
batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1;
batchedDrawState.vb[0]->nextFrame();
@@ -533,30 +530,12 @@ namespace love {
batchedDrawState.indexBuffer->nextFrame();
}
VkDescriptorSet* Graphics::getDescriptorSet(int currentFrame) {
DecriptorSetConfiguration config{};
config.texture = currentTexture;
config.buffer = getUniformBuffer();
for (auto i = 0; i < descriptorSetsMap.size(); i++) {
if (descriptorSetsMap[i].first == config) {
return &descriptorSetsMap[i].second[currentFrame];
}
}
auto descriptorSets = createDescriptorSets(config);
descriptorSetsMap.push_back(std::make_pair(config, descriptorSets));
return &descriptorSetsMap.back().second[currentFrame];
uint32_t Graphics::getNumImagesInFlight() const {
return MAX_FRAMES_IN_FLIGHT;
}
graphics::StreamBuffer* Graphics::getUniformBuffer() {
auto data = getCurrentBuiltinUniformData();
for (auto &it : uniformBufferMap) {
if (it.first == data) {
return it.second;
}
}
auto buffer = createUniformBufferFromData(data);
uniformBufferMap.push_back(std::make_pair(data, buffer));
return buffer;
const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const {
return vkCmdPushDescriptorSet;
}
VkCommandBuffer Graphics::beginSingleTimeCommands() {
@@ -629,15 +608,6 @@ namespace love {
return data;
}
graphics::StreamBuffer* Graphics::createUniformBufferFromData(graphics::Shader::BuiltinUniformData data) {
auto buffer = newStreamBuffer(BUFFERUSAGE_UNIFORM, sizeof(data));
auto mappedInfo = buffer->map(0);
memcpy(mappedInfo.data, &data, sizeof(data));
buffer->unmap(0);
return buffer;
}
void Graphics::createVulkanInstance() {
if (enableValidationLayers && !checkValidationSupport()) {
throw love::Exception("validation layers requested, but not available");
@@ -893,6 +863,11 @@ namespace love {
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
vkCmdPushDescriptorSet = (PFN_vkCmdPushDescriptorSetKHR) vkGetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR");
if (!vkCmdPushDescriptorSet) {
throw love::Exception("could not get a valid function pointer for vkCmdPushDescriptorSetKHR");
}
}
void Graphics::initVMA() {
@@ -1082,114 +1057,6 @@ namespace love {
}
}
void Graphics::createDescriptorSetLayout() {
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
throw love::Exception("failed to create descriptor set layout");
}
}
void Graphics::createDescriptorPool() {
std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
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) {
throw love::Exception("failed to create descriptor pool");
}
}
std::vector<VkDescriptorSet> Graphics::createDescriptorSets(DecriptorSetConfiguration config) {
std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
allocInfo.pSetLayouts = layouts.data();
std::vector<VkDescriptorSet> newDescriptorSets;
newDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
VkResult result = vkAllocateDescriptorSets(device, &allocInfo, newDescriptorSets.data());
if (result != VK_SUCCESS) {
switch (result) {
case VK_ERROR_OUT_OF_HOST_MEMORY:
throw love::Exception("failed to allocate descriptor sets: out of host memory");
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
throw love::Exception("failed to allocate descriptor sets: out of device memory");
case VK_ERROR_FRAGMENTED_POOL:
throw love::Exception("failed to allocate descriptor sets: fragmented pool");
case VK_ERROR_OUT_OF_POOL_MEMORY:
throw love::Exception("failed to allocate descriptor sets: out of pool memory");
default:
throw love::Exception("failed to allocate descriptor sets");
}
}
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = (VkBuffer)config.buffer->getHandle();
bufferInfo.offset = 0;
bufferInfo.range = sizeof(graphics::Shader::BuiltinUniformData);
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
Texture* vkTexture = (Texture*)config.texture;
imageInfo.imageView = vkTexture->getImageView();
imageInfo.sampler = vkTexture->getSampler();
std::array<VkWriteDescriptorSet, 2> descriptorWrite{};
descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[0].dstSet = newDescriptorSets[i];
descriptorWrite[0].dstBinding = 0;
descriptorWrite[0].dstArrayElement = 0;
descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite[0].descriptorCount = 1;
descriptorWrite[0].pBufferInfo = &bufferInfo;
descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[1].dstSet = newDescriptorSets[i];
descriptorWrite[1].dstBinding = 1;
descriptorWrite[1].dstArrayElement = 0;
descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[1].descriptorCount = 1;
descriptorWrite[1].pImageInfo = &imageInfo;
vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data(), 0, nullptr);
}
return newDescriptorSets;
}
void Graphics::createVulkanVertexFormat(
VertexAttributes vertexAttributes,
bool& useConstantVertexColor,
@@ -1305,16 +1172,19 @@ namespace love {
offsets.push_back((VkDeviceSize)0);
}
auto currentUniformData = getCurrentBuiltinUniformData();
configuration.shader->setUniformData(currentUniformData);
if (texture == nullptr) {
setTexture(standardTexture.get());
configuration.shader->setMainTex(standardTexture.get());
}
else {
setTexture(texture);
configuration.shader->setMainTex(texture);
}
configuration.shader->setVideoTextures(standardTexture.get(), standardTexture.get(), standardTexture.get());
ensureGraphicsPipelineConfiguration(configuration);
vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
configuration.shader->cmdPushDescriptorSets(commandBuffers.at(imageIndex), currentFrame);
vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
}
@@ -1451,17 +1321,6 @@ namespace love {
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw love::Exception("failed to create pipeline layout");
}
graphicsPipelineLayouts.push_back(pipelineLayout);
VkFormat framebufferOutputFormat = configuration.framebufferFormat;
VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
@@ -1481,7 +1340,7 @@ namespace love {
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.layout = configuration.shader->getGraphicsPipelineLayout();
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
@@ -1663,7 +1522,6 @@ namespace love {
vkDestroyFence(device, inFlightFences[i], nullptr);
}
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
@@ -1678,19 +1536,10 @@ namespace love {
}
graphicsPipelines.clear();
currentGraphicsPipeline = VK_NULL_HANDLE;
for (const auto pipelineLayout : graphicsPipelineLayouts) {
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
}
graphicsPipelineLayouts.clear();
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
vkDestroyImageView(device, swapChainImageViews[i], nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
for (auto p : uniformBufferMap) {
delete p.second;
}
uniformBufferMap.clear();
descriptorSetsMap.clear();
}
void Graphics::recreateSwapChain() {
@@ -1700,7 +1549,6 @@ namespace love {
createSwapChain();
createImageViews();
createDescriptorPool();
createCommandBuffers();
startRecordingGraphicsCommands();
}
+4 -20
View File
@@ -37,15 +37,6 @@ namespace love {
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
};
struct DecriptorSetConfiguration {
graphics::Texture* texture;
graphics::StreamBuffer* buffer;
bool operator==(const DecriptorSetConfiguration& conf2) {
return this->texture == conf2.texture && this->buffer == conf2.buffer;
}
};
struct BatchedDrawBuffers {
StreamBuffer* vertexBuffer1;
StreamBuffer* vertexBuffer2;
@@ -125,6 +116,9 @@ namespace love {
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer);
uint32_t getNumImagesInFlight() const;
const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
protected:
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
return new ShaderStage(this, stage, source, gles, cachekey);
@@ -155,9 +149,6 @@ namespace love {
void createSwapChain();
void createImageViews();
void createDefaultShaders();
void createDescriptorSetLayout();
void createDescriptorPool();
std::vector<VkDescriptorSet> createDescriptorSets(DecriptorSetConfiguration);
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
void createCommandPool();
void createCommandBuffers();
@@ -170,12 +161,8 @@ namespace love {
void startRecordingGraphicsCommands();
void endRecordingGraphicsCommands();
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
graphics::StreamBuffer* createUniformBufferFromData(graphics::Shader::BuiltinUniformData);
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
void setTexture(graphics::Texture* texture);
void updatedBatchedDrawBuffers();
VkDescriptorSet* getDescriptorSet(int currentFrame);
graphics::StreamBuffer* getUniformBuffer();
void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
void startRenderPass(Texture*, uint32_t w, uint32_t h);
@@ -192,11 +179,9 @@ namespace love {
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
VkExtent2D swapChainExtent = VkExtent2D();
std::vector<VkImageView> swapChainImageViews;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
std::vector<VkPipelineLayout> graphicsPipelineLayouts;
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
@@ -204,6 +189,7 @@ namespace love {
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
@@ -214,8 +200,6 @@ namespace love {
// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
std::vector<BatchedDrawBuffers> batchedDrawBuffers;
graphics::Texture* currentTexture = nullptr;
std::vector<std::pair<graphics::Shader::BuiltinUniformData, graphics::StreamBuffer*>> uniformBufferMap;
std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
// render pass variables.
+219 -27
View File
@@ -117,6 +117,8 @@ namespace love {
}
};
static const uint32_t STREAMBUFFER_SIZE = 1024;
static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
switch (type) {
case SHADERSTAGE_VERTEX:
@@ -148,12 +150,150 @@ namespace love {
}
bool Shader::loadVolatile() {
compileShaders();
createDescriptorSetLayout();
createPipelineLayout();
createStreamBuffers();
currentImage = 0;
count = 0;
return true;
}
void Shader::unloadVolatile() {
if (shaderModules.size() == 0) {
return;
}
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
auto device = gfx->getDevice();
// fixme: we shouldn't do a greedy wait here.
vkDeviceWaitIdle(device);
for (const auto shaderModule : shaderModules) {
vkDestroyShaderModule(device, shaderModule, nullptr);
}
shaderModules.clear();
shaderStages.clear();
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
for (const auto streamBuffer : streamBuffers) {
delete streamBuffer;
}
streamBuffers.clear();
}
const std::vector<VkPipelineShaderStageCreateInfo>& Shader::getShaderStages() const {
return shaderStages;
}
const VkPipelineLayout Shader::getGraphicsPipelineLayout() const {
return pipelineLayout;
}
static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) {
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
Texture* vkTexture = (Texture*)texture;
imageInfo.imageView = vkTexture->getImageView();
imageInfo.sampler = vkTexture->getSampler();
return imageInfo;
}
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
if (currentImage != imageIndex) {
currentImage = imageIndex;
count = 0;
}
else {
if (count >= STREAMBUFFER_SIZE) {
throw love::Exception("uniform stream buffer: out of memory (fixme: resize)");
}
}
auto mapInfo = streamBuffers[currentImage]->map(sizeof(BuiltinUniformData));
memcpy(mapInfo.data, &uniformData, sizeof(BuiltinUniformData));
streamBuffers[currentImage]->unmap(sizeof(BuiltinUniformData));
streamBuffers[currentImage]->markUsed(sizeof(BuiltinUniformData));
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle();
bufferInfo.offset = count * sizeof(BuiltinUniformData);
bufferInfo.range = sizeof(graphics::Shader::BuiltinUniformData);
auto mainTexImageInfo = createDescriptorImageInfo(mainTex);
auto ytextureImageInfo = createDescriptorImageInfo(ytexture);
auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture);
auto crtextureImageInfo = createDescriptorImageInfo(crtexture);
std::array<VkWriteDescriptorSet, 5> descriptorWrite{};
descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[0].dstSet = 0;
descriptorWrite[0].dstBinding = 0;
descriptorWrite[0].dstArrayElement = 0;
descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite[0].descriptorCount = 1;
descriptorWrite[0].pBufferInfo = &bufferInfo;
descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[1].dstSet = 0;
descriptorWrite[1].dstBinding = 1;
descriptorWrite[1].dstArrayElement = 0;
descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[1].descriptorCount = 1;
descriptorWrite[1].pImageInfo = &mainTexImageInfo;
descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[2].dstSet = 0;
descriptorWrite[2].dstBinding = 2;
descriptorWrite[2].dstArrayElement = 0;
descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[2].descriptorCount = 1;
descriptorWrite[2].pImageInfo = &ytextureImageInfo;
descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[3].dstSet = 0;
descriptorWrite[3].dstBinding = 3;
descriptorWrite[3].dstArrayElement = 0;
descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[3].descriptorCount = 1;
descriptorWrite[3].pImageInfo = &cbtextureImageInfo;
descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[4].dstSet = 0;
descriptorWrite[4].dstBinding = 4;
descriptorWrite[4].dstArrayElement = 0;
descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[4].descriptorCount = 1;
descriptorWrite[4].pImageInfo = &crtextureImageInfo;
vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data());
}
Shader::~Shader() {
unloadVolatile();
}
void Shader::attach() {
if (Shader::current != this) {
Graphics::flushBatchedDrawsGlobal();
Shader::current = this;
Vulkan::shaderSwitch();
}
}
int Shader::getVertexAttributeIndex(const std::string& name) {
return vertexAttributeIndices.at(name);
}
void Shader::compileShaders() {
using namespace glslang;
using namespace spirv_cross;
TProgram* program = new TProgram();
auto gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx;
device = vgfx->getDevice();
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
if (!stages[i])
@@ -240,40 +380,92 @@ namespace love {
shaderStages.push_back(shaderStageInfo);
}
return true;
}
void Shader::unloadVolatile() {
if (shaderModules.size() == 0) {
return;
}
// fixme: should generate this dynamically.
void Shader::createDescriptorSetLayout() {
auto vgfx = (Graphics*)gfx;
vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer();
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
auto device = gfx->getDevice();
// fixme: we shouldn't do a greedy wait here.
vkDeviceWaitIdle(device);
for (const auto shaderModule : shaderModules) {
vkDestroyShaderModule(device, shaderModule, nullptr);
}
shaderModules.clear();
shaderStages.clear();
}
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
Shader::~Shader() {
unloadVolatile();
}
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
void Shader::attach() {
if (Shader::current != this) {
Graphics::flushBatchedDrawsGlobal();
Shader::current = this;
Vulkan::shaderSwitch();
VkDescriptorSetLayoutBinding videoYBinding{};
videoYBinding.binding = 2;
videoYBinding.descriptorCount = 1;
videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoYBinding.pImmutableSamplers = nullptr;
videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCBBinding{};
videoCBBinding.binding = 3;
videoCBBinding.descriptorCount = 1;
videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCBBinding.pImmutableSamplers = nullptr;
videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCRinding{};
videoCRinding.binding = 4;
videoCRinding.descriptorCount = 1;
videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCRinding.pImmutableSamplers = nullptr;
videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 5> bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding };
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
throw love::Exception("failed to create descriptor set layout");
}
}
int Shader::getVertexAttributeIndex(const std::string& name) {
return vertexAttributeIndices.at(name);
void Shader::createPipelineLayout() {
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw love::Exception("failed to create pipeline layout");
}
}
void Shader::createStreamBuffers() {
auto vgfx = (Graphics*)gfx;
const auto numImagesInFlight = vgfx->getNumImagesInFlight();
streamBuffers.resize(numImagesInFlight);
for (uint32_t i = 0; i < numImagesInFlight; i++) {
streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * sizeof(BuiltinUniformData));
}
}
void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) {
this->ytexture = ytexture;
this->cbtexture = cbtexture;
this->crtexture = crtexture;
}
void Shader::setUniformData(BuiltinUniformData& data) {
uniformData = data;
}
void Shader::setMainTex(graphics::Texture* texture) {
mainTex = texture;
}
}
}
+32 -5
View File
@@ -21,9 +21,11 @@ namespace love {
bool loadVolatile() override;
void unloadVolatile() override;
const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const {
return shaderStages;
}
const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const;
const VkPipelineLayout getGraphicsPipelineLayout() const;
void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
void attach() override;
@@ -43,11 +45,29 @@ namespace love {
bool hasUniform(const std::string& name) const override { return false; }
void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override {}
void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override;
// fixme: use normal methods for this in the future.
void setUniformData(BuiltinUniformData& data);
void setMainTex(graphics::Texture* texture);
private:
void compileShaders();
void createDescriptorSetLayout();
void createPipelineLayout();
void createStreamBuffers();
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
std::vector<StreamBuffer*> streamBuffers;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> shaderModules;
Graphics* gfx;
VkDevice device;
std::map<std::string, int> vertexAttributeIndices = {
{ "VertexPosition", 0 },
@@ -63,7 +83,14 @@ namespace love {
{ "MainTex", 4 }
};
std::map<std::string, UniformInfo> uniforms;
BuiltinUniformData uniformData;
graphics::Texture* mainTex;
graphics::Texture* ytexture;
graphics::Texture* cbtexture;
graphics::Texture* crtexture;
uint32_t currentImage;
uint32_t count;
};
}
}