1
0
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
Josh Jones 9d3eb1f8ff
Merge bed8d6aa20 into 15ff450680 2025-02-28 05:59:33 +00:00
Josh Jones bed8d6aa20 Support output to HDR monitors
Co-authored-by: Alvin Wong <alvinhochun@gmail.com>
2025-02-27 21:20:25 -08:00
55 changed files with 1976 additions and 123 deletions

View File

@ -1510,6 +1510,13 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/size/no_focus", false);
GLOBAL_DEF("display/window/size/sharp_corners", false);
GLOBAL_DEF("display/window/hdr/enabled", false);
GLOBAL_DEF("display/window/hdr/prefer_high_precision", false);
GLOBAL_DEF("display/window/hdr/use_screen_luminance", true);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "display/window/hdr/reference_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), 80.0f); // sRGB standard of 80 nits
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "display/window/hdr/min_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), 0.0f);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "display/window/hdr/max_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), 1000.0f);
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution

View File

@ -1090,6 +1090,25 @@
[b]Note:[/b] On macOS, this method requires "Screen Recording" permission, if permission is not granted it will return desktop wallpaper color.
</description>
</method>
<method name="screen_get_max_average_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="screen" type="int" default="-1" />
<description>
Returns the maximum full screen luminance of [param screen] in nits (cd/m²).
Some displays support brighter luminance in small areas, but this is the maximum luminance that can be achieved across the entire screen.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] On Windows, this method is implemented only in builds with D3D12 enabled.
</description>
</method>
<method name="screen_get_max_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="screen" type="int" default="-1" />
<description>
Returns the maximum luminance of a pixel on [param screen] in nits (cd/m²).
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] On Windows, this method is implemented only in builds with D3D12 enabled.
</description>
</method>
<method name="screen_get_max_scale" qualifiers="const">
<return type="float" />
<description>
@ -1098,6 +1117,15 @@
[b]Note:[/b] This method is implemented only on macOS.
</description>
</method>
<method name="screen_get_min_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="screen" type="int" default="-1" />
<description>
Returns the minimum luminance of a pixel on [param screen] in nits (cd/m²).
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] On Windows, this method is implemented only in builds with D3D12 enabled.
</description>
</method>
<method name="screen_get_orientation" qualifiers="const">
<return type="int" enum="DisplayServer.ScreenOrientation" />
<param index="0" name="screen" type="int" default="-1" />
@ -1156,6 +1184,15 @@
[b]Note:[/b] This method is implemented on Android, iOS, Web, macOS, and Linux (Wayland).
</description>
</method>
<method name="screen_get_sdr_white_level" qualifiers="const">
<return type="float" />
<param index="0" name="screen" type="int" default="-1" />
<description>
Returns the standard dynamic range (SDR) white level of [param screen] in nits (cd/m²).
This is the maximum brightness SDR content (such as UI) should be rendered at to match other applications on the display.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="screen_get_size" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="screen" type="int" default="-1" />
@ -1170,6 +1207,15 @@
Returns the portion of the screen that is not obstructed by a status bar in pixels. See also [method screen_get_size].
</description>
</method>
<method name="screen_is_hdr_supported" qualifiers="const">
<return type="bool" />
<param index="0" name="screen" type="int" default="-1" />
<description>
Returns [code]true[/code] if the [param screen] supports HDR.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] On Windows, this method is implemented only in builds with D3D12 enabled.
</description>
</method>
<method name="screen_is_kept_on" qualifiers="const">
<return type="bool" />
<description>
@ -1481,6 +1527,30 @@
Returns the current value of the given window's [param flag].
</description>
</method>
<method name="window_get_hdr_output_max_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns the maximum luminance in nits (cd/m²) set for HDR content for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_get_hdr_output_min_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns the minimum luminance in nits (cd/m²) set for HDR content for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_get_hdr_output_reference_luminance" qualifiers="const">
<return type="float" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns the SDR reference luminance in nits (cd/m²) set for HDR content for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_get_max_size" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="window_id" type="int" default="0" />
@ -1576,6 +1646,29 @@
Returns [code]true[/code] if the window specified by [param window_id] is focused.
</description>
</method>
<method name="window_is_hdr_output_enabled" qualifiers="const">
<return type="bool" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns whether HDR output is requested for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_is_hdr_output_preferring_high_precision" qualifiers="const">
<return type="bool" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns whether the window specified by [param window_id] prefers a high precision (16-bit per color) HDR framebuffer.
</description>
</method>
<method name="window_is_hdr_output_using_screen_luminance" qualifiers="const">
<return type="bool" />
<param index="0" name="window_id" type="int" default="0" />
<description>
Returns whether the window specified by [param window_id] is using the screen's luminance for HDR output.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_is_maximize_allowed" qualifiers="const">
<return type="bool" />
<param index="0" name="window_id" type="int" default="0" />
@ -1648,6 +1741,71 @@
Enables or disables the given window's given [param flag]. See [enum WindowFlags] for possible values and their behavior.
</description>
</method>
<method name="window_set_hdr_output_enabled">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets whether HDR output should be enabled for the window specified by [param window_id], falling back to SDR if not supported.
Only available on platforms that support HDR output, have HDR enabled in the system settings, and have a compatible display connected.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] Requires support by the rendering device.
</description>
</method>
<method name="window_set_hdr_output_max_luminance">
<return type="void" />
<param index="0" name="max_luminance" type="float" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the maximum luminance in nits (cd/m²) for HDR content for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] Requires support by the rendering device.
[b]Note:[/b] Ignored if [method window_is_hdr_output_using_screen_luminance] is [code]true[/code].
</description>
</method>
<method name="window_set_hdr_output_min_luminance">
<return type="void" />
<param index="0" name="min_luminance" type="float" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the minimum luminance in nits (cd/m²) for HDR content for the window specified by [param window_id].
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] Requires support by the rendering device.
[b]Note:[/b] Ignored if [method window_is_hdr_output_using_screen_luminance] is [code]true[/code].
</description>
</method>
<method name="window_set_hdr_output_prefer_high_precision">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets whether the window specified by [param window_id] prefers a high precision (16-bit per color) framebuffer.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] Requires support by the rendering device.
</description>
</method>
<method name="window_set_hdr_output_reference_luminance">
<return type="void" />
<param index="0" name="reference_luminance" type="float" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the SDR reference luminance in nits (cd/m²) for HDR content for the window specified by [param window_id].
This controls the brightness of SDR content (such as UI) when HDR is enabled.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
[b]Note:[/b] Requires support by the rendering device.
[b]Note:[/b] Ignored if [method window_is_hdr_output_using_screen_luminance] is [code]true[/code].
</description>
</method>
<method name="window_set_hdr_output_use_screen_luminance">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets whether the window specified by [param window_id] should use the screen's luminance for HDR output.
While this is enabled, the window will ignore attempts to set luminance values.
[b]Note:[/b] Requires support for [constant FEATURE_HDR].
</description>
</method>
<method name="window_set_ime_active">
<return type="void" />
<param index="0" name="active" type="bool" />
@ -1952,6 +2110,9 @@
<constant name="FEATURE_EMOJI_AND_SYMBOL_PICKER" value="31" enum="Feature">
Display server supports system emoji and symbol picker. [b]Windows, macOS[/b]
</constant>
<constant name="FEATURE_HDR" value="32" enum="Feature">
Display server supports HDR output. [b]Windows[/b]
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>

View File

@ -836,6 +836,30 @@
The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values.
[b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/viewport_width] and [member display/window/size/viewport_height] accordingly.
</member>
<member name="display/window/hdr/enabled" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables HDR output on supported platforms, falling back to SDR if not supported.
Only available on platforms that support HDR output, have HDR enabled in the system settings, and have a compatible display connected.
</member>
<member name="display/window/hdr/max_luminance" type="float" setter="" getter="" default="1000.0">
Sets the maximum luminance of the display in nits (cd/m²) when HDR is enabled.
</member>
<member name="display/window/hdr/min_luminance" type="float" setter="" getter="" default="0.0">
Sets the minimum luminance of the display in nits (cd/m²) when HDR is enabled.
</member>
<member name="display/window/hdr/prefer_high_precision" type="bool" setter="" getter="" default="false">
If [code]true[/code], uses 16 bit per color buffers for the frame buffer when HDR is enabled.
If [code]false[/code], uses 10 bit per color buffers, with 2 bits for alpha, for the frame buffer when HDR is enabled.
This may improve color precision and reduce banding in the final image, but will use more memory and may reduce performance.
If transparent backgrounds are enabled, you may want to enable this setting to enable smooth blending of HDR content.
</member>
<member name="display/window/hdr/reference_luminance" type="float" setter="" getter="" default="80.0">
Sets the SDR reference luminance of the display in nits (cd/m²) when HDR is enabled.
This is used to scale the HDR effect to ensure sRGB content looks the same in both SDR and HDR.
</member>
<member name="display/window/hdr/use_screen_luminance" type="bool" setter="" getter="" default="true">
If [code]true[/code], uses the screen's luminance as reported by the display server to set luminance for the window.
If information is missing for the display, will fall back to luminance values set in project settings.
</member>
<member name="display/window/ios/allow_high_refresh_rate" type="bool" setter="" getter="" default="true">
If [code]true[/code], iOS devices that support high refresh rate/"ProMotion" will be allowed to render at up to 120 frames per second.
</member>
@ -3184,6 +3208,9 @@
[b]Note:[/b] This setting will have no effect when using the Compatibility renderer, which always renders in low dynamic range for performance reasons.
[b]Note:[/b] This property is only read when the project starts. To toggle HDR 2D at runtime, set [member Viewport.use_hdr_2d] on the root [Viewport].
</member>
<member name="rendering/viewport/tonemap_to_window" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables [member Viewport.tonemap_to_window] on the root viewport.
</member>
<member name="rendering/viewport/transparent_background" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables [member Viewport.transparent_bg] on the root viewport. This allows per-pixel transparency to be effective after also enabling [member display/window/size/transparent] and [member display/window/per_pixel_transparency/allowed].
</member>

View File

@ -758,6 +758,22 @@
Returns [code]true[/code] if implementation supports using a texture of [param format] with the given [param sampler_filter].
</description>
</method>
<method name="screen_get_color_format" qualifiers="const">
<return type="int" enum="RenderingDevice.DataFormat" />
<param index="0" name="screen" type="int" default="0" />
<description>
Returns the color format of the given screen.
[b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a format. If called on a local [RenderingDevice], this method prints an error and returns [constant DATA_FORMAT_MAX].
</description>
</method>
<method name="screen_get_color_space" qualifiers="const">
<return type="int" enum="RenderingDevice.ColorSpace" />
<param index="0" name="screen" type="int" default="0" />
<description>
Returns the color space of the given screen.
[b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a format. If called on a local [RenderingDevice], this method prints an error and returns [constant COLOR_SPACE_MAX].
</description>
</method>
<method name="screen_get_framebuffer_format" qualifiers="const">
<return type="int" />
<param index="0" name="screen" type="int" default="0" />
@ -1874,6 +1890,18 @@
<constant name="DATA_FORMAT_MAX" value="218" enum="DataFormat">
Represents the size of the [enum DataFormat] enum.
</constant>
<constant name="COLOR_SPACE_SRGB_LINEAR" value="0" enum="ColorSpace">
Color space using Rec 709 primaries and linear gamma.
</constant>
<constant name="COLOR_SPACE_SRGB_NONLINEAR" value="1" enum="ColorSpace">
Color space using Rec 709 primaries and non-linear sRGB gamma.
</constant>
<constant name="COLOR_SPACE_HDR10_ST2084" value="2" enum="ColorSpace">
Color space using Rec 2020 primaries and the ST 2084 transfer function.
</constant>
<constant name="COLOR_SPACE_MAX" value="3" enum="ColorSpace">
Represents the size of the [enum ColorSpace] enum.
</constant>
<constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true">
Vertex shader barrier mask.
</constant>
@ -2454,6 +2482,9 @@
<constant name="SUPPORTS_BUFFER_DEVICE_ADDRESS" value="6" enum="Features">
Features support for buffer device address extension.
</constant>
<constant name="SUPPORTS_HDR_OUTPUT" value="7" enum="Features">
Features support for high dynamic range (HDR) output.
</constant>
<constant name="LIMIT_MAX_BOUND_UNIFORM_SETS" value="0" enum="Limit">
Maximum number of uniform sets that can be bound at a given time.
</constant>

View File

@ -1493,6 +1493,16 @@
Sets the variables to be used with the "tonemap" post-process effect. See [Environment] for more details.
</description>
</method>
<method name="environment_set_tonemap_range">
<return type="void" />
<param index="0" name="env" type="RID" />
<param index="1" name="min_value" type="float" />
<param index="2" name="max_value" type="float" />
<description>
Sets the range of the tonemapper, defining the minimum and maximum values after tonemapping.
[b]Note:[/b] This is ignored when using [constant ENV_TONE_MAPPER_LINEAR].
</description>
</method>
<method name="environment_set_volumetric_fog">
<return type="void" />
<param index="0" name="env" type="RID" />
@ -4190,6 +4200,14 @@
[b]Note:[/b] When the 3D scaling mode is set to FSR 1.0, this value is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]-log2(1.0 / scale) + mipmap_bias[/code].
</description>
</method>
<method name="viewport_set_tonemap_to_screen">
<return type="void" />
<param index="0" name="viewport" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
If [code]true[/code], the viewport will tonemap its content to the window's HDR output luminance range, if HDR output is enabled.
</description>
</method>
<method name="viewport_set_transparent_background">
<return type="void" />
<param index="0" name="viewport" type="RID" />

View File

@ -415,6 +415,10 @@
[b]Note:[/b] If [member scaling_3d_scale] is lower than [code]1.0[/code] (exclusive), [member texture_mipmap_bias] is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]log2(scaling_3d_scale) + mipmap_bias[/code].
To control this property on the root viewport, set the [member ProjectSettings.rendering/textures/default_filters/texture_mipmap_bias] project setting.
</member>
<member name="tonemap_to_window" type="bool" setter="set_tonemap_to_window" getter="is_tonemapping_to_window" default="false">
If [code]true[/code], the viewport will tonemap its content to the window's HDR output luminance range, if HDR output is enabled.
This is useful for viewports that are not the root of the window, but still need their content to adjust to the capabilities of the display.
</member>
<member name="transparent_bg" type="bool" setter="set_transparent_background" getter="has_transparent_background" default="false">
If [code]true[/code], the viewport should render its background as transparent.
</member>

