1
0
mirror of https://github.com/godotengine/godot synced 2024-07-01 08:59:19 +00:00

Improvements to VRS/Foveated rendering

This commit is contained in:
Bastiaan Olij 2024-03-26 12:57:26 +11:00
parent 34b5e8f55c
commit 9042ddf19f
38 changed files with 708 additions and 124 deletions

View File

@ -4036,6 +4036,15 @@
The texture to use when the VRS mode is set to [constant RenderingServer.VIEWPORT_VRS_TEXTURE]. Equivalent to [member ProjectSettings.rendering/vrs/texture].
</description>
</method>
<method name="viewport_set_vrs_update_mode">
<return type="void" />
<param index="0" name="viewport" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.ViewportVRSUpdateMode" />
<description>
Sets the update mode for Variable Rate Shading (VRS) for the viewport. VRS requires the input texture to be converted to the format usable by the VRS method supported by the hardware. The update mode defines how often this happens. If the GPU does not support VRS, or VRS is not enabled, this property is ignored.
If set to [constant RenderingServer.VIEWPORT_VRS_UPDATE_ONCE], the input texture is copied once and the mode is changed to [constant RenderingServer.VIEWPORT_VRS_UPDATE_DISABLED].
</description>
</method>
<method name="visibility_notifier_create">
<return type="RID" />
<description>
@ -4976,11 +4985,23 @@
Variable rate shading uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view.
</constant>
<constant name="VIEWPORT_VRS_XR" value="2" enum="ViewportVRSMode">
Variable rate shading texture is supplied by the primary [XRInterface].
Variable rate shading texture is supplied by the primary [XRInterface]. Note that this may override the update mode.
</constant>
<constant name="VIEWPORT_VRS_MAX" value="3" enum="ViewportVRSMode">
Represents the size of the [enum ViewportVRSMode] enum.
</constant>
<constant name="VIEWPORT_VRS_UPDATE_DISABLED" value="0" enum="ViewportVRSUpdateMode">
The input texture for variable rate shading will not be processed.
</constant>
<constant name="VIEWPORT_VRS_UPDATE_ONCE" value="1" enum="ViewportVRSUpdateMode">
The input texture for variable rate shading will be processed once.
</constant>
<constant name="VIEWPORT_VRS_UPDATE_ALWAYS" value="2" enum="ViewportVRSUpdateMode">
The input texture for variable rate shading will be processed each frame.
</constant>
<constant name="VIEWPORT_VRS_UPDATE_MAX" value="3" enum="ViewportVRSUpdateMode">
Represents the size of the [enum ViewportVRSUpdateMode] enum.
</constant>
<constant name="SKY_MODE_AUTOMATIC" value="0" enum="SkyMode">
Automatically selects the appropriate process mode based on your sky shader. If your shader uses [code]TIME[/code] or [code]POSITION[/code], this will use [constant SKY_MODE_REALTIME]. If your shader uses any of the [code]LIGHT_*[/code] variables or any custom uniforms, this uses [constant SKY_MODE_INCREMENTAL]. Otherwise, this defaults to [constant SKY_MODE_QUALITY].
</constant>

View File

@ -407,6 +407,9 @@
- 8×8 = rgb(255, 255, 0) - #ffff00 - Not supported on most hardware
[/codeblock]
</member>
<member name="vrs_update_mode" type="int" setter="set_vrs_update_mode" getter="get_vrs_update_mode" enum="Viewport.VRSUpdateMode" default="1">
Sets the update mode for Variable Rate Shading (VRS) for the viewport. VRS requires the input texture to be converted to the format usable by the VRS method supported by the hardware. The update mode defines how often this happens. If the GPU does not support VRS, or VRS is not enabled, this property is ignored.
</member>
<member name="world_2d" type="World2D" setter="set_world_2d" getter="get_world_2d">
The custom [World2D] which can be used as 2D environment source.
</member>
@ -664,5 +667,17 @@
<constant name="VRS_MAX" value="3" enum="VRSMode">
Represents the size of the [enum VRSMode] enum.
</constant>
<constant name="VRS_UPDATE_DISABLED" value="0" enum="VRSUpdateMode">
The input texture for variable rate shading will not be processed.
</constant>
<constant name="VRS_UPDATE_ONCE" value="1" enum="VRSUpdateMode">
The input texture for variable rate shading will be processed once.
</constant>
<constant name="VRS_UPDATE_ALWAYS" value="2" enum="VRSUpdateMode">
The input texture for variable rate shading will be processed each frame.
</constant>
<constant name="VRS_UPDATE_MAX" value="3" enum="VRSUpdateMode">
Represents the size of the [enum VRSUpdateMode] enum.
</constant>
</constants>
</class>

30
doc/classes/XRVRS.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="XRVRS" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Helper class for XR interfaces that generates VRS images.
</brief_description>
<description>
This class is used by various XR interfaces to generate VRS textures that can be used to speed up rendering.
</description>
<tutorials>
</tutorials>
<methods>
<method name="make_vrs_texture">
<return type="RID" />
<param index="0" name="target_size" type="Vector2" />
<param index="1" name="eye_foci" type="PackedVector2Array" />
<description>
Generates the VRS texture based on a render [param target_size] adjusted by our VRS tile size. For each eyes focal point passed in [param eye_foci] a layer is created. Focal point should be in NDC.
The result will be cached, requesting a VRS texture with unchanged parameters and settings will return the cached RID.
</description>
</method>
</methods>
<members>
<member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
</member>
<member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is.
</member>
</members>
</class>

