mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
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:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user