View File

@ -614,6 +614,26 @@
<member name="force_native" type="bool" setter="set_force_native" getter="get_force_native" default="false">
If [code]true[/code], native window will be used regardless of parent viewport and project settings.
</member>
<member name="hdr_output_enabled" type="bool" setter="set_hdr_output_enabled" getter="is_hdr_output_enabled" default="false">
If [code]true[/code], the [Window] will output in HDR.
Only available on platforms that support HDR output, have HDR enabled in the system settings, and have a compatible display connected.
</member>
<member name="hdr_output_max_luminance" type="float" setter="set_hdr_output_max_luminance" getter="get_hdr_output_max_luminance" default="1000.0">
The maximum luminance in nits (cd/m²) when HDR is enabled.
</member>
<member name="hdr_output_min_luminance" type="float" setter="set_hdr_output_min_luminance" getter="get_hdr_output_min_luminance" default="0.0">
The minimum luminance in nits (cd/m²) when HDR is enabled.
</member>
<member name="hdr_output_prefer_high_precision" type="bool" setter="set_hdr_output_prefer_high_precision" getter="is_hdr_output_preferring_high_precision" default="false">
If [code]true[/code], the [Window] will prefer high-precision (16-bit per color) framebuffer when available and HDR is enabled.
</member>
<member name="hdr_output_reference_luminance" type="float" setter="set_hdr_output_reference_luminance" getter="get_hdr_output_reference_luminance" default="80.0">
The SDR reference luminance in nits (cd/m²) when HDR is enabled.
This controls the brightness of SDR content (such as UI) when HDR is enabled.
</member>
<member name="hdr_output_use_screen_luminance" type="bool" setter="set_hdr_output_use_screen_luminance" getter="is_hdr_output_using_screen_luminance" default="false">
If [code]true[/code], the [Window] will use the screen's luminance to adjust the HDR output.
</member>
<member name="initial_position" type="int" setter="set_initial_position" getter="get_initial_position" enum="Window.WindowInitialPosition" default="0">
Specifies the initial type of position for the [Window]. See [enum WindowInitialPosition] constants.
</member>
@ -693,6 +713,7 @@
<member name="title" type="String" setter="set_title" getter="get_title" default="&quot;&quot;">
The window's title. If the [Window] is native, title styles set in [Theme] will have no effect.
</member>
<member name="tonemap_to_window" type="bool" setter="set_tonemap_to_window" getter="is_tonemapping_to_window" overrides="Viewport" default="true" />
<member name="transient" type="bool" setter="set_transient" getter="is_transient" default="false">
If [code]true[/code], the [Window] is transient, i.e. it's considered a child of another [Window]. The transient window will be destroyed with its transient parent and will return focus to their parent when closed. The transient window is displayed on top of a non-exclusive full-screen parent window. Transient windows can't enter full-screen mode.
Note that behavior might be different depending on the platform.

View File

@ -282,6 +282,58 @@ DisplayServer::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(Sur
return surface->vsync_mode;
}
void RenderingContextDriverD3D12::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_output = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverD3D12::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_output;
}
void RenderingContextDriverD3D12::surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_prefer_high_precision = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverD3D12::surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_prefer_high_precision;
}
void RenderingContextDriverD3D12::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_reference_luminance = p_reference_luminance;
}
float RenderingContextDriverD3D12::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_reference_luminance;
}
void RenderingContextDriverD3D12::surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_min_luminance = p_min_luminance;
}
float RenderingContextDriverD3D12::surface_get_hdr_output_min_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_min_luminance;
}
void RenderingContextDriverD3D12::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_max_luminance = p_max_luminance;
}
float RenderingContextDriverD3D12::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_max_luminance;
}
uint32_t RenderingContextDriverD3D12::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;

View File

@ -109,6 +109,16 @@ public:
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) override;
virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) override;
virtual bool surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) override;
virtual float surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) override;
virtual float surface_get_hdr_output_min_luminance(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) override;
virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const override;
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
@ -128,6 +138,13 @@ public:
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
bool hdr_output = false;
bool hdr_prefer_high_precision = false;
float hdr_reference_luminance = 0.0f;
float hdr_min_luminance = 0.0f;
float hdr_max_luminance = 0.0f;
ComPtr<IDCompositionDevice> composition_device;
ComPtr<IDCompositionTarget> composition_target;
ComPtr<IDCompositionVisual> composition_visual;

View File

@ -334,6 +334,12 @@ const RenderingDeviceDriverD3D12::D3D12Format RenderingDeviceDriverD3D12::RD_TO_
/* DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM */ {},
};
const DXGI_COLOR_SPACE_TYPE RenderingDeviceDriverD3D12::RD_TO_DXGI_COLOR_SPACE_TYPE[RDD::COLOR_SPACE_MAX]{
/* COLOR_SPACE_SRGB_LINEAR */ DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709,
/* COLOR_SPACE_SRGB_NONLINEAR */ DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,
/* COLOR_SPACE_HDR10_ST2084 */ DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
};
Error RenderingDeviceDriverD3D12::DescriptorsHeap::allocate(ID3D12Device *p_device, D3D12_DESCRIPTOR_HEAP_TYPE p_type, uint32_t p_descriptor_count, bool p_for_gpu) {
ERR_FAIL_COND_V(heap, ERR_ALREADY_EXISTS);
ERR_FAIL_COND_V(p_descriptor_count == 0, ERR_INVALID_PARAMETER);
@ -2405,6 +2411,11 @@ void RenderingDeviceDriverD3D12::command_buffer_execute_secondary(CommandBufferI
void RenderingDeviceDriverD3D12::_swap_chain_release(SwapChain *p_swap_chain) {
_swap_chain_release_buffers(p_swap_chain);
if (p_swap_chain->render_pass.id != 0) {
render_pass_free(p_swap_chain->render_pass);
p_swap_chain->render_pass = RenderPassID();
}
p_swap_chain->d3d_swap_chain.Reset();
}
@ -2423,10 +2434,10 @@ void RenderingDeviceDriverD3D12::_swap_chain_release_buffers(SwapChain *p_swap_c
p_swap_chain->framebuffers.clear();
}
RDD::SwapChainID RenderingDeviceDriverD3D12::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) {
RDD::RenderPassID RenderingDeviceDriverD3D12::_swap_chain_create_render_pass(RDD::DataFormat p_format) {
// Create the render pass that will be used to draw to the swap chain's framebuffers.
RDD::Attachment attachment;
attachment.format = DATA_FORMAT_R8G8B8A8_UNORM;
attachment.format = p_format;
attachment.samples = RDD::TEXTURE_SAMPLES_1;
attachment.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR;
attachment.store_op = RDD::ATTACHMENT_STORE_OP_STORE;
@ -2437,14 +2448,35 @@ RDD::SwapChainID RenderingDeviceDriverD3D12::swap_chain_create(RenderingContextD
color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT);
subpass.color_references.push_back(color_ref);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1);
ERR_FAIL_COND_V(!render_pass, SwapChainID());
return render_pass_create(attachment, subpass, {}, 1);
}
// Create the empty swap chain until it is resized.
void RenderingDeviceDriverD3D12::_determine_swap_chain_format(SwapChain *p_swap_chain, DataFormat &r_format, ColorSpace &r_color_space) {
DEV_ASSERT(p_swap_chain);
DEV_ASSERT(p_swap_chain->surface != 0);
// Direct3D Hardware level 10 mandates support for all these formats.
// Godot requires at least Hardware level 11, so these formats are guaranteed to be supported.
if (context_driver->surface_get_hdr_output_enabled(p_swap_chain->surface)) {
if (context_driver->surface_get_hdr_output_prefer_high_precision(p_swap_chain->surface)) {
r_format = DATA_FORMAT_R16G16B16A16_SFLOAT;
r_color_space = COLOR_SPACE_SRGB_LINEAR;
} else {
r_format = DATA_FORMAT_A2R10G10B10_UNORM_PACK32;
r_color_space = COLOR_SPACE_HDR10_ST2084;
}
} else {
r_format = DATA_FORMAT_R8G8B8A8_UNORM;
r_color_space = COLOR_SPACE_SRGB_NONLINEAR;
}
}
RDD::SwapChainID RenderingDeviceDriverD3D12::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) {
DEV_ASSERT(p_surface != 0);
// Create an empty swap chain until it is resized.
SwapChain *swap_chain = memnew(SwapChain);
swap_chain->surface = p_surface;
swap_chain->data_format = attachment.format;
swap_chain->render_pass = render_pass;
return SwapChainID(swap_chain);
}
@ -2486,17 +2518,27 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
if (swap_chain->d3d_swap_chain != nullptr && creation_flags != swap_chain->creation_flags) {
// The swap chain must be recreated if the creation flags are different.
RDD::DataFormat new_data_format;
RDD::ColorSpace new_color_space;
_determine_swap_chain_format(swap_chain, new_data_format, new_color_space);
if (swap_chain->d3d_swap_chain != nullptr && (creation_flags != swap_chain->creation_flags || new_data_format != swap_chain->data_format)) {
// The swap chain must be recreated if the creation flags or data format are different.
_swap_chain_release(swap_chain);
}
swap_chain->data_format = new_data_format;
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
if (swap_chain->d3d_swap_chain != nullptr) {
_swap_chain_release_buffers(swap_chain);
res = swap_chain->d3d_swap_chain->ResizeBuffers(p_desired_framebuffer_count, surface->width, surface->height, DXGI_FORMAT_UNKNOWN, creation_flags);
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_UNAVAILABLE);
} else {
DEV_ASSERT(swap_chain->render_pass.id == 0);
swap_chain->render_pass = _swap_chain_create_render_pass(new_data_format);
ERR_FAIL_COND_V(!swap_chain->render_pass, ERR_CANT_CREATE);
swap_chain_desc.BufferCount = p_desired_framebuffer_count;
swap_chain_desc.Format = RD_TO_D3D12_FORMAT[swap_chain->data_format].general_format;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
@ -2525,6 +2567,13 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
}
if (swap_chain->color_space != new_color_space) {
res = swap_chain->d3d_swap_chain->SetColorSpace1(RD_TO_DXGI_COLOR_SPACE_TYPE[new_color_space]);
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
swap_chain->color_space = new_color_space;
}
if (surface->composition_device.Get() == nullptr) {
using PFN_DCompositionCreateDevice = HRESULT(WINAPI *)(IDXGIDevice *, REFIID, void **);
PFN_DCompositionCreateDevice pfn_DCompositionCreateDevice = (PFN_DCompositionCreateDevice)(void *)GetProcAddress(context_driver->lib_dcomp, "DCompositionCreateDevice");
@ -2634,10 +2683,14 @@ RDD::DataFormat RenderingDeviceDriverD3D12::swap_chain_get_format(SwapChainID p_
return swap_chain->data_format;
}
RDD::ColorSpace RenderingDeviceDriverD3D12::swap_chain_get_color_space(SwapChainID p_swap_chain) {
const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id);
return swap_chain->color_space;
}
void RenderingDeviceDriverD3D12::swap_chain_free(SwapChainID p_swap_chain) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
_swap_chain_release(swap_chain);
render_pass_free(swap_chain->render_pass);
memdelete(swap_chain);
}
@ -6280,6 +6333,8 @@ bool RenderingDeviceDriverD3D12::has_feature(Features p_feature) {
return true;
case SUPPORTS_BUFFER_DEVICE_ADDRESS:
return true;
case SUPPORTS_HDR_OUTPUT:
return true;
default:
return false;
}

View File

@ -100,6 +100,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
};
static const D3D12Format RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX];
static const DXGI_COLOR_SPACE_TYPE RD_TO_DXGI_COLOR_SPACE_TYPE[RDD::COLOR_SPACE_MAX];
struct DeviceLimits {
uint64_t max_srvs_per_shader_stage = 0;
@ -506,10 +507,13 @@ private:
TightLocalVector<TextureInfo> render_targets_info;
TightLocalVector<FramebufferID> framebuffers;
RDD::DataFormat data_format = DATA_FORMAT_MAX;
RDD::ColorSpace color_space = COLOR_SPACE_MAX;
};
void _swap_chain_release(SwapChain *p_swap_chain);
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
RenderPassID _swap_chain_create_render_pass(RDD::DataFormat p_format);
void _determine_swap_chain_format(SwapChain *p_swap_chain, DataFormat &r_format, ColorSpace &r_color_space);
public:
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override;
@ -517,6 +521,7 @@ public:
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override;
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override;
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
/*********************/

View File

@ -76,6 +76,16 @@ public:
void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) final override;
void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) final override;
DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const final override;
virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) final override;
virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const final override;
virtual void surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) override;
virtual bool surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) final override;
virtual float surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const final override;
virtual void surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) final override;
virtual float surface_get_hdr_output_min_luminance(SurfaceID p_surface) const final override;
virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) final override;
virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const final override;
uint32_t surface_get_width(SurfaceID p_surface) const final override;
uint32_t surface_get_height(SurfaceID p_surface) const final override;
void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) final override;
@ -109,6 +119,12 @@ public:
bool needs_resize = false;
double present_minimum_duration = 0.0;
bool hdr_output = false;
bool hdr_prefer_high_precision = false;
float hdr_reference_luminance = 0.0f;
float hdr_min_luminance = 0.0f;
float hdr_max_luminance = 0.0f;
Surface(
#ifdef __OBJC__
id<MTLDevice> p_device

View File

@ -211,6 +211,58 @@ DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(Sur
return surface->vsync_mode;
}
void RenderingContextDriverMetal::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_output = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverMetal::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_output;
}
void RenderingContextDriverMetal::surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_prefer_high_precision = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverMetal::surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_prefer_high_precision;
}
void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_reference_luminance = p_reference_luminance;
}
float RenderingContextDriverMetal::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_reference_luminance;
}
void RenderingContextDriverMetal::surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_min_luminance = p_min_luminance;
}
float RenderingContextDriverMetal::surface_get_hdr_output_min_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_min_luminance;
}
void RenderingContextDriverMetal::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_max_luminance = p_max_luminance;
}
float RenderingContextDriverMetal::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_max_luminance;
}
uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;

