diff --git a/deps/glslang/glslang.cpp b/deps/glslang/glslang.cpp index fff3929c28..a62ae05a8f 100644 --- a/deps/glslang/glslang.cpp +++ b/deps/glslang/glslang.cpp @@ -355,15 +355,18 @@ bool glslang::compile_spirv(const string &source, Stage stage, std::vector(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); + string msg; auto forbid_include = TShader::ForbidInclude(); - if (!shader.preprocess(&process.GetResources(), 100, ENoProfile, false, false, EShMsgDefault, &msg, forbid_include)) + if (!shader.preprocess(&process.GetResources(), 100, ENoProfile, false, false, + messages, &msg, forbid_include)) { fprintf(stderr, "%s\n", msg.c_str()); return {}; } - if (!shader.parse(&process.GetResources(), 100, false, EShMsgDefault)) + if (!shader.parse(&process.GetResources(), 100, false, messages)) { RARCH_ERR("%s\n", shader.getInfoLog()); RARCH_ERR("%s\n", shader.getInfoDebugLog()); @@ -372,7 +375,7 @@ bool glslang::compile_spirv(const string &source, Stage stage, std::vector parameters; vector filtered_parameters; + + struct PushConstant + { + VkShaderStageFlags stages = 0; + vector buffer; // uint32_t to have correct alignment. + }; + PushConstant push; }; // struct here since we're implementing the opaque typedef from C. @@ -1394,6 +1401,25 @@ bool Pass::init_pipeline_layout() layout_info.setLayoutCount = 1; layout_info.pSetLayouts = &set_layout; + // Push constants + VkPushConstantRange push_range = {}; + if (reflection.push_constant_stage_mask && reflection.push_constant_size) + { + if (reflection.push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK) + push_range.stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; + if (reflection.push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK) + push_range.stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; + + RARCH_LOG("[Vulkan]: Push Constant Block: %u bytes.\n", reflection.push_constant_size); + + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = &push_range; + push.buffer.resize((reflection.push_constant_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)); + } + + push.stages = push_range.stageFlags; + push_range.size = reflection.push_constant_size; + if (vkCreatePipelineLayout(device, &layout_info, NULL, &pipeline_layout) != VK_SUCCESS) return false; @@ -1738,8 +1764,11 @@ bool Pass::build() filtered_parameters.clear(); for (unsigned i = 0; i < reflection.semantic_float_parameters.size(); i++) { - if (reflection.semantic_float_parameters[i].uniform) + if (reflection.semantic_float_parameters[i].uniform || + reflection.semantic_float_parameters[i].push_constant) + { filtered_parameters.push_back(parameters[i]); + } } if (!init_pipeline()) @@ -1804,38 +1833,52 @@ void Pass::set_semantic_texture_array(VkDescriptorSet set, } } -void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic, - unsigned width, unsigned height) +void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic, + unsigned index, unsigned width, unsigned height) { - if (data && reflection.semantic_textures[semantic][0].uniform) + auto &refl = reflection.semantic_textures[semantic]; + if (index >= refl.size()) + return; + + if (data && refl[index].uniform) { build_vec4( - reinterpret_cast(data + reflection.semantic_textures[semantic][0].ubo_offset), + reinterpret_cast(data + refl[index].ubo_offset), + width, + height); + } + + if (refl[index].push_constant) + { + build_vec4( + reinterpret_cast(push.buffer.data() + (refl[index].push_constant_offset >> 2)), width, height); } } -void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic, - unsigned index, unsigned width, unsigned height) +void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic, + unsigned width, unsigned height) { - if (data && index < reflection.semantic_textures[semantic].size() && - reflection.semantic_textures[semantic][index].uniform) - { - build_vec4( - reinterpret_cast(data + reflection.semantic_textures[semantic][index].ubo_offset), - width, - height); - } + build_semantic_texture_array_vec4(data, semantic, 0, width, height); } void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, unsigned width, unsigned height) { - if (data && reflection.semantics[semantic].uniform) + auto &refl = reflection.semantics[semantic]; + if (data && refl.uniform) { build_vec4( - reinterpret_cast(data + reflection.semantics[semantic].ubo_offset), + reinterpret_cast(data + refl.ubo_offset), + width, + height); + } + + if (refl.push_constant) + { + build_vec4( + reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)), width, height); } @@ -1843,16 +1886,26 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value) { + auto &refl = reflection.semantic_float_parameters[index]; + // We will have filtered out stale parameters. - if (data) - *reinterpret_cast(data + reflection.semantic_float_parameters[index].ubo_offset) = value; + if (data && refl.uniform) + *reinterpret_cast(data + refl.ubo_offset) = value; + + if (refl.push_constant) + *reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)) = value; } void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic, uint32_t value) { - if (data && reflection.semantics[semantic].uniform) + auto &refl = reflection.semantics[semantic]; + + if (data && refl.uniform) *reinterpret_cast(data + reflection.semantics[semantic].ubo_offset) = value; + + if (refl.push_constant) + *reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)) = value; } void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer, @@ -1884,6 +1937,15 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer, build_identity_matrix(reinterpret_cast(buffer + offset)); } + if (reflection.semantics[SLANG_SEMANTIC_MVP].push_constant) + { + size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].push_constant_offset; + if (mvp) + memcpy(push.buffer.data() + (offset >> 2), mvp, sizeof(float) * 16); + else + build_identity_matrix(reinterpret_cast(push.buffer.data() + (offset >> 2))); + } + // Output information build_semantic_vec4(buffer, SLANG_SEMANTIC_OUTPUT, current_framebuffer_size.width, current_framebuffer_size.height); @@ -2023,6 +2085,13 @@ void Pass::build_commands( vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &sets[sync_index], 0, nullptr); + if (push.stages != 0) + { + vkCmdPushConstants(cmd, pipeline_layout, + push.stages, 0, reflection.push_constant_size, + push.buffer.data()); + } + VkDeviceSize offset = final_pass ? 16 * sizeof(float) : 0; vkCmdBindVertexBuffers(cmd, 0, 1, &common->vbo->get_buffer(), diff --git a/gfx/drivers_shader/slang_reflection.cpp b/gfx/drivers_shader/slang_reflection.cpp index 45c02581a1..bf8f228a00 100644 --- a/gfx/drivers_shader/slang_reflection.cpp +++ b/gfx/drivers_shader/slang_reflection.cpp @@ -166,77 +166,97 @@ static void resize_minimum(T &vec, unsigned minimum) static bool set_ubo_texture_offset(slang_reflection *reflection, slang_texture_semantic semantic, unsigned index, - size_t offset) + size_t offset, bool push_constant) { resize_minimum(reflection->semantic_textures[semantic], index + 1); auto &sem = reflection->semantic_textures[semantic][index]; + auto &active = push_constant ? sem.push_constant : sem.uniform; + auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset; - if (sem.uniform) + if (active) { - if (sem.ubo_offset != offset) + if (active_offset != offset) { RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s #%u (%u vs. %u).\n", texture_semantic_uniform_names[semantic], index, - unsigned(sem.ubo_offset), + unsigned(active_offset), unsigned(offset)); return false; } } - sem.uniform = true; - sem.ubo_offset = offset; + + active = true; + active_offset = offset; return true; } static bool set_ubo_float_parameter_offset(slang_reflection *reflection, - unsigned index, size_t offset, unsigned num_components) + unsigned index, size_t offset, unsigned num_components, bool push_constant) { resize_minimum(reflection->semantic_float_parameters, index + 1); auto &sem = reflection->semantic_float_parameters[index]; + auto &active = push_constant ? sem.push_constant : sem.uniform; + auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset; - if (sem.uniform) + if (active) { - if (sem.ubo_offset != offset) + if (active_offset != offset) { RARCH_ERR("[slang]: Vertex and fragment have different offsets for same parameter #%u (%u vs. %u).\n", index, - unsigned(sem.ubo_offset), + unsigned(active_offset), unsigned(offset)); return false; } } - sem.uniform = true; - sem.ubo_offset = offset; + + if (sem.num_components != num_components && (sem.uniform || sem.push_constant)) + { + RARCH_ERR("[slang]: Vertex and fragment have different components for same parameter #%u (%u vs. %u).\n", + index, + unsigned(sem.num_components), + unsigned(num_components)); + return false; + } + + active = true; + active_offset = offset; sem.num_components = num_components; return true; } static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic, - size_t offset, unsigned num_components) + size_t offset, unsigned num_components, bool push_constant) { auto &sem = reflection->semantics[semantic]; + auto &active = push_constant ? sem.push_constant : sem.uniform; + auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset; - if (sem.uniform) + if (active) { - if (sem.ubo_offset != offset) + if (active_offset != offset) { RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s (%u vs. %u).\n", semantic_uniform_names[semantic], - unsigned(sem.ubo_offset), + unsigned(active_offset), unsigned(offset)); return false; } - if (sem.num_components != num_components) - { - RARCH_ERR("[slang]: Vertex and fragment have different components for same semantic %s (%u vs. %u).\n", - semantic_uniform_names[semantic], - unsigned(sem.num_components), - unsigned(num_components)); - } } - sem.uniform = true; - sem.ubo_offset = offset; + + if (sem.num_components != num_components && (sem.uniform || sem.push_constant)) + { + RARCH_ERR("[slang]: Vertex and fragment have different components for same semantic %s (%u vs. %u).\n", + semantic_uniform_names[semantic], + unsigned(sem.num_components), + unsigned(num_components)); + return false; + } + + active = true; + active_offset = offset; sem.num_components = num_components; return true; } @@ -276,7 +296,7 @@ static bool validate_type_for_texture_semantic(const SPIRType &type) } static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &resource, - slang_reflection *reflection) + slang_reflection *reflection, bool push_constant) { // Get which uniforms are actually in use by this shader. auto ranges = compiler.get_active_buffer_ranges(resource.id); @@ -309,12 +329,12 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r switch (sem) { case SLANG_SEMANTIC_FLOAT_PARAMETER: - if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize)) + if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize, push_constant)) return false; break; default: - if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize)) + if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize, push_constant)) return false; break; } @@ -327,7 +347,7 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r return false; } - if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index, range.offset)) + if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index, range.offset, push_constant)) return false; } else @@ -350,12 +370,10 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm !vertex.subpass_inputs.empty() || !vertex.storage_images.empty() || !vertex.atomic_counters.empty() || - !vertex.push_constant_buffers.empty() || !fragment.storage_buffers.empty() || !fragment.subpass_inputs.empty() || !fragment.storage_images.empty() || - !fragment.atomic_counters.empty() || - !fragment.push_constant_buffers.empty()) + !fragment.atomic_counters.empty()) { RARCH_ERR("[slang]: Invalid resource type detected.\n"); return false; @@ -403,8 +421,23 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm return false; } + // Validate the single push constant buffer. + if (vertex.push_constant_buffers.size() > 1) + { + RARCH_ERR("[slang]: Vertex must use zero or one push constant buffers.\n"); + return false; + } + + if (fragment.push_constant_buffers.size() > 1) + { + RARCH_ERR("[slang]: Fragment must use zero or one push cosntant buffer.\n"); + return false; + } + uint32_t vertex_ubo = vertex.uniform_buffers.empty() ? 0 : vertex.uniform_buffers[0].id; uint32_t fragment_ubo = fragment.uniform_buffers.empty() ? 0 : fragment.uniform_buffers[0].id; + uint32_t vertex_push = vertex.push_constant_buffers.empty() ? 0 : vertex.push_constant_buffers[0].id; + uint32_t fragment_push = fragment.push_constant_buffers.empty() ? 0 : fragment.push_constant_buffers[0].id; if (vertex_ubo && vertex_compiler.get_decoration(vertex_ubo, spv::DecorationDescriptorSet) != 0) @@ -445,6 +478,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm reflection->ubo_binding = has_ubo ? ubo_binding : 0; reflection->ubo_stage_mask = 0; reflection->ubo_size = 0; + reflection->push_constant_size = 0; + reflection->push_constant_stage_mask = 0; if (vertex_ubo) { @@ -460,10 +495,35 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm fragment_compiler.get_declared_struct_size(fragment_compiler.get_type(fragment.uniform_buffers[0].base_type_id))); } - // Find all relevant uniforms. - if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler, vertex.uniform_buffers[0], reflection)) + if (vertex_push) + { + reflection->push_constant_stage_mask |= SLANG_STAGE_VERTEX_MASK; + reflection->push_constant_size = max(reflection->push_constant_size, + vertex_compiler.get_declared_struct_size(vertex_compiler.get_type(vertex.push_constant_buffers[0].base_type_id))); + } + + if (fragment_push) + { + reflection->push_constant_stage_mask |= SLANG_STAGE_FRAGMENT_MASK; + reflection->push_constant_size = max(reflection->push_constant_size, + fragment_compiler.get_declared_struct_size(fragment_compiler.get_type(fragment.push_constant_buffers[0].base_type_id))); + } + + // Validate push constant size against Vulkan's minimum spec to avoid cross-vendor issues. + if (reflection->push_constant_size > 128) + { + RARCH_ERR("[slang]: Exceeded maximum size of 128 bytes for push constant buffer.\n"); return false; - if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler, fragment.uniform_buffers[0], reflection)) + } + + // Find all relevant uniforms and push constants. + if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler, vertex.uniform_buffers[0], reflection, false)) + return false; + if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler, fragment.uniform_buffers[0], reflection, false)) + return false; + if (vertex_push && !add_active_buffer_ranges(vertex_compiler, vertex.push_constant_buffers[0], reflection, true)) + return false; + if (fragment_push && !add_active_buffer_ranges(fragment_compiler, fragment.push_constant_buffers[0], reflection, true)) return false; uint32_t binding_mask = has_ubo ? (1 << ubo_binding) : 0; @@ -529,6 +589,10 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm RARCH_LOG("[slang]: Uniforms (Vertex: %s, Fragment: %s):\n", reflection->ubo_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no", reflection->ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no"); + RARCH_LOG("[slang]: Push Constants (Vertex: %s, Fragment: %s):\n", + reflection->push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no", + reflection->push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no"); + for (unsigned i = 0; i < SLANG_NUM_SEMANTICS; i++) { if (reflection->semantics[i].uniform) @@ -536,6 +600,12 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm RARCH_LOG("[slang]: %s (Offset: %u)\n", semantic_uniform_names[i], unsigned(reflection->semantics[i].ubo_offset)); } + + if (reflection->semantics[i].push_constant) + { + RARCH_LOG("[slang]: %s (PushOffset: %u)\n", semantic_uniform_names[i], + unsigned(reflection->semantics[i].push_constant_offset)); + } } for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++) @@ -549,6 +619,13 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm index, unsigned(sem.ubo_offset)); } + + if (sem.push_constant) + { + RARCH_LOG("[slang]: %s (#%u) (PushOffset: %u)\n", texture_semantic_uniform_names[i], + index, + unsigned(sem.push_constant_offset)); + } index++; } } @@ -560,6 +637,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm { if (param.uniform) RARCH_LOG("[slang]: #%u (Offset: %u)\n", i, param.ubo_offset); + if (param.push_constant) + RARCH_LOG("[slang]: #%u (PushOffset: %u)\n", i, param.push_constant_offset); i++; } diff --git a/gfx/drivers_shader/slang_reflection.hpp b/gfx/drivers_shader/slang_reflection.hpp index 8fd2df5e58..b50e650e8c 100644 --- a/gfx/drivers_shader/slang_reflection.hpp +++ b/gfx/drivers_shader/slang_reflection.hpp @@ -86,18 +86,22 @@ enum slang_stage struct slang_texture_semantic_meta { size_t ubo_offset = 0; + size_t push_constant_offset = 0; unsigned binding = 0; uint32_t stage_mask = 0; bool texture = false; bool uniform = false; + bool push_constant = false; }; struct slang_semantic_meta { size_t ubo_offset = 0; + size_t push_constant_offset = 0; unsigned num_components = 0; bool uniform = false; + bool push_constant = false; }; struct slang_texture_semantic_map @@ -117,8 +121,11 @@ struct slang_reflection slang_reflection(); size_t ubo_size = 0; + size_t push_constant_size = 0; + unsigned ubo_binding = 0; uint32_t ubo_stage_mask = 0; + uint32_t push_constant_stage_mask = 0; std::vector semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS]; slang_semantic_meta semantics[SLANG_NUM_SEMANTICS];