From 60c7498cee169b2cd652045ec19bb526d76f9d61 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sun, 28 Jul 2019 13:42:15 -0300 Subject: Replaced GLSLang reflection by SPIRV-Reflect, eventually allowing to move GLSLang out. --- drivers/SCsub | 1 + drivers/spirv-reflect/SCsub | 16 ++ drivers/vulkan/rendering_device_vulkan.cpp | 255 ++++++++++++++++++++++++++++- 3 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 drivers/spirv-reflect/SCsub (limited to 'drivers') diff --git a/drivers/SCsub b/drivers/SCsub index 072addc2b3..48befd213c 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -33,6 +33,7 @@ else: # Core dependencies SConscript("png/SCsub") +SConscript("spirv-reflect/SCsub") if env['vsproj']: import os diff --git a/drivers/spirv-reflect/SCsub b/drivers/spirv-reflect/SCsub new file mode 100644 index 0000000000..f6b40ac433 --- /dev/null +++ b/drivers/spirv-reflect/SCsub @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +Import('env') + +env_spirv_reflect = env.Clone() + +thirdparty_dir = "#thirdparty/spirv-reflect/" +thirdparty_sources = [ + "spirv_reflect.c" +] + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_spirv_reflect.add_source_files(env.drivers_sources, thirdparty_sources) + +Export('env') diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index b2d5a3781c..7f4ebd5134 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1,10 +1,11 @@ #include "rendering_device_vulkan.h" -#include "drivers/vulkan/vulkan_context.h" - #include "core/hashfuncs.h" +#include "core/os/file_access.h" #include "core/project_settings.h" +#include "drivers/vulkan/vulkan_context.h" #include "thirdparty/glslang/SPIRV/GlslangToSpv.h" #include "thirdparty/glslang/glslang/Include/Types.h" +#include "thirdparty/spirv-reflect/spirv_reflect.h" void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { @@ -2371,7 +2372,6 @@ PoolVector RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3 image_copy_region.extent.depth = mm_depth; vkCmdCopyImage(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); - print_line("copying mipmap " + itos(i) + " w: " + itos(mm_width) + " h " + itos(mm_height) + " d " + itos(mm_depth)); } } @@ -3568,7 +3568,7 @@ RID RenderingDeviceVulkan::shader_create_from_source(const Vector > bindings; + Vector > set_bindings; Vector > uniform_info; Shader::PushConstant push_constant; push_constant.push_constant_size = 0; @@ -3652,11 +3652,27 @@ RID RenderingDeviceVulkan::shader_create_from_source(const VectorgetBasicType() == glslang::EbtBlock && reflection.getType()->getQualifier().storage == glslang::EvqUniform && reflection.getType()->getQualifier().layoutPushConstant) { + uint32_t len = reflection.size; + if (push_constant_debug.push_constant_size != 0 && push_constant_debug.push_constant_size != len) { + print_line("eep"); + } + + push_constant_debug.push_constant_size = len; + push_constant_debug.push_constants_vk_stage |= shader_stage_masks[p_stages[i].shader_stage]; + //print_line("Debug stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " push constant size: " + itos(push_constant_debug.push_constant_size)); + } + } + + for (int j = 0; j < program.getNumUniformVariables(); j++) { if (!_uniform_add_binding(bindings, uniform_info, program.getUniform(j), p_stages[i].shader_stage, push_constant, r_error)) { return RID(); @@ -3698,11 +3714,233 @@ RID RenderingDeviceVulkan::shader_create_from_source(const Vector SpirV; spv::SpvBuildLogger logger; glslang::SpvOptions spvOptions; glslang::GlslangToSpv(*program.getIntermediate(stages[p_stages[i].shader_stage]), SpirV, &logger, &spvOptions); + { + SpvReflectShaderModule module; + SpvReflectResult result = spvReflectCreateShaderModule(SpirV.size() * sizeof(uint32_t), &SpirV[0], &module); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed parsing shader."); + + uint32_t binding_count = 0; + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating descriptor bindings."); + + uint32_t stage = p_stages[i].shader_stage; + + if (binding_count > 0) { + + //Parse bindings + + Vector bindings; + bindings.resize(binding_count); + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); + + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed getting descriptor bindings."); + + for (uint32_t j = 0; j < binding_count; j++) { + const SpvReflectDescriptorBinding &binding = *bindings[j]; + + VkDescriptorSetLayoutBinding layout_binding; + UniformInfo info; + + bool need_array_dimensions = false; + bool need_block_size = false; + + switch (binding.descriptor_type) { + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + info.type = UNIFORM_TYPE_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + info.type = UNIFORM_TYPE_IMAGE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_TEXTURE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_IMAGE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + info.type = UNIFORM_TYPE_UNIFORM_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + info.type = UNIFORM_TYPE_STORAGE_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic uniform buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic storage buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + } break; + } + + if (need_array_dimensions) { + if (binding.array.dims_count == 0) { + info.length = 1; + } else { + for (uint32_t k = 0; k < binding.array.dims_count; k++) { + if (k == 0) { + info.length = binding.array.dims[0]; + } else { + info.length *= binding.array.dims[k]; + } + } + } + + layout_binding.descriptorCount = info.length; + + } else if (need_block_size) { + info.length = binding.block.size; + layout_binding.descriptorCount = 1; + } else { + info.length = 0; + layout_binding.descriptorCount = 1; + } + + info.binding = binding.binding; + uint32_t set = binding.set; + + //print_line("Stage: " + String(shader_stage_names[stage]) + " set=" + itos(set) + " binding=" + itos(info.binding) + " type=" + shader_uniform_names[info.type] + " length=" + itos(info.length)); + + ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, RID(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); + + ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, RID(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); + + if (set < (uint32_t)set_bindings.size()) { + //check if this already exists + bool exists = false; + for (int k = 0; k < set_bindings[set].size(); k++) { + if (set_bindings[set][k].binding == (uint32_t)info.binding) { + //already exists, verify that it's the same type + ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorType != layout_binding.descriptorType, RID(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); + + //also, verify that it's the same size + ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorCount != layout_binding.descriptorCount || uniform_info[set][k].length != info.length, RID(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); + + //just append stage mask and return + set_bindings.write[set].write[k].stageFlags |= shader_stage_masks[stage]; + uniform_info.write[set].write[k].stages |= 1 << stage; + exists = true; + } + } + + if (exists) { + continue; //merged + } + } + + layout_binding.binding = info.binding; + layout_binding.stageFlags = shader_stage_masks[stage]; + layout_binding.pImmutableSamplers = NULL; //no support for this yet + + info.stages = 1 << stage; + info.binding = info.binding; + + if (set >= (uint32_t)set_bindings.size()) { + set_bindings.resize(set + 1); + uniform_info.resize(set + 1); + } + + set_bindings.write[set].push_back(layout_binding); + uniform_info.write[set].push_back(info); + } + } + + if (stage == SHADER_STAGE_FRAGMENT) { + + uint32_t ov_count = 0; + result = spvReflectEnumerateOutputVariables(&module, &ov_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating output variables."); + + if (ov_count) { + Vector output_vars; + output_vars.resize(ov_count); + + result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables."); + + for (uint32_t j = 0; j < ov_count; j++) { + if (output_vars[j]) { + fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1); + } + } + } + } + uint32_t pc_count = 0; + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating push constants."); + + if (pc_count) { + ERR_FAIL_COND_V_MSG(pc_count > 1, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + + Vector pconstants; + pconstants.resize(pc_count); + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining push constants."); +#if 0 + if (pconstants[0] == NULL) { + FileAccess *f = FileAccess::open("res://popo.spv", FileAccess::WRITE); + f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); + memdelete(f); + } +#endif + + ERR_FAIL_COND_V_MSG(push_constant.push_constant_size && push_constant.push_constant_size != pconstants[0]->size, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + + push_constant.push_constant_size = pconstants[0]->size; + push_constant.push_constants_vk_stage |= shader_stage_masks[stage]; + + //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + } + + // Destroy the reflection data when no longer required. + spvReflectDestroyShaderModule(&module); + } + spirv_code.push_back(SpirV); stages_processed |= (1 << p_stages[i].shader_stage); @@ -3758,15 +3996,15 @@ RID RenderingDeviceVulkan::shader_create_from_source(const Vector &p_uniforms, const Uniform &uniform = uniforms[uniform_idx]; ERR_FAIL_COND_V_MSG(uniform.type != set_uniform.type, RID(), - "Mismatch uniform type for binding (" + itos(set_uniform.binding) + ")."); + "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "). Expected '" + shader_uniform_names[set_uniform.type] + "', supplied: '" + shader_uniform_names[uniform.type] + "'."); VkWriteDescriptorSet write; //common header write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -6048,7 +6286,6 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context) { } texture_upload_region_size_px = GLOBAL_DEF("rendering/vulkan/staging_buffer/texture_upload_region_size_px", 64); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); - print_line("update size: " + itos(texture_upload_region_size_px)); frames_drawn = frame_count; //start from frame count, so everything else is immediately old -- cgit v1.2.3