diff options
-rw-r--r-- | core/templates/rid_owner.h | 20 | ||||
-rw-r--r-- | doc/classes/@GlobalScope.xml | 11 | ||||
-rw-r--r-- | doc/classes/Curve.xml | 1 | ||||
-rw-r--r-- | doc/classes/Gradient.xml | 1 | ||||
-rw-r--r-- | doc/classes/RenderingDevice.xml | 6 | ||||
-rwxr-xr-x | doc/tools/make_rst.py | 56 | ||||
-rw-r--r-- | drivers/unix/ip_unix.cpp | 4 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 27 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.h | 12 | ||||
-rw-r--r-- | editor/editor_feature_profile.cpp | 5 | ||||
-rw-r--r-- | scene/gui/button.cpp | 3 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/framebuffer_cache_rd.cpp | 64 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/framebuffer_cache_rd.h | 310 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_compositor_rd.cpp | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_compositor_rd.h | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/uniform_set_cache_rd.h | 4 | ||||
-rw-r--r-- | servers/rendering/rendering_device.cpp | 1 | ||||
-rw-r--r-- | servers/rendering/rendering_device.h | 11 |
18 files changed, 490 insertions, 50 deletions
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index 9c74b41818..320faebe98 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -79,7 +79,7 @@ class RID_Alloc : public RID_AllocBase { const char *description = nullptr; - SpinLock spin_lock; + mutable SpinLock spin_lock; _FORCE_INLINE_ RID _allocate_rid() { if (THREAD_SAFE) { @@ -220,7 +220,7 @@ public: memnew_placement(mem, T(p_value)); } - _FORCE_INLINE_ bool owns(const RID &p_rid) { + _FORCE_INLINE_ bool owns(const RID &p_rid) const { if (THREAD_SAFE) { spin_lock.lock(); } @@ -292,7 +292,7 @@ public: _FORCE_INLINE_ uint32_t get_rid_count() const { return alloc_count; } - void get_owned_list(List<RID> *p_owned) { + void get_owned_list(List<RID> *p_owned) const { if (THREAD_SAFE) { spin_lock.lock(); } @@ -308,7 +308,7 @@ public: } //used for fast iteration in the elements or RIDs - void fill_owned_buffer(RID *p_rid_buffer) { + void fill_owned_buffer(RID *p_rid_buffer) const { if (THREAD_SAFE) { spin_lock.lock(); } @@ -402,7 +402,7 @@ public: *ptr = p_new_ptr; } - _FORCE_INLINE_ bool owns(const RID &p_rid) { + _FORCE_INLINE_ bool owns(const RID &p_rid) const { return alloc.owns(p_rid); } @@ -414,11 +414,11 @@ public: return alloc.get_rid_count(); } - _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { + _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) const { return alloc.get_owned_list(p_owned); } - void fill_owned_buffer(RID *p_rid_buffer) { + void fill_owned_buffer(RID *p_rid_buffer) const { alloc.fill_owned_buffer(p_rid_buffer); } @@ -458,7 +458,7 @@ public: return alloc.get_or_null(p_rid); } - _FORCE_INLINE_ bool owns(const RID &p_rid) { + _FORCE_INLINE_ bool owns(const RID &p_rid) const { return alloc.owns(p_rid); } @@ -470,10 +470,10 @@ public: return alloc.get_rid_count(); } - _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { + _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) const { return alloc.get_owned_list(p_owned); } - void fill_owned_buffer(RID *p_rid_buffer) { + void fill_owned_buffer(RID *p_rid_buffer) const { alloc.fill_owned_buffer(p_rid_buffer); } diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index d647621362..4a16244235 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -380,7 +380,7 @@ <argument index="1" name="to" type="float" /> <argument index="2" name="weight" type="float" /> <description> - Returns an interpolation or extrapolation factor considering the range specified in [code]from[/code] and [code]to[/code], and the interpolated value specified in [code]weight[/code]. The returned value will be between [code]0.0[/code] and [code]1.0[/code] if [code]weight[/code] is between [code]from[/code] and [code]to[/code] (inclusive). If [code]weight[/code] is located outside this range, then an extrapolation factor will be returned (return value lower than [code]0.0[/code] or greater than [code]1.0[/code]). + Returns an interpolation or extrapolation factor considering the range specified in [code]from[/code] and [code]to[/code], and the interpolated value specified in [code]weight[/code]. The returned value will be between [code]0.0[/code] and [code]1.0[/code] if [code]weight[/code] is between [code]from[/code] and [code]to[/code] (inclusive). If [code]weight[/code] is located outside this range, then an extrapolation factor will be returned (return value lower than [code]0.0[/code] or greater than [code]1.0[/code]). Use [method clamp] on the result of [method inverse_lerp] if this is not desired. [codeblock] # The interpolation ratio in the `lerp()` call below is 0.75. var middle = lerp(20, 30, 0.75) @@ -389,7 +389,7 @@ var ratio = inverse_lerp(20, 30, 27.5) # `ratio` is now 0.75. [/codeblock] - See also [method lerp] which performs the reverse of this operation. + See also [method lerp] which performs the reverse of this operation, and [method range_lerp] to map a continuous series of values to another. </description> </method> <method name="is_equal_approx"> @@ -444,11 +444,11 @@ <argument index="1" name="to" type="float" /> <argument index="2" name="weight" type="float" /> <description> - Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. + Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. Use [method clamp] on the result of [method lerp] if this is not desired. [codeblock] lerp(0, 4, 0.75) # Returns 3.0 [/codeblock] - See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. + See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. See also [method range_lerp] to map a continuous series of values to another. </description> </method> <method name="lerp_angle"> @@ -807,10 +807,11 @@ <argument index="3" name="ostart" type="float" /> <argument index="4" name="ostop" type="float" /> <description> - Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. + Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. See also [method lerp] and [method inverse_lerp]. If [code]value[/code] is outside [code][istart, istop][/code], then the resulting value will also be outside [code][ostart, ostop][/code]. Use [method clamp] on the result of [method range_lerp] if this is not desired. [codeblock] range_lerp(75, 0, 100, -1, 1) # Returns 0.5 [/codeblock] + For complex use cases where you need multiple ranges, consider using [Curve] or [Gradient] instead. </description> </method> <method name="rid_allocate_id"> diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 383d33532b..179b0344c2 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -5,6 +5,7 @@ </brief_description> <description> A curve that can be saved and re-used for other objects. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position. + See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index 89c968cc9e..8c5373216a 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -5,6 +5,7 @@ </brief_description> <description> Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the gradient will interpolate from color 1 to color 2 and from color 2 to color 3. The gradient will initially have 2 colors (black and white), one (black) at gradient lower offset 0 and the other (white) at the gradient higher offset 1. + See also [Curve] which supports more complex easing methods, but does not support colors. </description> <tutorials> </tutorials> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 6248394b1a..3962f309de 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -305,6 +305,12 @@ <description> </description> </method> + <method name="framebuffer_is_valid" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="framebuffer" type="RID" /> + <description> + </description> + </method> <method name="free_rid"> <return type="void" /> <argument index="0" name="rid" type="RID" /> diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index b3ceeea8b5..196a26ef77 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -237,8 +237,7 @@ class State: if desc_element is not None: annotation_desc = desc_element.text - return_type = TypeName("void") - annotation_def = MethodDef(annotation_name, return_type, params, annotation_desc, qualifiers) + annotation_def = AnnotationDef(annotation_name, params, annotation_desc, qualifiers) if annotation_name not in class_def.annotations: class_def.annotations[annotation_name] = [] @@ -359,6 +358,20 @@ class SignalDef: self.description = description +class AnnotationDef: + def __init__( + self, + name: str, + parameters: List[ParameterDef], + description: Optional[str], + qualifiers: Optional[str], + ) -> None: + self.name = name + self.parameters = parameters + self.description = description + self.qualifiers = qualifiers + + class MethodDef: def __init__( self, @@ -411,7 +424,7 @@ class ClassDef: self.methods: OrderedDict[str, List[MethodDef]] = OrderedDict() self.operators: OrderedDict[str, List[MethodDef]] = OrderedDict() self.signals: OrderedDict[str, SignalDef] = OrderedDict() - self.annotations: OrderedDict[str, List[MethodDef]] = OrderedDict() + self.annotations: OrderedDict[str, List[AnnotationDef]] = OrderedDict() self.theme_items: OrderedDict[str, ThemeItemDef] = OrderedDict() self.inherits: Optional[str] = None self.brief_description: Optional[str] = None @@ -791,6 +804,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("\n\n") + # Annotations if len(class_def.annotations) > 0: f.write(make_heading("Annotations", "-")) index = 0 @@ -803,8 +817,8 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: if i == 0: f.write(".. _class_{}_annotation_{}:\n\n".format(class_name, m.name.strip("@"))) - ret_type, signature = make_method_signature(class_def, m, "", state) - f.write("- {} {}\n\n".format(ret_type, signature)) + _, signature = make_method_signature(class_def, m, "", state) + f.write("- {}\n\n".format(signature)) if m.description is not None and m.description.strip() != "": f.write(rstize_text(m.description.strip(), state) + "\n\n") @@ -1396,32 +1410,36 @@ def make_enum(t: str, state: State) -> str: def make_method_signature( - class_def: ClassDef, method_def: Union[MethodDef, SignalDef], ref_type: str, state: State + class_def: ClassDef, definition: Union[AnnotationDef, MethodDef, SignalDef], ref_type: str, state: State ) -> Tuple[str, str]: - ret_type = " " + ret_type = "" - is_method_def = isinstance(method_def, MethodDef) + is_method_def = isinstance(definition, MethodDef) if is_method_def: - ret_type = method_def.return_type.to_rst(state) + ret_type = definition.return_type.to_rst(state) + + qualifiers = None + if is_method_def or isinstance(definition, AnnotationDef): + qualifiers = definition.qualifiers out = "" if is_method_def and ref_type != "": if ref_type == "operator": out += ":ref:`{0}<class_{1}_{2}_{3}_{4}>` ".format( - method_def.name.replace("<", "\\<"), # So operator "<" gets correctly displayed. + definition.name.replace("<", "\\<"), # So operator "<" gets correctly displayed. class_def.name, ref_type, - sanitize_operator_name(method_def.name, state), - method_def.return_type.type_name, + sanitize_operator_name(definition.name, state), + definition.return_type.type_name, ) else: - out += ":ref:`{0}<class_{1}_{2}_{0}>` ".format(method_def.name, class_def.name, ref_type) + out += ":ref:`{0}<class_{1}_{2}_{0}>` ".format(definition.name, class_def.name, ref_type) else: - out += "**{}** ".format(method_def.name) + out += "**{}** ".format(definition.name) out += "**(**" - for i, arg in enumerate(method_def.parameters): + for i, arg in enumerate(definition.parameters): if i > 0: out += ", " else: @@ -1432,18 +1450,18 @@ def make_method_signature( if arg.default_value is not None: out += "=" + arg.default_value - if isinstance(method_def, MethodDef) and method_def.qualifiers is not None and "vararg" in method_def.qualifiers: - if len(method_def.parameters) > 0: + if qualifiers is not None and "vararg" in qualifiers: + if len(definition.parameters) > 0: out += ", ..." else: out += " ..." out += " **)**" - if isinstance(method_def, MethodDef) and method_def.qualifiers is not None: + if qualifiers is not None: # Use substitutions for abbreviations. This is used to display tooltips on hover. # See `make_footer()` for descriptions. - for qualifier in method_def.qualifiers.split(): + for qualifier in qualifiers.split(): out += " |" + qualifier + "|" return ret_type, out diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 2deeb79957..0dc2efedc1 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -95,12 +95,12 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result); if (s != 0) { - ERR_PRINT("getaddrinfo failed! Cannot resolve hostname."); + print_verbose("getaddrinfo failed! Cannot resolve hostname."); return; } if (result == nullptr || result->ai_addr == nullptr) { - ERR_PRINT("Invalid response from getaddrinfo"); + print_verbose("Invalid response from getaddrinfo"); if (result) { freeaddrinfo(result); } diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 0979ae9e16..45e114b130 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3971,7 +3971,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c passes.push_back(pass); return framebuffer_format_create_multipass(p_format, passes, p_view_count); } -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; @@ -4110,7 +4110,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); } -RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; @@ -4181,6 +4181,22 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_form return framebuffer->format_id; } +bool RenderingDeviceVulkan::framebuffer_is_valid(RID p_framebuffer) const { + _THREAD_SAFE_METHOD_ + + return framebuffer_owner.owns(p_framebuffer); +} + +void RenderingDeviceVulkan::framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) { + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_COND(!framebuffer); + + framebuffer->invalidated_callback = p_callback; + framebuffer->invalidated_callback_userdata = p_userdata; +} + /*****************/ /**** SAMPLER ****/ /*****************/ @@ -6218,7 +6234,7 @@ bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { return uniform_set_owner.owns(p_uniform_set); } -void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) { +void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) { UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); ERR_FAIL_COND(!us); us->invalidated_callback = p_callback; @@ -8685,6 +8701,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (framebuffer_owner.owns(p_id)) { Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); + + if (framebuffer->invalidated_callback != nullptr) { + framebuffer->invalidated_callback(framebuffer->invalidated_callback_userdata); + } + framebuffer_owner.free(p_id); } else if (sampler_owner.owns(p_id)) { VkSampler *sampler = sampler_owner.get_or_null(p_id); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 7c8021251f..d98ac1114b 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -392,6 +392,8 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t storage_mask = 0; Vector<RID> texture_ids; + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; @@ -747,7 +749,7 @@ class RenderingDeviceVulkan : public RenderingDevice { LocalVector<AttachableTexture> attachable_textures; //used for validation Vector<Texture *> mutable_sampled_textures; //used for layout change Vector<Texture *> mutable_storage_textures; //used for layout change - UniformSetInvalidatedCallback invalidated_callback = nullptr; + InvalidationCallback invalidated_callback = nullptr; void *invalidated_callback_userdata = nullptr; }; @@ -1059,13 +1061,15 @@ public: /*********************/ virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); - virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); - virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); + virtual bool framebuffer_is_valid(RID p_framebuffer) const; + virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -1110,7 +1114,7 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); virtual bool uniform_set_is_valid(RID p_uniform_set); - virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata); + virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); //works for any buffer virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL); diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index ab6dec7eeb..f0bf9fd5b3 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -324,6 +324,11 @@ void EditorFeatureProfileManager::_notification(int p_what) { } _update_profile_list(current_profile); } break; + + case NOTIFICATION_THEME_CHANGED: { + // Make sure that the icons are correctly adjusted if the theme's lightness was switched. + _update_selected_profile(); + } break; } } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 9bcb061526..0a163b65ff 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -258,7 +258,8 @@ void Button::_notification(int p_what) { } if (icon_region.size.width > 0) { - draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); + Rect2 icon_region_rounded = Rect2(icon_region.position.round(), icon_region.size.round()); + draw_texture_rect(_icon, icon_region_rounded, false, color_icon); } } diff --git a/servers/rendering/renderer_rd/framebuffer_cache_rd.cpp b/servers/rendering/renderer_rd/framebuffer_cache_rd.cpp new file mode 100644 index 0000000000..9baa86a32d --- /dev/null +++ b/servers/rendering/renderer_rd/framebuffer_cache_rd.cpp @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* framebuffer_cache_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "framebuffer_cache_rd.h" + +FramebufferCacheRD *FramebufferCacheRD::singleton = nullptr; + +void FramebufferCacheRD::_invalidate(Cache *p_cache) { + if (p_cache->prev) { + p_cache->prev->next = p_cache->next; + } else { + // At beginning of table + uint32_t table_idx = p_cache->hash % HASH_TABLE_SIZE; + hash_table[table_idx] = p_cache->next; + } + + if (p_cache->next) { + p_cache->next->prev = p_cache->prev; + } + + cache_allocator.free(p_cache); + cache_instances_used--; +} +void FramebufferCacheRD::_framebuffer_invalidation_callback(void *p_userdata) { + singleton->_invalidate(reinterpret_cast<Cache *>(p_userdata)); +} + +FramebufferCacheRD::FramebufferCacheRD() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} + +FramebufferCacheRD::~FramebufferCacheRD() { + if (cache_instances_used > 0) { + ERR_PRINT("At exit: " + itos(cache_instances_used) + " framebuffer cache instance(s) still in use."); + } +} diff --git a/servers/rendering/renderer_rd/framebuffer_cache_rd.h b/servers/rendering/renderer_rd/framebuffer_cache_rd.h new file mode 100644 index 0000000000..f360e0fc6b --- /dev/null +++ b/servers/rendering/renderer_rd/framebuffer_cache_rd.h @@ -0,0 +1,310 @@ +/*************************************************************************/ +/* framebuffer_cache_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FRAMEBUFFER_CACHE_RD_H +#define FRAMEBUFFER_CACHE_RD_H + +#include "core/templates/local_vector.h" +#include "core/templates/paged_allocator.h" +#include "servers/rendering/rendering_device.h" + +class FramebufferCacheRD : public Object { + GDCLASS(FramebufferCacheRD, Object) + + struct Cache { + Cache *prev = nullptr; + Cache *next = nullptr; + uint32_t hash = 0; + RID cache; + LocalVector<RID> textures; + LocalVector<RD::FramebufferPass> passes; + uint32_t views = 0; + }; + + PagedAllocator<Cache> cache_allocator; + + enum { + HASH_TABLE_SIZE = 16381 // Prime + }; + + Cache *hash_table[HASH_TABLE_SIZE] = {}; + + static _FORCE_INLINE_ uint32_t _hash_pass(const RD::FramebufferPass &p, uint32_t h) { + h = hash_murmur3_one_32(p.depth_attachment, h); + h = hash_murmur3_one_32(p.vrs_attachment, h); + + h = hash_murmur3_one_32(p.color_attachments.size(), h); + for (int i = 0; i < p.color_attachments.size(); i++) { + h = hash_murmur3_one_32(p.color_attachments[i], h); + } + + h = hash_murmur3_one_32(p.resolve_attachments.size(), h); + for (int i = 0; i < p.resolve_attachments.size(); i++) { + h = hash_murmur3_one_32(p.resolve_attachments[i], h); + } + + h = hash_murmur3_one_32(p.preserve_attachments.size(), h); + for (int i = 0; i < p.preserve_attachments.size(); i++) { + h = hash_murmur3_one_32(p.preserve_attachments[i], h); + } + + return h; + } + + static _FORCE_INLINE_ bool _compare_pass(const RD::FramebufferPass &a, const RD::FramebufferPass &b) { + if (a.depth_attachment != b.depth_attachment) { + return false; + } + + if (a.vrs_attachment != b.vrs_attachment) { + return false; + } + + if (a.color_attachments.size() != b.color_attachments.size()) { + return false; + } + + for (int i = 0; i < a.color_attachments.size(); i++) { + if (a.color_attachments[i] != b.color_attachments[i]) { + return false; + } + } + + if (a.resolve_attachments.size() != b.resolve_attachments.size()) { + return false; + } + + for (int i = 0; i < a.resolve_attachments.size(); i++) { + if (a.resolve_attachments[i] != b.resolve_attachments[i]) { + return false; + } + } + + if (a.preserve_attachments.size() != b.preserve_attachments.size()) { + return false; + } + + for (int i = 0; i < a.preserve_attachments.size(); i++) { + if (a.preserve_attachments[i] != b.preserve_attachments[i]) { + return false; + } + } + + return true; + } + + _FORCE_INLINE_ uint32_t _hash_rids(uint32_t h, const RID &arg) { + return hash_murmur3_one_64(arg.get_id(), h); + } + + template <typename... Args> + uint32_t _hash_rids(uint32_t h, const RID &arg, Args... args) { + h = hash_murmur3_one_64(arg.get_id(), h); + return _hash_rids(h, args...); + } + + _FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg) { + return textures[idx] == arg; + } + + template <typename... Args> + _FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg, Args... args) { + if (textures[idx] != arg) { + return false; + } + return _compare_args(idx + 1, textures, args...); + } + + _FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg) { + textures.push_back(arg); + } + + template <typename... Args> + _FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg, Args... args) { + textures.push_back(arg); + _create_args(textures, args...); + } + + static FramebufferCacheRD *singleton; + + uint32_t cache_instances_used = 0; + + void _invalidate(Cache *p_cache); + static void _framebuffer_invalidation_callback(void *p_userdata); + + RID _allocate_from_data(uint32_t p_views, uint32_t p_hash, uint32_t p_table_idx, const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes) { + RID rid; + if (p_passes.size()) { + rid = RD::get_singleton()->framebuffer_create_multipass(p_textures, p_passes, RD::INVALID_ID, p_views); + } else { + rid = RD::get_singleton()->framebuffer_create(p_textures, RD::INVALID_ID, p_views); + } + + ERR_FAIL_COND_V(rid.is_null(), rid); + + Cache *c = cache_allocator.alloc(); + c->views = p_views; + c->cache = rid; + c->hash = p_hash; + c->textures.resize(p_textures.size()); + for (uint32_t i = 0; i < c->textures.size(); i++) { + c->textures[i] = p_textures[i]; + } + c->passes.resize(p_passes.size()); + for (uint32_t i = 0; i < c->passes.size(); i++) { + c->passes[i] = p_passes[i]; + } + c->prev = nullptr; + c->next = hash_table[p_table_idx]; + if (hash_table[p_table_idx]) { + hash_table[p_table_idx]->prev = c; + } + hash_table[p_table_idx] = c; + + RD::get_singleton()->framebuffer_set_invalidation_callback(rid, _framebuffer_invalidation_callback, c); + + cache_instances_used++; + + return rid; + } + +public: + template <typename... Args> + RID get_cache(Args... args) { + uint32_t h = hash_murmur3_one_32(1); //1 view + h = hash_murmur3_one_32(sizeof...(Args), h); + h = _hash_args(h, args...); + h = hash_murmur3_one_32(0, h); // 0 passes + h = hash_fmix32(h); + + uint32_t table_idx = h % HASH_TABLE_SIZE; + { + const Cache *c = hash_table[table_idx]; + + while (c) { + if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == 1 && _compare_args(0, c->textures, args...)) { + return c->cache; + } + c = c->next; + } + } + + // Not in cache, create: + + Vector<RID> textures; + _create_args(textures, args...); + + return _allocate_from_data(1, h, table_idx, textures, Vector<RD::FramebufferPass>()); + } + + template <typename... Args> + RID get_cache_multiview(uint32_t p_views, Args... args) { + uint32_t h = hash_murmur3_one_32(p_views); + h = hash_murmur3_one_32(sizeof...(Args), h); + h = _hash_args(h, args...); + h = hash_murmur3_one_32(0, h); // 0 passes + h = hash_fmix32(h); + + uint32_t table_idx = h % HASH_TABLE_SIZE; + { + const Cache *c = hash_table[table_idx]; + + while (c) { + if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == p_views && _compare_args(0, c->textures, args...)) { + return c->cache; + } + c = c->next; + } + } + + // Not in cache, create: + + Vector<RID> textures; + _create_args(textures, args...); + + return _allocate_from_data(p_views, h, table_idx, textures, Vector<RD::FramebufferPass>()); + } + + RID get_cache_multipass(const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes, uint32_t p_views = 1) { + uint32_t h = hash_murmur3_one_32(p_views); + h = hash_murmur3_one_32(p_textures.size()); + for (int i = 0; i < p_textures.size(); i++) { + h = hash_murmur3_one_64(p_textures[i].get_id(), h); + } + h = hash_murmur3_one_32(p_passes.size()); + for (int i = 0; i < p_passes.size(); i++) { + h = _hash_pass(p_passes[i], h); + } + + h = hash_fmix32(h); + + uint32_t table_idx = h % HASH_TABLE_SIZE; + { + const Cache *c = hash_table[table_idx]; + + while (c) { + if (c->hash == h && c->views == p_views && c->textures.size() == (uint32_t)p_textures.size() && c->passes.size() == (uint32_t)p_passes.size()) { + bool all_ok = true; + + for (int i = 0; i < p_textures.size(); i++) { + if (p_textures[i] != c->textures[i]) { + all_ok = false; + break; + } + } + + if (all_ok) { + for (int i = 0; i < p_passes.size(); i++) { + if (!_compare_pass(p_passes[i], c->passes[i])) { + all_ok = false; + break; + } + } + } + + if (all_ok) { + return c->cache; + } + } + c = c->next; + } + } + + // Not in cache, create: + return _allocate_from_data(p_views, h, table_idx, p_textures, p_passes); + } + + static FramebufferCacheRD *get_singleton() { return singleton; } + + FramebufferCacheRD(); + ~FramebufferCacheRD(); +}; + +#endif // FRAMEBUFFER_CACHE_RD_H diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index a61172c8f5..967b725b9e 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -249,6 +249,7 @@ RendererCompositorRD *RendererCompositorRD::singleton = nullptr; RendererCompositorRD::RendererCompositorRD() { uniform_set_cache = memnew(UniformSetCacheRD); + framebuffer_cache = memnew(FramebufferCacheRD); { String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path(); @@ -316,5 +317,6 @@ RendererCompositorRD::RendererCompositorRD() { RendererCompositorRD::~RendererCompositorRD() { memdelete(uniform_set_cache); + memdelete(framebuffer_cache); ShaderRD::set_shader_cache_dir(String()); } diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 564c26bfe4..a28335f800 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -37,6 +37,7 @@ #include "servers/rendering/renderer_rd/environment/fog.h" #include "servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h" #include "servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h" +#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h" #include "servers/rendering/renderer_rd/renderer_canvas_render_rd.h" #include "servers/rendering/renderer_rd/shaders/blit.glsl.gen.h" #include "servers/rendering/renderer_rd/storage_rd/light_storage.h" @@ -50,6 +51,7 @@ class RendererCompositorRD : public RendererCompositor { protected: UniformSetCacheRD *uniform_set_cache = nullptr; + FramebufferCacheRD *framebuffer_cache = nullptr; RendererCanvasRenderRD *canvas = nullptr; RendererRD::Utilities *utilities = nullptr; RendererRD::LightStorage *light_storage = nullptr; diff --git a/servers/rendering/renderer_rd/uniform_set_cache_rd.h b/servers/rendering/renderer_rd/uniform_set_cache_rd.h index abf110730b..bca8b02178 100644 --- a/servers/rendering/renderer_rd/uniform_set_cache_rd.h +++ b/servers/rendering/renderer_rd/uniform_set_cache_rd.h @@ -163,7 +163,7 @@ public: const Cache *c = hash_table[table_idx]; while (c) { - if (c->hash == h && c->set == p_set && c->shader == p_shader && _compare_args(0, c->uniforms, args...)) { + if (c->hash == h && c->set == p_set && c->shader == p_shader && sizeof...(Args) == c->uniforms.size() && _compare_args(0, c->uniforms, args...)) { return c->cache; } c = c->next; @@ -193,7 +193,7 @@ public: const Cache *c = hash_table[table_idx]; while (c) { - if (c->hash == h && c->set == p_set && c->shader == p_shader) { + if (c->hash == h && c->set == p_set && c->shader == p_shader && (uint32_t)p_uniforms.size() == c->uniforms.size()) { bool all_ok = true; for (int i = 0; i < p_uniforms.size(); i++) { if (!_compare_uniform(p_uniforms[i], c->uniforms[i])) { diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 0b76bb3051..c07a783302 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -387,6 +387,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("framebuffer_create_multipass", "textures", "passes", "validate_with_format", "view_count"), &RenderingDevice::_framebuffer_create_multipass, DEFVAL(INVALID_FORMAT_ID), DEFVAL(1)); ClassDB::bind_method(D_METHOD("framebuffer_create_empty", "size", "samples", "validate_with_format"), &RenderingDevice::framebuffer_create_empty, DEFVAL(TEXTURE_SAMPLES_1), DEFVAL(INVALID_FORMAT_ID)); ClassDB::bind_method(D_METHOD("framebuffer_get_format", "framebuffer"), &RenderingDevice::framebuffer_get_format); + ClassDB::bind_method(D_METHOD("framebuffer_is_valid", "framebuffer"), &RenderingDevice::framebuffer_is_valid); ClassDB::bind_method(D_METHOD("sampler_create", "state"), &RenderingDevice::_sampler_create); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 03aa6f7644..a864cfa74c 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -129,6 +129,8 @@ public: typedef Vector<uint8_t> (*ShaderCompileToSPIRVFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device); typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language); + typedef void (*InvalidationCallback)(void *); + private: static ShaderCompileToSPIRVFunction compile_to_spirv_function; static ShaderCacheFunction cache_function; @@ -547,13 +549,15 @@ public: int32_t vrs_attachment = ATTACHMENT_UNUSED; // density map for VRS, only used if supported }; - virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1) = 0; + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1) = 0; virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0; virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0) = 0; virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; - virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0; + virtual bool framebuffer_is_valid(RID p_framebuffer) const = 0; + virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) = 0; virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; @@ -793,8 +797,7 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) = 0; virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; - typedef void (*UniformSetInvalidatedCallback)(void *); - virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) = 0; + virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0; virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; |