View File

@ -222,6 +222,7 @@ public:
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
#pragma mark - Frame Buffer

View File

@ -1029,6 +1029,10 @@ void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain
metal_surface->set_max_fps(p_max_fps);
}
RDD::ColorSpace RenderingDeviceDriverMetal::swap_chain_get_color_space(SwapChainID p_swap_chain) {
return RDD::COLOR_SPACE_SRGB_NONLINEAR;
}
void RenderingDeviceDriverMetal::swap_chain_free(SwapChainID p_swap_chain) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
_swap_chain_release(swap_chain);

View File

@ -436,6 +436,9 @@ Error RenderingContextDriverVulkan::_initialize_instance_extensions() {
// This extension allows us to use the properties2 features to query additional device capabilities.
_register_requested_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
// This extension allows us to use colorspaces other than SRGB.
_register_requested_instance_extension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, false);
#if defined(USE_VOLK) && (defined(MACOS_ENABLED) || defined(IOS_ENABLED))
_register_requested_instance_extension(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, true);
#endif
@ -991,6 +994,58 @@ DisplayServer::VSyncMode RenderingContextDriverVulkan::surface_get_vsync_mode(Su
return surface->vsync_mode;
}
void RenderingContextDriverVulkan::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_output = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverVulkan::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_output;
}
void RenderingContextDriverVulkan::surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_prefer_high_precision = p_enabled;
surface->needs_resize = true;
}
bool RenderingContextDriverVulkan::surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_prefer_high_precision;
}
void RenderingContextDriverVulkan::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_reference_luminance = p_reference_luminance;
}
float RenderingContextDriverVulkan::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_reference_luminance;
}
void RenderingContextDriverVulkan::surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_min_luminance = p_min_luminance;
}
float RenderingContextDriverVulkan::surface_get_hdr_output_min_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_min_luminance;
}
void RenderingContextDriverVulkan::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_max_luminance = p_max_luminance;
}
float RenderingContextDriverVulkan::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_max_luminance;
}
uint32_t RenderingContextDriverVulkan::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;
@ -1021,6 +1076,10 @@ bool RenderingContextDriverVulkan::is_debug_utils_enabled() const {
return enabled_instance_extension_names.has(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
bool RenderingContextDriverVulkan::is_colorspace_supported() const {
return enabled_instance_extension_names.has(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
}
VkInstance RenderingContextDriverVulkan::instance_get() const {
return instance;
}

View File

@ -139,12 +139,23 @@ public:
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) override;
virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) override;
virtual bool surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) override;
virtual float surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) override;
virtual float surface_get_hdr_output_min_luminance(SurfaceID p_surface) const override;
virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) override;
virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const override;
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
virtual bool surface_get_needs_resize(SurfaceID p_surface) const override;
virtual void surface_destroy(SurfaceID p_surface) override;
virtual bool is_debug_utils_enabled() const override;
bool is_colorspace_supported() const;
// Vulkan-only methods.
struct Surface {
@ -153,6 +164,12 @@ public:
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
bool hdr_output = false;
bool hdr_prefer_high_precision = false;
float hdr_reference_luminance = 0.0f;
float hdr_min_luminance = 0.0f;
float hdr_max_luminance = 0.0f;
};
VkInstance instance_get() const;

View File

@ -2862,6 +2862,99 @@ void RenderingDeviceDriverVulkan::command_buffer_execute_secondary(CommandBuffer
/**** SWAP CHAIN ****/
/********************/
struct FormatCandidate {
VkFormat format;
VkColorSpaceKHR colorspace;
};
bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space) {
DEV_ASSERT(p_surface != 0);
RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface);
const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get();
// Retrieve the formats supported by the surface.
uint32_t format_count = 0;
VkResult err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, nullptr);
ERR_FAIL_COND_V(err != VK_SUCCESS, false);
TightLocalVector<VkSurfaceFormatKHR> formats;
formats.resize(format_count);
err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, formats.ptr());
ERR_FAIL_COND_V(err != VK_SUCCESS, false);
// If the format list includes just one entry of VK_FORMAT_UNDEFINED, the surface has no preferred format.
if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
r_format = VK_FORMAT_B8G8R8A8_UNORM;
r_color_space = formats[0].colorSpace;
return true;
}
bool colorspace_supported = context_driver->is_colorspace_supported();
bool hdr_output_requested = context_driver->surface_get_hdr_output_enabled(p_surface);
bool high_precision_requested = context_driver->surface_get_hdr_output_prefer_high_precision(p_surface);
// Determine which formats to prefer based on the requested capabilities.
LocalVector<FormatCandidate> preferred_formats = LocalVector<FormatCandidate>();
preferred_formats.reserve(5);
// If the surface requests HDR output, try to get an HDR format.
if (hdr_output_requested && colorspace_supported) {
if (high_precision_requested) {
// If high precision is requested, add the high precision format to the top.
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT });
// Followed by the HDR10 formats
preferred_formats.push_back({ VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT });
preferred_formats.push_back({ VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT });
} else {
// Otherwise add the HDR10 formats to the top.
preferred_formats.push_back({ VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT });
preferred_formats.push_back({ VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT });
// Followed by the high precision format for devices that don't support HDR 10.
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT });
}
}
// These formats are always considered for SDR.
preferred_formats.push_back({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
preferred_formats.push_back({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
bool found = false;
for (const FormatCandidate &candidate : preferred_formats) {
for (uint32_t i = 0; i < format_count; i++) {
if (formats[i].format == candidate.format && formats[i].colorSpace == candidate.colorspace) {
r_format = formats[i].format;
r_color_space = formats[i].colorSpace;
found = true;
break;
}
}
if (found) {
break;
}
}
// Warnings for when HDR capabilities are requested but not found.
if (hdr_output_requested) {
if (!colorspace_supported) {
WARN_PRINT("HDR output requested but the vulkan driver does not support VK_EXT_swapchain_colorspace, falling back to SDR.");
}
if (high_precision_requested && r_format != VK_FORMAT_R16G16B16A16_SFLOAT) {
WARN_PRINT("HDR output requested with high precision but no high precision format was found.");
}
if (r_color_space == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
WARN_PRINT("HDR output requested but no HDR compatible format was found, falling back to SDR.");
}
}
return found;
}
void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
// Destroy views and framebuffers associated to the swapchain's images.
for (FramebufferID framebuffer : swap_chain->framebuffers) {
@ -2890,6 +2983,11 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
swap_chain->vk_swapchain = VK_NULL_HANDLE;
}
if (swap_chain->render_pass.id != 0) {
vkDestroyRenderPass(vk_device, VkRenderPass(swap_chain->render_pass.id), VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS));
swap_chain->render_pass = RenderPassID();
}
for (uint32_t i = 0; i < swap_chain->command_queues_acquired.size(); i++) {
_recreate_image_semaphore(swap_chain->command_queues_acquired[i], swap_chain->command_queues_acquired_semaphores[i], false);
}
@ -2901,81 +2999,9 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
RenderingDeviceDriver::SwapChainID RenderingDeviceDriverVulkan::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) {
DEV_ASSERT(p_surface != 0);
RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface);
const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get();
// Retrieve the formats supported by the surface.
uint32_t format_count = 0;
VkResult err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, nullptr);
ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
TightLocalVector<VkSurfaceFormatKHR> formats;
formats.resize(format_count);
err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, formats.ptr());
ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
// If the format list includes just one entry of VK_FORMAT_UNDEFINED, the surface has no preferred format.
format = VK_FORMAT_B8G8R8A8_UNORM;
color_space = formats[0].colorSpace;
} else if (format_count > 0) {
// Use one of the supported formats, prefer B8G8R8A8_UNORM.
const VkFormat preferred_format = VK_FORMAT_B8G8R8A8_UNORM;
const VkFormat second_format = VK_FORMAT_R8G8B8A8_UNORM;
for (uint32_t i = 0; i < format_count; i++) {
if (formats[i].format == preferred_format || formats[i].format == second_format) {
format = formats[i].format;
if (formats[i].format == preferred_format) {
// This is the preferred format, stop searching.
break;
}
}
}
}
// No formats are supported.
ERR_FAIL_COND_V_MSG(format == VK_FORMAT_UNDEFINED, SwapChainID(), "Surface did not return any valid formats.");
// Create the render pass for the chosen format.
VkAttachmentDescription2KHR attachment = {};
attachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference2KHR color_reference = {};
color_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription2KHR subpass = {};
subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
VkRenderPassCreateInfo2KHR pass_info = {};
pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
pass_info.attachmentCount = 1;
pass_info.pAttachments = &attachment;
pass_info.subpassCount = 1;
pass_info.pSubpasses = &subpass;
VkRenderPass render_pass = VK_NULL_HANDLE;
err = _create_render_pass(vk_device, &pass_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS), &render_pass);
ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
// Create an empty swap chain until it is resized.
SwapChain *swap_chain = memnew(SwapChain);
swap_chain->surface = p_surface;
swap_chain->format = format;
swap_chain->color_space = color_space;
swap_chain->render_pass = RenderPassID(render_pass);
return SwapChainID(swap_chain);
}
@ -3109,6 +3135,16 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
has_comp_alpha[(uint64_t)p_cmd_queue.id] = (composite_alpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
}
// Determine the format and color space for the swap chain.
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
if (!_determine_swap_chain_format(swap_chain->surface, format, color_space)) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Surface did not return any valid formats.");
} else {
swap_chain->format = format;
swap_chain->color_space = color_space;
}
VkSwapchainCreateInfoKHR swap_create_info = {};
swap_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swap_create_info.surface = surface->vk_surface;
@ -3207,9 +3243,45 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_chain->framebuffers.reserve(image_count);
// Create the render pass for the chosen format.
VkAttachmentDescription2KHR attachment = {};
attachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference2KHR color_reference = {};
color_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription2KHR subpass = {};
subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
VkRenderPassCreateInfo2KHR pass_info = {};
pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
pass_info.attachmentCount = 1;
pass_info.pAttachments = &attachment;
pass_info.subpassCount = 1;
pass_info.pSubpasses = &subpass;
VkRenderPass render_pass = VK_NULL_HANDLE;
err = _create_render_pass(vk_device, &pass_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS), &render_pass);
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
DEV_ASSERT(swap_chain->render_pass.id == 0);
swap_chain->render_pass = RenderPassID(render_pass);
VkFramebufferCreateInfo fb_create_info = {};
fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fb_create_info.renderPass = VkRenderPass(swap_chain->render_pass.id);
fb_create_info.renderPass = render_pass;
fb_create_info.attachmentCount = 1;
fb_create_info.width = surface->width;
fb_create_info.height = surface->height;
@ -3322,6 +3394,12 @@ RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p
return DATA_FORMAT_B8G8R8A8_UNORM;
case VK_FORMAT_R8G8B8A8_UNORM:
return DATA_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return DATA_FORMAT_A2B10G10R10_UNORM_PACK32;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
return DATA_FORMAT_A2R10G10B10_UNORM_PACK32;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return DATA_FORMAT_R16G16B16A16_SFLOAT;
default:
DEV_ASSERT(false && "Unknown swap chain format.");
return DATA_FORMAT_MAX;
@ -3344,16 +3422,29 @@ void RenderingDeviceDriverVulkan::swap_chain_set_max_fps(SwapChainID p_swap_chai
#endif
}
RDD::ColorSpace RenderingDeviceDriverVulkan::swap_chain_get_color_space(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
switch (swap_chain->color_space) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return COLOR_SPACE_SRGB_NONLINEAR;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return COLOR_SPACE_HDR10_ST2084;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return COLOR_SPACE_SRGB_LINEAR;
default:
DEV_ASSERT(false && "Unknown swap chain color space.");
return COLOR_SPACE_MAX;
}
}
void RenderingDeviceDriverVulkan::swap_chain_free(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
_swap_chain_release(swap_chain);
if (swap_chain->render_pass.id != 0) {
vkDestroyRenderPass(vk_device, VkRenderPass(swap_chain->render_pass.id), VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS));
}
memdelete(swap_chain);
}
@ -5951,6 +6042,8 @@ bool RenderingDeviceDriverVulkan::has_feature(Features p_feature) {
return true;
case SUPPORTS_BUFFER_DEVICE_ADDRESS:
return buffer_device_address_support;
case SUPPORTS_HDR_OUTPUT:
return context_driver->is_colorspace_supported();
default:
return false;
}

View File

@ -370,6 +370,7 @@ private:
#endif
};
bool _determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space);
void _swap_chain_release(SwapChain *p_swap_chain);
public:
@ -380,6 +381,7 @@ public:
virtual int swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
/*********************/

View File

