Skip to content

Commit

Permalink
Improvements to VRS/Foveated rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Mar 26, 2024
1 parent 7c6e85d commit 7b43ddb
Show file tree
Hide file tree
Showing 26 changed files with 305 additions and 60 deletions.
23 changes: 22 additions & 1 deletion doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4022,6 +4022,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>
Expand Down Expand Up @@ -4962,11 +4971,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>
Expand Down
15 changes: 15 additions & 0 deletions doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,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>
Expand Down Expand Up @@ -635,5 +638,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>
8 changes: 8 additions & 0 deletions doc/classes/XRInterface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@
<member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" enum="XRInterface.PlayAreaMode" default="0">
The play area mode for this interface.
</member>
<member name="xr_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.
</member>
<member name="xr_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].
[b]Note:[/b] On the compatibility renderer use [member OpenXRInterface.foveation_level].
</member>
</members>
<signals>
<signal name="play_area_changed">
Expand Down
4 changes: 4 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) + ".");
Expand Down Expand Up @@ -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;
}
}
Expand Down
1 change: 1 addition & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
2 changes: 2 additions & 0 deletions drivers/gles3/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ class TextureStorage : public RendererTextureStorage {

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(); }

Expand Down
8 changes: 7 additions & 1 deletion drivers/vulkan/rendering_device_driver_vulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions drivers/vulkan/rendering_device_driver_vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
11 changes: 8 additions & 3 deletions modules/mobile_vr/mobile_vr_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,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;
}

// and add our blits
BlitToScreen blit;
Expand Down
35 changes: 35 additions & 0 deletions scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3707,6 +3707,28 @@ Viewport::VRSMode Viewport::get_vrs_mode() const {
return vrs_mode;
}

void Viewport::set_vrs_update_mode(Viewport::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;
Expand Down Expand Up @@ -4743,6 +4765,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);

Expand Down Expand Up @@ -4775,6 +4800,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_");
Expand Down Expand Up @@ -4898,12 +4924,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() {
Expand Down
12 changes: 12 additions & 0 deletions scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ class Viewport : public Node {
VRS_MAX
};

enum VRSUpdateMode {
VRS_UPDATE_DISABLED,
VRS_UPDATE_ONCE,
VRS_UPDATE_ALWAYS,
VRS_UPDATE_MAX
};

private:
friend class ViewportTexture;

Expand Down Expand Up @@ -337,6 +344,7 @@ class Viewport : public Node {

// VRS
VRSMode vrs_mode = VRS_DISABLED;
VRSUpdateMode vrs_update_mode = VRS_UPDATE_ONCE;
Ref<Texture2D> vrs_texture;

struct GUI {
Expand Down Expand Up @@ -636,6 +644,9 @@ class Viewport : public Node {
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;

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions servers/rendering/dummy/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ class TextureStorage : public RendererTextureStorage {

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(); }

Expand Down
22 changes: 17 additions & 5 deletions servers/rendering/renderer_rd/effects/vrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,24 @@ 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());

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();
}
Expand All @@ -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()) {
Expand Down Expand Up @@ -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();
}
}
7 changes: 4 additions & 3 deletions servers/rendering/renderer_rd/effects/vrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ class VRS {
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;
Expand Down
Loading

0 comments on commit 7b43ddb

Please sign in to comment.