View File

@ -5912,6 +5912,9 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
case LIMIT_VRS_TEXEL_WIDTH:
case LIMIT_VRS_TEXEL_HEIGHT:
return vrs_capabilities.ss_image_tile_size;
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
return vrs_capabilities.ss_max_fragment_size;
default: {
#ifdef DEV_ENABLED
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");
@ -6218,6 +6221,7 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing;
vrs_capabilities.ss_image_supported = true;
vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize;
vrs_capabilities.ss_max_fragment_size = 8; // TODO figure out if this is supplied and/or needed
vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported;
}
}

View File

@ -105,6 +105,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
bool primitive_in_multiviewport = false;
bool ss_image_supported = false; // We can provide a density map attachment on our framebuffer.
uint32_t ss_image_tile_size = 0;
uint32_t ss_max_fragment_size = 0;
bool additional_rates_supported = false;
};

View File

@ -672,6 +672,8 @@ public:
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; }
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }

View File

@ -758,11 +758,13 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() {
vrs_capabilities.min_texel_size.y = vrs_properties.minFragmentShadingRateAttachmentTexelSize.height;
vrs_capabilities.max_texel_size.x = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.width;
vrs_capabilities.max_texel_size.y = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.height;
vrs_capabilities.max_fragment_size.x = vrs_properties.maxFragmentSize.width; // either 4 or 8
vrs_capabilities.max_fragment_size.y = vrs_properties.maxFragmentSize.height; // generally the same as width
// We'll attempt to default to a texel size of 16x16.
vrs_capabilities.texel_size = Vector2i(16, 16).clamp(vrs_capabilities.min_texel_size, vrs_capabilities.max_texel_size);
print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")"));
print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")") + String(", max fragment size: (") + itos(vrs_capabilities.max_fragment_size.x) + String(", ") + itos(vrs_capabilities.max_fragment_size.y) + String(")"));
}
} else {
@ -4887,6 +4889,10 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) {
return vrs_capabilities.texel_size.x;
case LIMIT_VRS_TEXEL_HEIGHT:
return vrs_capabilities.texel_size.y;
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
return vrs_capabilities.max_fragment_size.x;
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
return vrs_capabilities.max_fragment_size.y;
default:
ERR_FAIL_V(0);
}

View File

@ -87,6 +87,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
Size2i min_texel_size;
Size2i max_texel_size;
Size2i max_fragment_size;
Size2i texel_size; // The texel size we'll use
};

View File

@ -40,6 +40,14 @@
<member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5">
The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance.
</member>
<member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
<member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
<member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" overrides="XRInterface" enum="XRInterface.PlayAreaMode" default="1" />
</members>
</class>

View File

