diff --git a/.editorconfig b/.editorconfig
index ec0645241..e273afa92 100755
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,7 +2,7 @@
 end_of_line = lf
 
 [*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
-charset = utf8
+charset = utf-8
 indent_size = 4
 indent_style = tab
 insert_final_newline = true
diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua
index 92b1acf8e..7ed6f85b8 100644
--- a/builtin/mainmenu/settings/dlg_settings.lua
+++ b/builtin/mainmenu/settings/dlg_settings.lua
@@ -49,6 +49,9 @@ end
 
 local change_keys = {
 	query_text = "Change keys",
+	requires = {
+		keyboard_mouse = true,
+	},
 	get_formspec = function(self, avail_w)
 		local btn_w = math.min(avail_w, 3)
 		return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change keys")), 0.8
@@ -173,7 +176,7 @@ end
 
 local function filter_page_content(page, query_keywords)
 	if #query_keywords == 0 then
-		return page.content
+		return page.content, 0
 	end
 
 	local retval = {}
@@ -209,12 +212,6 @@ local function update_filtered_pages(query)
 	filtered_pages = {}
 	filtered_page_by_id = {}
 
-	if query == "" or query == nil then
-		filtered_pages = all_pages
-		filtered_page_by_id = page_by_id
-		return filtered_pages[1].id
-	end
-
 	local query_keywords = {}
 	for word in query:lower():gmatch("%S+") do
 		table.insert(query_keywords, word)
@@ -225,7 +222,7 @@ local function update_filtered_pages(query)
 
 	for _, page in ipairs(all_pages) do
 		local content, page_weight = filter_page_content(page, query_keywords)
-		if #content > 0 then
+		if page_has_contents(content) then
 			local new_page = table.copy(page)
 			new_page.content = content
 
@@ -243,28 +240,113 @@ local function update_filtered_pages(query)
 end
 
 
-local function build_page_components(page)
-	local retval = {}
-	local j = 1
-	for i, content in ipairs(page.content) do
-		if content == false then
-			-- false is used to disable components conditionally (ie: Android specific)
-			j = j - 1
-		elseif type(content) == "string" then
-			local setting = get_setting_info(content)
-			assert(setting, "Unknown setting: " .. content)
+local function check_requirements(name, requires)
+	if requires == nil then
+		return true
+	end
 
+	local video_driver = core.get_active_driver()
+	local shaders_support = video_driver == "opengl" or video_driver == "ogles2"
+	local special = {
+		android = PLATFORM == "Android",
+		desktop = PLATFORM ~= "Android",
+		touchscreen_gui = TOUCHSCREEN_GUI,
+		keyboard_mouse = not TOUCHSCREEN_GUI,
+		shaders_support = shaders_support,
+		shaders = core.settings:get_bool("enable_shaders") and shaders_support,
+		opengl = video_driver == "opengl",
+		gles = video_driver:sub(1, 5) == "ogles",
+	}
+
+	for req_key, req_value in pairs(requires) do
+		if special[req_key] == nil then
+			local required_setting = get_setting_info(req_key)
+			if required_setting == nil then
+				core.log("warning", "Unknown setting " .. req_key .. " required by " .. name)
+			end
+			local actual_value = core.settings:get_bool(req_key,
+				required_setting and core.is_yes(required_setting.default))
+			if actual_value ~= req_value  then
+				return false
+			end
+		elseif special[req_key] ~= req_value then
+			return false
+		end
+	end
+
+	return true
+end
+
+
+function page_has_contents(content)
+	for _, item in ipairs(content) do
+		if item == false or item.heading then --luacheck: ignore
+			-- skip
+		elseif type(item) == "string" then
+			local setting = get_setting_info(item)
+			assert(setting, "Unknown setting: " .. item)
+			if check_requirements(setting.name, setting.requires) then
+				return true
+			end
+		elseif item.get_formspec then
+			if check_requirements(item.id, item.requires) then
+				return true
+			end
+		else
+			error("Unknown content in page: " .. dump(item))
+		end
+	end
+
+	return false
+end
+
+
+local function build_page_components(page)
+	-- Filter settings based on requirements
+	local content = {}
+	local last_heading
+	for _, item in ipairs(page.content) do
+		if item == false then --luacheck: ignore
+			-- skip
+		elseif item.heading then
+			last_heading = item
+		else
+			local name, requires
+			if type(item) == "string" then
+				local setting = get_setting_info(item)
+				assert(setting, "Unknown setting: " .. item)
+				name = setting.name
+				requires = setting.requires
+			elseif item.get_formspec then
+				name = item.id
+				requires = item.requires
+			else
+				error("Unknown content in page: " .. dump(item))
+			end
+
+			if check_requirements(name, requires) then
+				if last_heading then
+					content[#content + 1] = last_heading
+					last_heading = nil
+				end
+				content[#content + 1] = item
+			end
+		end
+	end
+
+	-- Create components
+	local retval = {}
+	for i, item in ipairs(content) do
+		if type(item) == "string" then
+			local setting = get_setting_info(item)
 			local component_func = component_funcs[setting.type]
 			assert(component_func, "Unknown setting type: " .. setting.type)
-			retval[j] = component_func(setting)
-		elseif content.get_formspec then
-			retval[j] = content
-		elseif content.heading then
-			retval[j] = component_funcs.heading(content.heading)
-		else
-			error("Unknown content in page: " .. dump(content))
+			retval[i] = component_func(setting)
+		elseif item.get_formspec then
+			retval[i] = item
+		elseif item.heading then
+			retval[i] = component_funcs.heading(item.heading)
 		end
-		j = j + 1
 	end
 	return retval
 end
@@ -343,7 +425,8 @@ local function get_formspec(dialogdata)
 	local last_section = nil
 	for _, other_page in ipairs(filtered_pages) do
 		if other_page.section ~= last_section then
-			fs[#fs + 1] = ("label[0.1,%f;%s]"):format(y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
+			fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
+				y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
 			last_section = other_page.section
 			y = y + 0.82
 		end
@@ -487,10 +570,15 @@ local function buttonhandler(this, fields)
 
 	for i, comp in ipairs(dialogdata.components) do
 		if comp.on_submit and comp:on_submit(fields, this) then
+			-- Clear components so they regenerate
+			dialogdata.components = nil
 			return true
 		end
 		if comp.setting and fields["reset_" .. i] then
 			core.settings:remove(comp.setting.name)
+
+			-- Clear components so they regenerate
+			dialogdata.components = nil
 			return true
 		end
 	end
diff --git a/builtin/mainmenu/settings/settingtypes.lua b/builtin/mainmenu/settings/settingtypes.lua
index 06389b085..d7ff0698e 100644
--- a/builtin/mainmenu/settings/settingtypes.lua
+++ b/builtin/mainmenu/settings/settingtypes.lua
@@ -51,20 +51,16 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 	line = line:gsub("\r", "")
 
 	-- comment
-	local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
-	if comment then
-		if settings.current_comment == "" then
-			settings.current_comment = comment
-		else
-			settings.current_comment = settings.current_comment .. "\n" .. comment
-		end
+	local comment_match = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
+	if comment_match then
+		settings.current_comment[#settings.current_comment + 1] = comment_match
 		return
 	end
 
 	-- clear current_comment so only comments directly above a setting are bound to it
 	-- but keep a local reference to it for variables in the current line
 	local current_comment = settings.current_comment
-	settings.current_comment = ""
+	settings.current_comment = {}
 
 	-- empty lines
 	if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
@@ -103,11 +99,32 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 		return "Tried to add \"secure.\" setting"
 	end
 
+	local requires = {}
+	local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
+	if last_line and last_line:lower():sub(1, 9) == "requires:" then
+		local parts = last_line:sub(10):split(",")
+		current_comment[#current_comment] = nil
+
+		for _, part in ipairs(parts) do
+			part = part:trim()
+
+			local value = true
+			if part:sub(1, 1) == "!" then
+				value = false
+				part = part:sub(2):trim()
+			end
+
+			requires[part] = value
+		end
+	end
+
 	if readable_name == "" then
 		readable_name = nil
 	end
 	local remaining_line = line:sub(first_part:len() + 1)
 
+	local comment = table.concat(current_comment, "\n"):trim()
+
 	if setting_type == "int" then
 		local default, min, max = remaining_line:match("^"
 				-- first int is required, the last 2 are optional
@@ -129,7 +146,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			default = default,
 			min = min,
 			max = max,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -151,7 +169,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			readable_name = readable_name,
 			type = setting_type,
 			default = default,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -203,7 +222,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 				flags = values[10]
 			},
 			values = values,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 			noise_params = true,
 			flags = flags_to_table("defaults,eased,absvalue")
 		})
@@ -220,7 +240,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			readable_name = readable_name,
 			type = "bool",
 			default = remaining_line,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -246,7 +267,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			default = default,
 			min = min,
 			max = max,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -268,7 +290,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			type = "enum",
 			default = default,
 			values = values:split(",", true),
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -285,7 +308,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			readable_name = readable_name,
 			type = setting_type,
 			default = default,
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -314,7 +338,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
 			type = "flags",
 			default = default,
 			possible = flags_to_table(possible),
-			comment = current_comment,
+			requires = requires,
+			comment = comment,
 		})
 		return
 	end
@@ -324,7 +349,7 @@ end
 
 local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
 	-- store this helper variable in the table so it's easier to pass to parse_setting_line()
-	result.current_comment = ""
+	result.current_comment = {}
 
 	local line = file:read("*line")
 	while line do
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 746e7ce2f..172616194 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -57,6 +57,25 @@
 # Comments and (Readable name) are handled by gettext.
 # Comments should be complete sentences that describe the setting and possibly
 #  give the user additional useful insight.
+# The last line of a comment can contain the requirements for the setting, ie:
+#
+#    # This is a comment
+#    #
+#    # Requires: shaders, enable_dynamic_shadows, !touchscreen_gui
+#    name (Readable name) type type_args
+#
+# A requirement can be the name of a boolean setting or an engine-defined value.
+# These requirements may be:
+#
+# * The value of a boolean setting, such as enable_dynamic_shadows
+# * An engine-defined value:
+#     * shaders_support (a video driver that supports shaders, may not be enabled)
+#     * shaders (both enable_shaders and shaders_support)
+#     * desktop / android
+#     * touchscreen_gui / keyboard_mouse
+#     * opengl / gles
+# * You can negate any requirement by prepending with !
+#
 # Sections are marked by a single line in the format: [Section Name]
 # Sub-section are marked by adding * in front of the section name: [*Sub-section]
 # Sub-sub-sections have two * etc.
@@ -103,32 +122,48 @@ safe_dig_and_place (Safe digging and placing) bool false
 [*Keyboard and Mouse]
 
 #    Invert vertical mouse movement.
+#
+#    Requires: keyboard_mouse
 invert_mouse (Invert mouse) bool false
 
 #    Mouse sensitivity multiplier.
+#
+#    Requires: keyboard_mouse
 mouse_sensitivity (Mouse sensitivity) float 0.2 0.001 10.0
 
 #    Enable mouse wheel (scroll) for item selection in hotbar.
+#
+#    Requires: keyboard_mouse
 enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true
 
 #    Invert mouse wheel (scroll) direction for item selection in hotbar.
+#
+#    Requires: keyboard_mouse
 invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
 
 [*Touchscreen]
 
 #    The length in pixels it takes for touch screen interaction to start.
+#
+#    Requires: touchscreen_gui
 touchscreen_threshold (Touch screen threshold) int 20 0 100
 
 #    Use crosshair to select object instead of whole screen.
 #    If enabled, a crosshair will be shown and will be used for selecting object.
+#
+#    Requires: touchscreen_gui
 touch_use_crosshair (Use crosshair for touch screen) bool false
 
 #    (Android) Fixes the position of virtual joystick.
 #    If disabled, virtual joystick will center to first-touch's position.
+#
+#    Requires: touchscreen_gui
 fixed_virtual_joystick (Fixed virtual joystick) bool false
 
 #    (Android) Use virtual joystick to trigger "Aux1" button.
 #    If enabled, virtual joystick will also tap "Aux1" button when out of main circle.
+#
+#    Requires: touchscreen_gui
 virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
 
 
@@ -139,25 +174,37 @@ virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool fals
 [**Screen]
 
 #    Width component of the initial window size.
+#
+#    Requires: desktop
 screen_w (Screen width) int 1024 1 65535
 
 #    Height component of the initial window size.
+#
+#    Requires: desktop
 screen_h (Screen height) int 600 1 65535
 
 #    Whether the window is maximized.
+#
+#    Requires: desktop
 window_maximized (Window maximized) bool false
 
 #    Save window size automatically when modified.
 #    If true, screen size is saved in screen_w and screen_h, and whether the window
 #    is maximized is stored in window_maximized.
 #    (Autosaving window_maximized only works if compiled with SDL.)
+#
+#    Requires: desktop
 autosave_screensize (Remember screen size) bool true
 
 #    Fullscreen mode.
+#
+#    Requires: desktop
 fullscreen (Full screen) bool false
 
 #    Open the pause menu when the window's focus is lost. Does not pause if a formspec is
 #    open.
+#
+#    Requires: desktop
 pause_on_lost_focus (Pause on lost window focus) bool false
 
 [**FPS]
@@ -258,14 +305,20 @@ ambient_occlusion_gamma (Ambient occlusion gamma) float 1.8 0.25 4.0
 
 #    Path to save screenshots at. Can be an absolute or relative path.
 #    The folder will be created if it doesn't already exist.
+#
+#    Requires: desktop
 screenshot_path (Screenshot folder) path screenshots
 
 #    Format of screenshots.
+#
+#    Requires: desktop
 screenshot_format (Screenshot format) enum png png,jpg
 
 #    Screenshot quality. Only used for JPEG format.
 #    1 means worst quality; 100 means best quality.
 #    Use 0 for default quality.
+#
+#    Requires: desktop
 screenshot_quality (Screenshot quality) int 0 0 100
 
 [**Node and Entity Highlighting]
@@ -297,9 +350,13 @@ crosshair_alpha (Crosshair alpha) int 255 0 255
 enable_fog (Fog) bool true
 
 #    Make fog and sky colors depend on daytime (dawn/sunset) and view direction.
+#
+#    Requires: enable_fog
 directional_colored_fog (Colored fog) bool true
 
 #    Fraction of the visible distance at which fog starts to be rendered
+#
+#    Requires: enable_fog
 fog_start (Fog start) float 0.4 0.0 0.99
 
 [**Clouds]
@@ -308,6 +365,8 @@ fog_start (Fog start) float 0.4 0.0 0.99
 enable_clouds (Clouds) bool true
 
 #    Use 3D cloud look instead of flat.
+#
+#    Requires: enable_clouds
 enable_3d_clouds (3D clouds) bool true
 
 [**Filtering and Antialiasing]
@@ -386,89 +445,118 @@ enable_raytraced_culling (Enable Raytraced Culling) bool true
 #    Shaders allow advanced visual effects and may increase performance on some video
 #    cards.
 #    This only works with the OpenGL video backend.
+#
+#    Requires: shaders_support
 enable_shaders (Shaders) bool true
 
 [**Waving Nodes]
 
 #    Set to true to enable waving leaves.
-#    Requires shaders to be enabled.
+#
+#    Requires: shaders
 enable_waving_leaves (Waving leaves) bool false
 
 #    Set to true to enable waving plants.
-#    Requires shaders to be enabled.
+#
+#    Requires: shaders
 enable_waving_plants (Waving plants) bool false
 
 #    Set to true to enable waving liquids (like water).
-#    Requires shaders to be enabled.
+#
+#    Requires: shaders
 enable_waving_water (Waving liquids) bool false
 
 #    The maximum height of the surface of waving liquids.
 #    4.0 = Wave height is two nodes.
 #    0.0 = Wave doesn't move at all.
 #    Default is 1.0 (1/2 node).
-#    Requires waving liquids to be enabled.
+#
+#    Requires: shaders, enable_waving_water
 water_wave_height (Waving liquids wave height) float 1.0 0.0 4.0
 
 #    Length of liquid waves.
-#    Requires waving liquids to be enabled.
+#
+#    Requires: shaders, enable_waving_water
 water_wave_length (Waving liquids wavelength) float 20.0 0.1
 
 #    How fast liquid waves will move. Higher = faster.
 #    If negative, liquid waves will move backwards.
-#    Requires waving liquids to be enabled.
+#
+#    Requires: shaders, enable_waving_water
 water_wave_speed (Waving liquids wave speed) float 5.0
 
 [**Dynamic shadows]
 
 #    Set to true to enable Shadow Mapping.
-#    Requires shaders to be enabled.
+#
+#    Requires: shaders, opengl
 enable_dynamic_shadows (Dynamic shadows) bool false
 
 #    Set the shadow strength gamma.
 #    Adjusts the intensity of in-game dynamic shadows.
 #    Lower value means lighter shadows, higher value means darker shadows.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_strength_gamma (Shadow strength gamma) float 1.0 0.1 10.0
 
 #    Maximum distance to render shadows.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 140.0 10.0 1000.0
 
 #    Texture size to render the shadow map on.
 #    This must be a power of two.
 #    Bigger numbers create better shadows but it is also more expensive.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_map_texture_size (Shadow map texture size) int 2048 128 8192
 
 #    Sets shadow texture quality to 32 bits.
 #    On false, 16 bits texture will be used.
 #    This can cause much more artifacts in the shadow.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
 
 #    Enable Poisson disk filtering.
 #    On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_poisson_filter (Poisson filtering) bool true
 
-#   Define shadow filtering quality.
-#   This simulates the soft shadows effect by applying a PCF or Poisson disk
-#   but also uses more resources.
+#    Define shadow filtering quality.
+#    This simulates the soft shadows effect by applying a PCF or Poisson disk
+#    but also uses more resources.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_filters (Shadow filter quality) enum 1 0,1,2
 
 #    Enable colored shadows.
 #    On true translucent nodes cast colored shadows. This is expensive.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_map_color (Colored shadows) bool false
 
 #    Spread a complete update of shadow map over given amount of frames.
 #    Higher values might make shadows laggy, lower values
 #    will consume more resources.
 #    Minimum value: 1; maximum value: 16
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_update_frames (Map shadows update frames) int 8 1 16
 
 #    Set the soft shadow radius size.
 #    Lower values mean sharper shadows, bigger values mean softer shadows.
 #    Minimum value: 1.0; maximum value: 15.0
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
 
 #    Set the default tilt of Sun/Moon orbit in degrees.
 #    Games may change orbit tilt via API.
 #    Value of 0 means no tilt / vertical orbit.
+#
+#    Requires: shaders, enable_dynamic_shadows, opengl
 shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
 
 [**Post Processing]
@@ -477,43 +565,59 @@ shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
 #    Simulates the tone curve of photographic film and how this approximates the
 #    appearance of high dynamic range images. Mid-range contrast is slightly
 #    enhanced, highlights and shadows are gradually compressed.
+#
+#    Requires: shaders
 tone_mapping (Filmic tone mapping) bool false
 
 #    Enable automatic exposure correction
 #    When enabled, the post-processing engine will
 #    automatically adjust to the brightness of the scene,
 #    simulating the behavior of human eye.
+#
+#    Requires: shaders
 enable_auto_exposure (Enable Automatic Exposure) bool false
 
 #    Set the exposure compensation in EV units.
 #    Value of 0.0 (default) means no exposure compensation.
 #    Range: from -1 to 1.0
+#
+#    Requires: shaders, enable_auto_exposure
 exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
 
 [**Bloom]
 
 #    Set to true to enable bloom effect.
 #    Bright colors will bleed over the neighboring objects.
+#
+#    Requires: shaders
 enable_bloom (Enable Bloom) bool false
 
 #    Set to true to render debugging breakdown of the bloom effect.
 #    In debug mode, the screen is split into 4 quadrants:
 #    top-left - processed base image, top-right - final image
 #    bottom-left - raw base image, bottom-right - bloom texture.
+#
+#    Requires: shaders, enable_bloom
 enable_bloom_debug (Enable Bloom Debug) bool false
 
 #    Defines how much bloom is applied to the rendered image
 #    Smaller values make bloom more subtle
 #    Range: from 0.01 to 1.0, default: 0.05
+#
+#    Requires: shaders, enable_bloom
 bloom_intensity (Bloom Intensity) float 0.05 0.01 1.0
 
 #    Defines the magnitude of bloom overexposure.
 #    Range: from 0.1 to 10.0, default: 1.0
+#
+#    Requires: shaders, enable_bloom
 bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
 
 #    Logical value that controls how far the bloom effect spreads
 #    from the bright objects.
 #    Range: from 0.1 to 8, default: 1
+#
+#    Requires: shaders, enable_bloom
 bloom_radius (Bloom Radius) float 1 0.1 8
 
 
@@ -1665,6 +1769,8 @@ ignore_world_load_errors (Ignore world errors) bool false
 [**Graphics]
 
 #    Path to shader directory. If no path is defined, default location will be used.
+#
+#    Requires: shaders
 shader_path (Shader path) path
 
 #    The rendering back-end.
@@ -1699,7 +1805,6 @@ mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50
 #    Value of 0 (default) will let Minetest autodetect the number of available threads.
 mesh_generation_threads (Mapblock mesh generation threads) int 0 0 8
 
-
 #    Size of the MapBlock cache of the mesh generator. Increasing this will
 #    increase the cache hit %, reducing the data being copied from the main
 #    thread, thus reducing jitter.