Browse Source

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.
reduz 4 years ago
parent
commit
b2f6db7aa8

+ 19 - 0
doc/classes/RDPipelineSpecializationConstant.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RDPipelineSpecializationConstant" inherits="RefCounted" version="4.0">
+	<brief_description>
+	</brief_description>
+	<description>
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<members>
+		<member name="constant_id" type="int" setter="set_constant_id" getter="get_constant_id" default="0">
+		</member>
+		<member name="value" type="Variant" setter="set_value" getter="get_value">
+		</member>
+	</members>
+	<constants>
+	</constants>
+</class>

+ 10 - 0
doc/classes/RenderingDevice.xml

@@ -140,6 +140,8 @@
 			</return>
 			<argument index="0" name="shader" type="RID">
 			</argument>
+			<argument index="1" name="specialization_constants" type="RDPipelineSpecializationConstant[]" default="[]">
+			</argument>
 			<description>
 			</description>
 		</method>
@@ -580,6 +582,8 @@
 			</argument>
 			<argument index="9" name="for_render_pass" type="int" default="0">
 			</argument>
+			<argument index="10" name="specialization_constants" type="RDPipelineSpecializationConstant[]" default="[]">
+			</argument>
 			<description>
 			</description>
 		</method>
@@ -1709,6 +1713,12 @@
 		</constant>
 		<constant name="SHADER_LANGUAGE_HLSL" value="1" enum="ShaderLanguage">
 		</constant>
+		<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL" value="0" enum="PipelineSpecializationConstantType">
+		</constant>
+		<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT" value="1" enum="PipelineSpecializationConstantType">
+		</constant>
+		<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT" value="2" enum="PipelineSpecializationConstantType">
+		</constant>
 		<constant name="LIMIT_MAX_BOUND_UNIFORM_SETS" value="0" enum="Limit">
 		</constant>
 		<constant name="LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS" value="1" enum="Limit">

+ 156 - 4
drivers/vulkan/rendering_device_vulkan.cpp

@@ -4374,6 +4374,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
 
 	uint32_t stages_processed = 0;
 
+	Vector<Shader::SpecializationConstant> 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<ShaderStageData> &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<SpvReflectSpecializationConstant *> 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<ShaderStageData> &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<uint8_t> 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<PipelineSpecializationConstant> &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<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages;
+	Vector<VkSpecializationInfo> specialization_info;
+	Vector<Vector<VkSpecializationMapEntry>> specialization_map_entries;
+	Vector<uint32_t> 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<PipelineSpecializationConstant> &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<VkSpecializationMapEntry> specialization_map_entries;
+	Vector<uint32_t> 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) + ".");

+ 8 - 2
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<Set> sets;
 		Vector<uint32_t> set_formats;
 		Vector<VkPipelineShaderStageCreateInfo> pipeline_stages;
+		Vector<SpecializationConstant> 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<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>());
 	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<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>());
 	virtual bool compute_pipeline_is_valid(RID p_pipeline);
 
 	/****************/

+ 1 - 0
servers/register_server_types.cpp

@@ -207,6 +207,7 @@ void register_server_types() {
 	ClassDB::register_class<RDShaderSource>();
 	ClassDB::register_class<RDShaderBytecode>();
 	ClassDB::register_class<RDShaderFile>();
+	ClassDB::register_class<RDPipelineSpecializationConstant>();
 
 	ClassDB::register_class<CameraFeed>();
 

+ 41 - 4
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<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) {
+static Vector<RenderingDevice::PipelineSpecializationConstant> _get_spec_constants(const TypedArray<RDPipelineSpecializationConstant> &p_constants) {
+	Vector<RenderingDevice::PipelineSpecializationConstant> ret;
+	ret.resize(p_constants.size());
+	for (int i = 0; i < p_constants.size(); i++) {
+		Ref<RDPipelineSpecializationConstant> 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<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &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<RDPipelineSpecializationConstant> &p_specialization_constants = TypedArray<RDPipelineSpecializationConstant>()) {
+	return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants));
 }
 
 Vector<int64_t> 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<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &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<RDPipelineSpecializationConstant>()));
 	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<RDPipelineSpecializationConstant>()));
 	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);

+ 32 - 3
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<uint8_t> 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<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()) = 0;
 
 	/**************************/
 	/**** COMPUTE PIPELINE ****/
 	/**************************/
 
-	virtual RID compute_pipeline_create(RID p_shader) = 0;
+	virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()) = 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<uint8_t> &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<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &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<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants);
+	RID _compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants);
 
 	Vector<int64_t> _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<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>());
 	void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &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)

+ 35 - 0
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;

+ 5 - 0
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
 

+ 293 - 0
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.

+ 136 - 3
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,

+ 53 - 0
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.