@ -250,6 +250,16 @@ void MobileVRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversample", PROPERTY_HINT_RANGE, "1.0,2.0,0.1"), "set_oversample", "get_oversample");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k1", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k1", "get_k1");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2");
ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &MobileVRInterface::get_vrs_min_radius);
ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &MobileVRInterface::set_vrs_min_radius);
ClassDB::bind_method(D_METHOD("get_vrs_strength"), &MobileVRInterface::get_vrs_strength);
ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &MobileVRInterface::set_vrs_strength);
ADD_GROUP("Vulkan VRS", "vrs_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
}
void MobileVRInterface::set_eye_height(const double p_eye_height) {
@ -316,6 +326,22 @@ double MobileVRInterface::get_k2() const {
return k2;
};
float MobileVRInterface::get_vrs_min_radius() const {
return xr_vrs.get_vrs_min_radius();
}
void MobileVRInterface::set_vrs_min_radius(float p_vrs_min_radius) {
xr_vrs.set_vrs_min_radius(p_vrs_min_radius);
}
float MobileVRInterface::get_vrs_strength() const {
return xr_vrs.get_vrs_strength();
}
void MobileVRInterface::set_vrs_strength(float p_vrs_strength) {
xr_vrs.set_vrs_strength(p_vrs_strength);
}
uint32_t MobileVRInterface::get_view_count() {
// needs stereo...
return 2;
@ -489,11 +515,16 @@ Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target,
Vector<BlitToScreen> blit_to_screen;
// We must have a valid render target
// We must have a valid render target.
ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen);
// Because we are rendering to our device we must use our main viewport!
ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen);
// We will only output to screen if this is our main viewport.
if (p_screen_rect == Rect2()) {
// Warn the developer once, it's up to the developer to output to screen.
WARN_PRINT_ONCE("SubViewport used with MobileVRInterface, no output to screen");
return blit_to_screen;
}
Rect2 modified_screen_rect = Rect2(p_screen_rect.position + offset_rect.position * p_screen_rect.size, p_screen_rect.size * offset_rect.size);
@ -542,6 +573,23 @@ void MobileVRInterface::process() {
};
};
RID MobileVRInterface::get_vrs_texture() {
PackedVector2Array eye_foci;
Size2 target_size = get_render_target_size();
real_t aspect_ratio = target_size.x / target_size.y;
uint32_t view_count = get_view_count();
for (uint32_t v = 0; v < view_count; v++) {
Projection cm = get_projection_for_view(v, aspect_ratio, 0.1, 1000.0);
Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0));
eye_foci.push_back(Vector2(center.x, center.y));
}
return xr_vrs.make_vrs_texture(target_size, eye_foci);
}
MobileVRInterface::MobileVRInterface() {}
MobileVRInterface::~MobileVRInterface() {

View File

@ -33,6 +33,7 @@
#include "servers/xr/xr_interface.h"
#include "servers/xr/xr_positional_tracker.h"
#include "servers/xr/xr_vrs.h"
/**
The mobile interface is a native VR interface that can be used on Android and iOS phones.
@ -72,6 +73,8 @@ private:
Ref<XRPositionalTracker> head;
Transform3D head_transform;
XRVRS xr_vrs;
/*
logic for processing our sensor data, this was originally in our positional tracker logic but I think
that doesn't make sense in hindsight. It only makes marginally more sense to park it here for now,
@ -138,6 +141,12 @@ public:
void set_k2(const double p_k2);
double get_k2() const;
float get_vrs_min_radius() const;
void set_vrs_min_radius(float p_vrs_min_radius);
float get_vrs_strength() const;
void set_vrs_strength(float p_vrs_strength);
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
@ -161,6 +170,8 @@ public:
virtual void process() override;
virtual RID get_vrs_texture() override;
MobileVRInterface();
~MobileVRInterface();
};

View File

@ -152,6 +152,14 @@
<member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0">
The render size multiplier for the current HMD. Must be set before the interface has been initialized.
</member>
<member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
<member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
</members>
<signals>
<signal name="instance_exiting">

View File

@ -34,6 +34,7 @@
#include "core/os/os.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::singleton = nullptr;
@ -106,3 +107,38 @@ void OpenXREyeGazeInteractionExtension::on_register_metadata() {
metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
}
bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, false);
if (!init_eye_gaze_pose) {
init_eye_gaze_pose = true;
eye_tracker = openxr_api->find_tracker("/user/eyes_ext");
if (eye_tracker.is_null()) {
WARN_PRINT("Couldn't obtain eye tracker");
}
eye_action = openxr_api->find_action("eye_gaze_pose");
if (eye_action.is_null()) {
WARN_PRINT("Couldn't obtain pose action for `eye_gaze_pose`, make sure to add this to your action map.");
}
}
if (eye_tracker.is_null() || eye_action.is_null()) {
return false;
}
Transform3D eye_transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(eye_action, eye_tracker, eye_transform, linear_velocity, angular_velocity);
if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
return false;
}
r_eye_pose = eye_transform.origin + eye_transform.basis[2] * p_dist;
return true;
}

View File

@ -50,11 +50,17 @@ public:
virtual void on_register_metadata() override;
bool get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose);
private:
static OpenXREyeGazeInteractionExtension *singleton;
bool available = false;
XrSystemEyeGazeInteractionPropertiesEXT properties;
bool init_eye_gaze_pose = false;
RID eye_tracker;
RID eye_action;
};
#endif // OPENXR_EYE_GAZE_INTERACTION_H

View File

@ -54,6 +54,7 @@
#endif
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
#include "extensions/openxr_fb_update_swapchain_extension.h"
@ -1826,6 +1827,46 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z
return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
}
Vector2 OpenXRAPI::get_eye_focus(uint32_t p_view, float p_aspect) {
ERR_FAIL_NULL_V(graphics_extension, Vector2());
if (!render_state.running) {
return Vector2();
}
// xrWaitFrame not run yet
if (render_state.predicted_display_time == 0) {
return Vector2();
}
// we don't have valid view info
if (render_state.views == nullptr || !render_state.view_pose_valid) {
return Vector2();
}
Projection cm;
if (!graphics_extension->create_projection_fov(render_state.views[p_view].fov, 0.1, 1000.0, cm)) {
return Vector2();
}
// Default focus to center...
Vector3 focus = cm.xform(Vector3(0.0, 0.0, 999.9));
// Lets check for eye tracking...
OpenXREyeGazeInteractionExtension *eye_gaze_interaction = OpenXREyeGazeInteractionExtension::get_singleton();
if (eye_gaze_interaction && eye_gaze_interaction->supports_eye_gaze_interaction()) {
Vector3 eye_gaze_pose;
if (eye_gaze_interaction->get_eye_gaze_pose(1.0, eye_gaze_pose)) {
Transform3D view_transform = transform_from_pose(render_state.views[p_view].pose);
eye_gaze_pose = view_transform.xform_inv(eye_gaze_pose);
focus = cm.xform(eye_gaze_pose);
}
}
return Vector2(focus.x, focus.y);
}
bool OpenXRAPI::poll_events() {
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
@ -2633,10 +2674,23 @@ bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const
RID OpenXRAPI::get_tracker_rid(XrPath p_path) {
List<RID> current;
tracker_owner.get_owned_list(&current);
for (int i = 0; i < current.size(); i++) {
Tracker *tracker = tracker_owner.get_or_null(current[i]);
for (const RID &E : current) {
Tracker *tracker = tracker_owner.get_or_null(E);
if (tracker && tracker->toplevel_path == p_path) {
return current[i];
return E;
}
}
return RID();
}
RID OpenXRAPI::find_tracker(const String &p_name) {
List<RID> current;
tracker_owner.get_owned_list(&current);
for (const RID &E : current) {
Tracker *tracker = tracker_owner.get_or_null(E);
if (tracker && tracker->name == p_name) {
return E;
}
}
@ -2827,10 +2881,23 @@ void OpenXRAPI::action_set_free(RID p_action_set) {
RID OpenXRAPI::get_action_rid(XrAction p_action) {
List<RID> current;
action_owner.get_owned_list(&current);
for (int i = 0; i < current.size(); i++) {
Action *action = action_owner.get_or_null(current[i]);
for (const RID &E : current) {
Action *action = action_owner.get_or_null(E);
if (action && action->handle == p_action) {
return current[i];
return E;
}
}
return RID();
}
RID OpenXRAPI::find_action(const String &p_name) {
List<RID> current;
action_owner.get_owned_list(&current);
for (const RID &E : current) {
Action *action = action_owner.get_or_null(E);
if (action && action->name == p_name) {
return E;
}
}
@ -2931,10 +2998,10 @@ void OpenXRAPI::action_free(RID p_action) {
RID OpenXRAPI::get_interaction_profile_rid(XrPath p_path) {
List<RID> current;
interaction_profile_owner.get_owned_list(&current);
for (int i = 0; i < current.size(); i++) {
InteractionProfile *ip = interaction_profile_owner.get_or_null(current[i]);
for (const RID &E : current) {
InteractionProfile *ip = interaction_profile_owner.get_or_null(E);
if (ip && ip->path == p_path) {
return current[i];
return E;
}
}

View File

@ -456,6 +456,7 @@ public:
XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
Vector2 get_eye_focus(uint32_t p_view, float p_aspect);
bool process();
void pre_render();
@ -515,6 +516,9 @@ public:
bool interaction_profile_suggest_bindings(RID p_interaction_profile);
void interaction_profile_free(RID p_interaction_profile);
RID find_tracker(const String &p_name);
RID find_action(const String &p_name);
bool sync_action_sets(const Vector<RID> p_active_sets);
bool get_action_bool(RID p_action, RID p_tracker);
float get_action_float(RID p_action, RID p_tracker);

View File

@ -97,6 +97,16 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported);
ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);
// VRS
ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius);
ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius);
ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength);
ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength);
ADD_GROUP("Vulkan VRS", "vrs_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
BIND_ENUM_CONSTANT(HAND_MAX);
@ -872,6 +882,22 @@ Array OpenXRInterface::get_action_sets() const {
return arr;
}
float OpenXRInterface::get_vrs_min_radius() const {
return xr_vrs.get_vrs_min_radius();
}
void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) {
xr_vrs.set_vrs_min_radius(p_vrs_min_radius);
}
float OpenXRInterface::get_vrs_strength() const {
return xr_vrs.get_vrs_strength();
}
void OpenXRInterface::set_vrs_strength(float p_vrs_strength) {
xr_vrs.set_vrs_strength(p_vrs_strength);
}
double OpenXRInterface::get_render_target_size_multiplier() const {
if (openxr_api == nullptr) {
return 1.0;
@ -1435,6 +1461,24 @@ Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints
return Vector3();
}
RID OpenXRInterface::get_vrs_texture() {
if (!openxr_api) {
return RID();
}
PackedVector2Array eye_foci;
Size2 target_size = get_render_target_size();
real_t aspect_ratio = target_size.x / target_size.y;
uint32_t view_count = get_view_count();
for (uint32_t v = 0; v < view_count; v++) {
eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio));
}
return xr_vrs.make_vrs_texture(target_size, eye_foci);
}
OpenXRInterface::OpenXRInterface() {
openxr_api = OpenXRAPI::get_singleton();
if (openxr_api) {

View File

@ -80,6 +80,8 @@ private:
XRPose::TrackingConfidence head_confidence;
Transform3D transform_for_view[2]; // We currently assume 2, but could be 4 for VARJO which we do not support yet
XRVRS xr_vrs;
void _load_action_map();
struct Action { // An action we've registered with OpenXR
@ -168,6 +170,12 @@ public:
bool get_foveation_dynamic() const;
void set_foveation_dynamic(bool p_foveation_dynamic);
float get_vrs_min_radius() const;
void set_vrs_min_radius(float p_vrs_min_radius);
float get_vrs_strength() const;
void set_vrs_strength(float p_vrs_strength);
virtual Size2 get_render_target_size() override;
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
@ -276,6 +284,8 @@ public:
Vector3 get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const;
Vector3 get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const;
virtual RID get_vrs_texture() override;
OpenXRInterface();
~OpenXRInterface();
};

View File

@ -3735,6 +3735,28 @@ Viewport::VRSMode Viewport::get_vrs_mode() const {
return vrs_mode;
}
void Viewport::set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode) {
ERR_MAIN_THREAD_GUARD;
vrs_update_mode = p_vrs_update_mode;
switch (p_vrs_update_mode) {
case VRS_UPDATE_ONCE: {
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ONCE);
} break;
case VRS_UPDATE_ALWAYS: {
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ALWAYS);
} break;
default: {
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_DISABLED);
} break;
}
}
Viewport::VRSUpdateMode Viewport::get_vrs_update_mode() const {
ERR_READ_THREAD_GUARD_V(VRS_UPDATE_DISABLED);
return vrs_update_mode;
}
void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) {
ERR_MAIN_THREAD_GUARD;
vrs_texture = p_texture;
@ -4775,6 +4797,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
ClassDB::bind_method(D_METHOD("set_vrs_update_mode", "mode"), &Viewport::set_vrs_update_mode);
ClassDB::bind_method(D_METHOD("get_vrs_update_mode"), &Viewport::get_vrs_update_mode);
ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture);
ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture);
@ -4807,6 +4832,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
ADD_GROUP("Variable Rate Shading", "vrs_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,Always"), "set_vrs_update_mode", "get_vrs_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture");
#endif
ADD_GROUP("Canvas Items", "canvas_item_");
@ -4930,12 +4956,21 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(VRS_TEXTURE);
BIND_ENUM_CONSTANT(VRS_XR);
BIND_ENUM_CONSTANT(VRS_MAX);
BIND_ENUM_CONSTANT(VRS_UPDATE_DISABLED);
BIND_ENUM_CONSTANT(VRS_UPDATE_ONCE);
BIND_ENUM_CONSTANT(VRS_UPDATE_ALWAYS);
BIND_ENUM_CONSTANT(VRS_UPDATE_MAX);
}
void Viewport::_validate_property(PropertyInfo &p_property) const {
if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (vrs_mode == VRS_DISABLED && (p_property.name == "vrs_update_mode")) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
Viewport::Viewport() {

View File

@ -213,6 +213,13 @@ public:
VRS_MAX
};
enum VRSUpdateMode {
VRS_UPDATE_DISABLED,
VRS_UPDATE_ONCE,
VRS_UPDATE_ALWAYS,
VRS_UPDATE_MAX
};
private:
friend class ViewportTexture;
@ -337,6 +344,7 @@ private:
// VRS
VRSMode vrs_mode = VRS_DISABLED;
VRSUpdateMode vrs_update_mode = VRS_UPDATE_ONCE;
Ref<Texture2D> vrs_texture;
struct GUI {
@ -636,6 +644,9 @@ public:
void set_vrs_mode(VRSMode p_vrs_mode);
VRSMode get_vrs_mode() const;
void set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode);
VRSUpdateMode get_vrs_update_mode() const;
void set_vrs_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_vrs_texture() const;
@ -844,6 +855,7 @@ VARIANT_ENUM_CAST(Viewport::DebugDraw);
VARIANT_ENUM_CAST(Viewport::SDFScale);
VARIANT_ENUM_CAST(Viewport::SDFOversize);
VARIANT_ENUM_CAST(Viewport::VRSMode);
VARIANT_ENUM_CAST(Viewport::VRSUpdateMode);
VARIANT_ENUM_CAST(SubViewport::ClearMode);
VARIANT_ENUM_CAST(Viewport::RenderInfo);
VARIANT_ENUM_CAST(Viewport::RenderInfoType);

View File

@ -325,6 +325,7 @@ void register_server_types() {
PhysicsServer3DManager::get_singleton()->set_default_server("GodotPhysics3D");
GDREGISTER_ABSTRACT_CLASS(XRInterface);
GDREGISTER_CLASS(XRVRS);
GDREGISTER_CLASS(XRBodyTracker);
GDREGISTER_CLASS(XRControllerTracker);
GDREGISTER_CLASS(XRFaceTracker);

View File

@ -193,6 +193,8 @@ public:
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; }
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }

View File

@ -80,7 +80,16 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
VRSMode mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT;
VRSPushConstant push_constant = {};
int mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT;
// Set maximum texel factor based on maximum fragment size, some GPUs do not support 8x8 (fragment shading rate approach).
if (MIN(RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_WIDTH), RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_HEIGHT)) > 4) {
push_constant.max_texel_factor = 3.0;
} else {
push_constant.max_texel_factor = 2.0;
}
RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
@ -88,7 +97,7 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>());
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
// RD::get_singleton()->draw_list_set_push_constant(draw_list, &vrs_shader.push_constant, sizeof(VRSPushConstant));
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(VRSPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@ -111,12 +120,11 @@ Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const {
void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
TextureStorage *texture_storage = TextureStorage::get_singleton();
RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(p_render_target);
RS::ViewportVRSUpdateMode vrs_update_mode = texture_storage->render_target_get_vrs_update_mode(p_render_target);
if (vrs_mode != RS::VIEWPORT_VRS_DISABLED) {
if (vrs_mode != RS::VIEWPORT_VRS_DISABLED && vrs_update_mode != RS::VIEWPORT_VRS_UPDATE_DISABLED) {
RD::get_singleton()->draw_command_begin_label("VRS Setup");
// TODO figure out if image has changed since it was last copied so we can save some resources..
if (vrs_mode == RS::VIEWPORT_VRS_TEXTURE) {
RID vrs_texture = texture_storage->render_target_get_vrs_texture(p_render_target);
if (vrs_texture.is_valid()) {
@ -145,6 +153,10 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
#endif // _3D_DISABLED
}
if (vrs_update_mode == RS::VIEWPORT_VRS_UPDATE_ONCE) {
texture_storage->render_target_set_vrs_update_mode(p_render_target, RS::VIEWPORT_VRS_UPDATE_DISABLED);
}
RD::get_singleton()->draw_command_end_label();
}
}

View File

@ -47,11 +47,12 @@ private:
VRS_MAX,
};
/* we have no push constant here (yet)
struct VRSPushConstant {
float max_texel_factor; // 4x8, 8x4 and 8x8 are only available on some GPUs.
float res1;
float res2;
float res3;
};
*/
struct VRSShader {
// VRSPushConstant push_constant;

View File

@ -19,6 +19,14 @@ layout(location = 0) out vec3 uv_interp;
layout(location = 0) out vec2 uv_interp;
#endif
layout(push_constant, std430) uniform Params {
float max_texel_factor;
float res1;
float res2;
float res3;
}
params;
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
@ -53,6 +61,14 @@ layout(set = 0, binding = 0) uniform sampler2D source_color;
layout(location = 0) out uint frag_color;
layout(push_constant, std430) uniform Params {
float max_texel_factor;
float res1;
float res2;
float res3;
}
params;
void main() {
#ifdef USE_MULTIVIEW
vec3 uv = uv_interp;
@ -60,20 +76,22 @@ void main() {
vec2 uv = uv_interp;
#endif
#ifdef USE_MULTIVIEW
vec4 color = textureLod(source_color, uv, 0.0);
frag_color = uint(color.r * 255.0);
#else /* USE_MULTIVIEW */
// Input is standardised. R for X, G for Y, 0.0 (0) = 1, 0.33 (85) = 2, 0.66 (170) = 3, 1.0 (255) = 8
vec4 color = textureLod(source_color, uv, 0.0);
// for user supplied VRS map we do a color mapping
color.r *= 3.0;
frag_color = int(color.r) << 2;
// Output image shading rate image for VRS according to VK_KHR_fragment_shading_rate.
color.r = clamp(floor(color.r * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
color.g = clamp(floor(color.g * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
color.g *= 3.0;
frag_color += int(color.g);
// Note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported:
if (color.r < (color.g - 1.0)) {
color.r = color.g - 1.0;
}
if (color.g < (color.r - 1.0)) {
color.g = color.r - 1.0;
}
// note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported
// 4x8, 8x4 and 8x8 are only available on some GPUs
#endif /* USE_MULTIVIEW */
// Encode to frag_color;
frag_color = int(color.r + 0.1) << 2;
frag_color += int(color.g + 0.1);
}

View File

@ -3960,6 +3960,20 @@ RS::ViewportVRSMode TextureStorage::render_target_get_vrs_mode(RID p_render_targ
return rt->vrs_mode;
}
void TextureStorage::render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
rt->vrs_update_mode = p_mode;
}
RS::ViewportVRSUpdateMode TextureStorage::render_target_get_vrs_update_mode(RID p_render_target) const {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, RS::VIEWPORT_VRS_UPDATE_DISABLED);
return rt->vrs_update_mode;
}
void TextureStorage::render_target_set_vrs_texture(RID p_render_target, RID p_texture) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);

View File

@ -366,6 +366,7 @@ private:
// VRS
RS::ViewportVRSMode vrs_mode = RS::VIEWPORT_VRS_DISABLED;
RS::ViewportVRSUpdateMode vrs_update_mode = RS::VIEWPORT_VRS_UPDATE_ONCE;
RID vrs_texture;
// overridden textures
@ -746,6 +747,8 @@ public:
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override;
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override;
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override;
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override;
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override;
virtual RID render_target_get_vrs_texture(RID p_render_target) const override;

View File

@ -1450,6 +1450,13 @@ void RendererViewport::viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode
_configure_3d_render_buffers(viewport);
}
void RendererViewport::viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
RSG::texture_storage->render_target_set_vrs_update_mode(viewport->render_target, p_mode);
}
void RendererViewport::viewport_set_vrs_texture(RID p_viewport, RID p_texture) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);

View File

@ -298,6 +298,7 @@ public:
virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const;
void viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode p_mode);
void viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode);
void viewport_set_vrs_texture(RID p_viewport, RID p_texture);
void handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time);

