Ver código fonte

Merge pull request #47250 from BastiaanOlij/check_vulkan_version

Obtain supported Vulkan API
Rémi Verschelde 4 anos atrás
pai
commit
9cd1e50969

+ 13 - 0
drivers/vulkan/rendering_device_vulkan.cpp

@@ -7834,6 +7834,18 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) {
 }
 
 void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) {
+	// get our device capabilities
+	{
+		device_capabilities.version_major = p_context->get_vulkan_major();
+		device_capabilities.version_minor = p_context->get_vulkan_minor();
+
+		// get info about subgroups
+		VulkanContext::SubgroupCapabilities subgroup_capabilities = p_context->get_subgroup_capabilities();
+		device_capabilities.subgroup_size = subgroup_capabilities.size;
+		device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd();
+		device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd();
+	}
+
 	context = p_context;
 	device = p_context->get_device();
 	if (p_local_device) {
@@ -8253,6 +8265,7 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() {
 }
 
 RenderingDeviceVulkan::RenderingDeviceVulkan() {
+	device_capabilities.device_family = DEVICE_VULKAN;
 }
 
 RenderingDeviceVulkan::~RenderingDeviceVulkan() {

+ 231 - 1
drivers/vulkan/vulkan_context.cpp

@@ -34,6 +34,7 @@
 #include "core/config/project_settings.h"
 #include "core/string/ustring.h"
 #include "core/version.h"
+#include "servers/rendering/rendering_device.h"
 
 #include "vk_enum_string_helper.h"
 
@@ -263,6 +264,39 @@ Error VulkanContext::_create_validation_layers() {
 	return OK;
 }
 
+typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *);
+
+Error VulkanContext::_obtain_vulkan_version() {
+	// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description
+	// for Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android.
+	_vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion");
+	if (func != nullptr) {
+		uint32_t api_version;
+		VkResult res = func(&api_version);
+		if (res == VK_SUCCESS) {
+			vulkan_major = VK_VERSION_MAJOR(api_version);
+			vulkan_minor = VK_VERSION_MINOR(api_version);
+			uint32_t vulkan_patch = VK_VERSION_PATCH(api_version);
+
+			print_line("Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch));
+		} else {
+			// according to the documentation this shouldn't fail with anything except a memory allocation error
+			// in which case we're in deep trouble anyway
+			ERR_FAIL_V(ERR_CANT_CREATE);
+		}
+	} else {
+		print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0");
+	}
+
+	// we don't go above 1.2
+	if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) {
+		vulkan_major = 1;
+		vulkan_minor = 2;
+	}
+
+	return OK;
+}
+
 Error VulkanContext::_initialize_extensions() {
 	uint32_t instance_extension_count = 0;
 
@@ -320,12 +354,200 @@ Error VulkanContext::_initialize_extensions() {
 	return OK;
 }
 
+typedef void(VKAPI_PTR *_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice, VkPhysicalDeviceProperties2 *);
+
+uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const {
+	uint32_t flags = 0;
+
+	if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) {
+		flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT;
+	}
+	if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
+		flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT;
+	}
+	if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
+		flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT;
+	}
+	// if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) {
+	// 	flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT;
+	// }
+	if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) {
+		flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT;
+	}
+	if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) {
+		flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT;
+	}
+
+	return flags;
+}
+
+String VulkanContext::SubgroupCapabilities::supported_stages_desc() const {
+	String res;
+
+	if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) {
+		res += ", STAGE_VERTEX";
+	}
+	if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
+		res += ", STAGE_TESSELLATION_CONTROL";
+	}
+	if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
+		res += ", STAGE_TESSELLATION_EVALUATION";
+	}
+	if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) {
+		res += ", STAGE_GEOMETRY";
+	}
+	if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) {
+		res += ", STAGE_FRAGMENT";
+	}
+	if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) {
+		res += ", STAGE_COMPUTE";
+	}
+
+	/* these are not defined on Android GRMBL */
+	if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) {
+		res += ", STAGE_RAYGEN_KHR";
+	}
+	if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) {
+		res += ", STAGE_ANY_HIT_KHR";
+	}
+	if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) {
+		res += ", STAGE_CLOSEST_HIT_KHR";
+	}
+	if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) {
+		res += ", STAGE_MISS_KHR";
+	}
+	if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) {
+		res += ", STAGE_INTERSECTION_KHR";
+	}
+	if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) {
+		res += ", STAGE_CALLABLE_KHR";
+	}
+	if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) {
+		res += ", STAGE_TASK_NV";
+	}
+	if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) {
+		res += ", STAGE_MESH_NV";
+	}
+
+	return res.substr(2); // remove first ", "
+}
+
+uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const {
+	uint32_t flags = 0;
+
+	if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT;
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) {
+		flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT;
+	}
+
+	return flags;
+}
+
+String VulkanContext::SubgroupCapabilities::supported_operations_desc() const {
+	String res;
+
+	if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) {
+		res += ", FEATURE_BASIC";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) {
+		res += ", FEATURE_VOTE";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) {
+		res += ", FEATURE_ARITHMETIC";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) {
+		res += ", FEATURE_BALLOT";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) {
+		res += ", FEATURE_SHUFFLE";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) {
+		res += ", FEATURE_SHUFFLE_RELATIVE";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) {
+		res += ", FEATURE_CLUSTERED";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) {
+		res += ", FEATURE_QUAD";
+	}
+	if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) {
+		res += ", FEATURE_PARTITIONED_NV";
+	}
+
+	return res.substr(2); // remove first ", "
+}
+
+Error VulkanContext::_check_capabilities() {
+	// check subgroups
+	// https://www.khronos.org/blog/vulkan-subgroup-tutorial
+	// for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android.
+	_vkGetPhysicalDeviceProperties2 func = (_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2");
+	if (func != nullptr) {
+		VkPhysicalDeviceSubgroupProperties subgroupProperties;
+		subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+		subgroupProperties.pNext = NULL;
+
+		VkPhysicalDeviceProperties2 physicalDeviceProperties;
+		physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+		physicalDeviceProperties.pNext = &subgroupProperties;
+
+		func(gpu, &physicalDeviceProperties);
+
+		subgroup_capabilities.size = subgroupProperties.subgroupSize;
+		subgroup_capabilities.supportedStages = subgroupProperties.supportedStages;
+		subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations;
+		// Note: quadOperationsInAllStages will be true if:
+		// - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT
+		// - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT
+		subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages;
+
+		// only output this when debugging?
+		print_line("- Vulkan subgroup size " + itos(subgroup_capabilities.size));
+		print_line("- Vulkan subgroup stages " + subgroup_capabilities.supported_stages_desc());
+		print_line("- Vulkan subgroup supported ops " + subgroup_capabilities.supported_operations_desc());
+		if (subgroup_capabilities.quadOperationsInAllStages) {
+			print_line("- Vulkan subgroup quad operations in all stages");
+		}
+	} else {
+		subgroup_capabilities.size = 0;
+		subgroup_capabilities.supportedStages = 0;
+		subgroup_capabilities.supportedOperations = 0;
+		subgroup_capabilities.quadOperationsInAllStages = false;
+	}
+
+	return OK;
+}
+
 Error VulkanContext::_create_physical_device() {
+	/* obtain version */
+	_obtain_vulkan_version();
+
 	/* Look for validation layers */
 	if (use_validation_layers) {
 		_create_validation_layers();
 	}
 
+	/* initialise extensions */
 	{
 		Error err = _initialize_extensions();
 		if (err != OK) {
@@ -343,7 +565,7 @@ Error VulkanContext::_create_physical_device() {
 		/*applicationVersion*/ 0,
 		/*pEngineName*/ namecs.get_data(),
 		/*engineVersion*/ 0,
-		/*apiVersion*/ VK_API_VERSION_1_0,
+		/*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0)
 	};
 	VkInstanceCreateInfo inst_info = {
 		/*sType*/ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
@@ -630,6 +852,14 @@ Error VulkanContext::_create_physical_device() {
 	GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR);
 	GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR);
 
+	// get info about what our vulkan driver is capable off
+	{
+		Error res = _check_capabilities();
+		if (res != OK) {
+			return res;
+		}
+	}
+
 	return OK;
 }
 

+ 25 - 0
drivers/vulkan/vulkan_context.h

@@ -41,6 +41,20 @@
 #include <vulkan/vulkan.h>
 
 class VulkanContext {
+public:
+	struct SubgroupCapabilities {
+		uint32_t size;
+		VkShaderStageFlags supportedStages;
+		VkSubgroupFeatureFlags supportedOperations;
+		VkBool32 quadOperationsInAllStages;
+
+		uint32_t supported_stages_flags_rd() const;
+		String supported_stages_desc() const;
+		uint32_t supported_operations_flags_rd() const;
+		String supported_operations_desc() const;
+	};
+
+private:
 	enum {
 		MAX_EXTENSIONS = 128,
 		MAX_LAYERS = 64,
@@ -57,6 +71,11 @@ class VulkanContext {
 	bool device_initialized = false;
 	bool inst_initialized = false;
 
+	// Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise
+	uint32_t vulkan_major = 1;
+	uint32_t vulkan_minor = 0;
+	SubgroupCapabilities subgroup_capabilities;
+
 	String device_vendor;
 	String device_name;
 	String pipeline_cache_id;
@@ -160,8 +179,10 @@ class VulkanContext {
 	VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE;
 	VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE;
 
+	Error _obtain_vulkan_version();
 	Error _create_validation_layers();
 	Error _initialize_extensions();
+	Error _check_capabilities();
 
 	VkBool32 _check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers);
 	static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(
@@ -206,6 +227,10 @@ protected:
 	}
 
 public:
+	uint32_t get_vulkan_major() const { return vulkan_major; };
+	uint32_t get_vulkan_minor() const { return vulkan_minor; };
+	SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; };
+
 	VkDevice get_device();
 	VkPhysicalDevice get_physical_device();
 	int get_swapchain_image_count() const;

+ 59 - 4
modules/glslang/register_types.cpp

@@ -37,7 +37,7 @@
 #include <glslang/Include/Types.h>
 #include <glslang/Public/ShaderLang.h>
 
-static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error) {
+static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) {
 	Vector<uint8_t> ret;
 
 	ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret);
@@ -51,20 +51,75 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
 	};
 
 	int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100
+	bool check_subgroup_support = true; // assume we support subgroups
 
-	glslang::EShTargetClientVersion VulkanClientVersion = glslang::EShTargetVulkan_1_0;
-	glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_3;
+	glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2;
+	glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
 	glslang::TShader::ForbidIncluder includer;
 
+	if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
+		if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) {
+			ClientVersion = glslang::EShTargetVulkan_1_0;
+			TargetVersion = glslang::EShTargetSpv_1_0;
+			check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0
+		} else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) {
+			ClientVersion = glslang::EShTargetVulkan_1_1;
+			TargetVersion = glslang::EShTargetSpv_1_3;
+		} else {
+			// use defaults
+		}
+	} else {
+		// once we support other backends we'll need to do something here
+		if (r_error) {
+			(*r_error) = "GLSLANG - Unsupported device family";
+		}
+		return ret;
+	}
+
 	glslang::TShader shader(stages[p_stage]);
 	CharString cs = p_source_code.ascii();
 	const char *cs_strings = cs.get_data();
