diff options
35 files changed, 345 insertions, 217 deletions
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 062c3fc702..762bab75e7 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -223,64 +223,7 @@ void CallQueue::_call_function(const Callable &p_callable, const Variant *p_args } } -Error CallQueue::_transfer_messages_to_main_queue() { - if (pages.size() == 0) { - return OK; - } - - CallQueue *mq = MessageQueue::main_singleton; - DEV_ASSERT(!mq->allocator_is_custom && !allocator_is_custom); // Transferring pages is only safe if using the same alloator parameters. - - mq->mutex.lock(); - - // Here we're transferring the data from this queue to the main one. - // However, it's very unlikely big amounts of messages will be queued here, - // so PagedArray/Pool would be overkill. Also, in most cases the data will fit - // an already existing page of the main queue. - - // Let's see if our first (likely only) page fits the current target queue page. - uint32_t src_page = 0; - { - if (mq->pages_used) { - uint32_t dst_page = mq->pages_used - 1; - uint32_t dst_offset = mq->page_bytes[dst_page]; - if (dst_offset + page_bytes[0] < uint32_t(PAGE_SIZE_BYTES)) { - memcpy(mq->pages[dst_page]->data + dst_offset, pages[0]->data, page_bytes[0]); - mq->page_bytes[dst_page] += page_bytes[0]; - src_page++; - } - } - } - - // Any other possibly existing source page needs to be added. - - if (mq->pages_used + (pages_used - src_page) > mq->max_pages) { - fprintf(stderr, "Failed appending thread queue. Message queue out of memory. %s\n", mq->error_text.utf8().get_data()); - mq->statistics(); - mq->mutex.unlock(); - return ERR_OUT_OF_MEMORY; - } - - for (; src_page < pages_used; src_page++) { - mq->_add_page(); - memcpy(mq->pages[mq->pages_used - 1]->data, pages[src_page]->data, page_bytes[src_page]); - mq->page_bytes[mq->pages_used - 1] = page_bytes[src_page]; - } - - mq->mutex.unlock(); - - page_bytes[0] = 0; - pages_used = 1; - - return OK; -} - Error CallQueue::flush() { - // Thread overrides are not meant to be flushed, but appended to the main one. - if (unlikely(this == MessageQueue::thread_singleton)) { - return _transfer_messages_to_main_queue(); - } - LOCK_MUTEX; if (pages.size() == 0) { diff --git a/core/object/message_queue.h b/core/object/message_queue.h index c2f4ad1643..9f567e4dd0 100644 --- a/core/object/message_queue.h +++ b/core/object/message_queue.h @@ -98,8 +98,6 @@ private: } } - Error _transfer_messages_to_main_queue(); - void _add_page(); void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error); diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp index 726be064bd..128b8bcd03 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -173,6 +173,7 @@ Error RenderingContextDriverD3D12::_initialize_devices() { Device &device = driver_devices[i]; device.name = desc.Description; device.vendor = Vendor(desc.VendorId); + device.workarounds = Workarounds(); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { device.type = DEVICE_TYPE_CPU; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 7048301e57..03f947cd05 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2394,6 +2394,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment); float bg_energy_multiplier = environment_get_bg_energy_multiplier(render_data.environment); bg_energy_multiplier *= environment_get_bg_intensity(render_data.environment); + RS::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(render_data.environment); if (render_data.camera_attributes.is_valid()) { bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(render_data.camera_attributes); @@ -2404,7 +2405,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (environment_get_fog_enabled(render_data.environment)) { + if (!render_data.transparent_bg && environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); } @@ -2414,13 +2415,13 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (environment_get_fog_enabled(render_data.environment)) { + if (!render_data.transparent_bg && environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); } } break; case RS::ENV_BG_SKY: { - draw_sky = true; + draw_sky = !render_data.transparent_bg; } break; case RS::ENV_BG_CANVAS: { keep_color = true; @@ -2433,8 +2434,9 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ default: { } } + // setup sky if used for ambient, reflections, or background - if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) { + if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) { RENDER_TIMESTAMP("Setup Sky"); Projection projection = render_data.cam_projection; if (is_reflection_probe) { diff --git a/drivers/vulkan/rendering_context_driver_vulkan.cpp b/drivers/vulkan/rendering_context_driver_vulkan.cpp index 6eb25743f9..7cba820978 100644 --- a/drivers/vulkan/rendering_context_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp @@ -502,6 +502,9 @@ Error RenderingContextDriverVulkan::_initialize_devices() { driver_device.name = String::utf8(props.deviceName); driver_device.vendor = Vendor(props.vendorID); driver_device.type = DeviceType(props.deviceType); + driver_device.workarounds = Workarounds(); + + _check_driver_workarounds(props, driver_device); uint32_t queue_family_properties_count = 0; vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &queue_family_properties_count, nullptr); @@ -515,6 +518,31 @@ Error RenderingContextDriverVulkan::_initialize_devices() { return OK; } +void RenderingContextDriverVulkan::_check_driver_workarounds(const VkPhysicalDeviceProperties &p_device_properties, Device &r_device) { + // Workaround for the Adreno 6XX family of devices. + // + // There's a known issue with the Vulkan driver in this family of devices where it'll crash if a dynamic state for drawing is + // used in a command buffer before a dispatch call is issued. As both dynamic scissor and viewport are basic requirements for + // the engine to not bake this state into the PSO, the only known way to fix this issue is to reset the command buffer entirely. + // + // As the render graph has no built in limitations of whether it'll issue compute work before anything needs to draw on the + // frame, and there's no guarantee that compute work will never be dependent on rasterization in the future, this workaround + // will end recording on the current command buffer any time a compute list is encountered after a draw list was executed. + // A new command buffer will be created afterwards and the appropriate synchronization primitives will be inserted. + // + // Executing this workaround has the added cost of synchronization between all the command buffers that are created as well as + // all the individual submissions. This performance hit is accepted for the sake of being able to support these devices without + // limiting the design of the renderer. + // + // This bug was fixed in driver version 512.503.0, so we only enabled it on devices older than this. + // + r_device.workarounds.avoid_compute_after_draw = + r_device.vendor == VENDOR_QUALCOMM && + p_device_properties.deviceID >= 0x6000000 && // Adreno 6xx + p_device_properties.driverVersion < VK_MAKE_VERSION(512, 503, 0) && + r_device.name.find("Turnip") < 0; +} + bool RenderingContextDriverVulkan::_use_validation_layers() const { return Engine::get_singleton()->is_validation_layers_enabled(); } diff --git a/drivers/vulkan/rendering_context_driver_vulkan.h b/drivers/vulkan/rendering_context_driver_vulkan.h index 6348f90d55..f1d4021e32 100644 --- a/drivers/vulkan/rendering_context_driver_vulkan.h +++ b/drivers/vulkan/rendering_context_driver_vulkan.h @@ -105,6 +105,7 @@ private: Error _initialize_instance_extensions(); Error _initialize_instance(); Error _initialize_devices(); + void _check_driver_workarounds(const VkPhysicalDeviceProperties &p_device_properties, Device &r_device); // Static callbacks. static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, VkDebugUtilsMessageTypeFlagsEXT p_message_type, const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index edd296adef..46a478b0c3 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1733,7 +1733,7 @@ void SpriteFramesEditor::_autoplay_pressed() { if (animated_sprite) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Toggle Autoplay"), UndoRedo::MERGE_DISABLE, frames.ptr()); + undo_redo->create_action(TTR("Toggle Autoplay"), UndoRedo::MERGE_DISABLE, animated_sprite); String current = animated_sprite->call("get_animation"); String current_auto = animated_sprite->call("get_autoplay"); if (current == current_auto) { diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index c0d768597d..637547062c 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -49,6 +49,11 @@ Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l for (const Point2i ®ion : disabled_branch_regions) { if (p_line >= region.x && p_line <= region.y) { + // When "color_regions[0].p_start_key.length() > 2", + // disabled_branch_region causes color_region to break. + // This should be seen as a temporary solution. + CodeHighlighter::_get_line_syntax_highlighting_impl(p_line); + Dictionary highlighter_info; highlighter_info["color"] = disabled_branch_color; diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index 708a11b05d..285302ca56 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -1628,7 +1628,7 @@ void TileMapLayerEditorTilesPlugin::_update_fix_selected_and_hovered() { hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; } - // Selection if needed. + // Cleanup tile set selection. for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E;) { RBSet<TileMapCell>::Element *N = E->next(); const TileMapCell *selected = &(E->get()); @@ -1640,12 +1640,15 @@ void TileMapLayerEditorTilesPlugin::_update_fix_selected_and_hovered() { E = N; } - if (!tile_map_selection.is_empty()) { - _update_selection_pattern_from_tilemap_selection(); - } else if (tiles_bottom_panel->is_visible_in_tree()) { - _update_selection_pattern_from_tileset_tiles_selection(); - } else { - _update_selection_pattern_from_tileset_pattern_selection(); + // Cleanup selection. + for (const KeyValue<Vector2i, TileMapCell> &E : selection_pattern->get_pattern()) { + const Vector2i key = E.key; + const TileMapCell &selected = E.value; + if (!tile_set->has_source(selected.source_id) || + !tile_set->get_source(selected.source_id)->has_tile(selected.get_atlas_coords()) || + !tile_set->get_source(selected.source_id)->has_alternative_tile(selected.get_atlas_coords(), selected.alternative_tile)) { + selection_pattern->remove_cell(key); + } } } @@ -2409,7 +2412,6 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { sources_list->set_stretch_ratio(0.25); sources_list->set_custom_minimum_size(Size2(70, 0) * EDSCALE); sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); - sources_list->connect("item_selected", callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1)); sources_list->connect("item_selected", callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_source_display).unbind(1)); sources_list->connect("item_selected", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current)); sources_list->connect("item_activated", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::display_tile_set_editor_panel).unbind(1)); @@ -3980,10 +3982,14 @@ void TileMapLayerEditor::_update_bottom_panel() { } // Update tabs visibility. - for (TileMapLayerSubEditorPlugin::TabData &tab_data : tabs_data) { - tab_data.panel->hide(); + for (int i = 0; i < int(tabs_data.size()); i++) { + TileMapLayerSubEditorPlugin::TabData &tab_data = tabs_data[i]; + if (i == tabs_bar->get_current_tab()) { + tab_data.panel->set_visible(!cant_edit_label->is_visible()); + } else { + tab_data.panel->hide(); + } } - tabs_data[tabs_bar->get_current_tab()].panel->set_visible(!cant_edit_label->is_visible()); } Vector<Vector2i> TileMapLayerEditor::get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell) { diff --git a/misc/dist/html/full-size.html b/misc/dist/html/full-size.html index 874fe2695e..fc34164368 100644 --- a/misc/dist/html/full-size.html +++ b/misc/dist/html/full-size.html @@ -38,7 +38,7 @@ body { } #status { - background-color: #38363A; + background-color: #242424; display: flex; flex-direction: column; justify-content: center; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 07abf672a0..de56dd5ece 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2920,7 +2920,7 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con return ""; } -void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types) { +void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'."); @@ -2934,13 +2934,8 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S return; } - GDScriptAnalyzer analyzer(&parser); - if (OK != analyzer.analyze()) { - return; - } - for (const String &E : parser.get_dependencies()) { - r_dependencies->push_back(E); + p_dependencies->push_back(E); } } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index f63b0da745..728459de44 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -638,7 +638,7 @@ public: virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; - virtual void get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types = false) override; + virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; }; class ResourceFormatSaverGDScript : public ResourceFormatSaver { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 39c678aabc..38d5c59e0e 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -561,11 +561,6 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c class_type.native_type = result.native_type; p_class->set_datatype(class_type); - // Add base class to the list of dependencies. - if (result.kind == GDScriptParser::DataType::CLASS) { - parser->add_dependency(result.script_path); - } - // Apply annotations. for (GDScriptParser::AnnotationNode *&E : p_class->annotations) { resolve_annotation(E); @@ -872,11 +867,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } p_type->set_datatype(result); - - if (result.kind == GDScriptParser::DataType::CLASS || result.kind == GDScriptParser::DataType::SCRIPT) { - parser->add_dependency(result.script_path); - } - return result; } @@ -4111,7 +4101,6 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (ScriptServer::is_global_class(name)) { p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier)); - parser->add_dependency(p_identifier->get_datatype().script_path); return; } @@ -4154,7 +4143,6 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } result.is_constant = true; p_identifier->set_datatype(result); - parser->add_dependency(autoload.path); return; } } @@ -4274,6 +4262,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { push_error("Preloaded path must be a constant string.", p_preload->path); } else { p_preload->resolved_path = p_preload->path->reduced_value; + // TODO: Save this as script dependency. if (p_preload->resolved_path.is_relative_path()) { p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path); } @@ -4304,8 +4293,6 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path); } } - - parser->add_dependency(p_preload->resolved_path); } } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 1e67e2d496..4c11fa7f8b 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1432,8 +1432,6 @@ private: void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token); void reset_extents(Node *p_node, Node *p_from); - HashSet<String> dependencies; - template <typename T> T *alloc_node() { T *node = memnew(T); @@ -1577,11 +1575,9 @@ public: bool annotation_exists(const String &p_annotation_name) const; const List<ParserError> &get_errors() const { return errors; } - const HashSet<String> &get_dependencies() const { - return dependencies; - } - void add_dependency(const String &p_dependency) { - dependencies.insert(p_dependency); + const List<String> get_dependencies() const { + // TODO: Keep track of deps. + return List<String>(); } #ifdef DEBUG_ENABLED const List<GDScriptWarning> &get_warnings() const { return warnings; } diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index d42303ad25..0bf3927e14 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -154,11 +154,11 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito String head_include; if (p_preset->get("html/export_icon")) { - head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n"; - head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n"; + head_include += "<link id=\"-gd-engine-icon\" rel=\"icon\" type=\"image/png\" href=\"" + p_name + ".icon.png\" />\n"; + head_include += "<link rel=\"apple-touch-icon\" href=\"" + p_name + ".apple-touch-icon.png\"/>\n"; } if (p_preset->get("progressive_web_app/enabled")) { - head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n"; + head_include += "<link rel=\"manifest\" href=\"" + p_name + ".manifest.json\">\n"; config["serviceWorker"] = p_name + ".service.worker.js"; } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index ad3f607661..15b7f62036 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -298,6 +298,7 @@ void Button::_notification(int p_what) { icon_size = Size2(icon_width, icon_height); } icon_size = _fit_icon_size(icon_size); + icon_size = icon_size.round(); } if (icon_size.width > 0.0f) { @@ -336,6 +337,7 @@ void Button::_notification(int p_what) { icon_ofs.y = size.y - style_margin_bottom - icon_size.height; } break; } + icon_ofs = icon_ofs.floor(); Rect2 icon_region = Rect2(icon_ofs, icon_size); draw_texture_rect(_icon, icon_region, false, icon_modulate_color); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index 31c8e68ea5..76e99aca92 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -33,13 +33,11 @@ #include "core/version.h" Mutex CanvasItemMaterial::material_mutex; -SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr; +SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials; HashMap<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData, CanvasItemMaterial::MaterialKey> CanvasItemMaterial::shader_map; CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr; void CanvasItemMaterial::init_shaders() { - dirty_materials = memnew(SelfList<CanvasItemMaterial>::List); - shader_names = memnew(ShaderNames); shader_names->particles_anim_h_frames = "particles_anim_h_frames"; @@ -48,14 +46,13 @@ void CanvasItemMaterial::init_shaders() { } void CanvasItemMaterial::finish_shaders() { - memdelete(dirty_materials); + dirty_materials.clear(); + memdelete(shader_names); - dirty_materials = nullptr; + shader_names = nullptr; } void CanvasItemMaterial::_update_shader() { - dirty_materials->remove(&element); - MaterialKey mk = _compute_key(); if (mk.key == current_key.key) { return; //no update required in the end @@ -153,8 +150,9 @@ void CanvasItemMaterial::_update_shader() { void CanvasItemMaterial::flush_changes() { MutexLock lock(material_mutex); - while (dirty_materials->first()) { - dirty_materials->first()->self()->_update_shader(); + while (dirty_materials.first()) { + dirty_materials.first()->self()->_update_shader(); + dirty_materials.first()->remove_from_list(); } } @@ -162,16 +160,10 @@ void CanvasItemMaterial::_queue_shader_change() { MutexLock lock(material_mutex); if (_is_initialized() && !element.in_list()) { - dirty_materials->add(&element); + dirty_materials.add(&element); } } -bool CanvasItemMaterial::_is_shader_dirty() const { - MutexLock lock(material_mutex); - - return element.in_list(); -} - void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) { blend_mode = p_blend_mode; _queue_shader_change(); @@ -288,7 +280,7 @@ CanvasItemMaterial::CanvasItemMaterial() : current_key.invalid_key = 1; - _mark_initialized(callable_mp(this, &CanvasItemMaterial::_queue_shader_change)); + _mark_initialized(callable_mp(this, &CanvasItemMaterial::_queue_shader_change), callable_mp(this, &CanvasItemMaterial::_update_shader)); } CanvasItemMaterial::~CanvasItemMaterial() { diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index 7dddd74a31..ef498c2ff6 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -98,12 +98,11 @@ private: } static Mutex material_mutex; - static SelfList<CanvasItemMaterial>::List *dirty_materials; + static SelfList<CanvasItemMaterial>::List dirty_materials; SelfList<CanvasItemMaterial> element; void _update_shader(); _FORCE_INLINE_ void _queue_shader_change(); - _FORCE_INLINE_ bool _is_shader_dirty() const; BlendMode blend_mode = BLEND_MODE_MIX; LightMode light_mode = LIGHT_MODE_NORMAL; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 0a45e57706..27da825bfe 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -81,24 +81,25 @@ void Material::_validate_property(PropertyInfo &p_property) const { } } -void Material::_mark_initialized(const Callable &p_queue_shader_change_callable) { +void Material::_mark_ready() { + init_state = INIT_STATE_INITIALIZING; +} + +void Material::_mark_initialized(const Callable &p_add_to_dirty_list, const Callable &p_update_shader) { // If this is happening as part of resource loading, it is not safe to queue the update - // as an addition to the dirty list, unless the load is happening on the main thread. - if (ResourceLoader::is_within_load() && Thread::get_caller_id() != Thread::get_main_id()) { + // as an addition to the dirty list. It would be if the load is happening on the main thread, + // but even so we'd rather perform the update directly instead of using the dirty list. + if (ResourceLoader::is_within_load()) { DEV_ASSERT(init_state != INIT_STATE_READY); if (init_state == INIT_STATE_UNINITIALIZED) { // Prevent queueing twice. - // Let's mark this material as being initialized. init_state = INIT_STATE_INITIALIZING; - // Knowing that the ResourceLoader will eventually feed deferred calls into the main message queue, let's do these: - // 1. Queue setting the init state to INIT_STATE_READY finally. - callable_mp(this, &Material::_mark_initialized).bind(p_queue_shader_change_callable).call_deferred(); - // 2. Queue an individual update of this material. - p_queue_shader_change_callable.call_deferred(); + callable_mp(this, &Material::_mark_ready).call_deferred(); + p_update_shader.call_deferred(); } } else { // Straightforward conditions. init_state = INIT_STATE_READY; - p_queue_shader_change_callable.callv(Array()); + p_add_to_dirty_list.call(); } } @@ -602,8 +603,6 @@ void BaseMaterial3D::finish_shaders() { } void BaseMaterial3D::_update_shader() { - dirty_materials.remove(&element); - MaterialKey mk = _compute_key(); if (mk == current_key) { return; //no update required in the end @@ -1854,6 +1853,7 @@ void BaseMaterial3D::flush_changes() { while (dirty_materials.first()) { dirty_materials.first()->self()->_update_shader(); + dirty_materials.first()->remove_from_list(); } } @@ -1865,12 +1865,6 @@ void BaseMaterial3D::_queue_shader_change() { } } -bool BaseMaterial3D::_is_shader_dirty() const { - MutexLock lock(material_mutex); - - return element.in_list(); -} - void BaseMaterial3D::set_albedo(const Color &p_albedo) { albedo = p_albedo; @@ -2823,7 +2817,7 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const { RID BaseMaterial3D::get_shader_rid() const { MutexLock lock(material_mutex); - if (element.in_list()) { // _is_shader_dirty() would create anoder mutex lock + if (element.in_list()) { ((BaseMaterial3D *)this)->_update_shader(); } ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); @@ -3411,7 +3405,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : current_key.invalid_key = 1; - _mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change)); + _mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change), callable_mp(this, &BaseMaterial3D::_update_shader)); } BaseMaterial3D::~BaseMaterial3D() { diff --git a/scene/resources/material.h b/scene/resources/material.h index ecf79c581b..50a774e961 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -62,7 +62,8 @@ protected: void _validate_property(PropertyInfo &p_property) const; - void _mark_initialized(const Callable &p_queue_shader_change_callable); + void _mark_ready(); + void _mark_initialized(const Callable &p_add_to_dirty_list, const Callable &p_update_shader); bool _is_initialized() { return init_state == INIT_STATE_READY; } GDVIRTUAL0RC(RID, _get_shader_rid) @@ -466,7 +467,6 @@ private: void _update_shader(); _FORCE_INLINE_ void _queue_shader_change(); - _FORCE_INLINE_ bool _is_shader_dirty() const; bool orm; diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 0b65b33240..81476eaa0f 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -33,14 +33,12 @@ #include "core/version.h" Mutex ParticleProcessMaterial::material_mutex; -SelfList<ParticleProcessMaterial>::List *ParticleProcessMaterial::dirty_materials = nullptr; +SelfList<ParticleProcessMaterial>::List ParticleProcessMaterial::dirty_materials; HashMap<ParticleProcessMaterial::MaterialKey, ParticleProcessMaterial::ShaderData, ParticleProcessMaterial::MaterialKey> ParticleProcessMaterial::shader_map; RBSet<String> ParticleProcessMaterial::min_max_properties; ParticleProcessMaterial::ShaderNames *ParticleProcessMaterial::shader_names = nullptr; void ParticleProcessMaterial::init_shaders() { - dirty_materials = memnew(SelfList<ParticleProcessMaterial>::List); - shader_names = memnew(ShaderNames); shader_names->direction = "direction"; @@ -140,15 +138,13 @@ void ParticleProcessMaterial::init_shaders() { } void ParticleProcessMaterial::finish_shaders() { - memdelete(dirty_materials); - dirty_materials = nullptr; + dirty_materials.clear(); memdelete(shader_names); + shader_names = nullptr; } void ParticleProcessMaterial::_update_shader() { - dirty_materials->remove(&element); - MaterialKey mk = _compute_key(); if (mk == current_key) { return; //no update required in the end @@ -1170,8 +1166,9 @@ void ParticleProcessMaterial::_update_shader() { void ParticleProcessMaterial::flush_changes() { MutexLock lock(material_mutex); - while (dirty_materials->first()) { - dirty_materials->first()->self()->_update_shader(); + while (dirty_materials.first()) { + dirty_materials.first()->self()->_update_shader(); + dirty_materials.first()->remove_from_list(); } } @@ -1179,16 +1176,10 @@ void ParticleProcessMaterial::_queue_shader_change() { MutexLock lock(material_mutex); if (_is_initialized() && !element.in_list()) { - dirty_materials->add(&element); + dirty_materials.add(&element); } } -bool ParticleProcessMaterial::_is_shader_dirty() const { - MutexLock lock(material_mutex); - - return element.in_list(); -} - bool ParticleProcessMaterial::has_min_max_property(const String &p_name) { return min_max_properties.has(p_name); } @@ -2330,7 +2321,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() : current_key.invalid_key = 1; - _mark_initialized(callable_mp(this, &ParticleProcessMaterial::_queue_shader_change)); + _mark_initialized(callable_mp(this, &ParticleProcessMaterial::_queue_shader_change), callable_mp(this, &ParticleProcessMaterial::_update_shader)); } ParticleProcessMaterial::~ParticleProcessMaterial() { diff --git a/scene/resources/particle_process_material.h b/scene/resources/particle_process_material.h index 94b2009654..25046b51cd 100644 --- a/scene/resources/particle_process_material.h +++ b/scene/resources/particle_process_material.h @@ -186,7 +186,7 @@ private: } static Mutex material_mutex; - static SelfList<ParticleProcessMaterial>::List *dirty_materials; + static SelfList<ParticleProcessMaterial>::List dirty_materials; struct ShaderNames { StringName direction; @@ -293,7 +293,6 @@ private: void _update_shader(); _FORCE_INLINE_ void _queue_shader_change(); - _FORCE_INLINE_ bool _is_shader_dirty() const; Vector3 direction; float spread = 0.0f; diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp index fd9141f46e..7284076a47 100644 --- a/servers/physics_3d/godot_soft_body_3d.cpp +++ b/servers/physics_3d/godot_soft_body_3d.cpp @@ -140,7 +140,12 @@ void GodotSoftBody3D::set_mesh(RID p_mesh) { Array arrays = RenderingServer::get_singleton()->mesh_surface_get_arrays(soft_mesh, 0); ERR_FAIL_COND(arrays.is_empty()); - bool success = create_from_trimesh(arrays[RenderingServer::ARRAY_INDEX], arrays[RenderingServer::ARRAY_VERTEX]); + const Vector<int> &indices = arrays[RenderingServer::ARRAY_INDEX]; + const Vector<Vector3> &vertices = arrays[RenderingServer::ARRAY_VERTEX]; + ERR_FAIL_COND_MSG(indices.is_empty(), "Soft body's mesh needs to have indices"); + ERR_FAIL_COND_MSG(vertices.is_empty(), "Soft body's mesh needs to have vertices"); + + bool success = create_from_trimesh(indices, vertices); if (!success) { destroy(); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 55c2908f59..068f9d9ef2 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1812,6 +1812,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + RS::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(p_render_data->environment); if (p_render_data->camera_attributes.is_valid()) { bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); @@ -1823,7 +1824,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if ((rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1833,13 +1834,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if ((rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } } break; case RS::ENV_BG_SKY: { - draw_sky = true; + draw_sky = !p_render_data->transparent_bg; } break; case RS::ENV_BG_CANVAS: { if (!is_reflection_probe) { @@ -1859,7 +1860,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } // setup sky if used for ambient, reflections, or background - if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(p_render_data->environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RS::ENV_AMBIENT_SOURCE_SKY) { + if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RS::ENV_AMBIENT_SOURCE_SKY) { RENDER_TIMESTAMP("Setup Sky"); RD::get_singleton()->draw_command_begin_label("Setup Sky"); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 5715d94d95..878ea7bcfb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -830,6 +830,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + RS::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(p_render_data->environment); if (p_render_data->camera_attributes.is_valid()) { bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); @@ -841,7 +842,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -851,13 +852,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } } break; case RS::ENV_BG_SKY: { - draw_sky = true; + draw_sky = !p_render_data->transparent_bg; } break; case RS::ENV_BG_CANVAS: { if (rb_data.is_valid()) { @@ -874,7 +875,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } // setup sky if used for ambient, reflections, or background - if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(p_render_data->environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RS::ENV_AMBIENT_SOURCE_SKY) { + if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RS::ENV_AMBIENT_SOURCE_SKY) { RENDER_TIMESTAMP("Setup Sky"); RD::get_singleton()->draw_command_begin_label("Setup Sky"); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 6cb03871c9..1e4880e67a 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1149,6 +1149,10 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render render_data.sdfgi_update_data = p_sdfgi_update_data; render_data.render_info = r_render_info; + + if (p_render_buffers.is_valid() && p_reflection_probe.is_null()) { + render_data.transparent_bg = texture_storage->render_target_get_transparent(rb->get_render_target()); + } } PagedArray<RID> empty; diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h index 2f61899a18..3cd397b8ed 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -78,6 +78,9 @@ public: RenderingMethod::RenderInfo *render_info = nullptr; + /* Viewport data */ + bool transparent_bg = false; + /* Shadow data */ const RendererSceneRender::RenderShadowData *render_shadows = nullptr; int render_shadow_count = 0; diff --git a/servers/rendering/rendering_context_driver.h b/servers/rendering/rendering_context_driver.h index df1424da95..539b3814a0 100644 --- a/servers/rendering/rendering_context_driver.h +++ b/servers/rendering/rendering_context_driver.h @@ -73,10 +73,15 @@ public: DEVICE_TYPE_MAX = 0x5 }; + struct Workarounds { + bool avoid_compute_after_draw = false; + }; + struct Device { String name = "Unknown"; Vendor vendor = VENDOR_UNKNOWN; DeviceType type = DEVICE_TYPE_OTHER; + Workarounds workarounds; }; virtual ~RenderingContextDriver(); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 31fc51efaa..6746a1dde1 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -4877,25 +4877,78 @@ void RenderingDevice::_end_frame() { ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); } - draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); driver->command_buffer_end(frames[frame].setup_command_buffer); - driver->command_buffer_end(frames[frame].draw_command_buffer); + + // The command buffer must be copied into a stack variable as the driver workarounds can change the command buffer in use. + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + draw_graph.end(RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS, command_buffer, frames[frame].command_buffer_pool); + driver->command_buffer_end(command_buffer); driver->end_segment(); } void RenderingDevice::_execute_frame(bool p_present) { + // Check whether this frame should present the swap chains and in which queue. const bool frame_can_present = p_present && !frames[frame].swap_chains_to_present.is_empty(); const bool separate_present_queue = main_queue != present_queue; - const VectorView<RDD::SemaphoreID> execute_draw_semaphore = frame_can_present && separate_present_queue ? frames[frame].draw_semaphore : VectorView<RDD::SemaphoreID>(); - const VectorView<RDD::SwapChainID> execute_draw_swap_chains = frame_can_present && !separate_present_queue ? frames[frame].swap_chains_to_present : VectorView<RDD::SwapChainID>(); + thread_local LocalVector<RDD::SwapChainID> swap_chains; + swap_chains.clear(); + + // Execute the setup command buffer. driver->command_queue_execute_and_present(main_queue, {}, frames[frame].setup_command_buffer, frames[frame].setup_semaphore, {}, {}); - driver->command_queue_execute_and_present(main_queue, frames[frame].setup_semaphore, frames[frame].draw_command_buffer, execute_draw_semaphore, frames[frame].draw_fence, execute_draw_swap_chains); + + // Execute command buffers and use semaphores to wait on the execution of the previous one. Normally there's only one command buffer, + // but driver workarounds can force situations where there'll be more. + uint32_t command_buffer_count = 1; + RDG::CommandBufferPool &buffer_pool = frames[frame].command_buffer_pool; + if (buffer_pool.buffers_used > 0) { + command_buffer_count += buffer_pool.buffers_used; + buffer_pool.buffers_used = 0; + } + + RDD::SemaphoreID wait_semaphore = frames[frame].setup_semaphore; + for (uint32_t i = 0; i < command_buffer_count; i++) { + RDD::CommandBufferID command_buffer; + RDD::SemaphoreID signal_semaphore; + RDD::FenceID signal_fence; + if (i > 0) { + command_buffer = buffer_pool.buffers[i - 1]; + signal_semaphore = buffer_pool.semaphores[i - 1]; + } else { + command_buffer = frames[frame].draw_command_buffer; + signal_semaphore = frames[frame].draw_semaphore; + } + + bool signal_semaphore_valid; + if (i == (command_buffer_count - 1)) { + // This is the last command buffer, it should signal the fence. + signal_fence = frames[frame].draw_fence; + signal_semaphore_valid = false; + + if (frame_can_present && separate_present_queue) { + // The semaphore is required if the frame can be presented and a separate present queue is used. + signal_semaphore_valid = true; + } else if (frame_can_present) { + // Just present the swap chains as part of the last command execution. + swap_chains = frames[frame].swap_chains_to_present; + } + } else { + // Semaphores always need to be signaled if it's not the last command buffer. + signal_semaphore_valid = true; + } + + driver->command_queue_execute_and_present(main_queue, wait_semaphore, command_buffer, signal_semaphore_valid ? signal_semaphore : VectorView<RDD::SemaphoreID>(), signal_fence, swap_chains); + + // Make the next command buffer wait on the semaphore signaled by this one. + wait_semaphore = signal_semaphore; + } + + // Indicate the fence has been signaled so the next time the frame's contents need to be used, the CPU needs to wait on the work to be completed. frames[frame].draw_fence_signaled = true; if (frame_can_present) { if (separate_present_queue) { // Issue the presentation separately if the presentation queue is different from the main queue. - driver->command_queue_execute_and_present(present_queue, frames[frame].draw_semaphore, {}, {}, {}, frames[frame].swap_chains_to_present); + driver->command_queue_execute_and_present(present_queue, wait_semaphore, {}, {}, {}, frames[frame].swap_chains_to_present); } frames[frame].swap_chains_to_present.clear(); @@ -5044,6 +5097,9 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); frames[i].timestamp_result_values.resize(max_timestamp_query_elements); frames[i].timestamp_result_count = 0; + + // Assign the main queue family and command pool to the command buffer pool. + frames[i].command_buffer_pool.pool = frames[i].command_pool; } // Start from frame count, so everything else is immediately old. @@ -5055,7 +5111,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ driver->command_buffer_begin(frames[0].draw_command_buffer); // Create draw graph and start it initialized as well. - draw_graph.initialize(driver, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME); + draw_graph.initialize(driver, device, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME); draw_graph.begin(); for (uint32_t i = 0; i < frames.size(); i++) { @@ -5388,6 +5444,11 @@ void RenderingDevice::finalize() { driver->semaphore_free(frames[i].setup_semaphore); driver->semaphore_free(frames[i].draw_semaphore); driver->fence_free(frames[i].draw_fence); + + RDG::CommandBufferPool &buffer_pool = frames[i].command_buffer_pool; + for (uint32_t j = 0; j < buffer_pool.buffers.size(); j++) { + driver->semaphore_free(buffer_pool.semaphores[j]); + } } if (pipeline_cache_enabled) { diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 9db2fdfbf4..020be6be18 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1261,6 +1261,9 @@ private: // Swap chains prepared for drawing during the frame that must be presented. LocalVector<RDD::SwapChainID> swap_chains_to_present; + // Extra command buffer pool used for driver workarounds. + RDG::CommandBufferPool command_buffer_pool; + struct Timestamp { String description; uint64_t value = 0; diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index b04f2ebbaa..c7de5c67cb 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -753,71 +753,96 @@ void RenderingDeviceGraph::_wait_for_secondary_command_buffer_tasks() { } } -void RenderingDeviceGraph::_run_render_commands(RDD::CommandBufferID p_command_buffer, int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level) { +void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool, int32_t &r_current_label_index, int32_t &r_current_label_level) { for (uint32_t i = 0; i < p_sorted_commands_count; i++) { const uint32_t command_index = p_sorted_commands[i].index; const uint32_t command_data_offset = command_data_offsets[command_index]; const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); - _run_label_command_change(p_command_buffer, command->label_index, p_level, false, true, &p_sorted_commands[i], p_sorted_commands_count - i, r_current_label_index, r_current_label_level); + _run_label_command_change(r_command_buffer, command->label_index, p_level, false, true, &p_sorted_commands[i], p_sorted_commands_count - i, r_current_label_index, r_current_label_level); switch (command->type) { case RecordedCommand::TYPE_BUFFER_CLEAR: { const RecordedBufferClearCommand *buffer_clear_command = reinterpret_cast<const RecordedBufferClearCommand *>(command); - driver->command_clear_buffer(p_command_buffer, buffer_clear_command->buffer, buffer_clear_command->offset, buffer_clear_command->size); + driver->command_clear_buffer(r_command_buffer, buffer_clear_command->buffer, buffer_clear_command->offset, buffer_clear_command->size); } break; case RecordedCommand::TYPE_BUFFER_COPY: { const RecordedBufferCopyCommand *buffer_copy_command = reinterpret_cast<const RecordedBufferCopyCommand *>(command); - driver->command_copy_buffer(p_command_buffer, buffer_copy_command->source, buffer_copy_command->destination, buffer_copy_command->region); + driver->command_copy_buffer(r_command_buffer, buffer_copy_command->source, buffer_copy_command->destination, buffer_copy_command->region); } break; case RecordedCommand::TYPE_BUFFER_GET_DATA: { const RecordedBufferGetDataCommand *buffer_get_data_command = reinterpret_cast<const RecordedBufferGetDataCommand *>(command); - driver->command_copy_buffer(p_command_buffer, buffer_get_data_command->source, buffer_get_data_command->destination, buffer_get_data_command->region); + driver->command_copy_buffer(r_command_buffer, buffer_get_data_command->source, buffer_get_data_command->destination, buffer_get_data_command->region); } break; case RecordedCommand::TYPE_BUFFER_UPDATE: { const RecordedBufferUpdateCommand *buffer_update_command = reinterpret_cast<const RecordedBufferUpdateCommand *>(command); const RecordedBufferCopy *command_buffer_copies = buffer_update_command->buffer_copies(); for (uint32_t j = 0; j < buffer_update_command->buffer_copies_count; j++) { - driver->command_copy_buffer(p_command_buffer, command_buffer_copies[j].source, buffer_update_command->destination, command_buffer_copies[j].region); + driver->command_copy_buffer(r_command_buffer, command_buffer_copies[j].source, buffer_update_command->destination, command_buffer_copies[j].region); } } break; case RecordedCommand::TYPE_COMPUTE_LIST: { + if (device.workarounds.avoid_compute_after_draw && workarounds_state.draw_list_found) { + // Avoid compute after draw workaround. Refer to the comment that enables this in the Vulkan driver for more information. + workarounds_state.draw_list_found = false; + + // Create or reuse a command buffer and finish recording the current one. + driver->command_buffer_end(r_command_buffer); + + while (r_command_buffer_pool.buffers_used >= r_command_buffer_pool.buffers.size()) { + RDD::CommandBufferID command_buffer = driver->command_buffer_create(r_command_buffer_pool.pool); + RDD::SemaphoreID command_semaphore = driver->semaphore_create(); + r_command_buffer_pool.buffers.push_back(command_buffer); + r_command_buffer_pool.semaphores.push_back(command_semaphore); + } + + // Start recording on the next usable command buffer from the pool. + uint32_t command_buffer_index = r_command_buffer_pool.buffers_used++; + r_command_buffer = r_command_buffer_pool.buffers[command_buffer_index]; + driver->command_buffer_begin(r_command_buffer); + } + const RecordedComputeListCommand *compute_list_command = reinterpret_cast<const RecordedComputeListCommand *>(command); - _run_compute_list_command(p_command_buffer, compute_list_command->instruction_data(), compute_list_command->instruction_data_size); + _run_compute_list_command(r_command_buffer, compute_list_command->instruction_data(), compute_list_command->instruction_data_size); } break; case RecordedCommand::TYPE_DRAW_LIST: { + if (device.workarounds.avoid_compute_after_draw) { + // Indicate that a draw list was encountered for the workaround. + workarounds_state.draw_list_found = true; + } + const RecordedDrawListCommand *draw_list_command = reinterpret_cast<const RecordedDrawListCommand *>(command); const VectorView clear_values(draw_list_command->clear_values(), draw_list_command->clear_values_count); - driver->command_begin_render_pass(p_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); - _run_draw_list_command(p_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); - driver->command_end_render_pass(p_command_buffer); + driver->command_begin_render_pass(r_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); + _run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); + driver->command_end_render_pass(r_command_buffer); } break; case RecordedCommand::TYPE_TEXTURE_CLEAR: { const RecordedTextureClearCommand *texture_clear_command = reinterpret_cast<const RecordedTextureClearCommand *>(command); - driver->command_clear_color_texture(p_command_buffer, texture_clear_command->texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_clear_command->color, texture_clear_command->range); + driver->command_clear_color_texture(r_command_buffer, texture_clear_command->texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_clear_command->color, texture_clear_command->range); } break; case RecordedCommand::TYPE_TEXTURE_COPY: { const RecordedTextureCopyCommand *texture_copy_command = reinterpret_cast<const RecordedTextureCopyCommand *>(command); - driver->command_copy_texture(p_command_buffer, texture_copy_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_copy_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_copy_command->region); + driver->command_copy_texture(r_command_buffer, texture_copy_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_copy_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_copy_command->region); } break; case RecordedCommand::TYPE_TEXTURE_GET_DATA: { const RecordedTextureGetDataCommand *texture_get_data_command = reinterpret_cast<const RecordedTextureGetDataCommand *>(command); const VectorView<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_view(texture_get_data_command->buffer_texture_copy_regions(), texture_get_data_command->buffer_texture_copy_regions_count); - driver->command_copy_texture_to_buffer(p_command_buffer, texture_get_data_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_get_data_command->to_buffer, command_buffer_texture_copy_regions_view); + driver->command_copy_texture_to_buffer(r_command_buffer, texture_get_data_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_get_data_command->to_buffer, command_buffer_texture_copy_regions_view); } break; case RecordedCommand::TYPE_TEXTURE_RESOLVE: { const RecordedTextureResolveCommand *texture_resolve_command = reinterpret_cast<const RecordedTextureResolveCommand *>(command); - driver->command_resolve_texture(p_command_buffer, texture_resolve_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_resolve_command->src_layer, texture_resolve_command->src_mipmap, texture_resolve_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_resolve_command->dst_layer, texture_resolve_command->dst_mipmap); + driver->command_resolve_texture(r_command_buffer, texture_resolve_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_resolve_command->src_layer, texture_resolve_command->src_mipmap, texture_resolve_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_resolve_command->dst_layer, texture_resolve_command->dst_mipmap); } break; case RecordedCommand::TYPE_TEXTURE_UPDATE: { const RecordedTextureUpdateCommand *texture_update_command = reinterpret_cast<const RecordedTextureUpdateCommand *>(command); const RecordedBufferToTextureCopy *command_buffer_to_texture_copies = texture_update_command->buffer_to_texture_copies(); for (uint32_t j = 0; j < texture_update_command->buffer_to_texture_copies_count; j++) { - driver->command_copy_buffer_to_texture(p_command_buffer, command_buffer_to_texture_copies[j].from_buffer, texture_update_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, command_buffer_to_texture_copies[j].region); + driver->command_copy_buffer_to_texture(r_command_buffer, command_buffer_to_texture_copies[j].from_buffer, texture_update_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, command_buffer_to_texture_copies[j].region); } } break; case RecordedCommand::TYPE_CAPTURE_TIMESTAMP: { const RecordedCaptureTimestampCommand *texture_capture_timestamp_command = reinterpret_cast<const RecordedCaptureTimestampCommand *>(command); - driver->command_timestamp_write(p_command_buffer, texture_capture_timestamp_command->pool, texture_capture_timestamp_command->index); + driver->command_timestamp_write(r_command_buffer, texture_capture_timestamp_command->pool, texture_capture_timestamp_command->index); } break; default: { DEV_ASSERT(false && "Unknown recorded command type."); @@ -1229,8 +1254,9 @@ void RenderingDeviceGraph::_print_compute_list(const uint8_t *p_instruction_data } } -void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) { +void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) { driver = p_driver; + device = p_device; frames.resize(p_frame_count); for (uint32_t i = 0; i < p_frame_count; i++) { @@ -1805,7 +1831,7 @@ void RenderingDeviceGraph::end_label() { command_label_index = -1; } -void RenderingDeviceGraph::end(RDD::CommandBufferID p_command_buffer, bool p_reorder_commands, bool p_full_barriers) { +void RenderingDeviceGraph::end(bool p_reorder_commands, bool p_full_barriers, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool) { if (command_count == 0) { // No commands have been logged, do nothing. return; @@ -1919,7 +1945,12 @@ void RenderingDeviceGraph::end(RDD::CommandBufferID p_command_buffer, bool p_reo if (command_count > 0) { int32_t current_label_index = -1; int32_t current_label_level = -1; - _run_label_command_change(p_command_buffer, -1, -1, true, true, nullptr, 0, current_label_index, current_label_level); + _run_label_command_change(r_command_buffer, -1, -1, true, true, nullptr, 0, current_label_index, current_label_level); + + if (device.workarounds.avoid_compute_after_draw) { + // Reset the state of the workaround. + workarounds_state.draw_list_found = false; + } if (p_reorder_commands) { #if PRINT_RENDER_GRAPH @@ -1946,8 +1977,8 @@ void RenderingDeviceGraph::end(RDD::CommandBufferID p_command_buffer, bool p_reo RecordedCommandSort *level_command_ptr = &commands_sorted[current_level_start]; uint32_t level_command_count = i - current_level_start; _boost_priority_for_render_commands(level_command_ptr, level_command_count, boosted_priority); - _group_barriers_for_render_commands(p_command_buffer, level_command_ptr, level_command_count, p_full_barriers); - _run_render_commands(p_command_buffer, current_level, level_command_ptr, level_command_count, current_label_index, current_label_level); + _group_barriers_for_render_commands(r_command_buffer, level_command_ptr, level_command_count, p_full_barriers); + _run_render_commands(current_level, level_command_ptr, level_command_count, r_command_buffer, r_command_buffer_pool, current_label_index, current_label_level); current_level = commands_sorted[i].level; current_level_start = i; } @@ -1956,20 +1987,20 @@ void RenderingDeviceGraph::end(RDD::CommandBufferID p_command_buffer, bool p_reo RecordedCommandSort *level_command_ptr = &commands_sorted[current_level_start]; uint32_t level_command_count = command_count - current_level_start; _boost_priority_for_render_commands(level_command_ptr, level_command_count, boosted_priority); - _group_barriers_for_render_commands(p_command_buffer, level_command_ptr, level_command_count, p_full_barriers); - _run_render_commands(p_command_buffer, current_level, level_command_ptr, level_command_count, current_label_index, current_label_level); + _group_barriers_for_render_commands(r_command_buffer, level_command_ptr, level_command_count, p_full_barriers); + _run_render_commands(current_level, level_command_ptr, level_command_count, r_command_buffer, r_command_buffer_pool, current_label_index, current_label_level); #if PRINT_RENDER_GRAPH print_line("COMMANDS", command_count, "LEVELS", current_level + 1); #endif } else { for (uint32_t i = 0; i < command_count; i++) { - _group_barriers_for_render_commands(p_command_buffer, &commands_sorted[i], 1, p_full_barriers); - _run_render_commands(p_command_buffer, i, &commands_sorted[i], 1, current_label_index, current_label_level); + _group_barriers_for_render_commands(r_command_buffer, &commands_sorted[i], 1, p_full_barriers); + _run_render_commands(i, &commands_sorted[i], 1, r_command_buffer, r_command_buffer_pool, current_label_index, current_label_level); } } - _run_label_command_change(p_command_buffer, -1, -1, true, false, nullptr, 0, current_label_index, current_label_level); + _run_label_command_change(r_command_buffer, -1, -1, true, false, nullptr, 0, current_label_index, current_label_level); #if PRINT_COMMAND_RECORDING print_line(vformat("Recorded %d commands", command_count)); diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index 3bc63bb297..a96382e0cc 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -184,6 +184,20 @@ public: } }; + struct CommandBufferPool { + // Provided by RenderingDevice. + RDD::CommandPoolID pool; + + // Created internally by RenderingDeviceGraph. + LocalVector<RDD::CommandBufferID> buffers; + LocalVector<RDD::SemaphoreID> semaphores; + uint32_t buffers_used = 0; + }; + + struct WorkaroundsState { + bool draw_list_found = false; + }; + private: struct InstructionList { LocalVector<uint8_t> data; @@ -560,6 +574,7 @@ private: }; RDD *driver = nullptr; + RenderingContextDriver::Device device; int64_t tracking_frame = 0; LocalVector<uint8_t> command_data; LocalVector<uint32_t> command_data_offsets; @@ -582,6 +597,7 @@ private: bool command_synchronization_pending = false; BarrierGroup barrier_group; bool driver_honors_barriers = false; + WorkaroundsState workarounds_state; TightLocalVector<Frame> frames; uint32_t frame = 0; @@ -608,7 +624,7 @@ private: void _run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); void _run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary); void _wait_for_secondary_command_buffer_tasks(); - void _run_render_commands(RDD::CommandBufferID p_command_buffer, int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level); + void _run_render_commands(int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool, int32_t &r_current_label_index, int32_t &r_current_label_level); void _run_label_command_change(RDD::CommandBufferID p_command_buffer, int32_t p_new_label_index, int32_t p_new_level, bool p_ignore_previous_value, bool p_use_label_for_empty, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level); void _boost_priority_for_render_commands(RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, uint32_t &r_boosted_priority); void _group_barriers_for_render_commands(RDD::CommandBufferID p_command_buffer, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, bool p_full_memory_barrier); @@ -619,7 +635,7 @@ private: public: RenderingDeviceGraph(); ~RenderingDeviceGraph(); - void initialize(RDD *p_driver, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame); + void initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame); void finalize(); void begin(); void add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size); @@ -664,7 +680,7 @@ public: void add_synchronization(); void begin_label(const String &p_label_name, const Color &p_color); void end_label(); - void end(RDD::CommandBufferID p_command_buffer, bool p_reorder_commands, bool p_full_barriers); + void end(bool p_reorder_commands, bool p_full_barriers, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool); static ResourceTracker *resource_tracker_create(); static void resource_tracker_free(ResourceTracker *tracker); }; diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h index 099f6fefa9..1248632630 100644 --- a/tests/scene/test_curve_2d.h +++ b/tests/scene/test_curve_2d.h @@ -147,13 +147,19 @@ TEST_CASE("[Curve2D] Sampling") { CHECK(curve->samplef(1) == Vector2(0, 50)); } - SUBCASE("sample_baked") { + SUBCASE("sample_baked, cubic = false") { CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0))) == Vector2(0, 0)); CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25))) == Vector2(0, 25)); CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50))) == Vector2(0, 50)); } - SUBCASE("sample_baked_with_rotation") { + SUBCASE("sample_baked, cubic = true") { + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0)), true) == Vector2(0, 0)); + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25)), true) == Vector2(0, 25)); + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50)), true) == Vector2(0, 50)); + } + + SUBCASE("sample_baked_with_rotation, cubic = false") { const real_t pi = 3.14159; const real_t half_pi = pi * 0.5; Ref<Curve2D> rot_curve = memnew(Curve2D); @@ -188,6 +194,41 @@ TEST_CASE("[Curve2D] Sampling") { CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi)); } + SUBCASE("sample_baked_with_rotation, cubic = true") { + const real_t pi = 3.14159; + const real_t half_pi = pi * 0.5; + Ref<Curve2D> rot_curve = memnew(Curve2D); + Transform2D t; + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(50, 0)); + t = rot_curve->sample_baked_with_rotation(25, true); + CHECK(t.get_origin() == Vector2(25, 0)); + CHECK(Math::is_equal_approx(t.get_rotation(), 0)); + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(0, 50)); + t = rot_curve->sample_baked_with_rotation(25, true); + CHECK(t.get_origin() == Vector2(0, 25)); + CHECK(Math::is_equal_approx(t.get_rotation(), half_pi)); + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(-50, 0)); + t = rot_curve->sample_baked_with_rotation(25, true); + CHECK(t.get_origin() == Vector2(-25, 0)); + CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(0, -50)); + t = rot_curve->sample_baked_with_rotation(25, true); + CHECK(t.get_origin() == Vector2(0, -25)); + CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi)); + } + SUBCASE("get_closest_point") { CHECK(curve->get_closest_point(Vector2(0, 0)) == Vector2(0, 0)); CHECK(curve->get_closest_point(Vector2(0, 25)) == Vector2(0, 25)); diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h index d73bb1ad35..2e60a9c6e6 100644 --- a/tests/scene/test_curve_3d.h +++ b/tests/scene/test_curve_3d.h @@ -177,12 +177,30 @@ TEST_CASE("[Curve3D] Sampling") { CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Vector3(0, 50, 0)); } - SUBCASE("sample_baked_with_rotation") { + SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = false") { CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0))); CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0))); CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0))); } + SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = true") { + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0))); + } + + SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = false") { + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0))); + } + + SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = true") { + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0))); + } + SUBCASE("sample_baked_tilt") { CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 0, 0))) == 0); CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 25, 0))) == 0); diff --git a/version.py b/version.py index e915a6e530..d5ea5cfd91 100644 --- a/version.py +++ b/version.py @@ -3,7 +3,7 @@ name = "Godot Engine" major = 4 minor = 3 patch = 0 -status = "dev" +status = "beta" module_config = "" website = "https://godotengine.org" docs = "latest" |