View File

@ -810,6 +810,8 @@ public:
LIMIT_SUBGROUP_OPERATIONS,
LIMIT_VRS_TEXEL_WIDTH,
LIMIT_VRS_TEXEL_HEIGHT,
LIMIT_VRS_MAX_FRAGMENT_WIDTH,
LIMIT_VRS_MAX_FRAGMENT_HEIGHT,
};
enum Features {

View File

@ -672,6 +672,7 @@ public:
FUNC2(call_set_vsync_mode, DisplayServer::VSyncMode, DisplayServer::WindowID)
FUNC2(viewport_set_vrs_mode, RID, ViewportVRSMode)
FUNC2(viewport_set_vrs_update_mode, RID, ViewportVRSUpdateMode)
FUNC2(viewport_set_vrs_texture, RID, RID)
/* COMPOSITOR EFFECT */

View File

@ -169,6 +169,8 @@ public:
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) = 0;
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const = 0;
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) = 0;
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const = 0;
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) = 0;
virtual RID render_target_get_vrs_texture(RID p_render_target) const = 0;

View File

@ -2824,6 +2824,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_get_measured_render_time_gpu", "viewport"), &RenderingServer::viewport_get_measured_render_time_gpu);
ClassDB::bind_method(D_METHOD("viewport_set_vrs_mode", "viewport", "mode"), &RenderingServer::viewport_set_vrs_mode);
ClassDB::bind_method(D_METHOD("viewport_set_vrs_update_mode", "viewport", "mode"), &RenderingServer::viewport_set_vrs_update_mode);
ClassDB::bind_method(D_METHOD("viewport_set_vrs_texture", "viewport", "texture"), &RenderingServer::viewport_set_vrs_texture);
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_BILINEAR);
@ -2832,7 +2833,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_MAX);
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_DISABLED);
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ONCE); // Then goes to disabled); must be manually updated.
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ONCE); // Then goes to disabled, must be manually updated.
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_WHEN_VISIBLE); // Default
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE);
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ALWAYS);
@ -2914,6 +2915,11 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(VIEWPORT_VRS_XR);
BIND_ENUM_CONSTANT(VIEWPORT_VRS_MAX);
BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_DISABLED);
BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_ONCE); // Then goes to disabled, must be manually updated.
BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_ALWAYS);
BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_MAX);
/* SKY API */
ClassDB::bind_method(D_METHOD("sky_create"), &RenderingServer::sky_create);