@ -412,6 +412,27 @@ void EditorNode::_update_from_settings() {
scene_root->set_default_canvas_item_texture_repeat(tr);
}
// Enable HDR if requested and available.
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HDR) && RD::get_singleton()->has_feature(RD::Features::SUPPORTS_HDR_OUTPUT)) {
bool hdr_enabled = GLOBAL_GET("display/window/hdr/enabled");
bool prefer_high_precision = GLOBAL_GET("display/window/hdr/prefer_high_precision");
bool use_screen_luminance = GLOBAL_GET("display/window/hdr/use_screen_luminance");
DisplayServer::get_singleton()->window_set_hdr_output_enabled(hdr_enabled);
DisplayServer::get_singleton()->window_set_hdr_output_prefer_high_precision(prefer_high_precision);
DisplayServer::get_singleton()->window_set_hdr_output_use_screen_luminance(use_screen_luminance);
if (!use_screen_luminance) {
float reference_luminance = GLOBAL_GET("display/window/hdr/reference_luminance");
float min_luminance = GLOBAL_GET("display/window/hdr/min_luminance");
float max_luminance = GLOBAL_GET("display/window/hdr/max_luminance");
DisplayServer::get_singleton()->window_set_hdr_output_reference_luminance(reference_luminance);
DisplayServer::get_singleton()->window_set_hdr_output_min_luminance(min_luminance);
DisplayServer::get_singleton()->window_set_hdr_output_max_luminance(max_luminance);
}
}
RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")));
RS::get_singleton()->camera_attributes_set_dof_blur_bokeh_shape(dof_shape);
RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality")));
@ -470,6 +491,10 @@ void EditorNode::_update_from_settings() {
scene_root->set_use_hdr_2d(use_hdr_2d);
get_viewport()->set_use_hdr_2d(use_hdr_2d);
const bool tonemap_to_window = GLOBAL_GET("rendering/viewport/tonemap_to_window");
scene_root->set_tonemap_to_window(tonemap_to_window);
get_viewport()->set_tonemap_to_window(tonemap_to_window);
float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
scene_root->set_mesh_lod_threshold(mesh_lod_threshold);

View File

@ -2875,6 +2875,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
viewport->set_use_hdr_2d(use_hdr_2d);
const bool tonemap_to_window = GLOBAL_GET("rendering/viewport/tonemap_to_window");
viewport->set_tonemap_to_window(tonemap_to_window);
const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
viewport->set_use_debanding(use_debanding);

View File

@ -3237,6 +3237,27 @@ Error Main::setup2(bool p_show_boot_logo) {
}
}
// Enable HDR if requested and available.
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HDR) && RD::get_singleton()->has_feature(RD::Features::SUPPORTS_HDR_OUTPUT)) {
bool hdr_enabled = GLOBAL_GET("display/window/hdr/enabled");
bool prefer_high_precision = GLOBAL_GET("display/window/hdr/prefer_high_precision");
bool use_screen_luminance = GLOBAL_GET("display/window/hdr/use_screen_luminance");
DisplayServer::get_singleton()->window_set_hdr_output_enabled(hdr_enabled);
DisplayServer::get_singleton()->window_set_hdr_output_prefer_high_precision(prefer_high_precision);
DisplayServer::get_singleton()->window_set_hdr_output_use_screen_luminance(use_screen_luminance);
if (!use_screen_luminance) {
float reference_luminance = GLOBAL_GET("display/window/hdr/reference_luminance");
float min_luminance = GLOBAL_GET("display/window/hdr/min_luminance");
float max_luminance = GLOBAL_GET("display/window/hdr/max_luminance");
DisplayServer::get_singleton()->window_set_hdr_output_reference_luminance(reference_luminance);
DisplayServer::get_singleton()->window_set_hdr_output_min_luminance(min_luminance);
DisplayServer::get_singleton()->window_set_hdr_output_max_luminance(max_luminance);
}
}
Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
RenderingServer::get_singleton()->set_default_clear_color(clear);

View File

@ -136,6 +136,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_EMBEDDING:
case FEATURE_WINDOW_DRAG:
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
case FEATURE_HDR:
return true;
case FEATURE_EMOJI_AND_SYMBOL_PICKER:
return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.
@ -1181,6 +1182,14 @@ typedef struct {
float rate;
} EnumRefreshRateData;
typedef struct {
Vector<DISPLAYCONFIG_PATH_INFO> paths;
Vector<DISPLAYCONFIG_MODE_INFO> modes;
int count;
int screen;
float sdrWhiteLevelInNits;
} EnumSdrWhiteLevelData;
static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumSizeData *data = (EnumSizeData *)dwData;
if (data->count == data->screen) {
@ -1491,6 +1500,172 @@ Ref<Image> DisplayServerWindows::screen_get_image_rect(const Rect2i &p_rect) con
return img;
}
#ifdef D3D12_ENABLED
static bool _get_screen_desc(int p_screen, DXGI_OUTPUT_DESC1 &r_Desc) {
ComPtr<IDXGIFactory4> dxgi_factory;
r_Desc = {};
if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgi_factory)))) {
return false;
}
// Retrieve the current default adapter.
ComPtr<IDXGIAdapter1> dxgiAdapter;
if (FAILED(dxgi_factory->EnumAdapters1(0, &dxgiAdapter))) {
return false;
}
UINT i = 0;
ComPtr<IDXGIOutput> screenOutput;
while (dxgiAdapter->EnumOutputs(i, &screenOutput) != DXGI_ERROR_NOT_FOUND) {
if (i == (UINT)p_screen) {
break;
}
i++;
}
ComPtr<IDXGIOutput6> output6;
if (FAILED(screenOutput.As(&output6))) {
return false;
}
DXGI_OUTPUT_DESC1 desc1;
if (FAILED(output6->GetDesc1(&desc1))) {
return false;
}
r_Desc = desc1;
return true;
}
#endif // D3D12_ENABLED
bool DisplayServerWindows::screen_is_hdr_supported(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
#ifdef D3D12_ENABLED
DXGI_OUTPUT_DESC1 desc1;
if (!_get_screen_desc(p_screen, desc1)) {
return false;
}
return (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
#else
return false;
#endif // D3D12_ENABLED
}
float DisplayServerWindows::screen_get_min_luminance(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
#ifdef D3D12_ENABLED
DXGI_OUTPUT_DESC1 desc1;
if (!_get_screen_desc(p_screen, desc1)) {
return 0.0f;
}
return desc1.MinLuminance;
#else
return 0.0f;
#endif // D3D12_ENABLED
}
float DisplayServerWindows::screen_get_max_luminance(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
#ifdef D3D12_ENABLED
DXGI_OUTPUT_DESC1 desc1;
if (!_get_screen_desc(p_screen, desc1)) {
return 0.0f;
}
return desc1.MaxLuminance;
#else
return 0.0f;
#endif // D3D12_ENABLED
}
float DisplayServerWindows::screen_get_max_average_luminance(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
#ifdef D3D12_ENABLED
DXGI_OUTPUT_DESC1 desc1;
if (!_get_screen_desc(p_screen, desc1)) {
return 0.0f;
}
return desc1.MaxFullFrameLuminance;
#else
return 0.0f;
#endif // D3D12_ENABLED
}
static BOOL CALLBACK _MonitorEnumProcSdrWhiteLevel(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumSdrWhiteLevelData *data = (EnumSdrWhiteLevelData *)dwData;
if (data->count == data->screen) {
MONITORINFOEXW minfo;
memset(&minfo, 0, sizeof(minfo));
minfo.cbSize = sizeof(minfo);
GetMonitorInfoW(hMonitor, &minfo);
// First, find this screen's path.
for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;
memset(&source_name, 0, sizeof(source_name));
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
source_name.header.size = sizeof(source_name);
source_name.header.adapterId = path.sourceInfo.adapterId;
source_name.header.id = path.sourceInfo.id;
if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {
if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0) {
// Found it, now we can query the SDR white level.
DISPLAYCONFIG_SDR_WHITE_LEVEL sdr_white_level;
memset(&sdr_white_level, 0, sizeof(sdr_white_level));
sdr_white_level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
sdr_white_level.header.size = sizeof(sdr_white_level);
sdr_white_level.header.adapterId = path.targetInfo.adapterId;
sdr_white_level.header.id = path.targetInfo.id;
LONG result = DisplayConfigGetDeviceInfo(&sdr_white_level.header);
if (result == ERROR_SUCCESS) {
data->sdrWhiteLevelInNits = (float)(sdr_white_level.SDRWhiteLevel / 1000) * 80;
}
break;
}
}
}
}
data->count++;
return TRUE;
}
float DisplayServerWindows::screen_get_sdr_white_level(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
EnumSdrWhiteLevelData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, 0.0f };
uint32_t path_count = 0;
uint32_t mode_count = 0;
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {
data.paths.resize(path_count);
data.modes.resize(mode_count);
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {
data.paths.clear();
data.modes.clear();
}
}
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSdrWhiteLevel, (LPARAM)&data);
return data.sdrWhiteLevelInNits;
}
float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
@ -2973,6 +3148,74 @@ HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWN
return NULL;
}
// Get screen HDR capabilities for internal use only.
// Do not report values from this method to the user.
DisplayServerWindows::ScreenHdrData DisplayServerWindows::_get_screen_hdr_data(int p_screen) const {
ScreenHdrData data;
#ifdef D3D12_ENABLED
DXGI_OUTPUT_DESC1 desc;
if (_get_screen_desc(p_screen, desc)) {
data.hdr_supported = desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
data.min_luminance = desc.MinLuminance;
data.max_luminance = desc.MaxLuminance;
data.max_average_luminance = desc.MaxFullFrameLuminance;
}
#else
// If we don't have D3D12, assume HDR is supported to avoid blocking Vulkan only builds.
data.hdr_supported = true;
data.min_luminance = 0.0f;
data.max_luminance = 0.0f;
data.max_average_luminance = 0.0f;
#endif // D3D12_ENABLED
data.sdr_white_level = screen_get_sdr_white_level(p_screen);
return data;
}
void DisplayServerWindows::_update_hdr_output_for_window(WindowID p_window, const WindowData &p_window_data, ScreenHdrData p_screen_data) {
#ifdef RD_ENABLED
if (rendering_context) {
bool current_hdr_enabled = rendering_context->window_get_hdr_output_enabled(p_window);
bool desired_hdr_enabled = p_window_data.hdr_output_requested && p_screen_data.hdr_supported;
if (current_hdr_enabled != desired_hdr_enabled) {
rendering_context->window_set_hdr_output_enabled(p_window, desired_hdr_enabled);
}
if (p_window_data.hdr_output_use_screen_luminance) {
if (p_screen_data.sdr_white_level > 0.0f) {
rendering_context->window_set_hdr_output_reference_luminance(p_window, p_screen_data.sdr_white_level);
}
if (p_screen_data.min_luminance > 0.0f) {
rendering_context->window_set_hdr_output_min_luminance(p_window, p_screen_data.min_luminance);
}
if (p_screen_data.max_luminance > 0.0f) {
rendering_context->window_set_hdr_output_max_luminance(p_window, p_screen_data.max_luminance);
}
}
}
#endif // RD_ENABLED
}
void DisplayServerWindows::_update_hdr_output_for_tracked_windows() {
HashMap<int, ScreenHdrData> outputs;
for (const KeyValue<WindowID, WindowData> &E : windows) {
if (E.value.hdr_output_requested) {
int screen = window_get_current_screen(E.key);
ScreenHdrData data;
if (!outputs.has(screen)) {
data = _get_screen_hdr_data(screen);
outputs.insert(screen, data);
} else {
data = outputs[screen];
}
_update_hdr_output_for_window(E.key, E.value, data);
}
}
}
Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {
_THREAD_SAFE_METHOD_
@ -4095,6 +4338,155 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_
return DisplayServer::VSYNC_ENABLED;
}
void DisplayServerWindows::window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
ERR_FAIL_COND_MSG((rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) == false, "HDR output is not supported by the rendering device.");
#endif
WindowData &wd = windows[p_window];
wd.hdr_output_requested = p_enabled;
int screen = window_get_current_screen(p_window);
DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen);
_update_hdr_output_for_window(p_window, wd, data);
}
bool DisplayServerWindows::window_is_hdr_output_enabled(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_enabled(p_window);
}
#endif
return false;
}
void DisplayServerWindows::window_set_hdr_output_prefer_high_precision(const bool p_enabled, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
if (p_enabled == rendering_context->window_get_hdr_output_prefer_high_precision(p_window)) {
return;
}
rendering_context->window_set_hdr_output_prefer_high_precision(p_window, p_enabled);
}
#endif
}
bool DisplayServerWindows::window_is_hdr_output_preferring_high_precision(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_prefer_high_precision(p_window);
}
#endif
return false;
}
void DisplayServerWindows::window_set_hdr_output_use_screen_luminance(const bool p_enabled, WindowID p_window) {
_THREAD_SAFE_METHOD_
WindowData &wd = windows[p_window];
wd.hdr_output_use_screen_luminance = p_enabled;
int screen = window_get_current_screen(p_window);
DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen);
_update_hdr_output_for_window(p_window, wd, data);
}
bool DisplayServerWindows::window_is_hdr_output_using_screen_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
const WindowData &wd = windows[p_window];
return wd.hdr_output_use_screen_luminance;
}
void DisplayServerWindows::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) {
_THREAD_SAFE_METHOD_
const WindowData &wd = windows[p_window];
if (wd.hdr_output_use_screen_luminance) {
WARN_PRINT("Reference luminance is ignored when using screen luminance.");
return;
}
#if defined(RD_ENABLED)
if (rendering_context) {
rendering_context->window_set_hdr_output_reference_luminance(p_window, p_reference_luminance);
}
#endif
}
float DisplayServerWindows::window_get_hdr_output_reference_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_reference_luminance(p_window);
}
#endif
return 0.0f;
}
void DisplayServerWindows::window_set_hdr_output_min_luminance(const float p_min_luminance, WindowID p_window) {
_THREAD_SAFE_METHOD_
const WindowData &wd = windows[p_window];
if (wd.hdr_output_use_screen_luminance) {
WARN_PRINT("Minimum luminance is ignored when using screen luminance.");
return;
}
#if defined(RD_ENABLED)
if (rendering_context) {
rendering_context->window_set_hdr_output_min_luminance(p_window, p_min_luminance);
}
#endif
}
float DisplayServerWindows::window_get_hdr_output_min_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_min_luminance(p_window);
}
#endif
return 0.0f;
}
void DisplayServerWindows::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
_THREAD_SAFE_METHOD_
const WindowData &wd = windows[p_window];
if (wd.hdr_output_use_screen_luminance) {
WARN_PRINT("Maximum luminance is ignored when using screen luminance.");
return;
}
#if defined(RD_ENABLED)
if (rendering_context) {
rendering_context->window_set_hdr_output_max_luminance(p_window, p_max_luminance);
}
#endif
}
float DisplayServerWindows::window_get_hdr_output_max_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_max_luminance(p_window);
}
#endif
return 0.0f;
}
void DisplayServerWindows::window_start_drag(WindowID p_window) {
_THREAD_SAFE_METHOD_
@ -5561,6 +5953,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
} break;
case WM_DISPLAYCHANGE: {
_update_hdr_output_for_tracked_windows();
} break;
case WM_WINDOWPOSCHANGED: {
Rect2i window_client_rect;
Rect2i window_rect;
@ -5644,6 +6040,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));
}
_update_hdr_output_for_tracked_windows();
// Update cursor clip region after window rect has changed.
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
RECT crect;