+	std::string preamble = "";
 
 	shader.setStrings(&cs_strings, 1);
 	shader.setEnvInput(glslang::EShSourceGlsl, stages[p_stage], glslang::EShClientVulkan, ClientInputSemanticsVersion);
-	shader.setEnvClient(glslang::EShClientVulkan, VulkanClientVersion);
+	shader.setEnvClient(glslang::EShClientVulkan, ClientVersion);
 	shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion);
 
+	if (check_subgroup_support) {
+		uint32_t stage_bit = 1 << p_stage;
+
+		if ((p_capabilities->subgroup_in_shaders & stage_bit) == stage_bit) {
+			// stage supports subgroups
+			preamble += "#define has_GL_KHR_shader_subgroup_basic 1\n";
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_vote 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_arithmetic 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_ballot 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_shuffle 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_shuffle_relative 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_clustered 1\n";
+			}
+			if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
+				preamble += "#define has_GL_KHR_shader_subgroup_quad 1\n";
+			}
+		}
+	}
+
+	if (preamble != "") {
+		shader.setPreamble(preamble.c_str());
+	}
+
 	EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
 	const int DefaultVersion = 100;
 	std::string pre_processed_code;

+ 1 - 1
servers/rendering/renderer_rd/shaders/cluster_render.glsl