View File

@ -1056,7 +1056,15 @@ public:
VIEWPORT_VRS_MAX,
};
enum ViewportVRSUpdateMode {
VIEWPORT_VRS_UPDATE_DISABLED,
VIEWPORT_VRS_UPDATE_ONCE,
VIEWPORT_VRS_UPDATE_ALWAYS,
VIEWPORT_VRS_UPDATE_MAX,
};
virtual void viewport_set_vrs_mode(RID p_viewport, ViewportVRSMode p_mode) = 0;
virtual void viewport_set_vrs_update_mode(RID p_viewport, ViewportVRSUpdateMode p_mode) = 0;
virtual void viewport_set_vrs_texture(RID p_viewport, RID p_texture) = 0;
/* SKY API */
@ -1815,6 +1823,7 @@ VARIANT_ENUM_CAST(RenderingServer::ViewportOcclusionCullingBuildQuality);
VARIANT_ENUM_CAST(RenderingServer::ViewportSDFOversize);
VARIANT_ENUM_CAST(RenderingServer::ViewportSDFScale);
VARIANT_ENUM_CAST(RenderingServer::ViewportVRSMode);
VARIANT_ENUM_CAST(RenderingServer::ViewportVRSUpdateMode);
VARIANT_ENUM_CAST(RenderingServer::SkyMode);
VARIANT_ENUM_CAST(RenderingServer::CompositorEffectCallbackType);
VARIANT_ENUM_CAST(RenderingServer::CompositorEffectFlags);