View File

@ -548,6 +548,10 @@ class DisplayServerWindows : public DisplayServer {
bool is_popup = false;
Rect2i parent_safe_rect;
// HDR
bool hdr_output_requested = false;
bool hdr_output_use_screen_luminance = false;
bool initialized = false;
HWND parent_hwnd = 0;
@ -687,6 +691,17 @@ class DisplayServerWindows : public DisplayServer {
HWND _find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd);
struct ScreenHdrData {
bool hdr_supported = false;
float min_luminance = 0.0f;
float max_luminance = 0.0f;
float max_average_luminance = 0.0f;
float sdr_white_level = 0.0f;
};
ScreenHdrData _get_screen_hdr_data(int p_screen) const;
void _update_hdr_output_for_window(WindowID p_window, const WindowData &p_window_data, ScreenHdrData p_screen_data);
void _update_hdr_output_for_tracked_windows();
public:
LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@ -747,6 +762,13 @@ public:
virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Ref<Image> screen_get_image_rect(const Rect2i &p_rect) const override;
// Display capabilities for HDR.
virtual bool screen_is_hdr_supported(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_min_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_max_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_max_average_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_sdr_white_level(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver
virtual bool screen_is_kept_on() const override;
@ -827,6 +849,19 @@ public:
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
virtual void window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_prefer_high_precision(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_preferring_high_precision(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_use_screen_luminance(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_using_screen_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_min_luminance(const float p_min_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_min_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window = MAIN_WINDOW_ID) override;

View File

@ -1900,6 +1900,9 @@ SceneTree::SceneTree() {
const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
root->set_use_hdr_2d(use_hdr_2d);
const bool tonemap_to_window = GLOBAL_DEF("rendering/viewport/tonemap_to_window", true);
root->set_tonemap_to_window(tonemap_to_window);
const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), 0);
root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));

View File

@ -1206,6 +1206,17 @@ bool Viewport::is_using_hdr_2d() const {
return use_hdr_2d;
}
void Viewport::set_tonemap_to_window(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
tonemap_to_window = p_enable;
RS::get_singleton()->viewport_set_tonemap_to_screen(viewport, p_enable);
}
bool Viewport::is_tonemapping_to_window() const {
ERR_READ_THREAD_GUARD_V(false);
return tonemap_to_window;
}
void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
ERR_MAIN_THREAD_GUARD;
if (world_2d == p_world_2d) {
@ -4777,6 +4788,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
ClassDB::bind_method(D_METHOD("set_tonemap_to_window", "enable"), &Viewport::set_tonemap_to_window);
ClassDB::bind_method(D_METHOD("is_tonemapping_to_window"), &Viewport::is_tonemapping_to_window);
ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
@ -4955,6 +4969,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe,Normal Buffer,VoxelGI Albedo,VoxelGI Lighting,VoxelGI Emission,Shadow Atlas,Directional Shadow Map,Scene Luminance,SSAO,SSIL,Directional Shadow Splits,Decal Atlas,SDFGI Cascades,SDFGI Probes,VoxelGI/SDFGI Buffer,Disable Mesh LOD,OmniLight3D Cluster,SpotLight3D Cluster,Decal Cluster,ReflectionProbe Cluster,Occlusion Culling Buffer,Motion Vectors,Internal Buffer"), "set_debug_draw", "get_debug_draw");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tonemap_to_window"), "set_tonemap_to_window", "is_tonemapping_to_window");
#ifndef _3D_DISABLED
ADD_GROUP("Scaling 3D", "");

View File

@ -264,6 +264,7 @@ private:
bool transparent_bg = false;
bool use_hdr_2d = false;
bool gen_mipmaps = false;
bool tonemap_to_window = false;
bool snap_controls_to_pixels = true;
bool snap_2d_transforms_to_pixel = false;
@ -541,6 +542,9 @@ public:
void set_use_hdr_2d(bool p_enable);
bool is_using_hdr_2d() const;
void set_tonemap_to_window(bool p_enable);
bool is_tonemapping_to_window() const;
Ref<ViewportTexture> get_texture() const;
void set_positional_shadow_atlas_size(int p_size);

View File

@ -578,6 +578,126 @@ bool Window::get_flag(Flags p_flag) const {
return flags[p_flag];
}
void Window::set_hdr_output_enabled(bool p_enabled) {
ERR_MAIN_THREAD_GUARD;
hdr_output_enabled = p_enabled;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_enabled(hdr_output_enabled, window_id);
}
}
bool Window::is_hdr_output_enabled() const {
ERR_READ_THREAD_GUARD_V(false);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_enabled = DisplayServer::get_singleton()->window_is_hdr_output_enabled(window_id);
}
return hdr_output_enabled;
}
void Window::set_hdr_output_prefer_high_precision(bool p_enabled) {
ERR_MAIN_THREAD_GUARD;
hdr_output_prefer_high_precision = p_enabled;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_prefer_high_precision(hdr_output_prefer_high_precision, window_id);
}
}
bool Window::is_hdr_output_preferring_high_precision() const {
ERR_READ_THREAD_GUARD_V(false);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_prefer_high_precision = DisplayServer::get_singleton()->window_is_hdr_output_preferring_high_precision(window_id);
}
return hdr_output_prefer_high_precision;
}
void Window::set_hdr_output_use_screen_luminance(bool p_enabled) {
ERR_MAIN_THREAD_GUARD;
hdr_output_use_screen_luminance = p_enabled;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_use_screen_luminance(hdr_output_use_screen_luminance, window_id);
}
}
bool Window::is_hdr_output_using_screen_luminance() const {
ERR_READ_THREAD_GUARD_V(false);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_use_screen_luminance = DisplayServer::get_singleton()->window_is_hdr_output_using_screen_luminance(window_id);
}
return hdr_output_use_screen_luminance;
}
void Window::set_hdr_output_reference_luminance(float p_reference_luminance) {
ERR_MAIN_THREAD_GUARD;
hdr_output_reference_luminance = p_reference_luminance;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_reference_luminance(hdr_output_reference_luminance, window_id);
}
}
float Window::get_hdr_output_reference_luminance() const {
ERR_READ_THREAD_GUARD_V(0.0f);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_reference_luminance = DisplayServer::get_singleton()->window_get_hdr_output_reference_luminance(window_id);
}
return hdr_output_reference_luminance;
}
void Window::set_hdr_output_min_luminance(float p_min_luminance) {
ERR_MAIN_THREAD_GUARD;
hdr_output_min_luminance = p_min_luminance;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_min_luminance(hdr_output_min_luminance, window_id);
}
}
float Window::get_hdr_output_min_luminance() const {
ERR_READ_THREAD_GUARD_V(0.0f);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_min_luminance = DisplayServer::get_singleton()->window_get_hdr_output_min_luminance(window_id);
}
return hdr_output_min_luminance;
}
void Window::set_hdr_output_max_luminance(float p_max_luminance) {
ERR_MAIN_THREAD_GUARD;
hdr_output_max_luminance = p_max_luminance;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_hdr_output_max_luminance(hdr_output_max_luminance, window_id);
}
}
float Window::get_hdr_output_max_luminance() const {
ERR_READ_THREAD_GUARD_V(0.0f);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
hdr_output_max_luminance = DisplayServer::get_singleton()->window_get_hdr_output_max_luminance(window_id);
}
return hdr_output_max_luminance;
}
bool Window::is_maximize_allowed() const {
ERR_READ_THREAD_GUARD_V(false);
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
@ -678,6 +798,16 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
// Set HDR output settings.
DisplayServer::get_singleton()->window_set_hdr_output_enabled(hdr_output_enabled, window_id);
DisplayServer::get_singleton()->window_set_hdr_output_prefer_high_precision(hdr_output_prefer_high_precision, window_id);
DisplayServer::get_singleton()->window_set_hdr_output_use_screen_luminance(hdr_output_use_screen_luminance, window_id);
if (!hdr_output_use_screen_luminance) {
DisplayServer::get_singleton()->window_set_hdr_output_reference_luminance(hdr_output_reference_luminance, window_id);
DisplayServer::get_singleton()->window_set_hdr_output_min_luminance(hdr_output_min_luminance, window_id);
DisplayServer::get_singleton()->window_set_hdr_output_max_luminance(hdr_output_max_luminance, window_id);
}
_update_window_size();
if (transient_parent) {
@ -1395,6 +1525,15 @@ void Window::_notification(int p_what) {
size = DisplayServer::get_singleton()->window_get_size(window_id);
focused = DisplayServer::get_singleton()->window_is_focused(window_id);
}
// Update HDR settings to reflect the current state of the main window.
{
hdr_output_enabled = DisplayServer::get_singleton()->window_is_hdr_output_enabled(window_id);
hdr_output_prefer_high_precision = DisplayServer::get_singleton()->window_is_hdr_output_preferring_high_precision(window_id);
hdr_output_use_screen_luminance = DisplayServer::get_singleton()->window_is_hdr_output_using_screen_luminance(window_id);
hdr_output_reference_luminance = DisplayServer::get_singleton()->window_get_hdr_output_reference_luminance(window_id);
hdr_output_min_luminance = DisplayServer::get_singleton()->window_get_hdr_output_min_luminance(window_id);
hdr_output_max_luminance = DisplayServer::get_singleton()->window_get_hdr_output_max_luminance(window_id);
}
_update_window_size(); // Inform DisplayServer of minimum and maximum size.
_update_viewport_size(); // Then feed back to the viewport.
_update_window_callbacks();
@ -2916,6 +3055,24 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flag", "flag", "enabled"), &Window::set_flag);
ClassDB::bind_method(D_METHOD("get_flag", "flag"), &Window::get_flag);
ClassDB::bind_method(D_METHOD("set_hdr_output_enabled", "enabled"), &Window::set_hdr_output_enabled);
ClassDB::bind_method(D_METHOD("is_hdr_output_enabled"), &Window::is_hdr_output_enabled);
ClassDB::bind_method(D_METHOD("set_hdr_output_prefer_high_precision", "enabled"), &Window::set_hdr_output_prefer_high_precision);
ClassDB::bind_method(D_METHOD("is_hdr_output_preferring_high_precision"), &Window::is_hdr_output_preferring_high_precision);
ClassDB::bind_method(D_METHOD("set_hdr_output_use_screen_luminance", "enabled"), &Window::set_hdr_output_use_screen_luminance);
ClassDB::bind_method(D_METHOD("is_hdr_output_using_screen_luminance"), &Window::is_hdr_output_using_screen_luminance);
ClassDB::bind_method(D_METHOD("set_hdr_output_reference_luminance", "reference_luminance"), &Window::set_hdr_output_reference_luminance);
ClassDB::bind_method(D_METHOD("get_hdr_output_reference_luminance"), &Window::get_hdr_output_reference_luminance);
ClassDB::bind_method(D_METHOD("set_hdr_output_min_luminance", "min_luminance"), &Window::set_hdr_output_min_luminance);
ClassDB::bind_method(D_METHOD("get_hdr_output_min_luminance"), &Window::get_hdr_output_min_luminance);
ClassDB::bind_method(D_METHOD("set_hdr_output_max_luminance", "max_luminance"), &Window::set_hdr_output_max_luminance);
ClassDB::bind_method(D_METHOD("get_hdr_output_max_luminance"), &Window::get_hdr_output_max_luminance);
ClassDB::bind_method(D_METHOD("is_maximize_allowed"), &Window::is_maximize_allowed);
ClassDB::bind_method(D_METHOD("request_attention"), &Window::request_attention);
@ -3098,6 +3255,14 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor");
ADD_GROUP("HDR Output", "hdr_output_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr_output_enabled"), "set_hdr_output_enabled", "is_hdr_output_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr_output_prefer_high_precision"), "set_hdr_output_prefer_high_precision", "is_hdr_output_preferring_high_precision");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr_output_use_screen_luminance"), "set_hdr_output_use_screen_luminance", "is_hdr_output_using_screen_luminance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "hdr_output_reference_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), "set_hdr_output_reference_luminance", "get_hdr_output_reference_luminance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "hdr_output_min_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), "set_hdr_output_min_luminance", "get_hdr_output_min_luminance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "hdr_output_max_luminance", PROPERTY_HINT_RANGE, "0,2000,1,or_greater"), "set_hdr_output_max_luminance", "get_hdr_output_max_luminance");
#ifndef DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_translate", "is_auto_translating");
#endif
@ -3201,6 +3366,10 @@ Window::Window() {
theme_owner = memnew(ThemeOwner(this));
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
// Tonemap the root viewport of this window by default.
tonemap_to_window = true;
RS::get_singleton()->viewport_set_tonemap_to_screen(get_viewport_rid(), tonemap_to_window);
}
Window::~Window() {

View File

@ -132,6 +132,13 @@ private:
WindowInitialPosition initial_position = WINDOW_INITIAL_POSITION_ABSOLUTE;
bool force_native = false;
mutable bool hdr_output_enabled = false;
mutable bool hdr_output_prefer_high_precision = false;
mutable bool hdr_output_use_screen_luminance = false;
mutable float hdr_output_reference_luminance = 80.0f;
mutable float hdr_output_min_luminance = 0.0f;
mutable float hdr_output_max_luminance = 1000.0f;
bool use_font_oversampling = false;
bool transient = false;
bool transient_to_focused = false;
@ -311,6 +318,24 @@ public:
void set_flag(Flags p_flag, bool p_enabled);
bool get_flag(Flags p_flag) const;
void set_hdr_output_enabled(bool p_enabled);
bool is_hdr_output_enabled() const;
void set_hdr_output_prefer_high_precision(bool p_enabled);
bool is_hdr_output_preferring_high_precision() const;
void set_hdr_output_use_screen_luminance(bool p_enabled);
bool is_hdr_output_using_screen_luminance() const;
void set_hdr_output_reference_luminance(float p_reference_luminance);
float get_hdr_output_reference_luminance() const;
void set_hdr_output_min_luminance(float p_min_luminance);
float get_hdr_output_min_luminance() const;
void set_hdr_output_max_luminance(float p_max_luminance);
float get_hdr_output_max_luminance() const;
bool is_maximize_allowed() const;
void request_attention();

View File

@ -810,6 +810,54 @@ DisplayServer::VSyncMode DisplayServer::window_get_vsync_mode(WindowID p_window)
return VSyncMode::VSYNC_ENABLED;
}
void DisplayServer::window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
bool DisplayServer::window_is_hdr_output_enabled(WindowID p_window) const {
return false;
}
void DisplayServer::window_set_hdr_output_prefer_high_precision(const bool p_enabled, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
bool DisplayServer::window_is_hdr_output_preferring_high_precision(WindowID p_window) const {
return false;
}
void DisplayServer::window_set_hdr_output_use_screen_luminance(const bool p_enabled, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
bool DisplayServer::window_is_hdr_output_using_screen_luminance(WindowID p_window) const {
return false;
}
void DisplayServer::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
float DisplayServer::window_get_hdr_output_reference_luminance(WindowID p_window) const {
return 0.0f;
}
void DisplayServer::window_set_hdr_output_min_luminance(const float p_min_luminance, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
float DisplayServer::window_get_hdr_output_min_luminance(WindowID p_window) const {
return 0.0f;
}
void DisplayServer::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
WARN_PRINT("HDR output is not supported by this display server.");
}
float DisplayServer::window_get_hdr_output_max_luminance(WindowID p_window) const {
return 0.0f;
}
DisplayServer::WindowID DisplayServer::get_focused_window() const {
return MAIN_WINDOW_ID; // Proper value for single windows.
}
@ -945,6 +993,12 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("screen_get_image", "screen"), &DisplayServer::screen_get_image, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_image_rect", "rect"), &DisplayServer::screen_get_image_rect);
ClassDB::bind_method(D_METHOD("screen_is_hdr_supported", "screen"), &DisplayServer::screen_is_hdr_supported, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_min_luminance", "screen"), &DisplayServer::screen_get_min_luminance, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_max_luminance", "screen"), &DisplayServer::screen_get_max_luminance, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_max_average_luminance", "screen"), &DisplayServer::screen_get_max_average_luminance, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_sdr_white_level", "screen"), &DisplayServer::screen_get_sdr_white_level, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
@ -1012,6 +1066,19 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("window_set_vsync_mode", "vsync_mode", "window_id"), &DisplayServer::window_set_vsync_mode, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_vsync_mode", "window_id"), &DisplayServer::window_get_vsync_mode, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_enabled", "enabled", "window_id"), &DisplayServer::window_set_hdr_output_enabled, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_is_hdr_output_enabled", "window_id"), &DisplayServer::window_is_hdr_output_enabled, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_prefer_high_precision", "enabled", "window_id"), &DisplayServer::window_set_hdr_output_prefer_high_precision, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_is_hdr_output_preferring_high_precision", "window_id"), &DisplayServer::window_is_hdr_output_preferring_high_precision, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_use_screen_luminance", "enabled", "window_id"), &DisplayServer::window_set_hdr_output_use_screen_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_is_hdr_output_using_screen_luminance", "window_id"), &DisplayServer::window_is_hdr_output_using_screen_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_reference_luminance", "reference_luminance", "window_id"), &DisplayServer::window_set_hdr_output_reference_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_hdr_output_reference_luminance", "window_id"), &DisplayServer::window_get_hdr_output_reference_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_min_luminance", "min_luminance", "window_id"), &DisplayServer::window_set_hdr_output_min_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_hdr_output_min_luminance", "window_id"), &DisplayServer::window_get_hdr_output_min_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_hdr_output_max_luminance", "max_luminance", "window_id"), &DisplayServer::window_set_hdr_output_max_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_hdr_output_max_luminance", "window_id"), &DisplayServer::window_get_hdr_output_max_luminance, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_is_maximize_allowed", "window_id"), &DisplayServer::window_is_maximize_allowed, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_maximize_on_title_dbl_click"), &DisplayServer::window_maximize_on_title_dbl_click);
ClassDB::bind_method(D_METHOD("window_minimize_on_title_dbl_click"), &DisplayServer::window_minimize_on_title_dbl_click);
@ -1113,6 +1180,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_WINDOW_EMBEDDING);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE_MIME);
BIND_ENUM_CONSTANT(FEATURE_EMOJI_AND_SYMBOL_PICKER);
BIND_ENUM_CONSTANT(FEATURE_HDR);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);