@@ -65,7 +65,7 @@ void main() {
 
 VERSION_DEFINES
 
-#if defined(GL_KHR_shader_subgroup_ballot) && defined(GL_KHR_shader_subgroup_arithmetic) && defined(GL_KHR_shader_subgroup_vote)
+#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) && defined(has_GL_KHR_shader_subgroup_vote)
 
 #extension GL_KHR_shader_subgroup_ballot : enable
 #extension GL_KHR_shader_subgroup_arithmetic : enable

+ 1 - 1
servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl

@@ -3,7 +3,7 @@
 
 #define MAX_GI_PROBES 8
 
-#if defined(GL_KHR_shader_subgroup_ballot) && defined(GL_KHR_shader_subgroup_arithmetic)
+#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
 
 #extension GL_KHR_shader_subgroup_ballot : enable
 #extension GL_KHR_shader_subgroup_arithmetic : enable

+ 1 - 1
servers/rendering/renderer_rd/shaders/volumetric_fog.glsl

@@ -5,10 +5,10 @@
 VERSION_DEFINES
 
 /* Do not use subgroups here, seems there is not much advantage and causes glitches
+#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
 #extension GL_KHR_shader_subgroup_ballot: enable
 #extension GL_KHR_shader_subgroup_arithmetic: enable
 
-#if defined(GL_KHR_shader_subgroup_ballot) && defined(GL_KHR_shader_subgroup_arithmetic)
 #define USE_SUBGROUPS
 #endif
 */

+ 1 - 1
servers/rendering/rendering_device.cpp

@@ -59,7 +59,7 @@ Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage,
 
 	ERR_FAIL_COND_V(!compile_function, Vector<uint8_t>());
 
-	return compile_function(p_stage, p_source_code, p_language, r_error);
+	return compile_function(p_stage, p_source_code, p_language, r_error, &device_capabilities);
 }
 
 RID RenderingDevice::_texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data) {

+ 34 - 1
servers/rendering/rendering_device.h

@@ -51,6 +51,13 @@ class RDPipelineColorBlendState;
 class RenderingDevice : public Object {
 	GDCLASS(RenderingDevice, Object)
 public:
+	enum DeviceFamily {
+		DEVICE_UNKNOWN,
+		DEVICE_OPENGL,
+		DEVICE_VULKAN,
+		DEVICE_DIRECTX
+	};
+
 	enum ShaderStage {
 		SHADER_STAGE_VERTEX,
 		SHADER_STAGE_FRAGMENT,
@@ -70,7 +77,29 @@ public:
 		SHADER_LANGUAGE_HLSL
 	};
 
-	typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error);
+	enum SubgroupOperations {
+		SUBGROUP_BASIC_BIT = 1,
+		SUBGROUP_VOTE_BIT = 2,
+		SUBGROUP_ARITHMETIC_BIT = 4,
+		SUBGROUP_BALLOT_BIT = 8,
+		SUBGROUP_SHUFFLE_BIT = 16,
+		SUBGROUP_SHUFFLE_RELATIVE_BIT = 32,
+		SUBGROUP_CLUSTERED_BIT = 64,
+		SUBGROUP_QUAD_BIT = 128,
+	};
+
+	struct Capabilities {
+		// main device info
+		DeviceFamily device_family = DEVICE_UNKNOWN;
+		uint32_t version_major = 1.0;
+		uint32_t version_minor = 0.0;
+		// subgroup capabilities
+		uint32_t subgroup_size = 0;
+		uint32_t subgroup_in_shaders = 0; // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc.
+		uint32_t subgroup_operations = 0; // Set flags, using SubgroupOperations
+	};
+
+	typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const Capabilities *p_capabilities);
 	typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language);
 
 private:
@@ -82,6 +111,8 @@ private:
 protected:
 	static void _bind_methods();
 
+	Capabilities device_capabilities;
+
 public:
 	//base numeric ID for all types
 	enum {
@@ -597,6 +628,8 @@ public:
 	/**** SHADER ****/
 	/****************/
 
+	const Capabilities *get_device_capabilities() const { return &device_capabilities; };
+
 	virtual Vector<uint8_t> shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true);
 
 	static void shader_set_compile_function(ShaderCompileFunction p_function);