View File

@ -132,13 +132,7 @@ void XRInterface::set_primary(bool p_primary) {
XRInterface::XRInterface() {}
XRInterface::~XRInterface() {
if (vrs.vrs_texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(vrs.vrs_texture);
vrs.vrs_texture = RID();
}
}
XRInterface::~XRInterface() {}
// query if this interface supports this play area mode
bool XRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
@ -176,80 +170,7 @@ int XRInterface::get_camera_feed_id() {
}
RID XRInterface::get_vrs_texture() {
// Default logic will return a standard VRS image based on our target size and default projections.
// Note that this only gets called if VRS is supported on the hardware.
int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH);
int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT);
int view_count = get_view_count();
Size2 target_size = get_render_target_size();
real_t aspect = target_size.x / target_size.y; // is this y/x ?
Size2 vrs_size = Size2(round(0.5 + target_size.x / texel_width), round(0.5 + target_size.y / texel_height));
real_t radius = vrs_size.length() * 0.5;
Size2 vrs_sizei = vrs_size;
if (vrs.size != vrs_sizei) {
const uint8_t densities[] = {
0, // 1x1
1, // 1x2
// 2, // 1x4 - not supported
// 3, // 1x8 - not supported
// 4, // 2x1
5, // 2x2
6, // 2x4
// 9, // 4x2
10, // 4x4
};
// out with the old
if (vrs.vrs_texture.is_valid()) {
RS::get_singleton()->free(vrs.vrs_texture);
vrs.vrs_texture = RID();
}
// in with the new
Vector<Ref<Image>> images;
vrs.size = vrs_sizei;
for (int i = 0; i < view_count && i < 2; i++) {
PackedByteArray data;
data.resize(vrs_sizei.x * vrs_sizei.y);
uint8_t *data_ptr = data.ptrw();
// Our near and far don't matter much for what we're doing here, but there are some interfaces that will remember this as the near and far and may fail as a result...
Projection cm = get_projection_for_view(i, aspect, 0.1, 1000.0);
Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0));
Vector2i view_center;
view_center.x = int(vrs_size.x * (center.x + 1.0) * 0.5);
view_center.y = int(vrs_size.y * (center.y + 1.0) * 0.5);
int d = 0;
for (int y = 0; y < vrs_sizei.y; y++) {
for (int x = 0; x < vrs_sizei.x; x++) {
Vector2 offset = Vector2(x - view_center.x, y - view_center.y);
offset.y *= aspect;
real_t distance = offset.length();
int idx = round(5.0 * distance / radius);
if (idx > 4) {
idx = 4;
}
uint8_t density = densities[idx];
data_ptr[d++] = density;
}
}
images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data));
}
if (images.size() == 1) {
vrs.vrs_texture = RS::get_singleton()->texture_2d_create(images[0]);
} else {
vrs.vrs_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TEXTURE_LAYERED_2D_ARRAY);
}
}
return vrs.vrs_texture;
return RID();
}
/** these are optional, so we want dummies **/

