diff options
22 files changed, 510 insertions, 129 deletions
diff --git a/core/object/object.h b/core/object/object.h index 3ad8391dd6..446550f91f 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -556,6 +556,7 @@ public: CONNECT_PERSIST = 2, // hint for scene to save this connection CONNECT_ONE_SHOT = 4, CONNECT_REFERENCE_COUNTED = 8, + CONNECT_INHERITED = 16, // Used in editor builds. }; struct Connection { diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml index 3c811e6226..b2c5a1756f 100644 --- a/doc/classes/BackBufferCopy.xml +++ b/doc/classes/BackBufferCopy.xml @@ -4,8 +4,8 @@ Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts through the [code]texture(SCREEN_TEXTURE, ...)[/code] function. </brief_description> <description> - Node for back-buffering the currently-displayed screen. The region defined in the BackBufferCopy node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the [code]texture(SCREEN_TEXTURE, ...)[/code] function in your shader scripts to access the buffer. - [b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the BackBufferCopy node instead of adding them as children. + Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the [code]texture(SCREEN_TEXTURE, ...)[/code] function in your shader scripts to access the buffer. + [b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children. </description> <tutorials> </tutorials> @@ -14,18 +14,18 @@ Buffer mode. See [enum CopyMode] constants. </member> <member name="rect" type="Rect2" setter="set_rect" getter="get_rect" default="Rect2(-100, -100, 200, 200)"> - The area covered by the BackBufferCopy. Only used if [member copy_mode] is [constant COPY_MODE_RECT]. + The area covered by the [BackBufferCopy]. Only used if [member copy_mode] is [constant COPY_MODE_RECT]. </member> </members> <constants> <constant name="COPY_MODE_DISABLED" value="0" enum="CopyMode"> - Disables the buffering mode. This means the BackBufferCopy node will directly use the portion of screen it covers. + Disables the buffering mode. This means the [BackBufferCopy] node will directly use the portion of screen it covers. </constant> <constant name="COPY_MODE_RECT" value="1" enum="CopyMode"> - BackBufferCopy buffers a rectangular region. + [BackBufferCopy] buffers a rectangular region. </constant> <constant name="COPY_MODE_VIEWPORT" value="2" enum="CopyMode"> - BackBufferCopy buffers the entire screen. + [BackBufferCopy] buffers the entire screen. </constant> </constants> </class> diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 2bd77bf99c..1f0cc1dc77 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -788,23 +788,7 @@ bool ConnectionsDock::_is_item_signal(TreeItem &p_item) { } bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) { - Node *scene_root = EditorNode::get_singleton()->get_edited_scene(); - Ref<PackedScene> scn = ResourceLoader::load(scene_root->get_scene_file_path()); - ERR_FAIL_NULL_V(scn, false); - - Ref<SceneState> state = scn->get_state(); - ERR_FAIL_NULL_V(state, false); - - Node *source = Object::cast_to<Node>(p_connection.signal.get_object()); - Node *target = Object::cast_to<Node>(p_connection.callable.get_object()); - - const NodePath source_path = scene_root->get_path_to(source); - const NodePath target_path = scene_root->get_path_to(target); - const StringName signal_name = p_connection.signal.get_name(); - const StringName method_name = p_connection.callable.get_method(); - - // If it cannot be found in PackedScene, this connection was inherited. - return !state->has_connection(source_path, signal_name, target_path, method_name, true); + return bool(p_connection.flags & CONNECT_INHERITED); } /* diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 5345346c48..9de2f94900 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -81,8 +81,8 @@ bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExpo // Look for export templates (first official, and if defined custom templates). String arch = p_preset->get("binary_format/architecture"); - bool dvalid = exists_export_template(get_template_file_name("template_debug", arch), &err); - bool rvalid = exists_export_template(get_template_file_name("template_release", arch), &err); + bool dvalid = exists_export_template(get_template_file_name("debug", arch), &err); + bool rvalid = exists_export_template(get_template_file_name("release", arch), &err); if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 1ae419053e..4f0894a1a9 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -96,6 +96,7 @@ protected: static void _bind_methods(); public: + virtual String get_name() const override { return "Shader"; } virtual void edit(Object *p_object) override; virtual bool handles(Object *p_object) const override; virtual void make_visible(bool p_visible) override; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 9990d5c06f..cf811067c9 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -269,6 +269,19 @@ void VisualShaderGraphPlugin::set_expression(VisualShader::Type p_type, int p_no links[p_node_id].expression_edit->set_text(p_expression); } +Ref<Script> VisualShaderGraphPlugin::get_node_script(int p_node_id) const { + if (!links.has(p_node_id)) { + return Ref<Script>(); + } + + Ref<VisualShaderNodeCustom> custom = Ref<VisualShaderNodeCustom>(links[p_node_id].visual_node); + if (custom.is_valid()) { + return custom->get_script(); + } + + return Ref<Script>(); +} + void VisualShaderGraphPlugin::update_node_size(int p_node_id) { if (!links.has(p_node_id)) { return; @@ -1137,10 +1150,6 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { } } -void VisualShaderEditor::update_nodes() { - _update_nodes(); -} - void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) { if (plugins.has(p_plugin)) { return; @@ -1202,6 +1211,228 @@ void VisualShaderEditor::add_custom_type(const String &p_name, const Ref<Script> add_options.push_back(ao); } +Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node) { + Dictionary dict; + dict["script"] = p_custom_node->get_script(); + + String name; + if (p_custom_node->has_method("_get_name")) { + name = (String)p_custom_node->call("_get_name"); + } else { + name = "Unnamed"; + } + dict["name"] = name; + + String description = ""; + if (p_custom_node->has_method("_get_description")) { + description = (String)p_custom_node->call("_get_description"); + } + dict["description"] = description; + + int return_icon_type = -1; + if (p_custom_node->has_method("_get_return_icon_type")) { + return_icon_type = (int)p_custom_node->call("_get_return_icon_type"); + } + dict["return_icon_type"] = return_icon_type; + + String category = ""; + if (p_custom_node->has_method("_get_category")) { + category = (String)p_custom_node->call("_get_category"); + } + category = category.rstrip("/"); + category = category.lstrip("/"); + category = "Addons/" + category; + + String subcategory = ""; + if (p_custom_node->has_method("_get_subcategory")) { + subcategory = (String)p_custom_node->call("_get_subcategory"); + } + if (!subcategory.is_empty()) { + category += "/" + subcategory; + } + dict["category"] = category; + + bool highend = false; + if (p_custom_node->has_method("_is_highend")) { + highend = (bool)p_custom_node->call("_is_highend"); + } + dict["highend"] = highend; + + return dict; +} + +void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { + Ref<Script> scr = Ref<Script>(p_resource.ptr()); + if (scr.is_null() || scr->get_instance_base_type() != String("VisualShaderNodeCustom")) { + return; + } + + Ref<VisualShaderNodeCustom> ref; + ref.instantiate(); + ref->set_script(scr); + if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) { + for (int i = 0; i < add_options.size(); i++) { + if (add_options[i].is_custom && add_options[i].script == scr) { + add_options.remove_at(i); + _update_options_menu(); + // TODO: Make indication for the existed custom nodes with that script on graph to be disabled. + break; + } + } + return; + } + Dictionary dict = get_custom_node_data(ref); + + bool found_type = false; + bool need_rebuild = false; + + for (int i = 0; i < add_options.size(); i++) { + if (add_options[i].is_custom && add_options[i].script == scr) { + found_type = true; + + add_options.write[i].name = dict["name"]; + add_options.write[i].return_type = dict["return_icon_type"]; + add_options.write[i].description = dict["description"]; + add_options.write[i].category = dict["category"]; + add_options.write[i].highend = dict["highend"]; + + int max_type = 0; + int type_offset = 0; + switch (visual_shader->get_mode()) { + case Shader::MODE_CANVAS_ITEM: + case Shader::MODE_SPATIAL: { + max_type = 3; + } break; + case Shader::MODE_PARTICLES: { + max_type = 5; + type_offset = 3; + } break; + case Shader::MODE_SKY: { + max_type = 1; + type_offset = 8; + } break; + case Shader::MODE_FOG: { + max_type = 1; + type_offset = 9; + } break; + default: { + } break; + } + max_type = type_offset + max_type; + + for (int t = type_offset; t < max_type; t++) { + VisualShader::Type type = (VisualShader::Type)t; + Vector<int> nodes = visual_shader->get_node_list(type); + + List<VisualShader::Connection> node_connections; + visual_shader->get_node_connections(type, &node_connections); + + List<VisualShader::Connection> custom_node_input_connections; + List<VisualShader::Connection> custom_node_output_connections; + for (const VisualShader::Connection &E : node_connections) { + int from = E.from_node; + int from_idx = E.from_port; + int to = E.to_node; + int to_idx = E.to_port; + + if (graph_plugin->get_node_script(from) == scr) { + custom_node_output_connections.push_back({ from, from_idx, to, to_idx }); + } else if (graph_plugin->get_node_script(to) == scr) { + custom_node_input_connections.push_back({ from, from_idx, to, to_idx }); + } + } + + for (int j = 0; j < nodes.size(); j++) { + int node_id = nodes[j]; + + Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, node_id); + if (vsnode.is_null()) { + continue; + } + Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr()); + if (custom_node.is_null() || custom_node->get_script() != scr) { + continue; + } + need_rebuild = true; + + // Removes invalid connections. + { + int prev_input_port_count = custom_node->get_input_port_count(); + int prev_output_port_count = custom_node->get_output_port_count(); + + custom_node->update_ports(); + + int input_port_count = custom_node->get_input_port_count(); + int output_port_count = custom_node->get_output_port_count(); + + if (output_port_count != prev_output_port_count) { + for (const VisualShader::Connection &E : custom_node_output_connections) { + int from = E.from_node; + int from_idx = E.from_port; + int to = E.to_node; + int to_idx = E.to_port; + + if (from_idx >= output_port_count) { + visual_shader->disconnect_nodes(type, from, from_idx, to, to_idx); + graph_plugin->disconnect_nodes(type, from, from_idx, to, to_idx); + } + } + } + if (input_port_count != prev_input_port_count) { + for (const VisualShader::Connection &E : custom_node_input_connections) { + int from = E.from_node; + int from_idx = E.from_port; + int to = E.to_node; + int to_idx = E.to_port; + + if (to_idx >= input_port_count) { + visual_shader->disconnect_nodes(type, from, from_idx, to, to_idx); + graph_plugin->disconnect_nodes(type, from, from_idx, to, to_idx); + } + } + } + } + + graph_plugin->update_node(type, node_id); + } + } + break; + } + } + + if (!found_type) { + add_custom_type(dict["name"], dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]); + } + + // To prevent updating options multiple times when multiple scripts are saved. + if (!_block_update_options_menu) { + _block_update_options_menu = true; + + call_deferred(SNAME("_update_options_menu_deferred")); + } + + // To prevent rebuilding the shader multiple times when multiple scripts are saved. + if (need_rebuild && !_block_rebuild_shader) { + _block_rebuild_shader = true; + + call_deferred(SNAME("_rebuild_shader_deferred")); + } +} + +void VisualShaderEditor::_update_options_menu_deferred() { + _update_options_menu(); + + _block_update_options_menu = false; +} + +void VisualShaderEditor::_rebuild_shader_deferred() { + if (visual_shader.is_valid()) { + visual_shader->rebuild(); + } + + _block_rebuild_shader = false; +} + bool VisualShaderEditor::_is_available(int p_mode) { int current_mode = edit_type->get_selected(); @@ -1243,57 +1474,10 @@ void VisualShaderEditor::_update_nodes() { if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) { continue; } - - String name; - if (ref->has_method("_get_name")) { - name = (String)ref->call("_get_name"); - } else { - name = "Unnamed"; - } - - String description = ""; - if (ref->has_method("_get_description")) { - description = (String)ref->call("_get_description"); - } - - int return_icon_type = -1; - if (ref->has_method("_get_return_icon_type")) { - return_icon_type = (int)ref->call("_get_return_icon_type"); - } - - String category = ""; - if (ref->has_method("_get_category")) { - category = (String)ref->call("_get_category"); - } - - String subcategory = ""; - if (ref->has_method("_get_subcategory")) { - subcategory = (String)ref->call("_get_subcategory"); - } - - bool highend = false; - if (ref->has_method("_is_highend")) { - highend = (bool)ref->call("_is_highend"); - } - - Dictionary dict; - dict["name"] = name; - dict["script"] = scr; - dict["description"] = description; - dict["return_icon_type"] = return_icon_type; - - category = category.rstrip("/"); - category = category.lstrip("/"); - category = "Addons/" + category; - if (!subcategory.is_empty()) { - category += "/" + subcategory; - } - - dict["category"] = category; - dict["highend"] = highend; + Dictionary dict = get_custom_node_data(ref); String key; - key = category + "/" + name; + key = String(dict["category"]) + "/" + String(dict["name"]); added[key] = dict; } @@ -4694,6 +4878,8 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_constant", &VisualShaderEditor::_update_constant); ClassDB::bind_method("_update_parameter", &VisualShaderEditor::_update_parameter); ClassDB::bind_method("_expand_output_port", &VisualShaderEditor::_expand_output_port); + ClassDB::bind_method("_update_options_menu_deferred", &VisualShaderEditor::_update_options_menu_deferred); + ClassDB::bind_method("_rebuild_shader_deferred", &VisualShaderEditor::_rebuild_shader_deferred); ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); @@ -4704,6 +4890,7 @@ void VisualShaderEditor::_bind_methods() { VisualShaderEditor::VisualShaderEditor() { ShaderLanguage::get_keyword_list(&keyword_list); + EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::update_custom_type)); graph = memnew(GraphEdit); graph->get_zoom_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 8afad9f668..d559237569 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -133,6 +133,7 @@ public: void update_curve_xyz(int p_node_id); void set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression); int get_constant_index(float p_constant) const; + Ref<Script> get_node_script(int p_node_id) const; void update_node_size(int p_node_id); void update_theme(); VisualShader::Type get_shader_type() const; @@ -190,6 +191,9 @@ class VisualShaderEditor : public VBoxContainer { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; + bool _block_update_options_menu = false; + bool _block_rebuild_shader = false; + Point2 saved_node_pos; bool saved_node_pos_dirty = false; @@ -497,6 +501,9 @@ class VisualShaderEditor : public VBoxContainer { void _update_parameter_refs(HashSet<String> &p_names); void _update_varyings(); + void _update_options_menu_deferred(); + void _rebuild_shader_deferred(); + void _visibility_changed(); protected: @@ -504,7 +511,6 @@ protected: static void _bind_methods(); public: - void update_nodes(); void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); @@ -513,6 +519,9 @@ public: void clear_custom_types(); void add_custom_type(const String &p_name, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend); + Dictionary get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node); + void update_custom_type(const Ref<Resource> &p_resource); + virtual Size2 get_minimum_size() const override; void edit(VisualShader *p_visual_shader); VisualShaderEditor(); diff --git a/main/main.cpp b/main/main.cpp index dd33dec543..09cec68ef9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -3363,6 +3363,7 @@ void Main::cleanup(bool p_force) { finalize_theme_db(); // Before deinitializing server extensions, finalize servers which may be loaded as extensions. + finalize_navigation_server(); finalize_physics(); NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SERVERS); @@ -3386,7 +3387,6 @@ void Main::cleanup(bool p_force) { OS::get_singleton()->finalize(); - finalize_navigation_server(); finalize_display(); if (input) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 1456612915..693863ab38 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1612,7 +1612,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } } - if (!found) { + if (!found && base.value.get_type() != Variant::NIL) { found = _guess_method_return_type_from_base(c, base, call->function_name, r_type); } } diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index aa4ae01fd9..9c332123e3 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -71,12 +71,19 @@ Rect2 BackBufferCopy::get_rect() const { void BackBufferCopy::set_copy_mode(CopyMode p_mode) { copy_mode = p_mode; _update_copy_mode(); + notify_property_list_changed(); } BackBufferCopy::CopyMode BackBufferCopy::get_copy_mode() const { return copy_mode; } +void BackBufferCopy::_validate_property(PropertyInfo &p_property) const { + if (copy_mode != COPY_MODE_RECT && p_property.name == "rect") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + void BackBufferCopy::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rect", "rect"), &BackBufferCopy::set_rect); ClassDB::bind_method(D_METHOD("get_rect"), &BackBufferCopy::get_rect); diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index 1f2d5810b0..caacbc83c6 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -51,6 +51,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; public: #ifdef TOOLS_ENABLED diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 4d4ea2b26d..ced77b26ee 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -995,9 +995,11 @@ void TileMap::_recompute_rect_cache() { } } + bool changed = rect_cache != r_total; + rect_cache = r_total; - item_rect_changed(); + item_rect_changed(changed); rect_cache_dirty = false; #endif diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index e99821e9b9..11b4718802 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -100,7 +100,7 @@ void TouchScreenButton::_notification(int p_what) { if (!is_inside_tree()) { return; } - if (!Engine::get_singleton()->is_editor_hint() && !!DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) { + if (!Engine::get_singleton()->is_editor_hint() && !DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) { return; } @@ -137,7 +137,7 @@ void TouchScreenButton::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { - if (!Engine::get_singleton()->is_editor_hint() && !!DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) { + if (!Engine::get_singleton()->is_editor_hint() && !DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) { return; } queue_redraw(); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index f4b7f3d0b2..5316b524ba 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -450,7 +450,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { callable = callable.bindp(argptrs, binds.size()); } - cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags); + cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED)); } //Node *s = ret_nodes[0]; diff --git a/thirdparty/README.md b/thirdparty/README.md index 937d149747..ed38fe0ec8 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -20,7 +20,7 @@ Files extracted from upstream source: ## basis_universal - Upstream: https://github.com/BinomialLLC/basis_universal -- Version: git (1531cfaf9ed5232248a0a45736686a849ca3befc, 2022) +- Version: git (a91e94c8495d7f470d3df326a364d49324cfd4a3, 2022) - License: Apache 2.0 Files extracted from upstream source: diff --git a/thirdparty/basis_universal/encoder/basisu_comp.cpp b/thirdparty/basis_universal/encoder/basisu_comp.cpp index 166a1c4fe0..41eae2b78a 100644 --- a/thirdparty/basis_universal/encoder/basisu_comp.cpp +++ b/thirdparty/basis_universal/encoder/basisu_comp.cpp @@ -1501,7 +1501,8 @@ namespace basisu if (m_params.m_compute_stats) { - printf("Slice: %u\n", slice_index); + if (m_params.m_print_stats) + printf("Slice: %u\n", slice_index); image_stats& s = m_stats[slice_index]; @@ -1511,81 +1512,100 @@ namespace basisu // ---- .basis stats em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 3); - em.print(".basis RGB Avg: "); + if (m_params.m_print_stats) + em.print(".basis RGB Avg: "); s.m_basis_rgb_avg_psnr = em.m_psnr; em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 4); - em.print(".basis RGBA Avg: "); + if (m_params.m_print_stats) + em.print(".basis RGBA Avg: "); s.m_basis_rgba_avg_psnr = em.m_psnr; em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 1); - em.print(".basis R Avg: "); + if (m_params.m_print_stats) + em.print(".basis R Avg: "); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 1, 1); - em.print(".basis G Avg: "); + if (m_params.m_print_stats) + em.print(".basis G Avg: "); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 2, 1); - em.print(".basis B Avg: "); + if (m_params.m_print_stats) + em.print(".basis B Avg: "); if (m_params.m_uastc) { em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 3, 1); - em.print(".basis A Avg: "); + if (m_params.m_print_stats) + em.print(".basis A Avg: "); s.m_basis_a_avg_psnr = em.m_psnr; } em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0); - em.print(".basis 709 Luma: "); + if (m_params.m_print_stats) + em.print(".basis 709 Luma: "); s.m_basis_luma_709_psnr = static_cast<float>(em.m_psnr); s.m_basis_luma_709_ssim = static_cast<float>(em.m_ssim); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0, true, true); - em.print(".basis 601 Luma: "); + if (m_params.m_print_stats) + em.print(".basis 601 Luma: "); s.m_basis_luma_601_psnr = static_cast<float>(em.m_psnr); if (m_slice_descs.size() == 1) { const uint32_t output_size = comp_size ? (uint32_t)comp_size : (uint32_t)comp_data.size(); - debug_printf(".basis RGB PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_rgb_avg_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height))); - debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_luma_709_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height))); + if (m_params.m_print_stats) + { + debug_printf(".basis RGB PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_rgb_avg_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height))); + debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_luma_709_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height))); + } } if (m_decoded_output_textures_unpacked_bc7[slice_index].get_width()) { // ---- BC7 stats em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 3); - em.print("BC7 RGB Avg: "); + if (m_params.m_print_stats) + em.print("BC7 RGB Avg: "); s.m_bc7_rgb_avg_psnr = em.m_psnr; em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 4); - em.print("BC7 RGBA Avg: "); + if (m_params.m_print_stats) + em.print("BC7 RGBA Avg: "); s.m_bc7_rgba_avg_psnr = em.m_psnr; em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 1); - em.print("BC7 R Avg: "); + if (m_params.m_print_stats) + em.print("BC7 R Avg: "); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 1, 1); - em.print("BC7 G Avg: "); + if (m_params.m_print_stats) + em.print("BC7 G Avg: "); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 2, 1); - em.print("BC7 B Avg: "); + if (m_params.m_print_stats) + em.print("BC7 B Avg: "); if (m_params.m_uastc) { em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 3, 1); - em.print("BC7 A Avg: "); + if (m_params.m_print_stats) + em.print("BC7 A Avg: "); s.m_bc7_a_avg_psnr = em.m_psnr; } em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0); - em.print("BC7 709 Luma: "); + if (m_params.m_print_stats) + em.print("BC7 709 Luma: "); s.m_bc7_luma_709_psnr = static_cast<float>(em.m_psnr); s.m_bc7_luma_709_ssim = static_cast<float>(em.m_ssim); em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0, true, true); - em.print("BC7 601 Luma: "); + if (m_params.m_print_stats) + em.print("BC7 601 Luma: "); s.m_bc7_luma_601_psnr = static_cast<float>(em.m_psnr); } @@ -1593,16 +1613,19 @@ namespace basisu { // ---- Nearly best possible ETC1S stats em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 3); - em.print("Unquantized ETC1S RGB Avg: "); + if (m_params.m_print_stats) + em.print("Unquantized ETC1S RGB Avg: "); s.m_best_etc1s_rgb_avg_psnr = static_cast<float>(em.m_psnr); em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0); - em.print("Unquantized ETC1S 709 Luma: "); + if (m_params.m_print_stats) + em.print("Unquantized ETC1S 709 Luma: "); s.m_best_etc1s_luma_709_psnr = static_cast<float>(em.m_psnr); s.m_best_etc1s_luma_709_ssim = static_cast<float>(em.m_ssim); em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0, true, true); - em.print("Unquantized ETC1S 601 Luma: "); + if (m_params.m_print_stats) + em.print("Unquantized ETC1S 601 Luma: "); s.m_best_etc1s_luma_601_psnr = static_cast<float>(em.m_psnr); } } @@ -2311,6 +2334,8 @@ namespace basisu } comp_params.m_compute_stats = (pStats != nullptr); + comp_params.m_print_stats = (flags_and_quality & cFlagPrintStats) != 0; + comp_params.m_status_output = (flags_and_quality & cFlagPrintStatus) != 0; // Create the compressor, initialize it, and process the input basis_compressor comp; @@ -2328,6 +2353,11 @@ namespace basisu return nullptr; } + if ((pStats) && (comp.get_opencl_failed())) + { + pStats->m_opencl_failed = true; + } + // Get the output file data and return it to the caller void* pFile_data = nullptr; const uint8_vec* pFile_data_vec = comp_params.m_create_ktx2_file ? &comp.get_output_ktx2_file() : &comp.get_output_basis_file(); @@ -2388,4 +2418,108 @@ namespace basisu free(p); } + bool basis_benchmark_etc1s_opencl(bool* pOpenCL_failed) + { + if (pOpenCL_failed) + *pOpenCL_failed = false; + + if (!opencl_is_available()) + { + error_printf("basis_benchmark_etc1s_opencl: OpenCL support must be enabled first!\n"); + return false; + } + + const uint32_t W = 1024, H = 1024; + basisu::vector<image> images; + image& img = images.enlarge(1)->resize(W, H); + + const uint32_t NUM_RAND_LETTERS = 6000;// 40000; + + rand r; + r.seed(200); + + for (uint32_t i = 0; i < NUM_RAND_LETTERS; i++) + { + uint32_t x = r.irand(0, W - 1), y = r.irand(0, H - 1); + uint32_t sx = r.irand(1, 4), sy = r.irand(1, 4); + color_rgba c(r.byte(), r.byte(), r.byte(), 255); + + img.debug_text(x, y, sx, sy, c, nullptr, false, "%c", static_cast<char>(r.irand(32, 127))); + } + + //save_png("test.png", img); + + image_stats stats; + + uint32_t flags_and_quality = cFlagSRGB | cFlagThreaded | 255; + size_t comp_size = 0; + + double best_cpu_time = 1e+9f, best_gpu_time = 1e+9f; + + const uint32_t TIMES_TO_ENCODE = 2; + interval_timer tm; + + for (uint32_t i = 0; i < TIMES_TO_ENCODE; i++) + { + tm.start(); + void* pComp_data = basis_compress( + images, + flags_and_quality, 1.0f, + &comp_size, + &stats); + double cpu_time = tm.get_elapsed_secs(); + if (!pComp_data) + { + error_printf("basis_benchmark_etc1s_opencl: basis_compress() failed (CPU)!\n"); + return false; + } + + best_cpu_time = minimum(best_cpu_time, cpu_time); + + basis_free_data(pComp_data); + } + + printf("Best CPU time: %3.3f\n", best_cpu_time); + + for (uint32_t i = 0; i < TIMES_TO_ENCODE; i++) + { + tm.start(); + void* pComp_data = basis_compress( + images, + flags_and_quality | cFlagUseOpenCL, 1.0f, + &comp_size, + &stats); + + if (stats.m_opencl_failed) + { + error_printf("basis_benchmark_etc1s_opencl: OpenCL failed!\n"); + + basis_free_data(pComp_data); + + if (pOpenCL_failed) + *pOpenCL_failed = true; + + return false; + } + + double gpu_time = tm.get_elapsed_secs(); + if (!pComp_data) + { + error_printf("basis_benchmark_etc1s_opencl: basis_compress() failed (GPU)!\n"); + return false; + } + + best_gpu_time = minimum(best_gpu_time, gpu_time); + + basis_free_data(pComp_data); + } + + printf("Best GPU time: %3.3f\n", best_gpu_time); + + return best_gpu_time < best_cpu_time; + } + } // namespace basisu + + + diff --git a/thirdparty/basis_universal/encoder/basisu_comp.h b/thirdparty/basis_universal/encoder/basisu_comp.h index aa5ea6fec3..b6c9fef9e2 100644 --- a/thirdparty/basis_universal/encoder/basisu_comp.h +++ b/thirdparty/basis_universal/encoder/basisu_comp.h @@ -92,6 +92,8 @@ namespace basisu m_best_etc1s_luma_709_psnr = 0.0f; m_best_etc1s_luma_601_psnr = 0.0f; m_best_etc1s_luma_709_ssim = 0.0f; + + m_opencl_failed = false; } std::string m_filename; @@ -119,6 +121,8 @@ namespace basisu float m_best_etc1s_luma_709_psnr; float m_best_etc1s_luma_601_psnr; float m_best_etc1s_luma_709_ssim; + + bool m_opencl_failed; }; template<bool def> @@ -255,6 +259,7 @@ namespace basisu m_write_output_basis_files.clear(); m_compression_level.clear(); m_compute_stats.clear(); + m_print_stats.clear(); m_check_for_alpha.clear(); m_force_alpha.clear(); m_multithreading.clear(); @@ -373,6 +378,9 @@ namespace basisu // Compute and display image metrics bool_param<false> m_compute_stats; + + // Print stats to stdout, if m_compute_stats is true. + bool_param<true> m_print_stats; // Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels bool_param<true> m_check_for_alpha; @@ -583,11 +591,16 @@ namespace basisu cFlagYFlip = 1 << 16, // flip source image on Y axis before compression cFlagUASTC = 1 << 17, // use UASTC compression vs. ETC1S - cFlagUASTCRDO = 1 << 18 // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar) + cFlagUASTCRDO = 1 << 18, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar) + + cFlagPrintStats = 1 << 19, // print image stats to stdout + cFlagPrintStatus = 1 << 20 // print status to stdout }; // This function accepts an array of source images. // If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled. + // Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data. The returned block must be freed using basis_free_data(). + // basisu_encoder_init() MUST be called first! void* basis_compress( const basisu::vector<image> &source_images, uint32_t flags_and_quality, float uastc_rdo_quality, @@ -604,6 +617,12 @@ namespace basisu // Frees the dynamically allocated file data returned by basis_compress(). void basis_free_data(void* p); + // Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled. + // Returns true if opencl is worth using on this system, otherwise false. + // If pOpenCL_failed is not null, it will be set to true if OpenCL encoding failed *on this particular machine/driver/BasisU version* and the encoder falled back to CPU encoding. + // basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false. + bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr); + // Parallel compression API struct parallel_results { diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp index b427215ee3..c431ceaf12 100644 --- a/thirdparty/basis_universal/encoder/basisu_enc.cpp +++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp @@ -187,6 +187,8 @@ namespace basisu opencl_init(opencl_force_serialization); } + interval_timer::init(); // make sure interval_timer globals are initialized from main thread to avoid TSAN reports + g_library_initialized = true; } @@ -227,7 +229,7 @@ namespace basisu { QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(pTicks)); } -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) #include <sys/time.h> inline void query_counter(timer_ticks* pTicks) { diff --git a/thirdparty/basis_universal/encoder/basisu_frontend.cpp b/thirdparty/basis_universal/encoder/basisu_frontend.cpp index 00210e6679..1f30a33c70 100644 --- a/thirdparty/basis_universal/encoder/basisu_frontend.cpp +++ b/thirdparty/basis_universal/encoder/basisu_frontend.cpp @@ -2328,8 +2328,6 @@ namespace basisu m_optimized_cluster_selectors.resize(total_selector_clusters); - uint32_t total_clusters_processed = 0; - // For each selector codebook entry, and for each of the 4x4 selectors, determine which selector minimizes the error across all the blocks that use that quantized selector. const uint32_t N = 256; for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N) @@ -2338,7 +2336,7 @@ namespace basisu const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N); #ifndef __EMSCRIPTEN__ - m_params.m_pJob_pool->add_job([this, first_index, last_index, &total_clusters_processed, &total_selector_clusters] { + m_params.m_pJob_pool->add_job([this, first_index, last_index] { #endif for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++) diff --git a/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp b/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp index 630731900f..3aeba0ee7a 100644 --- a/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp +++ b/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp @@ -16867,7 +16867,7 @@ namespace basist { m_format = basist::basis_tex_format::cETC1S; - // 3.10.2: "Whether the image has 1 or 2 slices can be determined from the DFD’s sample count." + // 3.10.2: "Whether the image has 1 or 2 slices can be determined from the DFD's sample count." // If m_has_alpha is true it may be 2-channel RRRG or 4-channel RGBA, but we let the caller deal with that. m_has_alpha = (m_header.m_dfd_byte_length == 60); diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp index c98f61fa53..7f7a009a1e 100644 --- a/thirdparty/embree/common/sys/sysinfo.cpp +++ b/thirdparty/embree/common/sys/sysinfo.cpp @@ -640,6 +640,12 @@ namespace embree #if defined(__EMSCRIPTEN__) #include <emscripten.h> + +// -- GODOT start -- +extern "C" { +extern int godot_js_os_hw_concurrency_get(); +} +// -- GODOT end -- #endif namespace embree @@ -653,21 +659,9 @@ namespace embree nThreads = sysconf(_SC_NPROCESSORS_ONLN); // does not work in Linux LXC container assert(nThreads); #elif defined(__EMSCRIPTEN__) - // WebAssembly supports pthreads, but not pthread_getaffinity_np. Get the number of logical - // threads from the browser or Node.js using JavaScript. - nThreads = MAIN_THREAD_EM_ASM_INT({ - const isBrowser = typeof window !== 'undefined'; - const isNode = typeof process !== 'undefined' && process.versions != null && - process.versions.node != null; - if (isBrowser) { - // Return 1 if the browser does not expose hardwareConcurrency. - return window.navigator.hardwareConcurrency || 1; - } else if (isNode) { - return require('os').cpus().length; - } else { - return 1; - } - }); + // -- GODOT start -- + nThreads = godot_js_os_hw_concurrency_get(); + // -- GODOT end -- #else cpu_set_t set; if (pthread_getaffinity_np(pthread_self(), sizeof(set), &set) == 0) diff --git a/thirdparty/embree/patches/emscripten-nthreads.patch b/thirdparty/embree/patches/emscripten-nthreads.patch new file mode 100644 index 0000000000..e42f203475 --- /dev/null +++ b/thirdparty/embree/patches/emscripten-nthreads.patch @@ -0,0 +1,42 @@ +diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp +index c98f61fa53..7f7a009a1e 100644 +--- a/thirdparty/embree/common/sys/sysinfo.cpp ++++ b/thirdparty/embree/common/sys/sysinfo.cpp +@@ -640,6 +640,12 @@ namespace embree + + #if defined(__EMSCRIPTEN__) + #include <emscripten.h> ++ ++// -- GODOT start -- ++extern "C" { ++extern int godot_js_os_hw_concurrency_get(); ++} ++// -- GODOT end -- + #endif + + namespace embree +@@ -653,21 +659,9 @@ namespace embree + nThreads = sysconf(_SC_NPROCESSORS_ONLN); // does not work in Linux LXC container + assert(nThreads); + #elif defined(__EMSCRIPTEN__) +- // WebAssembly supports pthreads, but not pthread_getaffinity_np. Get the number of logical +- // threads from the browser or Node.js using JavaScript. +- nThreads = MAIN_THREAD_EM_ASM_INT({ +- const isBrowser = typeof window !== 'undefined'; +- const isNode = typeof process !== 'undefined' && process.versions != null && +- process.versions.node != null; +- if (isBrowser) { +- // Return 1 if the browser does not expose hardwareConcurrency. +- return window.navigator.hardwareConcurrency || 1; +- } else if (isNode) { +- return require('os').cpus().length; +- } else { +- return 1; +- } +- }); ++ // -- GODOT start -- ++ nThreads = godot_js_os_hw_concurrency_get(); ++ // -- GODOT end -- + #else + cpu_set_t set; + if (pthread_getaffinity_np(pthread_self(), sizeof(set), &set) == 0) |