Merge pull request #47250 from BastiaanOlij/check_vulkan_version

Obtain supported Vulkan API
This commit is contained in:
Rémi Verschelde 2021-03-31 13:45:43 +02:00 committed by GitHub
commit 9cd1e50969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 366 additions and 10 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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