View File

@ -162,6 +162,7 @@ public:
FEATURE_WINDOW_EMBEDDING,
FEATURE_NATIVE_DIALOG_FILE_MIME,
FEATURE_EMOJI_AND_SYMBOL_PICKER,
FEATURE_HDR,
};
virtual bool has_feature(Feature p_feature) const = 0;
@ -362,6 +363,13 @@ public:
virtual Ref<Image> screen_get_image_rect(const Rect2i &p_rect) const { return Ref<Image>(); }
virtual bool is_touchscreen_available() const;
// Display capabilities for HDR.
virtual bool screen_is_hdr_supported(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return false; }
virtual float screen_get_min_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return 0.0f; }
virtual float screen_get_max_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return 0.0f; }
virtual float screen_get_max_average_luminance(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return 0.0f; }
virtual float screen_get_sdr_white_level(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return 0.0f; }
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
// project setting hint.
enum ScreenOrientation {
@ -483,6 +491,19 @@ public:
virtual void window_set_vsync_mode(VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID);
virtual VSyncMode window_get_vsync_mode(WindowID p_window) const;
virtual void window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_hdr_output_prefer_high_precision(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
virtual bool window_is_hdr_output_preferring_high_precision(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_hdr_output_use_screen_luminance(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
virtual bool window_is_hdr_output_using_screen_luminance(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID);
virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_hdr_output_min_luminance(const float p_min_luminance, WindowID p_window = MAIN_WINDOW_ID);
virtual float window_get_hdr_output_min_luminance(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID);
virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const;
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) = 0;

View File

@ -120,6 +120,13 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
// Static values that work well in most cases
// Consider reading from the scene itself in the future.
tonemap.push_constant.source_min_value = 0.0f;
tonemap.push_constant.source_max_value = 16.0f;
tonemap.push_constant.dest_min_value = p_settings.min_value;
tonemap.push_constant.dest_max_value = p_settings.max_value;
tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
@ -206,6 +213,13 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
// Static values that work well in most cases
// Consider reading from the scene itself in the future.
tonemap.push_constant.source_min_value = 0.0f;
tonemap.push_constant.source_max_value = 16.0f;
tonemap.push_constant.dest_min_value = p_settings.min_value;
tonemap.push_constant.dest_max_value = p_settings.max_value;
tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;

View File

@ -87,6 +87,11 @@ private:
float white; // 4 - 88
float auto_exposure_scale; // 4 - 92
float luminance_multiplier; // 4 - 96
float source_min_value; // 4 - 100
float source_max_value; // 4 - 104
float dest_min_value; // 4 - 108
float dest_max_value; // 4 - 112
};
/* tonemap actually writes to a framebuffer, which is
@ -126,6 +131,8 @@ public:
RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR;
float exposure = 1.0;
float white = 1.0;
float min_value = 0.0;
float max_value = 1.0;
bool use_auto_exposure = false;
float auto_exposure_scale = 0.5;

View File

@ -43,9 +43,15 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
return;
}
_ensure_blit_pipelines();
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen);
ERR_FAIL_COND(draw_list == RD::INVALID_ID);
const RD::ColorSpace color_space = RD::get_singleton()->screen_get_color_space(p_screen);
const float reference_luminance = RD::get_singleton()->get_context_driver()->window_get_hdr_output_reference_luminance(p_screen);
const float reference_multiplier = _compute_reference_multiplier(color_space, reference_luminance);
for (int i = 0; i < p_amount; i++) {
RID rd_texture = texture_storage->render_target_get_rd_texture(p_render_targets[i].render_target);
ERR_CONTINUE(rd_texture.is_null());
@ -65,6 +71,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
Size2 screen_size(RD::get_singleton()->screen_get_width(p_screen), RD::get_singleton()->screen_get_height(p_screen));
BlitMode mode = p_render_targets[i].lens_distortion.apply ? BLIT_MODE_LENS : (p_render_targets[i].multi_view.use_layer ? BLIT_MODE_USE_LAYER : BLIT_MODE_NORMAL);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[mode]);
RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0);
@ -94,7 +101,9 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2;
blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
blit.push_constant.source_is_srgb = !texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
blit.push_constant.target_color_space = color_space;
blit.push_constant.reference_multiplier = reference_multiplier;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
@ -129,16 +138,8 @@ void RendererCompositorRD::initialize() {
blit_modes.push_back("\n");
blit.shader.initialize(blit_modes);
blit.shader_version = blit.shader.version_create();
for (int i = 0; i < BLIT_MODE_MAX; i++) {
blit.pipelines[i] = RD::get_singleton()->render_pipeline_create(blit.shader.version_get_shader(blit.shader_version, i), RD::get_singleton()->screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), i == BLIT_MODE_NORMAL_ALPHA ? RenderingDevice::PipelineColorBlendState::create_blend() : RenderingDevice::PipelineColorBlendState::create_disabled(), 0);
// Unload shader modules to save memory.
RD::get_singleton()->shader_destroy_modules(blit.shader.version_get_shader(blit.shader_version, i));
}
//create index array for copy shader
Vector<uint8_t> pv;
pv.resize(6 * 2);
@ -178,6 +179,39 @@ void RendererCompositorRD::finalize() {
RD::get_singleton()->free(blit.sampler);
}
void RendererCompositorRD::_ensure_blit_pipelines() {
RenderingDevice::FramebufferFormatID main_window_format = RD::get_singleton()->screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID);
if (main_window_format != prev_main_window_format) {
for (int i = 0; i < BLIT_MODE_MAX; i++) {
if (blit.pipelines[i].is_valid()) {
RD::get_singleton()->free(blit.pipelines[i]);
}
blit.pipelines[i] = RD::get_singleton()->render_pipeline_create(blit.shader.version_get_shader(blit.shader_version, i), main_window_format, RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), i == BLIT_MODE_NORMAL_ALPHA ? RenderingDevice::PipelineColorBlendState::create_blend() : RenderingDevice::PipelineColorBlendState::create_disabled(), 0);
}
prev_main_window_format = main_window_format;
}
}
float RendererCompositorRD::_compute_reference_multiplier(RD::ColorSpace p_color_space, const float p_reference_luminance) {
switch (p_color_space) {
case RD::COLOR_SPACE_HDR10_ST2084:
// Max brightness of ST2084 is 10000 nits, we output from 0 to 1.
return p_reference_luminance / 10000.0f;
case RD::COLOR_SPACE_SRGB_LINEAR:
#ifdef WINDOWS_ENABLED
// Windows expects multiples of 80 nits.
return p_reference_luminance / 80.0f;
#else
// Default to 100 nits.
return p_reference_luminance / 100.0f;
#endif
default:
return 1.0f;
}
}
void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
if (p_image.is_null() || p_image->is_empty()) {
return;
@ -189,6 +223,8 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
return;
}
_ensure_blit_pipelines();
RID texture = texture_storage->texture_allocate();
texture_storage->texture_2d_initialize(texture, p_image);
RID rd_texture = texture_storage->texture_get_rd_texture(texture, false);
@ -225,6 +261,10 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
screenrect.position /= window_size;
screenrect.size /= window_size;
const RD::ColorSpace color_space = RD::get_singleton()->screen_get_color_space(DisplayServer::MAIN_WINDOW_ID);
const float reference_luminance = RD::get_singleton()->get_context_driver()->window_get_hdr_output_reference_luminance(DisplayServer::MAIN_WINDOW_ID);
const float reference_multiplier = _compute_reference_multiplier(color_space, reference_luminance);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(DisplayServer::MAIN_WINDOW_ID, p_color);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[BLIT_MODE_NORMAL_ALPHA]);
@ -250,7 +290,9 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
blit.push_constant.k2 = 0;
blit.push_constant.upscale = 1.0;
blit.push_constant.aspect_ratio = 1.0;
blit.push_constant.convert_to_srgb = false;
blit.push_constant.source_is_srgb = true;
blit.push_constant.target_color_space = color_space;
blit.push_constant.reference_multiplier = reference_multiplier;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);

View File

@ -69,21 +69,23 @@ protected:
};
struct BlitPushConstant {
float src_rect[4];
float dst_rect[4];
float src_rect[4]; // 16 - 16
float dst_rect[4]; // 16 - 32
float rotation_sin;
float rotation_cos;
float pad[2];
float rotation_sin; // 4 - 36
float rotation_cos; // 4 - 40
float eye_center[2];
float k1;
float k2;
float eye_center[2]; // 8 - 48
float k1; // 4 - 52
float k2; // 4 - 56
float upscale;
float aspect_ratio;
uint32_t layer;
uint32_t convert_to_srgb;
float upscale; // 4 - 60
float aspect_ratio; // 4 - 64
uint32_t layer; // 4 - 68
uint32_t source_is_srgb; // 4 - 72
uint32_t target_color_space; // 4 - 76
float reference_multiplier; // 4 - 80
};
struct Blit {
@ -104,6 +106,10 @@ protected:
static uint64_t frame;
static RendererCompositorRD *singleton;
RenderingDevice::FramebufferFormatID prev_main_window_format;
void _ensure_blit_pipelines();
float _compute_reference_multiplier(RD::ColorSpace p_color_space, const float p_reference_luminance);
public:
RendererUtilities *get_utilities() { return utilities; }
RendererLightStorage *get_light_storage() { return light_storage; }

View File

@ -630,6 +630,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment);
tonemap.white = environment_get_white(p_render_data->environment);
tonemap.exposure = environment_get_exposure(p_render_data->environment);
tonemap.min_value = environment_get_min_value(p_render_data->environment);
tonemap.max_value = environment_get_max_value(p_render_data->environment);
}
tonemap.use_color_correction = false;
@ -724,6 +726,8 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment);
tonemap.exposure = environment_get_exposure(p_render_data->environment);
tonemap.white = environment_get_white(p_render_data->environment);
tonemap.min_value = environment_get_min_value(p_render_data->environment);
tonemap.max_value = environment_get_max_value(p_render_data->environment);
}
// We don't support glow or auto exposure here, if they are needed, don't use subpasses!

View File

@ -10,7 +10,6 @@ layout(push_constant, std140) uniform Pos {
float rotation_sin;
float rotation_cos;
vec2 pad;
vec2 eye_center;
float k1;
@ -19,7 +18,10 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
bool convert_to_srgb;
bool source_is_srgb;
uint target_color_space;
float reference_multiplier;
}
data;
@ -50,7 +52,6 @@ layout(push_constant, std140) uniform Pos {
float rotation_sin;
float rotation_cos;
vec2 pad;
vec2 eye_center;
float k1;
@ -59,7 +60,10 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
bool convert_to_srgb;
bool source_is_srgb;
uint target_color_space;
float reference_multiplier;
}
data;
@ -73,6 +77,15 @@ layout(binding = 0) uniform sampler2DArray src_rt;
layout(binding = 0) uniform sampler2D src_rt;
#endif
// Keep in sync with RenderingDeviceCommons::ColorSpace
#define COLOR_SPACE_SRGB_LINEAR 0
#define COLOR_SPACE_SRGB_NONLINEAR 1
#define COLOR_SPACE_HDR10_ST2084 2
vec3 srgb_to_linear(vec3 color) {
return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
}
vec3 linear_to_srgb(vec3 color) {
// If going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
@ -80,6 +93,29 @@ vec3 linear_to_srgb(vec3 color) {
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
vec3 rec709_to_rec2020(vec3 color) {
const mat3 conversion = mat3(
0.627402, 0.069095, 0.016394,
0.329292, 0.919544, 0.088028,
0.043306, 0.011360, 0.895578);
return conversion * color;
}
vec3 linear_to_st2084(vec3 color) {
// Linear color should already be adjusted between 0 and 10,000 nits.
color = clamp(color, vec3(0.0), vec3(1.0));
// Apply ST2084 curve
const float c1 = 0.8359375;
const float c2 = 18.8515625;
const float c3 = 18.6875;
const float m1 = 0.1593017578125;
const float m2 = 78.84375;
vec3 cp = pow(abs(color), vec3(m1));
return pow((c1 + c2 * cp) / (1 + c3 * cp), vec3(m2));
}
void main() {
#ifdef APPLY_LENS_DISTORTION
vec2 coords = uv * 2.0 - 1.0;
@ -116,7 +152,33 @@ void main() {
color = texture(src_rt, uv);
#endif
if (data.convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
// Colorspace conversion for final blit
if (data.target_color_space == COLOR_SPACE_SRGB_LINEAR) {
if (data.source_is_srgb == true) {
// sRGB -> linear conversion
color.rgb = srgb_to_linear(color.rgb);
}
// Adjust brightness of SDR content to reference luminance
color.rgb *= data.reference_multiplier;
} else if (data.target_color_space == COLOR_SPACE_SRGB_NONLINEAR) {
if (data.source_is_srgb == false) {
// linear -> sRGB conversion
color.rgb = linear_to_srgb(color.rgb);
}
} else if (data.target_color_space == COLOR_SPACE_HDR10_ST2084) {
if (data.source_is_srgb == true) {
// sRGB -> linear conversion
color.rgb = srgb_to_linear(color.rgb);
}
// Convert to Rec.2020 primaries
color.rgb = rec709_to_rec2020(color.rgb);
// Adjust brightness of SDR content to reference luminance
color.rgb *= data.reference_multiplier;
// Apply the ST2084 curve
color.rgb = linear_to_st2084(color.rgb);
}
}

View File

@ -97,6 +97,11 @@ layout(push_constant, std430) uniform Params {
float white;
float auto_exposure_scale;
float luminance_multiplier;
float source_min_value;
float source_max_value;
float dest_min_value;
float dest_max_value;
}
params;
@ -344,6 +349,14 @@ vec3 linear_to_srgb(vec3 color) {
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
vec3 normalize(vec3 value, vec3 min, vec3 max) {
return (value - min) / (max - min);
}
vec3 denormalize(vec3 value, vec3 min, vec3 max) {
return value * (max - min) + min;
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
@ -353,17 +366,26 @@ vec3 linear_to_srgb(vec3 color) {
vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
// Linear is special: it always passes through with no adjustments.
if (params.tonemapper == TONEMAPPER_LINEAR) {
return color;
} else if (params.tonemapper == TONEMAPPER_REINHARD) {
return tonemap_reinhard(max(vec3(0.0f), color), white);
color = tonemap_reinhard(max(vec3(0.0f), color), white);
} else if (params.tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(max(vec3(0.0f), color), white);
color = tonemap_filmic(max(vec3(0.0f), color), white);
} else if (params.tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), white);
color = tonemap_aces(max(vec3(0.0f), color), white);
} else { // TONEMAPPER_AGX
return tonemap_agx(color);
color = tonemap_agx(color);
}
// Expand color to the target output range for HDR render targets.
if (!bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
color = denormalize(color, vec3(params.dest_min_value), vec3(params.dest_max_value));
}
return color;
}
#ifdef USE_MULTIVIEW
@ -410,14 +432,33 @@ vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
#define GLOW_MODE_REPLACE 3
#define GLOW_MODE_MIX 4
vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode
vec3 apply_glow(vec3 color, vec3 glow, bool hdrInput) { // apply glow using the selected blending mode
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
if (hdrInput) {
// Compress the color and glow from scene intensity to [0, 1] to avoid artifacts due to the color clamping.
color = normalize(color, vec3(params.source_min_value), vec3(params.source_max_value));
glow = normalize(glow, vec3(params.source_min_value), vec3(params.source_max_value));
}
// Needs color clamping.
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
return max((color + glow) - (color * glow), vec3(0.0));
color = max((color + glow) - (color * glow), vec3(0.0));
if (hdrInput) {
// Expand the color back to the original intensity range.
color = denormalize(color, vec3(params.source_min_value), vec3(params.source_max_value));
}
return color;
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
if (hdrInput) {
// Compress the color and glow from scene intensity to [0, 1] to avoid artifacts due to the color clamping.
color = normalize(color, vec3(params.source_min_value), vec3(params.source_max_value));
glow = normalize(glow, vec3(params.source_min_value), vec3(params.source_max_value));
}
// Needs color clamping.
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
glow = glow * vec3(0.5f) + vec3(0.5f);
@ -425,6 +466,12 @@ vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blendi
color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g)));
color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b)));
if (hdrInput) {
// Expand the color back to the original intensity range.
color = denormalize(color, vec3(params.source_min_value), vec3(params.source_max_value));
}
return color;
} else { //replace
return glow;
@ -579,7 +626,7 @@ void main() {
glow = linear_to_srgb(glow);
}
color.rgb = apply_glow(color.rgb, glow);
color.rgb = apply_glow(color.rgb, glow, !bool(params.flags & FLAG_CONVERT_TO_SRGB));
}
#endif

View File

@ -1266,9 +1266,12 @@ public:
// Tonemap
PASS4(environment_set_tonemap, RID, RS::EnvironmentToneMapper, float, float)
PASS3(environment_set_tonemap_range, RID, float, float)
PASS1RC(RS::EnvironmentToneMapper, environment_get_tone_mapper, RID)
PASS1RC(float, environment_get_exposure, RID)
PASS1RC(float, environment_get_white, RID)
PASS1RC(float, environment_get_min_value, RID)
PASS1RC(float, environment_get_max_value, RID)
// Fog
PASS11(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float, RS::EnvironmentFogMode)

View File

@ -365,6 +365,10 @@ void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentTone
environment_storage.environment_set_tonemap(p_env, p_tone_mapper, p_exposure, p_white);
}
void RendererSceneRender::environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value) {
environment_storage.environment_set_tonemap_range(p_env, p_min_value, p_max_value);
}
RS::EnvironmentToneMapper RendererSceneRender::environment_get_tone_mapper(RID p_env) const {
return environment_storage.environment_get_tone_mapper(p_env);
}
@ -377,6 +381,14 @@ float RendererSceneRender::environment_get_white(RID p_env) const {
return environment_storage.environment_get_white(p_env);
}
float RendererSceneRender::environment_get_min_value(RID p_env) const {
return environment_storage.environment_get_min_value(p_env);
}
float RendererSceneRender::environment_get_max_value(RID p_env) const {
return environment_storage.environment_get_max_value(p_env);
}
// Fog
void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) {

View File

@ -136,9 +136,12 @@ public:
// Tonemap
void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white);
void environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value);
RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const;
float environment_get_exposure(RID p_env) const;
float environment_get_white(RID p_env) const;
float environment_get_min_value(RID p_env) const;
float environment_get_max_value(RID p_env) const;
// Fog
void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode);

View File

@ -333,6 +333,27 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
// The scene renderer will still copy over the last frame, so we need to clear the render target.
force_clear_render_target = true;
}
// Check if the viewport is tonemapping to the screen.
if (p_viewport->tonemap_to_screen) {
DisplayServer::WindowID parent_window = _get_containing_window(p_viewport);
if (parent_window != DisplayServer::INVALID_WINDOW_ID) {
RenderingContextDriver *context_driver = RD::get_singleton()->get_context_driver();
if (context_driver->window_get_hdr_output_enabled(parent_window)) {
float min_luminance = context_driver->window_get_hdr_output_min_luminance(parent_window);
float max_luminance = context_driver->window_get_hdr_output_max_luminance(parent_window);
float reference_luminance = context_driver->window_get_hdr_output_reference_luminance(parent_window);
float min_value = MAX(min_luminance / MAX(reference_luminance, 1.0f), 0.0f);
float max_value = MAX(max_luminance / MAX(reference_luminance, 1.0f), 1.0f);
RSG::scene->environment_set_tonemap_range(environment, min_value, max_value);
} else {
RSG::scene->environment_set_tonemap_range(environment, 0.0f, 1.0f);
}
}
}
}
}
@ -710,6 +731,21 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
}
}
DisplayServer::WindowID RendererViewport::_get_containing_window(Viewport *p_viewport) {
if (p_viewport->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID) {
return p_viewport->viewport_to_screen;
}
if (p_viewport->parent.is_valid()) {
Viewport *parent = viewport_owner.get_or_null(p_viewport->parent);
if (parent) {
return _get_containing_window(parent);
}
}
return DisplayServer::INVALID_WINDOW_ID;
}
void RendererViewport::draw_viewports(bool p_swap_buffers) {
timestamp_vp_map.clear();
@ -1124,6 +1160,13 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool
}
}
void RendererViewport::viewport_set_tonemap_to_screen(RID p_viewport, bool p_enable) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
viewport->tonemap_to_screen = p_enable;
}
void RendererViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);

View File

@ -87,6 +87,7 @@ public:
DisplayServer::WindowID viewport_to_screen;
Rect2 viewport_to_screen_rect;
bool viewport_render_direct_to_screen;
bool tonemap_to_screen;
bool disable_2d = false;
RS::ViewportEnvironmentMode disable_environment = RS::VIEWPORT_ENVIRONMENT_INHERIT;
@ -211,6 +212,7 @@ private:
void _configure_3d_render_buffers(Viewport *p_viewport);
void _draw_3d(Viewport *p_viewport);
void _draw_viewport(Viewport *p_viewport);
DisplayServer::WindowID _get_containing_window(Viewport *p_viewport);
int occlusion_rays_per_thread = 512;
@ -226,6 +228,7 @@ public:
void viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect = Rect2(), DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
void viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable);
void viewport_set_tonemap_to_screen(RID p_viewport, bool p_enable);
void viewport_set_active(RID p_viewport, bool p_active);
void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport);

View File

@ -75,6 +75,86 @@ DisplayServer::VSyncMode RenderingContextDriver::window_get_vsync_mode(DisplaySe
}
}
void RenderingContextDriver::window_set_hdr_output_enabled(DisplayServer::WindowID p_window, bool p_enabled) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
surface_set_hdr_output_enabled(surface, p_enabled);
}
}
bool RenderingContextDriver::window_get_hdr_output_enabled(DisplayServer::WindowID p_window) const {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
return surface_get_hdr_output_enabled(surface);
} else {
return false;
}
}
void RenderingContextDriver::window_set_hdr_output_prefer_high_precision(DisplayServer::WindowID p_window, bool p_enabled) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
surface_set_hdr_output_prefer_high_precision(surface, p_enabled);
}
}
bool RenderingContextDriver::window_get_hdr_output_prefer_high_precision(DisplayServer::WindowID p_window) const {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
return surface_get_hdr_output_prefer_high_precision(surface);
} else {
return false;
}
}
void RenderingContextDriver::window_set_hdr_output_reference_luminance(DisplayServer::WindowID p_window, float p_reference_luminance) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
surface_set_hdr_output_reference_luminance(surface, p_reference_luminance);
}
}
float RenderingContextDriver::window_get_hdr_output_reference_luminance(DisplayServer::WindowID p_window) const {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
return surface_get_hdr_output_reference_luminance(surface);
} else {
return 0.0f;
}
}
void RenderingContextDriver::window_set_hdr_output_min_luminance(DisplayServer::WindowID p_window, float p_min_luminance) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
surface_set_hdr_output_min_luminance(surface, p_min_luminance);
}
}
float RenderingContextDriver::window_get_hdr_output_min_luminance(DisplayServer::WindowID p_window) const {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
return surface_get_hdr_output_min_luminance(surface);
} else {
return 0.0f;
}
}
void RenderingContextDriver::window_set_hdr_output_max_luminance(DisplayServer::WindowID p_window, float p_max_luminance) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
surface_set_hdr_output_max_luminance(surface, p_max_luminance);
}
}
float RenderingContextDriver::window_get_hdr_output_max_luminance(DisplayServer::WindowID p_window) const {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
return surface_get_hdr_output_max_luminance(surface);
} else {
return 0.0f;
}
}
void RenderingContextDriver::window_destroy(DisplayServer::WindowID p_window) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {

View File

@ -48,6 +48,16 @@ public:
void window_set_size(DisplayServer::WindowID p_window, uint32_t p_width, uint32_t p_height);
void window_set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_vsync_mode);
DisplayServer::VSyncMode window_get_vsync_mode(DisplayServer::WindowID p_window) const;
void window_set_hdr_output_enabled(DisplayServer::WindowID p_window, bool p_enabled);
bool window_get_hdr_output_enabled(DisplayServer::WindowID p_window) const;
void window_set_hdr_output_prefer_high_precision(DisplayServer::WindowID p_window, bool p_enabled);
bool window_get_hdr_output_prefer_high_precision(DisplayServer::WindowID p_window) const;
void window_set_hdr_output_reference_luminance(DisplayServer::WindowID p_window, float p_reference_luminance);
float window_get_hdr_output_reference_luminance(DisplayServer::WindowID p_window) const;
void window_set_hdr_output_min_luminance(DisplayServer::WindowID p_window, float p_min_luminance);
float window_get_hdr_output_min_luminance(DisplayServer::WindowID p_window) const;
void window_set_hdr_output_max_luminance(DisplayServer::WindowID p_window, float p_max_luminance);
float window_get_hdr_output_max_luminance(DisplayServer::WindowID p_window) const;
void window_destroy(DisplayServer::WindowID p_window);
public:
@ -98,6 +108,16 @@ public:
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) = 0;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) = 0;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const = 0;
virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) = 0;
virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const = 0;
virtual void surface_set_hdr_output_prefer_high_precision(SurfaceID p_surface, bool p_enabled) = 0;
virtual bool surface_get_hdr_output_prefer_high_precision(SurfaceID p_surface) const = 0;
virtual void surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) = 0;
virtual float surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const = 0;
virtual void surface_set_hdr_output_min_luminance(SurfaceID p_surface, float p_min_luminance) = 0;
virtual float surface_get_hdr_output_min_luminance(SurfaceID p_surface) const = 0;
virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) = 0;
virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const = 0;
virtual uint32_t surface_get_width(SurfaceID p_surface) const = 0;
virtual uint32_t surface_get_height(SurfaceID p_surface) const = 0;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) = 0;

View File

@ -4209,6 +4209,28 @@ RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_for
return const_cast<RenderingDevice *>(this)->framebuffer_format_create(screen_attachment);
}
RenderingDevice::DataFormat RenderingDevice::screen_get_color_format(DisplayServer::WindowID p_screen) const {
_THREAD_SAFE_METHOD_
HashMap<DisplayServer::WindowID, RDD::SwapChainID>::ConstIterator it = screen_swap_chains.find(p_screen);
ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), DATA_FORMAT_MAX, "Screen was never prepared.");
DataFormat format = driver->swap_chain_get_format(it->value);
ERR_FAIL_COND_V_MSG(format == DATA_FORMAT_MAX, DATA_FORMAT_MAX, "Unknown format.");
return format;
}
RenderingDevice::ColorSpace RenderingDevice::screen_get_color_space(DisplayServer::WindowID p_screen) const {
_THREAD_SAFE_METHOD_
HashMap<DisplayServer::WindowID, RDD::SwapChainID>::ConstIterator it = screen_swap_chains.find(p_screen);
ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), COLOR_SPACE_MAX, "Screen was never prepared.");
ColorSpace color_space = driver->swap_chain_get_color_space(it->value);
ERR_FAIL_COND_V_MSG(color_space == COLOR_SPACE_MAX, COLOR_SPACE_MAX, "Unknown color space.");
return color_space;
}
Error RenderingDevice::screen_free(DisplayServer::WindowID p_screen) {
_THREAD_SAFE_METHOD_
@ -7337,6 +7359,8 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("screen_get_width", "screen"), &RenderingDevice::screen_get_width, DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("screen_get_height", "screen"), &RenderingDevice::screen_get_height, DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("screen_get_framebuffer_format", "screen"), &RenderingDevice::screen_get_framebuffer_format, DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("screen_get_color_format", "screen"), &RenderingDevice::screen_get_color_format, DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("screen_get_color_space", "screen"), &RenderingDevice::screen_get_color_space, DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color()));
@ -7682,6 +7706,11 @@ void RenderingDevice::_bind_methods() {
BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM);
BIND_ENUM_CONSTANT(DATA_FORMAT_MAX);
BIND_ENUM_CONSTANT(COLOR_SPACE_SRGB_LINEAR);
BIND_ENUM_CONSTANT(COLOR_SPACE_SRGB_NONLINEAR);
BIND_ENUM_CONSTANT(COLOR_SPACE_HDR10_ST2084);
BIND_ENUM_CONSTANT(COLOR_SPACE_MAX);
#ifndef DISABLE_DEPRECATED
BIND_BITFIELD_FLAG(BARRIER_MASK_VERTEX);
BIND_BITFIELD_FLAG(BARRIER_MASK_FRAGMENT);
@ -7906,6 +7935,7 @@ void RenderingDevice::_bind_methods() {
BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT);
BIND_ENUM_CONSTANT(SUPPORTS_BUFFER_DEVICE_ADDRESS);
BIND_ENUM_CONSTANT(SUPPORTS_HDR_OUTPUT);
BIND_ENUM_CONSTANT(LIMIT_MAX_BOUND_UNIFORM_SETS);
BIND_ENUM_CONSTANT(LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS);

View File

@ -1159,6 +1159,8 @@ public:
int screen_get_height(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
int screen_get_pre_rotation_degrees(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
FramebufferFormatID screen_get_framebuffer_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
DataFormat screen_get_color_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
ColorSpace screen_get_color_space(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
Error screen_free(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
/*************************/
@ -1678,6 +1680,7 @@ VARIANT_ENUM_CAST(RenderingDevice::ShaderStage)
VARIANT_ENUM_CAST(RenderingDevice::ShaderLanguage)
VARIANT_ENUM_CAST(RenderingDevice::CompareOperator)
VARIANT_ENUM_CAST(RenderingDevice::DataFormat)
VARIANT_ENUM_CAST(RenderingDevice::ColorSpace)
VARIANT_ENUM_CAST(RenderingDevice::TextureType)
VARIANT_ENUM_CAST(RenderingDevice::TextureSamples)
VARIANT_BITFIELD_CAST(RenderingDevice::TextureUsageBits)

View File

@ -273,6 +273,13 @@ public:
DATA_FORMAT_MAX,
};
enum ColorSpace {
COLOR_SPACE_SRGB_LINEAR,
COLOR_SPACE_SRGB_NONLINEAR,
COLOR_SPACE_HDR10_ST2084,
COLOR_SPACE_MAX,
};
// Breadcrumb markers are useful for debugging GPU crashes (i.e. DEVICE_LOST). Internally
// they're just an uint32_t to "tag" a GPU command. These are only used for debugging and do not
// (or at least shouldn't) alter the execution behavior in any way.
@ -887,6 +894,7 @@ public:
// If not supported, a fragment shader with only side effects (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver.
SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS,
SUPPORTS_BUFFER_DEVICE_ADDRESS,
SUPPORTS_HDR_OUTPUT,
};
enum SubgroupOperations {

View File

@ -473,6 +473,9 @@ public:
// Android uses this with Swappy library. Some implementations or platforms may ignore this hint.
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {}
// Retrieve the color space used by the swap chain's framebuffers.
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) = 0;
// Wait until all rendering associated to the swap chain is finished before deleting it.
virtual void swap_chain_free(SwapChainID p_swap_chain) = 0;