View File

@ -34,6 +34,7 @@
#include "core/math/projection.h"
#include "core/os/thread_safe.h"
#include "servers/xr_server.h"
#include "xr_vrs.h"
// forward declaration
struct BlitToScreen;
@ -133,7 +134,6 @@ public:
// These methods are called from the rendering thread.
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */
virtual RID get_vrs_texture(); /* obtain VRS texture */
virtual RID get_color_texture(); /* obtain color output texture (if applicable) */
virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */
virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */
@ -149,19 +149,16 @@ public:
virtual bool start_passthrough() { return false; }
virtual void stop_passthrough() {}
/** environment blend mode. */
/** environment blend mode **/
virtual Array get_supported_environment_blend_modes();
virtual XRInterface::EnvironmentBlendMode get_environment_blend_mode() const { return XR_ENV_BLEND_MODE_OPAQUE; }
virtual bool set_environment_blend_mode(EnvironmentBlendMode mode) { return false; }
/** VRS **/
virtual RID get_vrs_texture(); /* obtain VRS texture */
XRInterface();
~XRInterface();
private:
struct VRSData {
RID vrs_texture;
Size2i size;
} vrs;
};
VARIANT_ENUM_CAST(XRInterface::Capabilities);

151
servers/xr/xr_vrs.cpp Normal file
View File

