diff --git a/CMakeLists.txt b/CMakeLists.txt
index 43da82e07..fb2b170d9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -381,6 +381,9 @@ endif()
 if(NOT DEFINED XRT_FEATURE_OPENXR_VISIBILITY_MASK)
 	set(XRT_FEATURE_OPENXR_VISIBILITY_MASK ON)
 endif()
+if(NOT DEFINED XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS)
+	set(XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS OFF)
+endif()
 option(
 	XRT_FEATURE_OPENXR_VULKAN_SWAPCHAIN_FORMAT_LIST
 	"Enable support for the XR_KHR_vulkan_swapchain_format_list extension" ON
@@ -656,6 +659,7 @@ message(STATUS "#    FEATURE_OPENXR_LAYER_FB_ALPHA_BLEND:          ${XRT_FEATURE
 message(STATUS "#    FEATURE_OPENXR_LAYER_FB_IMAGE_LAYOUT          ${XRT_FEATURE_OPENXR_LAYER_FB_IMAGE_LAYOUT}")
 message(STATUS "#    FEATURE_OPENXR_LAYER_FB_SETTINGS:             ${XRT_FEATURE_OPENXR_LAYER_FB_SETTINGS}")
 message(STATUS "#    FEATURE_OPENXR_OVERLAY:                       ${XRT_FEATURE_OPENXR_OVERLAY}")
+message(STATUS "#    FEATURE_OPENXR_PERFORMANCE_SETTINGS:          ${XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS}")
 message(STATUS "#    FEATURE_OPENXR_SPACE_LOCAL_FLOOR:             ${XRT_FEATURE_OPENXR_SPACE_LOCAL_FLOOR}")
 message(STATUS "#    FEATURE_OPENXR_SPACE_UNBOUNDED:               ${XRT_FEATURE_OPENXR_SPACE_UNBOUNDED}")
 message(STATUS "#    FEATURE_OPENXR_VISIBILITY_MASK                ${XRT_FEATURE_OPENXR_VISIBILITY_MASK}")
diff --git a/scripts/generate_oxr_ext_support.py b/scripts/generate_oxr_ext_support.py
index 712548771..eff136fff 100755
--- a/scripts/generate_oxr_ext_support.py
+++ b/scripts/generate_oxr_ext_support.py
@@ -63,6 +63,7 @@ EXTENSIONS = (
     ['XR_EXT_hp_mixed_reality_controller', 'XRT_FEATURE_OPENXR_INTERACTION_WINMR'],
     ['XR_EXT_local_floor', 'XRT_FEATURE_OPENXR_SPACE_LOCAL_FLOOR'],
     ['XR_EXT_palm_pose', 'XRT_FEATURE_OPENXR_INTERACTION_EXT_PALM_POSE'],
+    ['XR_EXT_performance_settings', 'XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS'],
     ['XR_EXT_samsung_odyssey_controller', 'XRT_FEATURE_OPENXR_INTERACTION_WINMR'],
     ['XR_FB_composition_layer_alpha_blend', 'XRT_FEATURE_OPENXR_LAYER_FB_ALPHA_BLEND'],
     ['XR_FB_composition_layer_image_layout', 'XRT_FEATURE_OPENXR_LAYER_FB_IMAGE_LAYOUT'],
diff --git a/src/xrt/state_trackers/oxr/oxr_api_session.c b/src/xrt/state_trackers/oxr/oxr_api_session.c
index 0b02f919b..0242b77e2 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_session.c
+++ b/src/xrt/state_trackers/oxr/oxr_api_session.c
@@ -307,14 +307,14 @@ oxr_xrGetVisibilityMaskKHR(XrSession session,
 }
 #endif // OXR_HAVE_KHR_visibility_mask
 
+
 /*
  *
  * XR_EXT_performance_settings
  *
  */
 
-#ifdef XR_EXT_performance_settings
-
+#ifdef OXR_HAVE_EXT_performance_settings
 XRAPI_ATTR XrResult XRAPI_CALL
 oxr_xrPerfSettingsSetPerformanceLevelEXT(XrSession session,
                                          XrPerfSettingsDomainEXT domain,
@@ -326,11 +326,24 @@ oxr_xrPerfSettingsSetPerformanceLevelEXT(XrSession session,
 	struct oxr_logger log;
 	OXR_VERIFY_SESSION_AND_INIT_LOG(&log, session, sess, "xrPerfSettingsSetPerformanceLevelEXT");
 	OXR_VERIFY_SESSION_NOT_LOST(&log, sess);
+	OXR_VERIFY_EXTENSION(&log, sess->sys->inst, EXT_performance_settings);
+	// check parameters
+	if (domain != XR_PERF_SETTINGS_DOMAIN_CPU_EXT && domain != XR_PERF_SETTINGS_DOMAIN_GPU_EXT) {
+		return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Invalid domain %d, must be 1(CPU) or 2(GPU)",
+		                 domain);
+	}
 
-	return oxr_error(&log, XR_ERROR_HANDLE_INVALID, "Not implemented");
+	if (level != XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT && level != XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT &&
+	    level != XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT && level != XR_PERF_SETTINGS_LEVEL_BOOST_EXT) {
+		return oxr_error(
+		    &log, XR_ERROR_VALIDATION_FAILURE,
+		    "Invalid level %d, must be 0(POWER SAVE), 25(SUSTAINED LOW), 50(SUSTAINED_HIGH) or 75(BOOST)",
+		    level);
+	}
+
+	return oxr_session_set_perf_level(&log, sess, domain, level);
 }
-
-#endif
+#endif // OXR_HAVE_EXT_performance_settings
 
 
 /*
diff --git a/src/xrt/state_trackers/oxr/oxr_conversions.h b/src/xrt/state_trackers/oxr/oxr_conversions.h
index 466e4784c..9fd0c95db 100644
--- a/src/xrt/state_trackers/oxr/oxr_conversions.h
+++ b/src/xrt/state_trackers/oxr/oxr_conversions.h
@@ -158,3 +158,57 @@ xrt_input_type_to_str(enum xrt_input_type type)
 	}
 	// clang-format on
 }
+
+static inline enum xrt_perf_set_level
+xr_perf_level_to_xrt(XrPerfSettingsLevelEXT level)
+{
+	switch (level) {
+	case XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT: return XRT_PERF_SET_LEVEL_POWER_SAVINGS;
+	case XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT: return XRT_PERF_SET_LEVEL_SUSTAINED_LOW;
+	case XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT: return XRT_PERF_SET_LEVEL_SUSTAINED_HIGH;
+	case XR_PERF_SETTINGS_LEVEL_BOOST_EXT: return XRT_PERF_SET_LEVEL_BOOST;
+	default: assert(false); return 0;
+	}
+}
+
+static inline enum xrt_perf_domain
+xr_perf_domain_to_xrt(XrPerfSettingsDomainEXT domain)
+{
+	switch (domain) {
+	case XR_PERF_SETTINGS_DOMAIN_CPU_EXT: return XRT_PERF_DOMAIN_CPU;
+	case XR_PERF_SETTINGS_DOMAIN_GPU_EXT: return XRT_PERF_DOMAIN_GPU;
+	default: assert(false); return 0;
+	}
+}
+
+static inline XrPerfSettingsDomainEXT
+xrt_perf_domain_to_xr(enum xrt_perf_domain domain)
+{
+	switch (domain) {
+	case XRT_PERF_DOMAIN_CPU: return XR_PERF_SETTINGS_DOMAIN_CPU_EXT;
+	case XRT_PERF_DOMAIN_GPU: return XR_PERF_SETTINGS_DOMAIN_GPU_EXT;
+	default: assert(false); return 0;
+	}
+}
+
+static inline XrPerfSettingsSubDomainEXT
+xrt_perf_sub_domain_to_xr(enum xrt_perf_sub_domain subDomain)
+{
+	switch (subDomain) {
+	case XRT_PERF_SUB_DOMAIN_COMPOSITING: return XR_PERF_SETTINGS_SUB_DOMAIN_COMPOSITING_EXT;
+	case XRT_PERF_SUB_DOMAIN_RENDERING: return XR_PERF_SETTINGS_SUB_DOMAIN_RENDERING_EXT;
+	case XRT_PERF_SUB_DOMAIN_THERMAL: return XR_PERF_SETTINGS_SUB_DOMAIN_THERMAL_EXT;
+	default: assert(false); return 0;
+	}
+}
+
+static inline XrPerfSettingsNotificationLevelEXT
+xrt_perf_notify_level_to_xr(enum xrt_perf_notify_level level)
+{
+	switch (level) {
+	case XRT_PERF_NOTIFY_LEVEL_NORMAL: return XR_PERF_SETTINGS_NOTIF_LEVEL_NORMAL_EXT;
+	case XRT_PERF_NOTIFY_LEVEL_WARNING: return XR_PERF_SETTINGS_NOTIF_LEVEL_WARNING_EXT;
+	case XRT_PERF_NOTIFY_LEVEL_IMPAIRED: return XR_PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED_EXT;
+	default: assert(false); return 0;
+	}
+}
diff --git a/src/xrt/state_trackers/oxr/oxr_event.c b/src/xrt/state_trackers/oxr/oxr_event.c
index 114c0c496..9943e7a97 100644
--- a/src/xrt/state_trackers/oxr/oxr_event.c
+++ b/src/xrt/state_trackers/oxr/oxr_event.c
@@ -13,6 +13,7 @@
 
 #include "oxr_objects.h"
 #include "oxr_logger.h"
+#include "oxr_conversions.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -266,6 +267,35 @@ oxr_event_push_XrEventDataMainSessionVisibilityChangedEXTX(struct oxr_logger *lo
 }
 #endif // OXR_HAVE_EXTX_overlay
 
+
+#ifdef OXR_HAVE_EXT_performance_settings
+XrResult
+oxr_event_push_XrEventDataPerfSettingsEXTX(struct oxr_logger *log,
+                                           struct oxr_session *sess,
+                                           enum xrt_perf_domain domain,
+                                           enum xrt_perf_sub_domain subDomain,
+                                           enum xrt_perf_notify_level fromLevel,
+                                           enum xrt_perf_notify_level toLevel)
+{
+	struct oxr_instance *inst = sess->sys->inst;
+	XrEventDataPerfSettingsEXT *changed;
+	struct oxr_event *event = NULL;
+
+	ALLOC(log, inst, &event, &changed);
+	changed->type = XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT;
+	changed->domain = xrt_perf_domain_to_xr(domain);
+	changed->subDomain = xrt_perf_sub_domain_to_xr(subDomain);
+	changed->fromLevel = xrt_perf_notify_level_to_xr(fromLevel);
+	changed->toLevel = xrt_perf_notify_level_to_xr(toLevel);
+	event->result = XR_SUCCESS;
+	lock(inst);
+	push(inst, event);
+	unlock(inst);
+
+	return XR_SUCCESS;
+}
+#endif // OXR_HAVE_EXT_performance_settings
+
 XrResult
 oxr_event_remove_session_events(struct oxr_logger *log, struct oxr_session *sess)
 {
diff --git a/src/xrt/state_trackers/oxr/oxr_extension_support.h b/src/xrt/state_trackers/oxr/oxr_extension_support.h
index 95e4a643d..acd619f81 100644
--- a/src/xrt/state_trackers/oxr/oxr_extension_support.h
+++ b/src/xrt/state_trackers/oxr/oxr_extension_support.h
@@ -360,6 +360,17 @@
 #endif
 
 
+/*
+ * XR_EXT_performance_settings
+ */
+#if defined(XR_EXT_performance_settings) && defined(XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS)
+#define OXR_HAVE_EXT_performance_settings
+#define OXR_EXTENSION_SUPPORT_EXT_performance_settings(_) _(EXT_performance_settings, EXT_PERFORMANCE_SETTINGS)
+#else
+#define OXR_EXTENSION_SUPPORT_EXT_performance_settings(_)
+#endif
+
+
 /*
  * XR_EXT_samsung_odyssey_controller
  */
@@ -620,6 +631,7 @@
     OXR_EXTENSION_SUPPORT_EXT_hp_mixed_reality_controller(_) \
     OXR_EXTENSION_SUPPORT_EXT_local_floor(_) \
     OXR_EXTENSION_SUPPORT_EXT_palm_pose(_) \
+    OXR_EXTENSION_SUPPORT_EXT_performance_settings(_) \
     OXR_EXTENSION_SUPPORT_EXT_samsung_odyssey_controller(_) \
     OXR_EXTENSION_SUPPORT_FB_composition_layer_alpha_blend(_) \
     OXR_EXTENSION_SUPPORT_FB_composition_layer_image_layout(_) \
diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h
index 3cebd91e6..539feaf88 100644
--- a/src/xrt/state_trackers/oxr/oxr_objects.h
+++ b/src/xrt/state_trackers/oxr/oxr_objects.h
@@ -771,6 +771,14 @@ oxr_session_get_visibility_mask(struct oxr_logger *log,
                                 XrVisibilityMaskKHR *visibilityMask);
 #endif // OXR_HAVE_KHR_visibility_mask
 
+#ifdef OXR_HAVE_EXT_performance_settings
+XrResult
+oxr_session_set_perf_level(struct oxr_logger *log,
+                           struct oxr_session *sess,
+                           XrPerfSettingsDomainEXT domain,
+                           XrPerfSettingsLevelEXT level);
+#endif // OXR_HAVE_EXT_performance_settings
+
 /*
  *
  * oxr_space.c
@@ -968,6 +976,15 @@ oxr_event_push_XrEventDataMainSessionVisibilityChangedEXTX(struct oxr_logger *lo
                                                            bool visible);
 #endif // OXR_HAVE_EXTX_overlay
 
+#ifdef OXR_HAVE_EXT_performance_settings
+XrResult
+oxr_event_push_XrEventDataPerfSettingsEXTX(struct oxr_logger *log,
+                                           struct oxr_session *sess,
+                                           enum xrt_perf_domain domain,
+                                           enum xrt_perf_sub_domain subDomain,
+                                           enum xrt_perf_notify_level fromLevel,
+                                           enum xrt_perf_notify_level toLevel);
+#endif // OXR_HAVE_EXT_performance_settings
 /*!
  * This clears all pending events refers to the given session.
  */
diff --git a/src/xrt/state_trackers/oxr/oxr_session.c b/src/xrt/state_trackers/oxr/oxr_session.c
index b68ee3c26..3972da9e6 100644
--- a/src/xrt/state_trackers/oxr/oxr_session.c
+++ b/src/xrt/state_trackers/oxr/oxr_session.c
@@ -363,6 +363,13 @@ oxr_session_poll(struct oxr_logger *log, struct oxr_session *sess)
 		case XRT_SESSION_EVENT_REFERENCE_SPACE_CHANGE_PENDING:
 			handle_reference_space_change_pending(log, sess, &xse.ref_change);
 			break;
+		case XRT_SESSION_EVENT_PERFORMANCE_CHANGE:
+#ifdef OXR_HAVE_EXT_performance_settings
+			oxr_event_push_XrEventDataPerfSettingsEXTX(
+			    log, sess, xse.performance.domain, xse.performance.sub_domain, xse.performance.from_level,
+			    xse.performance.to_level);
+#endif // OXR_HAVE_EXT_performance_settings
+			break;
 		default: U_LOG_W("unhandled event type! %d", xse.type); break;
 		}
 	}
@@ -1394,3 +1401,23 @@ oxr_session_request_display_refresh_rate(struct oxr_logger *log, struct oxr_sess
 	return XR_SUCCESS;
 }
 #endif // OXR_HAVE_FB_display_refresh_rate
+
+#ifdef OXR_HAVE_EXT_performance_settings
+XrResult
+oxr_session_set_perf_level(struct oxr_logger *log,
+                           struct oxr_session *sess,
+                           XrPerfSettingsDomainEXT domain,
+                           XrPerfSettingsLevelEXT level)
+{
+	struct xrt_compositor *xc = &sess->xcn->base;
+
+	if (xc->set_performance_level == NULL) {
+		return XR_ERROR_FUNCTION_UNSUPPORTED;
+	}
+	enum xrt_perf_domain oxr_domain = xr_perf_domain_to_xrt(domain);
+	enum xrt_perf_set_level oxr_level = xr_perf_level_to_xrt(level);
+	xrt_comp_set_performance_level(xc, oxr_domain, oxr_level);
+
+	return XR_SUCCESS;
+}
+#endif // OXR_HAVE_EXT_performance_settings