View File

@ -186,10 +186,13 @@ public:
// Tonemap
virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0;
virtual void environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value) = 0;
virtual RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const = 0;
virtual float environment_get_exposure(RID p_env) const = 0;
virtual float environment_get_white(RID p_env) const = 0;
virtual float environment_get_min_value(RID p_env) const = 0;
virtual float environment_get_max_value(RID p_env) const = 0;
// Fog
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode = RS::EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL) = 0;

View File

@ -690,6 +690,7 @@ public:
FUNC3(viewport_attach_to_screen, RID, const Rect2 &, int)
FUNC2(viewport_set_render_direct_to_screen, RID, bool)
FUNC2(viewport_set_tonemap_to_screen, RID, bool)
FUNC2(viewport_set_scaling_3d_mode, RID, ViewportScaling3DMode)
FUNC2(viewport_set_scaling_3d_scale, RID, float)
@ -813,6 +814,7 @@ public:
FUNC1(environment_glow_set_use_bicubic_upscale, bool)
FUNC4(environment_set_tonemap, RID, EnvironmentToneMapper, float, float)
FUNC3(environment_set_tonemap_range, RID, float, float)
FUNC7(environment_set_adjustment, RID, bool, float, float, float, bool, RID)

View File

@ -211,6 +211,14 @@ void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::Environm
env->white = p_white;
}
void RendererEnvironmentStorage::environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value) {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL(env);
ERR_FAIL_COND(p_min_value >= p_max_value);
env->min_value = p_min_value;
env->max_value = p_max_value;
}
RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL_V(env, RS::ENV_TONE_MAPPER_LINEAR);
@ -229,6 +237,18 @@ float RendererEnvironmentStorage::environment_get_white(RID p_env) const {
return env->white;
}
float RendererEnvironmentStorage::environment_get_min_value(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL_V(env, 0.0);
return env->min_value;
}
float RendererEnvironmentStorage::environment_get_max_value(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL_V(env, 1.0);
return env->max_value;
}
// Fog
void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) {

View File

@ -63,6 +63,8 @@ private:
RS::EnvironmentToneMapper tone_mapper;
float exposure = 1.0;
float white = 1.0;
float min_value = 0.0;
float max_value = 1.0;
// Fog
bool fog_enabled = false;
@ -201,9 +203,12 @@ public:
// Tonemap
void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white);
void environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value);
RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const;
float environment_get_exposure(RID p_env) const;
float environment_get_white(RID p_env) const;
float environment_get_min_value(RID p_env) const;
float environment_get_max_value(RID p_env) const;
// Fog
void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode);