@ -0,0 +1,151 @@
/**************************************************************************/
/* xr_vrs.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "xr_vrs.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering_server.h"
void XRVRS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &XRVRS::get_vrs_min_radius);
ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &XRVRS::set_vrs_min_radius);
ClassDB::bind_method(D_METHOD("get_vrs_strength"), &XRVRS::get_vrs_strength);
ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &XRVRS::set_vrs_strength);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
ClassDB::bind_method(D_METHOD("make_vrs_texture", "target_size", "eye_foci"), &XRVRS::make_vrs_texture);
}
XRVRS::~XRVRS() {
if (vrs_texture.is_valid()) {
ERR_FAIL_NULL(RS::get_singleton());
RS::get_singleton()->free(vrs_texture);
vrs_texture = RID();
}
}
float XRVRS::get_vrs_min_radius() const {
return vrs_min_radius;
}
void XRVRS::set_vrs_min_radius(float p_vrs_min_radius) {
if (p_vrs_min_radius < 1.0) {
WARN_PRINT_ONCE("VRS minimum radius can not be set below 1.0");
vrs_min_radius = 1.0;
} else if (p_vrs_min_radius > 100.0) {
WARN_PRINT_ONCE("VRS minimum radius can not be set above 100.0");
vrs_min_radius = 100.0;
} else {
vrs_min_radius = p_vrs_min_radius;
vrs_dirty = true;
}
}
float XRVRS::get_vrs_strength() const {
return vrs_strength;
}
void XRVRS::set_vrs_strength(float p_vrs_strength) {
if (p_vrs_strength < 0.1) {
WARN_PRINT_ONCE("VRS strength can not be set below 0.1");
vrs_strength = 0.1;
} else if (p_vrs_strength > 10.0) {
WARN_PRINT_ONCE("VRS strength can not be set above 10.0");
vrs_strength = 10.0;
} else {
vrs_strength = p_vrs_strength;
vrs_dirty = true;
}
}
RID XRVRS::make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array &p_eye_foci) {
ERR_FAIL_COND_V(p_eye_foci.is_empty(), RID());
int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH);
int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT);
Size2 vrs_size = Size2(0.5 + p_target_size.x / texel_width, 0.5 + p_target_size.y / texel_height).round();
float max_radius = 0.5 * MIN(vrs_size.x, vrs_size.y); // Maximum radius that fits inside of our image
float min_radius = vrs_min_radius * max_radius / 100.0; // Minimum radius as a percentage of our size
real_t outer_radius = MAX(1.0, (max_radius - min_radius) / vrs_strength);
Size2 vrs_sizei = vrs_size;
// Our density map is now unified, with a value of (0.0, 0.0) meaning a 1x1 texel size and (1.0, 1.0) an max texel size.
// For our standard VRS extension on Vulkan this means a maximum of 8x8.
// For the density map extension this scales depending on the max texel size.
if (target_size != vrs_sizei || eye_foci != p_eye_foci || vrs_dirty) {
// Out with the old.
if (vrs_texture.is_valid()) {
RS::get_singleton()->free(vrs_texture);
vrs_texture = RID();
}
// In with the new.
Vector<Ref<Image>> images;
target_size = vrs_sizei;
eye_foci = p_eye_foci;
for (int i = 0; i < eye_foci.size() && i < RendererSceneRender::MAX_RENDER_VIEWS; i++) {
PackedByteArray data;
data.resize(vrs_sizei.x * vrs_sizei.y * 2);
uint8_t *data_ptr = data.ptrw();
Vector2i view_center;
view_center.x = int(vrs_size.x * (eye_foci[i].x + 1.0) * 0.5);
view_center.y = int(vrs_size.y * (eye_foci[i].y + 1.0) * 0.5);
int d = 0;
for (int y = 0; y < vrs_sizei.y; y++) {
for (int x = 0; x < vrs_sizei.x; x++) {
Vector2 offset = Vector2(x - view_center.x, y - view_center.y);
real_t density = 255.0 * MAX(0.0, (Math::abs(offset.x) - min_radius) / outer_radius);
data_ptr[d++] = MIN(255, density);
density = 255.0 * MAX(0.0, (Math::abs(offset.y) - min_radius) / outer_radius);
data_ptr[d++] = MIN(255, density);
}
}
images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_RG8, data));
}
if (images.size() == 1) {
vrs_texture = RS::get_singleton()->texture_2d_create(images[0]);
} else {
vrs_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TEXTURE_LAYERED_2D_ARRAY);
}
vrs_dirty = false;
}
return vrs_texture;
}

67
servers/xr/xr_vrs.h Normal file
View File

@ -0,0 +1,67 @@
/**************************************************************************/
/* xr_vrs.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef XR_VRS_H
#define XR_VRS_H
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/templates/rid.h"
#include "core/variant/variant.h"
/* This is a helper class for generating stereoscopic VRS images */
class XRVRS : public Object {
GDCLASS(XRVRS, Object);
private:
float vrs_min_radius = 20.0;
float vrs_strength = 1.0;
bool vrs_dirty = true;
RID vrs_texture;
Size2i target_size;
PackedVector2Array eye_foci;
protected:
static void _bind_methods();
public:
~XRVRS();
float get_vrs_min_radius() const;
void set_vrs_min_radius(float p_vrs_min_radius);
float get_vrs_strength() const;
void set_vrs_strength(float p_vrs_strength);
RID make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array &p_eye_foci);
};
#endif // XR_VRS_H