From b2f6db7aa817159bd57d88bff8af017a89759aab Mon Sep 17 00:00:00 2001 From: reduz Date: Fri, 9 Jul 2021 16:48:28 -0300 Subject: [PATCH] Implement Specialization Constants * Added support to our local copy of SpirV Reflect (which does not support it). * Pass them on render or compute pipeline creation. * Not implemented in our shaders yet. --- .../RDPipelineSpecializationConstant.xml | 19 ++ doc/classes/RenderingDevice.xml | 10 + drivers/vulkan/rendering_device_vulkan.cpp | 160 +++++++++- drivers/vulkan/rendering_device_vulkan.h | 10 +- servers/register_server_types.cpp | 1 + servers/rendering/rendering_device.cpp | 45 ++- servers/rendering/rendering_device.h | 35 ++- servers/rendering/rendering_device_binds.h | 35 +++ thirdparty/README.md | 5 + .../patches/specialization-constants.patch | 293 ++++++++++++++++++ thirdparty/spirv-reflect/spirv_reflect.c | 139 ++++++++- thirdparty/spirv-reflect/spirv_reflect.h | 53 ++++ 12 files changed, 789 insertions(+), 16 deletions(-) create mode 100644 doc/classes/RDPipelineSpecializationConstant.xml create mode 100644 thirdparty/spirv-reflect/patches/specialization-constants.patch diff --git a/doc/classes/RDPipelineSpecializationConstant.xml b/doc/classes/RDPipelineSpecializationConstant.xml new file mode 100644 index 0000000000..4d9481b846 --- /dev/null +++ b/doc/classes/RDPipelineSpecializationConstant.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index f2b22af8c6..901a985961 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -140,6 +140,8 @@ + + @@ -580,6 +582,8 @@ + + @@ -1709,6 +1713,12 @@ + + + + + + diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 6c1f1e4852..d3d49503d8 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -4374,6 +4374,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector &p_stages uint32_t stages_processed = 0; + Vector specialization_constants; + bool is_compute = false; uint32_t compute_local_size[3] = { 0, 0, 0 }; @@ -4560,6 +4562,62 @@ RID RenderingDeviceVulkan::shader_create(const Vector &p_stages } } + { + //specialization constants + + uint32_t sc_count = 0; + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); + 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 specialization constants."); + + if (sc_count) { + Vector spec_constants; + spec_constants.resize(sc_count); + + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.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 specialization constants."); + + for (uint32_t j = 0; j < sc_count; j++) { + int32_t existing = -1; + Shader::SpecializationConstant sconst; + sconst.constant.constant_id = spec_constants[j]->constant_id; + switch (spec_constants[j]->constant_type) { + case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sconst.constant.bool_value = spec_constants[j]->default_value.int_bool_value != 0; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sconst.constant.int_value = spec_constants[j]->default_value.int_bool_value; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sconst.constant.float_value = spec_constants[j]->default_value.float_value; + } break; + } + sconst.stage_flags = 1 << p_stages[i].shader_stage; + + print_line("spec constant " + itos(i) + ": " + String(spec_constants[j]->name) + " type " + itos(spec_constants[j]->constant_type) + " id " + itos(spec_constants[j]->constant_id)); + + for (int k = 0; k < specialization_constants.size(); k++) { + if (specialization_constants[k].constant.constant_id == sconst.constant.constant_id) { + ERR_FAIL_COND_V_MSG(specialization_constants[k].constant.type != sconst.constant.type, RID(), "More than one specialization constant used for id (" + itos(sconst.constant.constant_id) + "), but their types differ."); + ERR_FAIL_COND_V_MSG(specialization_constants[k].constant.int_value != sconst.constant.int_value, RID(), "More than one specialization constant used for id (" + itos(sconst.constant.constant_id) + "), but their default values differ."); + existing = k; + break; + } + } + + if (existing > 0) { + specialization_constants.write[existing].stage_flags |= sconst.stage_flags; + } else { + specialization_constants.push_back(sconst); + } + } + } + } + if (stage == SHADER_STAGE_VERTEX) { uint32_t iv_count = 0; result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); @@ -4656,6 +4714,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector &p_stages shader.compute_local_size[0] = compute_local_size[0]; shader.compute_local_size[1] = compute_local_size[1]; shader.compute_local_size[2] = compute_local_size[2]; + shader.specialization_constants = specialization_constants; String error_text; @@ -5651,7 +5710,7 @@ Vector RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { /**** RENDER PIPELINE ****/ /*************************/ -RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) { +RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader @@ -5969,8 +6028,63 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.pNext = nullptr; graphics_pipeline_create_info.flags = 0; - graphics_pipeline_create_info.stageCount = shader->pipeline_stages.size(); - graphics_pipeline_create_info.pStages = shader->pipeline_stages.ptr(); + Vector pipeline_stages = shader->pipeline_stages; + Vector specialization_info; + Vector> specialization_map_entries; + Vector specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + specialization_info.resize(pipeline_stages.size()); + specialization_map_entries.resize(pipeline_stages.size()); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overriden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = sc.constant.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + for (int j = 0; j < SHADER_STAGE_MAX; j++) { + if (sc.stage_flags & (1 << j)) { + VkShaderStageFlagBits stage = shader_stage_masks[j]; + for (int k = 0; k < pipeline_stages.size(); k++) { + if (pipeline_stages[k].stage == stage) { + specialization_map_entries.write[k].push_back(entry); + } + } + } + } + } + + for (int k = 0; k < pipeline_stages.size(); k++) { + if (specialization_map_entries[k].size()) { + specialization_info.write[k].dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.write[k].pData = data_ptr; + specialization_info.write[k].mapEntryCount = specialization_map_entries[k].size(); + specialization_info.write[k].pMapEntries = specialization_map_entries[k].ptr(); + + pipeline_stages.write[k].pSpecializationInfo = specialization_info.ptr(); + } + } + } + + graphics_pipeline_create_info.stageCount = pipeline_stages.size(); + graphics_pipeline_create_info.pStages = pipeline_stages.ptr(); + graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info; graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; graphics_pipeline_create_info.pTessellationState = &tessellation_create_info; @@ -6039,7 +6153,7 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { /**** COMPUTE PIPELINE ****/ /**************************/ -RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { +RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader @@ -6061,6 +6175,44 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { compute_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; compute_pipeline_create_info.basePipelineIndex = 0; + VkSpecializationInfo specialization_info; + Vector specialization_map_entries; + Vector specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overriden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = sc.constant.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + specialization_map_entries.push_back(entry); + } + + specialization_info.dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.pData = data_ptr; + specialization_info.mapEntryCount = specialization_map_entries.size(); + specialization_info.pMapEntries = specialization_map_entries.ptr(); + + compute_pipeline_create_info.stage.pSpecializationInfo = &specialization_info; + } + ComputePipeline pipeline; VkResult err = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_pipeline_create_info, nullptr, &pipeline.pipeline); ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index ff9ad71268..8b95ff43b8 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -623,11 +623,17 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t compute_local_size[3] = { 0, 0, 0 }; + struct SpecializationConstant { + PipelineSpecializationConstant constant; + uint32_t stage_flags = 0; + }; + bool is_compute = false; int max_output = 0; Vector sets; Vector set_formats; Vector pipeline_stages; + Vector specialization_constants; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; }; @@ -1100,14 +1106,14 @@ public: /**** RENDER PIPELINE ****/ /*************************/ - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0); + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector &p_specialization_constants = Vector()); virtual bool render_pipeline_is_valid(RID p_pipeline); /**************************/ /**** COMPUTE PIPELINE ****/ /**************************/ - virtual RID compute_pipeline_create(RID p_shader); + virtual RID compute_pipeline_create(RID p_shader, const Vector &p_specialization_constants = Vector()); virtual bool compute_pipeline_is_valid(RID p_pipeline); /****************/ diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 2118dca015..ef82ce5cae 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -207,6 +207,7 @@ void register_server_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 9f586b29fc..3594939362 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -221,7 +221,36 @@ Error RenderingDevice::_buffer_update(RID p_buffer, uint32_t p_offset, uint32_t return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier); } -RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref &p_rasterization_state, const Ref &p_multisample_state, const Ref &p_depth_stencil_state, const Ref &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) { +static Vector _get_spec_constants(const TypedArray &p_constants) { + Vector ret; + ret.resize(p_constants.size()); + for (int i = 0; i < p_constants.size(); i++) { + Ref c = p_constants[i]; + ERR_CONTINUE(c.is_null()); + RenderingDevice::PipelineSpecializationConstant &sc = ret.write[i]; + Variant value = c->get_value(); + switch (value.get_type()) { + case Variant::BOOL: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sc.bool_value = value; + } break; + case Variant::INT: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sc.int_value = value; + } break; + case Variant::FLOAT: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sc.float_value = value; + } break; + default: { + } + } + + sc.constant_id = c->get_constant_id(); + } + return ret; +} +RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref &p_rasterization_state, const Ref &p_multisample_state, const Ref &p_depth_stencil_state, const Ref &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray &p_specialization_constants) { PipelineRasterizationState rasterization_state; if (p_rasterization_state.is_valid()) { rasterization_state = p_rasterization_state->base; @@ -252,7 +281,11 @@ RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p } } - return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass); + return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass, _get_spec_constants(p_specialization_constants)); +} + +RID RenderingDevice::_compute_pipeline_create(RID p_shader, const TypedArray &p_specialization_constants = TypedArray()) { + return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants)); } Vector RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray &p_storage_textures) { @@ -348,10 +381,10 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::buffer_clear, DEFVAL(BARRIER_MASK_ALL)); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer"), &RenderingDevice::buffer_get_data); - ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass", "specialization_constants"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0), DEFVAL(TypedArray())); ClassDB::bind_method(D_METHOD("render_pipeline_is_valid", "render_pipeline"), &RenderingDevice::render_pipeline_is_valid); - ClassDB::bind_method(D_METHOD("compute_pipeline_create", "shader"), &RenderingDevice::compute_pipeline_create); + ClassDB::bind_method(D_METHOD("compute_pipeline_create", "shader", "specialization_constants"), &RenderingDevice::_compute_pipeline_create, DEFVAL(TypedArray())); ClassDB::bind_method(D_METHOD("compute_pipeline_is_valid", "compute_pieline"), &RenderingDevice::compute_pipeline_is_valid); ClassDB::bind_method(D_METHOD("screen_get_width", "screen"), &RenderingDevice::screen_get_width, DEFVAL(DisplayServer::MAIN_WINDOW_ID)); @@ -853,6 +886,10 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(SHADER_LANGUAGE_GLSL); BIND_ENUM_CONSTANT(SHADER_LANGUAGE_HLSL); + BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL); + BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT); + BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT); + BIND_ENUM_CONSTANT(LIMIT_MAX_BOUND_UNIFORM_SETS); BIND_ENUM_CONSTANT(LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS); BIND_ENUM_CONSTANT(LIMIT_MAX_TEXTURES_PER_UNIFORM_SET); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 0ca84bd79e..9a154ef7e9 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -48,6 +48,7 @@ class RDPipelineMultisampleState; class RDPipelineDepthStencilState; class RDPipelineColorBlendState; class RDFramebufferPass; +class RDPipelineSpecializationConstant; class RenderingDevice : public Object { GDCLASS(RenderingDevice, Object) @@ -722,6 +723,32 @@ public: virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; virtual Vector buffer_get_data(RID p_buffer) = 0; //this causes stall, only use to retrieve large buffers for saving + /******************************************/ + /**** PIPELINE SPECIALIZATION CONSTANT ****/ + /******************************************/ + + enum PipelineSpecializationConstantType { + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL, + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT, + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT, + }; + + struct PipelineSpecializationConstant { + PipelineSpecializationConstantType type; + uint32_t constant_id; + union { + uint32_t int_value; + float float_value; + bool bool_value; + }; + + PipelineSpecializationConstant() { + type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + constant_id = 0; + int_value = 0; + } + }; + /*************************/ /**** RENDER PIPELINE ****/ /*************************/ @@ -978,13 +1005,13 @@ public: }; virtual bool render_pipeline_is_valid(RID p_pipeline) = 0; - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0) = 0; + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector &p_specialization_constants = Vector()) = 0; /**************************/ /**** COMPUTE PIPELINE ****/ /**************************/ - virtual RID compute_pipeline_create(RID p_shader) = 0; + virtual RID compute_pipeline_create(RID p_shader, const Vector &p_specialization_constants = Vector()) = 0; virtual bool compute_pipeline_is_valid(RID p_pipeline) = 0; /****************/ @@ -1173,7 +1200,8 @@ protected: Error _buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); - RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref &p_rasterization_state, const Ref &p_multisample_state, const Ref &p_depth_stencil_state, const Ref &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0); + RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref &p_rasterization_state, const Ref &p_multisample_state, const Ref &p_depth_stencil_state, const Ref &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray &p_specialization_constants); + RID _compute_pipeline_create(RID p_shader, const TypedArray &p_specialization_constants); Vector _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values = Vector(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray &p_storage_textures = TypedArray()); void _draw_list_set_push_constant(DrawListID p_list, const Vector &p_data, uint32_t p_data_size); @@ -1205,6 +1233,7 @@ VARIANT_ENUM_CAST(RenderingDevice::LogicOperation) VARIANT_ENUM_CAST(RenderingDevice::BlendFactor) VARIANT_ENUM_CAST(RenderingDevice::BlendOperation) VARIANT_ENUM_CAST(RenderingDevice::PipelineDynamicStateFlags) +VARIANT_ENUM_CAST(RenderingDevice::PipelineSpecializationConstantType) VARIANT_ENUM_CAST(RenderingDevice::InitialAction) VARIANT_ENUM_CAST(RenderingDevice::FinalAction) VARIANT_ENUM_CAST(RenderingDevice::Limit) diff --git a/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h index dc59568ce9..1af427b356 100644 --- a/servers/rendering/rendering_device_binds.h +++ b/servers/rendering/rendering_device_binds.h @@ -452,6 +452,41 @@ protected: ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_ids", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_ids", "get_ids"); } }; + +class RDPipelineSpecializationConstant : public RefCounted { + GDCLASS(RDPipelineSpecializationConstant, RefCounted) + friend class RenderingDevice; + + Variant value = false; + uint32_t constant_id; + +public: + void set_value(const Variant &p_value) { + ERR_FAIL_COND(p_value.get_type() != Variant::BOOL && p_value.get_type() != Variant::INT && p_value.get_type() != Variant::FLOAT); + value = p_value; + } + Variant get_value() const { return value; } + + void set_constant_id(uint32_t p_id) { + constant_id = p_id; + } + uint32_t get_constant_id() const { + return constant_id; + } + +protected: + static void _bind_methods() { + ClassDB::bind_method(D_METHOD("set_value", "value"), &RDPipelineSpecializationConstant::set_value); + ClassDB::bind_method(D_METHOD("get_value"), &RDPipelineSpecializationConstant::get_value); + + ClassDB::bind_method(D_METHOD("set_constant_id", "constant_id"), &RDPipelineSpecializationConstant::set_constant_id); + ClassDB::bind_method(D_METHOD("get_constant_id"), &RDPipelineSpecializationConstant::get_constant_id); + + ADD_PROPERTY(PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_value", "get_value"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "constant_id", PROPERTY_HINT_RANGE, "0,65535,0"), "set_constant_id", "get_constant_id"); + } +}; + class RDPipelineRasterizationState : public RefCounted { GDCLASS(RDPipelineRasterizationState, RefCounted) friend class RenderingDevice; diff --git a/thirdparty/README.md b/thirdparty/README.md index 03a2ddf5e4..31e88180d0 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -616,6 +616,11 @@ Files extracted from upstream source: - `include` folder - `LICENSE` +Some downstream changes have been made and are identified by +`// -- GODOT begin --` and `// -- GODOT end --` comments. +They can be reapplied using the patch included in the `patches` +folder. + ## squish diff --git a/thirdparty/spirv-reflect/patches/specialization-constants.patch b/thirdparty/spirv-reflect/patches/specialization-constants.patch new file mode 100644 index 0000000000..8ff1dcc2e5 --- /dev/null +++ b/thirdparty/spirv-reflect/patches/specialization-constants.patch @@ -0,0 +1,293 @@ +diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c +index 0fc979a8a4..3e3643717a 100644 +--- a/thirdparty/spirv-reflect/spirv_reflect.c ++++ b/thirdparty/spirv-reflect/spirv_reflect.c +@@ -124,6 +124,9 @@ typedef struct Decorations { + NumberDecoration location; + NumberDecoration offset; + NumberDecoration uav_counter_buffer; ++// -- GODOT begin -- ++ NumberDecoration specialization_constant; ++// -- GODOT end -- + StringDecoration semantic; + uint32_t array_stride; + uint32_t matrix_stride; +@@ -610,6 +613,9 @@ static SpvReflectResult ParseNodes(Parser* p_parser) + p_parser->nodes[i].decorations.offset.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.uav_counter_buffer.value = (uint32_t)INVALID_VALUE; + p_parser->nodes[i].decorations.built_in = (SpvBuiltIn)INVALID_VALUE; ++// -- GODOT begin -- ++ p_parser->nodes[i].decorations.specialization_constant.value = (SpvBuiltIn)INVALID_VALUE; ++// -- GODOT end -- + } + // Mark source file id node + p_parser->source_file_id = (uint32_t)INVALID_VALUE; +@@ -800,10 +806,16 @@ static SpvReflectResult ParseNodes(Parser* p_parser) + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + } + break; +- ++// -- GODOT begin -- + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: +- case SpvOpSpecConstant: ++ case SpvOpSpecConstant: { ++ CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); ++ CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); ++ p_node->is_type = true; ++ } ++ break; ++// -- GODOT end -- + case SpvOpSpecConstantComposite: + case SpvOpSpecConstantOp: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); +@@ -1309,6 +1321,9 @@ static SpvReflectResult ParseDecorations(Parser* p_parser) + skip = true; + } + break; ++// -- GODOT begin -- ++ case SpvDecorationSpecId: ++// -- GODOT end -- + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationColMajor: +@@ -1441,7 +1456,14 @@ static SpvReflectResult ParseDecorations(Parser* p_parser) + p_target_decorations->input_attachment_index.word_offset = word_offset; + } + break; +- ++// -- GODOT begin -- ++ case SpvDecorationSpecId: { ++ uint32_t word_offset = p_node->word_offset + member_offset+ 3; ++ CHECKED_READU32(p_parser, word_offset, p_target_decorations->specialization_constant.value); ++ p_target_decorations->specialization_constant.word_offset = word_offset; ++ } ++ break; ++// -- GODOT end -- + case SpvReflectDecorationHlslCounterBufferGOOGLE: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->uav_counter_buffer.value); +@@ -1731,6 +1753,13 @@ static SpvReflectResult ParseType( + p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE; + } + break; ++// -- GODOT begin -- ++ case SpvOpSpecConstantTrue: ++ case SpvOpSpecConstantFalse: ++ case SpvOpSpecConstant: { ++ } ++ break; ++// -- GODOT end -- + } + + if (result == SPV_REFLECT_RESULT_SUCCESS) { +@@ -3187,6 +3216,69 @@ static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderMo + return SPV_REFLECT_RESULT_SUCCESS; + } + ++// -- GODOT begin -- ++static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflectShaderModule* p_module) ++{ ++ p_module->specialization_constant_count = 0; ++ p_module->specialization_constants = NULL; ++ for (size_t i = 0; i < p_parser->node_count; ++i) { ++ Node* p_node = &(p_parser->nodes[i]); ++ if (p_node->op == SpvOpSpecConstantTrue || p_node->op == SpvOpSpecConstantFalse || p_node->op == SpvOpSpecConstant) { ++ p_module->specialization_constant_count++; ++ } ++ } ++ ++ if (p_module->specialization_constant_count == 0) { ++ return SPV_REFLECT_RESULT_SUCCESS; ++ } ++ ++ p_module->specialization_constants = (SpvReflectSpecializationConstant*)calloc(p_module->specialization_constant_count, sizeof(SpvReflectSpecializationConstant)); ++ ++ uint32_t index = 0; ++ ++ for (size_t i = 0; i < p_parser->node_count; ++i) { ++ Node* p_node = &(p_parser->nodes[i]); ++ switch(p_node->op) { ++ default: continue; ++ case SpvOpSpecConstantTrue: { ++ p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL; ++ p_module->specialization_constants[index].default_value.int_bool_value = 1; ++ } break; ++ case SpvOpSpecConstantFalse: { ++ p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL; ++ p_module->specialization_constants[index].default_value.int_bool_value = 0; ++ } break; ++ case SpvOpSpecConstant: { ++ SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS; ++ uint32_t element_type_id = (uint32_t)INVALID_VALUE; ++ uint32_t default_value = 0; ++ IF_READU32(result, p_parser, p_node->word_offset + 1, element_type_id); ++ IF_READU32(result, p_parser, p_node->word_offset + 3, default_value); ++ ++ Node* p_next_node = FindNode(p_parser, element_type_id); ++ ++ if (p_next_node->op == SpvOpTypeInt) { ++ p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_INT; ++ } else if (p_next_node->op == SpvOpTypeFloat) { ++ p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT; ++ } else { ++ return SPV_REFLECT_RESULT_ERROR_PARSE_FAILED; ++ } ++ ++ p_module->specialization_constants[index].default_value.int_bool_value = default_value; //bits are the same for int and float ++ } break; ++ } ++ ++ p_module->specialization_constants[index].name = p_node->name; ++ p_module->specialization_constants[index].constant_id = p_node->decorations.specialization_constant.value; ++ p_module->specialization_constants[index].spirv_id = p_node->result_id; ++ index++; ++ } ++ ++ return SPV_REFLECT_RESULT_SUCCESS; ++} ++// -- GODOT end -- ++ + static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShaderModule* p_module) + { + for (size_t i = 0; i < p_parser->node_count; ++i) { +@@ -3562,6 +3654,12 @@ SpvReflectResult spvReflectCreateShaderModule( + result = ParsePushConstantBlocks(&parser, p_module); + SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); + } ++// -- GODOT begin -- ++ if (result == SPV_REFLECT_RESULT_SUCCESS) { ++ result = ParseSpecializationConstants(&parser, p_module); ++ SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); ++ } ++// -- GODOT end -- + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseEntryPoints(&parser, p_module); + SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); +@@ -3691,6 +3789,9 @@ void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module) + SafeFree(p_entry->used_push_constants); + } + SafeFree(p_module->entry_points); ++// -- GODOT begin -- ++ SafeFree(p_module->specialization_constants); ++// -- GODOT end -- + + // Push constants + for (size_t i = 0; i < p_module->push_constant_block_count; ++i) { +@@ -3959,6 +4060,38 @@ SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables( + return SPV_REFLECT_RESULT_SUCCESS; + } + ++// -- GODOT begin -- ++SpvReflectResult spvReflectEnumerateSpecializationConstants( ++ const SpvReflectShaderModule* p_module, ++ uint32_t* p_count, ++ SpvReflectSpecializationConstant** pp_constants ++) ++{ ++ if (IsNull(p_module)) { ++ return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; ++ } ++ if (IsNull(p_count)) { ++ return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; ++ } ++ ++ if (IsNotNull(pp_constants)) { ++ if (*p_count != p_module->specialization_constant_count) { ++ return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; ++ } ++ ++ for (uint32_t index = 0; index < *p_count; ++index) { ++ SpvReflectSpecializationConstant *p_const = &p_module->specialization_constants[index]; ++ pp_constants[index] = p_const; ++ } ++ } ++ else { ++ *p_count = p_module->specialization_constant_count; ++ } ++ ++ return SPV_REFLECT_RESULT_SUCCESS; ++} ++// -- GODOT end -- ++ + SpvReflectResult spvReflectEnumerateInputVariables( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, +diff --git a/thirdparty/spirv-reflect/spirv_reflect.h b/thirdparty/spirv-reflect/spirv_reflect.h +index a5a956e9e8..21f8160770 100644 +--- a/thirdparty/spirv-reflect/spirv_reflect.h ++++ b/thirdparty/spirv-reflect/spirv_reflect.h +@@ -292,6 +292,28 @@ typedef struct SpvReflectTypeDescription { + struct SpvReflectTypeDescription* members; + } SpvReflectTypeDescription; + ++// -- GODOT begin -- ++/*! @struct SpvReflectSpecializationConstant ++ ++*/ ++ ++typedef enum SpvReflectSpecializationConstantType { ++ SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL = 0, ++ SPV_REFLECT_SPECIALIZATION_CONSTANT_INT = 1, ++ SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT = 2, ++} SpvReflectSpecializationConstantType; ++ ++typedef struct SpvReflectSpecializationConstant { ++ const char* name; ++ uint32_t spirv_id; ++ uint32_t constant_id; ++ SpvReflectSpecializationConstantType constant_type; ++ union { ++ float float_value; ++ uint32_t int_bool_value; ++ } default_value; ++} SpvReflectSpecializationConstant; ++// -- GODOT end -- + + /*! @struct SpvReflectInterfaceVariable + +@@ -439,6 +461,10 @@ typedef struct SpvReflectShaderModule { + SpvReflectInterfaceVariable* interface_variables; + uint32_t push_constant_block_count; + SpvReflectBlockVariable* push_constant_blocks; ++ // -- GODOT begin -- ++ uint32_t specialization_constant_count; ++ SpvReflectSpecializationConstant* specialization_constants; ++ // -- GODOT end -- + + struct Internal { + size_t spirv_size; +@@ -694,6 +720,33 @@ SpvReflectResult spvReflectEnumerateInputVariables( + SpvReflectInterfaceVariable** pp_variables + ); + ++// -- GOODT begin -- ++/*! @fn spvReflectEnumerateSpecializationConstants ++ @brief If the module contains multiple entry points, this will only get ++ the specialization constants for the first one. ++ @param p_module Pointer to an instance of SpvReflectShaderModule. ++ @param p_count If pp_constants is NULL, the module's specialization constant ++ count will be stored here. ++ If pp_variables is not NULL, *p_count must contain ++ the module's specialization constant count. ++ @param pp_variables If NULL, the module's specialization constant count will be ++ written to *p_count. ++ If non-NULL, pp_constants must point to an array with ++ *p_count entries, where pointers to the module's ++ specialization constants will be written. The caller must not ++ free the specialization constants written to this array. ++ @return If successful, returns SPV_REFLECT_RESULT_SUCCESS. ++ Otherwise, the error code indicates the cause of the ++ failure. ++ ++*/ ++SpvReflectResult spvReflectEnumerateSpecializationConstants( ++ const SpvReflectShaderModule* p_module, ++ uint32_t* p_count, ++ SpvReflectSpecializationConstant** pp_constants ++); ++// -- GODOT end -- ++ + /*! @fn spvReflectEnumerateEntryPointInputVariables + @brief Enumerate the input variables for a given entry point. + @param entry_point The name of the entry point to get the input variables for. diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c index 0fc979a8a4..8f614c8874 100644 --- a/thirdparty/spirv-reflect/spirv_reflect.c +++ b/thirdparty/spirv-reflect/spirv_reflect.c @@ -124,6 +124,9 @@ typedef struct Decorations { NumberDecoration location; NumberDecoration offset; NumberDecoration uav_counter_buffer; +// -- GODOT begin -- + NumberDecoration specialization_constant; +// -- GODOT end -- StringDecoration semantic; uint32_t array_stride; uint32_t matrix_stride; @@ -610,6 +613,9 @@ static SpvReflectResult ParseNodes(Parser* p_parser) p_parser->nodes[i].decorations.offset.value = (uint32_t)INVALID_VALUE; p_parser->nodes[i].decorations.uav_counter_buffer.value = (uint32_t)INVALID_VALUE; p_parser->nodes[i].decorations.built_in = (SpvBuiltIn)INVALID_VALUE; +// -- GODOT begin -- + p_parser->nodes[i].decorations.specialization_constant.value = (SpvBuiltIn)INVALID_VALUE; +// -- GODOT end -- } // Mark source file id node p_parser->source_file_id = (uint32_t)INVALID_VALUE; @@ -800,10 +806,16 @@ static SpvReflectResult ParseNodes(Parser* p_parser) CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); } break; - +// -- GODOT begin -- case SpvOpSpecConstantTrue: case SpvOpSpecConstantFalse: - case SpvOpSpecConstant: + case SpvOpSpecConstant: { + CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); + CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id); + p_node->is_type = true; + } + break; +// -- GODOT end -- case SpvOpSpecConstantComposite: case SpvOpSpecConstantOp: { CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id); @@ -1309,6 +1321,9 @@ static SpvReflectResult ParseDecorations(Parser* p_parser) skip = true; } break; +// -- GODOT begin -- + case SpvDecorationSpecId: +// -- GODOT end -- case SpvDecorationBlock: case SpvDecorationBufferBlock: case SpvDecorationColMajor: @@ -1441,7 +1456,14 @@ static SpvReflectResult ParseDecorations(Parser* p_parser) p_target_decorations->input_attachment_index.word_offset = word_offset; } break; - +// -- GODOT begin -- + case SpvDecorationSpecId: { + uint32_t word_offset = p_node->word_offset + member_offset+ 3; + CHECKED_READU32(p_parser, word_offset, p_target_decorations->specialization_constant.value); + p_target_decorations->specialization_constant.word_offset = word_offset; + } + break; +// -- GODOT end -- case SpvReflectDecorationHlslCounterBufferGOOGLE: { uint32_t word_offset = p_node->word_offset + member_offset+ 3; CHECKED_READU32(p_parser, word_offset, p_target_decorations->uav_counter_buffer.value); @@ -1731,6 +1753,13 @@ static SpvReflectResult ParseType( p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE; } break; +// -- GODOT begin -- + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: { + } + break; +// -- GODOT end -- } if (result == SPV_REFLECT_RESULT_SUCCESS) { @@ -3187,6 +3216,69 @@ static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderMo return SPV_REFLECT_RESULT_SUCCESS; } +// -- GODOT begin -- +static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflectShaderModule* p_module) +{ + p_module->specialization_constant_count = 0; + p_module->specialization_constants = NULL; + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + if (p_node->op == SpvOpSpecConstantTrue || p_node->op == SpvOpSpecConstantFalse || p_node->op == SpvOpSpecConstant) { + p_module->specialization_constant_count++; + } + } + + if (p_module->specialization_constant_count == 0) { + return SPV_REFLECT_RESULT_SUCCESS; + } + + p_module->specialization_constants = (SpvReflectSpecializationConstant*)calloc(p_module->specialization_constant_count, sizeof(SpvReflectSpecializationConstant)); + + uint32_t index = 0; + + for (size_t i = 0; i < p_parser->node_count; ++i) { + Node* p_node = &(p_parser->nodes[i]); + switch(p_node->op) { + default: continue; + case SpvOpSpecConstantTrue: { + p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL; + p_module->specialization_constants[index].default_value.int_bool_value = 1; + } break; + case SpvOpSpecConstantFalse: { + p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL; + p_module->specialization_constants[index].default_value.int_bool_value = 0; + } break; + case SpvOpSpecConstant: { + SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS; + uint32_t element_type_id = (uint32_t)INVALID_VALUE; + uint32_t default_value = 0; + IF_READU32(result, p_parser, p_node->word_offset + 1, element_type_id); + IF_READU32(result, p_parser, p_node->word_offset + 3, default_value); + + Node* p_next_node = FindNode(p_parser, element_type_id); + + if (p_next_node->op == SpvOpTypeInt) { + p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_INT; + } else if (p_next_node->op == SpvOpTypeFloat) { + p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT; + } else { + return SPV_REFLECT_RESULT_ERROR_PARSE_FAILED; + } + + p_module->specialization_constants[index].default_value.int_bool_value = default_value; //bits are the same for int and float + } break; + } + + p_module->specialization_constants[index].name = p_node->name; + p_module->specialization_constants[index].constant_id = p_node->decorations.specialization_constant.value; + p_module->specialization_constants[index].spirv_id = p_node->result_id; + index++; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} +// -- GODOT end -- + static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShaderModule* p_module) { for (size_t i = 0; i < p_parser->node_count; ++i) { @@ -3562,6 +3654,12 @@ SpvReflectResult spvReflectCreateShaderModule( result = ParsePushConstantBlocks(&parser, p_module); SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); } +// -- GODOT begin -- + if (result == SPV_REFLECT_RESULT_SUCCESS) { + result = ParseSpecializationConstants(&parser, p_module); + SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); + } +// -- GODOT end -- if (result == SPV_REFLECT_RESULT_SUCCESS) { result = ParseEntryPoints(&parser, p_module); SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS); @@ -3691,6 +3789,9 @@ void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module) SafeFree(p_entry->used_push_constants); } SafeFree(p_module->entry_points); +// -- GODOT begin -- + SafeFree(p_module->specialization_constants); +// -- GODOT end -- // Push constants for (size_t i = 0; i < p_module->push_constant_block_count; ++i) { @@ -3959,6 +4060,38 @@ SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables( return SPV_REFLECT_RESULT_SUCCESS; } +// -- GODOT begin -- +SpvReflectResult spvReflectEnumerateSpecializationConstants( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectSpecializationConstant** pp_constants +) +{ + if (IsNull(p_module)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + if (IsNull(p_count)) { + return SPV_REFLECT_RESULT_ERROR_NULL_POINTER; + } + + if (IsNotNull(pp_constants)) { + if (*p_count != p_module->specialization_constant_count) { + return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH; + } + + for (uint32_t index = 0; index < *p_count; ++index) { + SpvReflectSpecializationConstant *p_const = &p_module->specialization_constants[index]; + pp_constants[index] = p_const; + } + } + else { + *p_count = p_module->specialization_constant_count; + } + + return SPV_REFLECT_RESULT_SUCCESS; +} +// -- GODOT end -- + SpvReflectResult spvReflectEnumerateInputVariables( const SpvReflectShaderModule* p_module, uint32_t* p_count, diff --git a/thirdparty/spirv-reflect/spirv_reflect.h b/thirdparty/spirv-reflect/spirv_reflect.h index a5a956e9e8..21f8160770 100644 --- a/thirdparty/spirv-reflect/spirv_reflect.h +++ b/thirdparty/spirv-reflect/spirv_reflect.h @@ -292,6 +292,28 @@ typedef struct SpvReflectTypeDescription { struct SpvReflectTypeDescription* members; } SpvReflectTypeDescription; +// -- GODOT begin -- +/*! @struct SpvReflectSpecializationConstant + +*/ + +typedef enum SpvReflectSpecializationConstantType { + SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL = 0, + SPV_REFLECT_SPECIALIZATION_CONSTANT_INT = 1, + SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT = 2, +} SpvReflectSpecializationConstantType; + +typedef struct SpvReflectSpecializationConstant { + const char* name; + uint32_t spirv_id; + uint32_t constant_id; + SpvReflectSpecializationConstantType constant_type; + union { + float float_value; + uint32_t int_bool_value; + } default_value; +} SpvReflectSpecializationConstant; +// -- GODOT end -- /*! @struct SpvReflectInterfaceVariable @@ -439,6 +461,10 @@ typedef struct SpvReflectShaderModule { SpvReflectInterfaceVariable* interface_variables; uint32_t push_constant_block_count; SpvReflectBlockVariable* push_constant_blocks; + // -- GODOT begin -- + uint32_t specialization_constant_count; + SpvReflectSpecializationConstant* specialization_constants; + // -- GODOT end -- struct Internal { size_t spirv_size; @@ -694,6 +720,33 @@ SpvReflectResult spvReflectEnumerateInputVariables( SpvReflectInterfaceVariable** pp_variables ); +// -- GOODT begin -- +/*! @fn spvReflectEnumerateSpecializationConstants + @brief If the module contains multiple entry points, this will only get + the specialization constants for the first one. + @param p_module Pointer to an instance of SpvReflectShaderModule. + @param p_count If pp_constants is NULL, the module's specialization constant + count will be stored here. + If pp_variables is not NULL, *p_count must contain + the module's specialization constant count. + @param pp_variables If NULL, the module's specialization constant count will be + written to *p_count. + If non-NULL, pp_constants must point to an array with + *p_count entries, where pointers to the module's + specialization constants will be written. The caller must not + free the specialization constants written to this array. + @return If successful, returns SPV_REFLECT_RESULT_SUCCESS. + Otherwise, the error code indicates the cause of the + failure. + +*/ +SpvReflectResult spvReflectEnumerateSpecializationConstants( + const SpvReflectShaderModule* p_module, + uint32_t* p_count, + SpvReflectSpecializationConstant** pp_constants +); +// -- GODOT end -- + /*! @fn spvReflectEnumerateEntryPointInputVariables @brief Enumerate the input variables for a given entry point. @param entry_point The name of the entry point to get the input variables for.