View File

@ -2810,6 +2810,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_parent_viewport", "viewport", "parent_viewport"), &RenderingServer::viewport_set_parent_viewport);
ClassDB::bind_method(D_METHOD("viewport_attach_to_screen", "viewport", "rect", "screen"), &RenderingServer::viewport_attach_to_screen, DEFVAL(Rect2()), DEFVAL(DisplayServer::MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("viewport_set_render_direct_to_screen", "viewport", "enabled"), &RenderingServer::viewport_set_render_direct_to_screen);
ClassDB::bind_method(D_METHOD("viewport_set_tonemap_to_screen", "viewport", "enabled"), &RenderingServer::viewport_set_tonemap_to_screen);
ClassDB::bind_method(D_METHOD("viewport_set_canvas_cull_mask", "viewport", "canvas_cull_mask"), &RenderingServer::viewport_set_canvas_cull_mask);
ClassDB::bind_method(D_METHOD("viewport_set_scaling_3d_mode", "viewport", "scaling_3d_mode"), &RenderingServer::viewport_set_scaling_3d_mode);
@ -3022,6 +3023,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_set_ambient_light", "env", "color", "ambient", "energy", "sky_contribution", "reflection_source"), &RenderingServer::environment_set_ambient_light, DEFVAL(RS::ENV_AMBIENT_SOURCE_BG), DEFVAL(1.0), DEFVAL(0.0), DEFVAL(RS::ENV_REFLECTION_SOURCE_BG));
ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap", "glow_map_strength", "glow_map"), &RenderingServer::environment_set_glow);
ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white"), &RenderingServer::environment_set_tonemap);
ClassDB::bind_method(D_METHOD("environment_set_tonemap_range", "env", "min_value", "max_value"), &RenderingServer::environment_set_tonemap_range);
ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment);
ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr);
ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "power", "detail", "horizon", "sharpness", "light_affect", "ao_channel_affect"), &RenderingServer::environment_set_ssao);

View File

@ -966,6 +966,7 @@ public:
virtual void viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect = Rect2(), DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) = 0;
virtual void viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable) = 0;
virtual void viewport_set_tonemap_to_screen(RID p_viewport, bool p_enable) = 0;
virtual void viewport_set_scaling_3d_mode(RID p_viewport, ViewportScaling3DMode p_scaling_3d_mode) = 0;
virtual void viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) = 0;
@ -1257,6 +1258,7 @@ public:
};
virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0;
virtual void environment_set_tonemap_range(RID p_env, float p_min_value, float p_max_value) = 0;
virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0;
virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0;