Merge pull request #50325 from reduz/implement-specialization-constants

Implement specialization constants
This commit is contained in:
Rémi Verschelde 2021-07-11 23:57:08 +02:00 committed by GitHub
commit d1f25bb5fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 789 additions and 16 deletions

View file

@ -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>

View file

@ -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">

View file

@ -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) + ".");

View file

@ -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);
/****************/

View file

@ -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>();

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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.

View file

@ -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,

View file

@ -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.