diff options
Diffstat (limited to 'servers')
106 files changed, 11959 insertions, 2408 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 455a8b49a1..65d88a0eba 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -810,7 +810,11 @@ void AudioStreamPlaybackRandomizer::tag_used_streams() { int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (playing.is_valid()) { - return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); + int mixed_samples = playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); + for (int samp = 0; samp < mixed_samples; samp++) { + p_buffer[samp] *= volume_scale; + } + return mixed_samples; } else { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 511a8b3122..026bc6675a 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -122,8 +122,10 @@ int AudioDriver::_get_configured_mix_rate() { // In the case of invalid mix rate, let's default to a sensible value.. if (mix_rate <= 0) { +#ifndef WEB_ENABLED WARN_PRINT(vformat("Invalid mix rate of %d, consider reassigning setting \'%s\'. \nDefaulting mix rate to value %d.", mix_rate, audio_driver_setting, AudioDriverManager::DEFAULT_MIX_RATE)); +#endif mix_rate = AudioDriverManager::DEFAULT_MIX_RATE; } @@ -199,8 +201,8 @@ int AudioDriverManager::get_driver_count() { void AudioDriverManager::initialize(int p_driver) { GLOBAL_DEF_RST("audio/driver/enable_input", false); - GLOBAL_DEF_RST("audio/driver/mix_rate", DEFAULT_MIX_RATE); - GLOBAL_DEF_RST("audio/driver/mix_rate.web", 0); // Safer default output_latency for web (use browser default). + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/mix_rate", PROPERTY_HINT_RANGE, "11025,192000,1,or_greater,suffix:Hz"), DEFAULT_MIX_RATE); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/mix_rate.web", PROPERTY_HINT_RANGE, "0,192000,1,or_greater,suffix:Hz"), 0); // Safer default output_latency for web (use browser default). int failed_driver = -1; @@ -1147,6 +1149,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Has for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) { new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx]; } + idx++; } playback_node->bus_details = new_bus_details; playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails(); diff --git a/servers/debugger/servers_debugger.cpp b/servers/debugger/servers_debugger.cpp index 9161c5c8b7..bf7dd6c29e 100644 --- a/servers/debugger/servers_debugger.cpp +++ b/servers/debugger/servers_debugger.cpp @@ -108,12 +108,13 @@ Array ServersDebugger::ServersProfilerFrame::serialize() { } } - arr.push_back(script_functions.size() * 4); + arr.push_back(script_functions.size() * 5); for (int i = 0; i < script_functions.size(); i++) { arr.push_back(script_functions[i].sig_id); arr.push_back(script_functions[i].call_count); arr.push_back(script_functions[i].self_time); arr.push_back(script_functions[i].total_time); + arr.push_back(script_functions[i].internal_time); } return arr; } @@ -149,14 +150,15 @@ bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) { int func_size = p_arr[idx]; idx += 1; CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame"); - for (int i = 0; i < func_size / 4; i++) { + for (int i = 0; i < func_size / 5; i++) { ScriptFunctionInfo fi; fi.sig_id = p_arr[idx]; fi.call_count = p_arr[idx + 1]; fi.self_time = p_arr[idx + 2]; fi.total_time = p_arr[idx + 3]; + fi.internal_time = p_arr[idx + 4]; script_functions.push_back(fi); - idx += 4; + idx += 5; } CHECK_END(p_arr, idx, "ServersProfilerFrame"); return true; @@ -210,8 +212,11 @@ public: sig_map.clear(); for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptServer::get_language(i)->profiling_start(); + if (p_opts.size() == 2 && p_opts[1].get_type() == Variant::BOOL) { + ScriptServer::get_language(i)->profiling_set_save_native_calls(p_opts[1]); + } } - if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) { + if (p_opts.size() > 0 && p_opts[0].get_type() == Variant::INT) { max_frame_functions = MAX(0, int(p_opts[0])); } } else { @@ -265,6 +270,7 @@ public: w[i].call_count = ptrs[i]->call_count; w[i].total_time = ptrs[i]->total_time / 1000000.0; w[i].self_time = ptrs[i]->self_time / 1000000.0; + w[i].internal_time = ptrs[i]->internal_time / 1000000.0; } } diff --git a/servers/debugger/servers_debugger.h b/servers/debugger/servers_debugger.h index c7a11439e0..6efed9f8b9 100644 --- a/servers/debugger/servers_debugger.h +++ b/servers/debugger/servers_debugger.h @@ -69,6 +69,7 @@ public: int call_count = 0; double self_time = 0; double total_time = 0; + double internal_time = 0; }; // Servers profiler diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 6459cc7462..bb28bc0eb8 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -267,6 +267,11 @@ void DisplayServer::global_menu_clear(const String &p_menu_root) { WARN_PRINT("Global menus not supported by this display server."); } +Dictionary DisplayServer::global_menu_get_system_menu_roots() const { + WARN_PRINT("Global menus not supported by this display server."); + return Dictionary(); +} + bool DisplayServer::tts_is_speaking() const { WARN_PRINT("TTS is not supported by this display server."); return false; @@ -652,6 +657,8 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu_root", "idx"), &DisplayServer::global_menu_remove_item); ClassDB::bind_method(D_METHOD("global_menu_clear", "menu_root"), &DisplayServer::global_menu_clear); + ClassDB::bind_method(D_METHOD("global_menu_get_system_menu_roots"), &DisplayServer::global_menu_get_system_menu_roots); + ClassDB::bind_method(D_METHOD("tts_is_speaking"), &DisplayServer::tts_is_speaking); ClassDB::bind_method(D_METHOD("tts_is_paused"), &DisplayServer::tts_is_paused); ClassDB::bind_method(D_METHOD("tts_get_voices"), &DisplayServer::tts_get_voices); diff --git a/servers/display_server.h b/servers/display_server.h index d2e112d224..4450677f71 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -185,6 +185,8 @@ public: virtual void global_menu_remove_item(const String &p_menu_root, int p_idx); virtual void global_menu_clear(const String &p_menu_root); + virtual Dictionary global_menu_get_system_menu_roots() const; + struct TTSUtterance { String text; String voice; diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 3804b45e1a..81f389e19b 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -59,6 +59,8 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update); + ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer2D::map_get_random_point); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create); @@ -78,10 +80,12 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform); + ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer2D::region_get_transform); ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon); ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map); @@ -111,19 +115,31 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer2D::agent_set_paused); ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer2D::agent_get_paused); ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance); + ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer2D::agent_get_neighbor_distance); ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors); + ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer2D::agent_get_max_neighbors); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer2D::agent_get_time_horizon_agents); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer2D::agent_get_time_horizon_obstacles); ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius); + ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer2D::agent_get_radius); ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed); + ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer2D::agent_get_max_speed); ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced); ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity); + ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer2D::agent_get_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position); + ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer2D::agent_get_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed); ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback); + ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer2D::agent_has_avoidance_callback); ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer2D::agent_get_avoidance_layers); ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer2D::agent_get_avoidance_mask); ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer2D::agent_get_avoidance_priority); ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer2D::obstacle_set_avoidance_enabled); @@ -133,10 +149,15 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer2D::obstacle_set_paused); ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer2D::obstacle_get_paused); ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer2D::obstacle_set_radius); + ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer2D::obstacle_get_radius); ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer2D::obstacle_set_velocity); + ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer2D::obstacle_get_velocity); ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position); + ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer2D::obstacle_get_position); ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices); + ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer2D::obstacle_get_vertices); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_layers); ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_polygon", "source_geometry_data", "root_node", "callback"), &NavigationServer2D::parse_source_geometry_data, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data, DEFVAL(Callable())); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 0afd794c26..be3818fe9d 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -103,6 +103,8 @@ public: virtual void map_force_update(RID p_map) = 0; + virtual Vector2 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new region. virtual RID region_create() = 0; @@ -136,6 +138,7 @@ public: /// Set the global transformation of this region. virtual void region_set_transform(RID p_region, Transform2D p_transform) = 0; + virtual Transform2D region_get_transform(RID p_region) const = 0; /// Set the navigation poly of this region. virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) = 0; @@ -145,6 +148,8 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; @@ -204,6 +209,7 @@ public: /// low, the simulation will not be safe. /// Must be non-negative. virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0; + virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0; /// The maximum number of other agents this /// agent takes into account in the navigation. @@ -212,6 +218,7 @@ public: /// number is too low, the simulation will not /// be safe. virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0; + virtual int agent_get_max_neighbors(RID p_agent) const = 0; /// The minimal amount of time for which this /// agent's velocities that are computed by the @@ -221,17 +228,20 @@ public: /// other agents, but the less freedom this /// agent has in choosing its velocities. /// Must be positive. - virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0; virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0; /// The radius of this agent. /// Must be non-negative. virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0; + virtual real_t agent_get_radius(RID p_agent) const = 0; /// The maximum speed of this agent. /// Must be non-negative. virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0; + virtual real_t agent_get_max_speed(RID p_agent) const = 0; /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0; @@ -239,19 +249,27 @@ public: /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0; + virtual Vector2 agent_get_velocity(RID p_agent) const = 0; /// Position of the agent in world space. virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0; + virtual Vector2 agent_get_position(RID p_agent) const = 0; /// Returns true if the map got changed the previous frame. virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0; + virtual bool agent_has_avoidance_callback(RID p_agent) const = 0; virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0; + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0; + virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0; + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0; + virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0; + virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0; /// Creates the obstacle. virtual RID obstacle_create() = 0; @@ -262,10 +280,15 @@ public: virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) = 0; virtual bool obstacle_get_paused(RID p_obstacle) const = 0; virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0; + virtual real_t obstacle_get_radius(RID p_obstacle) const = 0; virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) = 0; + virtual Vector2 obstacle_get_velocity(RID p_obstacle) const = 0; virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) = 0; + virtual Vector2 obstacle_get_position(RID p_obstacle) const = 0; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) = 0; + virtual Vector<Vector2> obstacle_get_vertices(RID p_obstacle) const = 0; virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0; + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0; /// Returns a customized navigation path using a query parameters object virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const = 0; diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index d64a9454aa..5b33a4f170 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -58,6 +58,7 @@ public: TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); } void map_force_update(RID p_map) override {} + Vector2 map_get_random_point(RID p_map, uint32_t p_naviation_layers, bool p_uniformly) const override { return Vector2(); }; RID region_create() override { return RID(); } void region_set_enabled(RID p_region, bool p_enabled) override {} @@ -76,10 +77,12 @@ public: void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {} uint32_t region_get_navigation_layers(RID p_region) const override { return 0; } void region_set_transform(RID p_region, Transform2D p_transform) override {} + Transform2D region_get_transform(RID p_region) const override { return Transform2D(); } void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override {} int region_get_connections_count(RID p_region) const override { return 0; } Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); } Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); } + Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }; RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} @@ -109,19 +112,31 @@ public: void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {} bool agent_get_avoidance_enabled(RID p_agent) const override { return false; } void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {} + real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; } void agent_set_max_neighbors(RID p_agent, int p_count) override {} + int agent_get_max_neighbors(RID p_agent) const override { return 0; } void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; } void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; } void agent_set_radius(RID p_agent, real_t p_radius) override {} + real_t agent_get_radius(RID p_agent) const override { return 0; } void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {} + real_t agent_get_max_speed(RID p_agent) const override { return 0; } void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override {} void agent_set_velocity(RID p_agent, Vector2 p_velocity) override {} + Vector2 agent_get_velocity(RID p_agent) const override { return Vector2(); } void agent_set_position(RID p_agent, Vector2 p_position) override {} + Vector2 agent_get_position(RID p_agent) const override { return Vector2(); } bool agent_is_map_changed(RID p_agent) const override { return false; } void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {} + bool agent_has_avoidance_callback(RID p_agent) const override { return false; } void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {} + uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; } void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {} + uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; } void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {} + real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; } RID obstacle_create() override { return RID(); } void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override {} @@ -131,10 +146,15 @@ public: void obstacle_set_paused(RID p_obstacle, bool p_paused) override {} bool obstacle_get_paused(RID p_obstacle) const override { return false; } void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {} + real_t obstacle_get_radius(RID p_agent) const override { return 0; } void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override {} + Vector2 obstacle_get_velocity(RID p_agent) const override { return Vector2(); } void obstacle_set_position(RID p_obstacle, Vector2 p_position) override {} + Vector2 obstacle_get_position(RID p_agent) const override { return Vector2(); } void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override {} + Vector<Vector2> obstacle_get_vertices(RID p_agent) const override { return Vector<Vector2>(); } void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} + uint32_t obstacle_get_avoidance_layers(RID p_agent) const override { return 0; } void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override {} diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index eccacd3ea8..21e1ccaa72 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -64,6 +64,8 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer3D::map_force_update); + ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer3D::map_get_random_point); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::query_path); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create); @@ -83,6 +85,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer3D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform); + ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer3D::region_get_transform); ClassDB::bind_method(D_METHOD("region_set_navigation_mesh", "region", "navigation_mesh"), &NavigationServer3D::region_set_navigation_mesh); #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("region_bake_navigation_mesh", "navigation_mesh", "root_node"), &NavigationServer3D::region_bake_navigation_mesh); @@ -90,6 +93,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map); @@ -122,20 +126,33 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer3D::agent_set_paused); ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer3D::agent_get_paused); ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer3D::agent_set_neighbor_distance); + ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer3D::agent_get_neighbor_distance); ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer3D::agent_set_max_neighbors); + ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer3D::agent_get_max_neighbors); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_agents); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer3D::agent_get_time_horizon_agents); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_obstacles); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer3D::agent_get_time_horizon_obstacles); ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer3D::agent_set_radius); + ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer3D::agent_get_radius); ClassDB::bind_method(D_METHOD("agent_set_height", "agent", "height"), &NavigationServer3D::agent_set_height); + ClassDB::bind_method(D_METHOD("agent_get_height", "agent"), &NavigationServer3D::agent_get_height); ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer3D::agent_set_max_speed); + ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer3D::agent_get_max_speed); ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer3D::agent_set_velocity_forced); ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer3D::agent_set_velocity); + ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer3D::agent_get_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer3D::agent_set_position); + ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer3D::agent_get_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer3D::agent_is_map_changed); ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer3D::agent_set_avoidance_callback); + ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer3D::agent_has_avoidance_callback); ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer3D::agent_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer3D::agent_get_avoidance_layers); ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer3D::agent_set_avoidance_mask); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer3D::agent_get_avoidance_mask); ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer3D::agent_set_avoidance_priority); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer3D::agent_get_avoidance_priority); ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer3D::obstacle_create); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer3D::obstacle_set_avoidance_enabled); @@ -147,11 +164,17 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer3D::obstacle_set_paused); ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer3D::obstacle_get_paused); ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer3D::obstacle_set_radius); + ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer3D::obstacle_get_radius); ClassDB::bind_method(D_METHOD("obstacle_set_height", "obstacle", "height"), &NavigationServer3D::obstacle_set_height); + ClassDB::bind_method(D_METHOD("obstacle_get_height", "obstacle"), &NavigationServer3D::obstacle_get_height); ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer3D::obstacle_set_velocity); + ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer3D::obstacle_get_velocity); ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer3D::obstacle_set_position); + ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer3D::obstacle_get_position); ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer3D::obstacle_set_vertices); + ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer3D::obstacle_get_vertices); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_layers); ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable())); @@ -190,12 +213,12 @@ NavigationServer3D::NavigationServer3D() { ERR_FAIL_COND(singleton != nullptr); singleton = this; - GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 1.0); GLOBAL_DEF("navigation/2d/use_edge_connections", true); GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1.0); GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4.0); - GLOBAL_DEF_BASIC("navigation/3d/default_cell_size", 0.25); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/3d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 0.25); GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", 0.25); GLOBAL_DEF("navigation/3d/default_up", Vector3(0, 1, 0)); GLOBAL_DEF("navigation/3d/use_edge_connections", true); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 53d33d0058..dc58502b1e 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -114,6 +114,8 @@ public: virtual void map_force_update(RID p_map) = 0; + virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new region. virtual RID region_create() = 0; @@ -147,6 +149,7 @@ public: /// Set the global transformation of this region. virtual void region_set_transform(RID p_region, Transform3D p_transform) = 0; + virtual Transform3D region_get_transform(RID p_region) const = 0; /// Set the navigation mesh of this region. virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0; @@ -161,6 +164,8 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; @@ -223,6 +228,7 @@ public: /// low, the simulation will not be safe. /// Must be non-negative. virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0; + virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0; /// The maximum number of other agents this /// agent takes into account in the navigation. @@ -231,25 +237,32 @@ public: /// number is too low, the simulation will not /// be safe. virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0; + virtual int agent_get_max_neighbors(RID p_agent) const = 0; // Sets the minimum amount of time in seconds that an agent's // must be able to stay on the calculated velocity while still avoiding collisions with agent's // if this value is set to high an agent will often fall back to using a very low velocity just to be safe virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0; /// Sets the minimum amount of time in seconds that an agent's // must be able to stay on the calculated velocity while still avoiding collisions with obstacle's // if this value is set to high an agent will often fall back to using a very low velocity just to be safe virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0; /// The radius of this agent. /// Must be non-negative. virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0; + virtual real_t agent_get_radius(RID p_agent) const = 0; + virtual void agent_set_height(RID p_agent, real_t p_height) = 0; + virtual real_t agent_get_height(RID p_agent) const = 0; /// The maximum speed of this agent. /// Must be non-negative. virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0; + virtual real_t agent_get_max_speed(RID p_agent) const = 0; /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0; @@ -257,22 +270,31 @@ public: /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0; + virtual Vector3 agent_get_velocity(RID p_agent) const = 0; /// Position of the agent in world space. virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0; + virtual Vector3 agent_get_position(RID p_agent) const = 0; /// Returns true if the map got changed the previous frame. virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0; + virtual bool agent_has_avoidance_callback(RID p_agent) const = 0; virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0; + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0; + virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0; + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0; + virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0; + virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0; /// Creates the obstacle. virtual RID obstacle_create() = 0; + virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0; virtual RID obstacle_get_map(RID p_obstacle) const = 0; @@ -281,14 +303,22 @@ public: virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) = 0; virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const = 0; + virtual void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) = 0; virtual bool obstacle_get_use_3d_avoidance(RID p_obstacle) const = 0; + virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0; + virtual real_t obstacle_get_radius(RID p_obstacle) const = 0; virtual void obstacle_set_height(RID p_obstacle, real_t p_height) = 0; + virtual real_t obstacle_get_height(RID p_obstacle) const = 0; virtual void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) = 0; + virtual Vector3 obstacle_get_velocity(RID p_obstacle) const = 0; virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) = 0; + virtual Vector3 obstacle_get_position(RID p_obstacle) const = 0; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) = 0; + virtual Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const = 0; virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0; + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0; /// Destroy the `RID` virtual void free(RID p_object) = 0; diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 5258022328..dd0dfc7348 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -58,6 +58,7 @@ public: Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override { return Vector3(); } Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override { return Vector3(); } RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override { return RID(); } + Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); } @@ -80,6 +81,7 @@ public: void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {} uint32_t region_get_navigation_layers(RID p_region) const override { return 0; } void region_set_transform(RID p_region, Transform3D p_transform) override {} + Transform3D region_get_transform(RID p_region) const override { return Transform3D(); } void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) override {} #ifndef DISABLE_DEPRECATED void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override {} @@ -87,6 +89,7 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); } Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); } + Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} RID link_get_map(RID p_link) const override { return RID(); } @@ -116,20 +119,33 @@ public: void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) override {} bool agent_get_use_3d_avoidance(RID p_agent) const override { return false; } void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {} + real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; } void agent_set_max_neighbors(RID p_agent, int p_count) override {} + int agent_get_max_neighbors(RID p_agent) const override { return 0; } void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; } void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; } void agent_set_radius(RID p_agent, real_t p_radius) override {} + real_t agent_get_radius(RID p_agent) const override { return 0; } void agent_set_height(RID p_agent, real_t p_height) override {} + real_t agent_get_height(RID p_agent) const override { return 0; } void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {} + real_t agent_get_max_speed(RID p_agent) const override { return 0; } void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) override {} void agent_set_velocity(RID p_agent, Vector3 p_velocity) override {} + Vector3 agent_get_velocity(RID p_agent) const override { return Vector3(); } void agent_set_position(RID p_agent, Vector3 p_position) override {} + Vector3 agent_get_position(RID p_agent) const override { return Vector3(); } bool agent_is_map_changed(RID p_agent) const override { return false; } void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {} + bool agent_has_avoidance_callback(RID p_agent) const override { return false; } void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {} + uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; } void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {} + uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; } void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {} + real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; } RID obstacle_create() override { return RID(); } void obstacle_set_map(RID p_obstacle, RID p_map) override {} RID obstacle_get_map(RID p_obstacle) const override { return RID(); } @@ -140,11 +156,17 @@ public: void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) override {} bool obstacle_get_use_3d_avoidance(RID p_obstacle) const override { return false; } void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {} + real_t obstacle_get_radius(RID p_obstacle) const override { return 0; } void obstacle_set_height(RID p_obstacle, real_t p_height) override {} + real_t obstacle_get_height(RID p_obstacle) const override { return 0; } void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) override {} + Vector3 obstacle_get_velocity(RID p_obstacle) const override { return Vector3(); } void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {} + Vector3 obstacle_get_position(RID p_obstacle) const override { return Vector3(); } void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {} + Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const override { return Vector<Vector3>(); } void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} + uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; } void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} diff --git a/servers/physics_2d/godot_area_2d.h b/servers/physics_2d/godot_area_2d.h index 234e4eb9a9..d14ddb6a2e 100644 --- a/servers/physics_2d/godot_area_2d.h +++ b/servers/physics_2d/godot_area_2d.h @@ -167,7 +167,7 @@ void GodotArea2D::add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, void GodotArea2D::remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) { BodyKey bk(p_body, p_body_shape, p_area_shape); monitored_bodies[bk].dec(); - if (!monitor_query_list.in_list()) { + if (get_space() && !monitor_query_list.in_list()) { _queue_monitor_update(); } } @@ -183,7 +183,7 @@ void GodotArea2D::add_area_to_query(GodotArea2D *p_area, uint32_t p_area_shape, void GodotArea2D::remove_area_from_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) { BodyKey bk(p_area, p_area_shape, p_self_shape); monitored_areas[bk].dec(); - if (!monitor_query_list.in_list()) { + if (get_space() && !monitor_query_list.in_list()) { _queue_monitor_update(); } } diff --git a/servers/physics_2d/godot_area_pair_2d.cpp b/servers/physics_2d/godot_area_pair_2d.cpp index bd1540bac8..ca12e30c29 100644 --- a/servers/physics_2d/godot_area_pair_2d.cpp +++ b/servers/physics_2d/godot_area_pair_2d.cpp @@ -66,6 +66,7 @@ bool GodotAreaPair2D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; body->add_area(area); } @@ -74,6 +75,7 @@ bool GodotAreaPair2D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; body->remove_area(area); } @@ -103,7 +105,8 @@ GodotAreaPair2D::GodotAreaPair2D(GodotBody2D *p_body, int p_body_shape, GodotAre GodotAreaPair2D::~GodotAreaPair2D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; body->remove_area(area); } if (area->has_monitor_callback()) { diff --git a/servers/physics_2d/godot_area_pair_2d.h b/servers/physics_2d/godot_area_pair_2d.h index 938b9813b4..eb091288a9 100644 --- a/servers/physics_2d/godot_area_pair_2d.h +++ b/servers/physics_2d/godot_area_pair_2d.h @@ -43,6 +43,7 @@ class GodotAreaPair2D : public GodotConstraint2D { bool colliding = false; bool has_space_override = false; bool process_collision = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/physics_2d/godot_body_2d.cpp b/servers/physics_2d/godot_body_2d.cpp index 12c3e1e5b4..01996dc43c 100644 --- a/servers/physics_2d/godot_body_2d.cpp +++ b/servers/physics_2d/godot_body_2d.cpp @@ -407,7 +407,8 @@ void GodotBody2D::set_space(GodotSpace2D *p_space) { if (get_space()) { _mass_properties_changed(); - if (active) { + + if (active && !active_list.in_list()) { get_space()->body_add_to_active_list(&active_list); } } diff --git a/servers/physics_2d/godot_collision_object_2d.cpp b/servers/physics_2d/godot_collision_object_2d.cpp index 4fb53a2d89..9851cac140 100644 --- a/servers/physics_2d/godot_collision_object_2d.cpp +++ b/servers/physics_2d/godot_collision_object_2d.cpp @@ -212,20 +212,21 @@ void GodotCollisionObject2D::_update_shapes_with_motion(const Vector2 &p_motion) } void GodotCollisionObject2D::_set_space(GodotSpace2D *p_space) { - if (space) { - space->remove_object(this); + GodotSpace2D *old_space = space; + space = p_space; + + if (old_space) { + old_space->remove_object(this); for (int i = 0; i < shapes.size(); i++) { Shape &s = shapes.write[i]; if (s.bpid) { - space->get_broadphase()->remove(s.bpid); + old_space->get_broadphase()->remove(s.bpid); s.bpid = 0; } } } - space = p_space; - if (space) { space->add_object(this); _update_shapes(); diff --git a/servers/physics_2d/godot_shape_2d.cpp b/servers/physics_2d/godot_shape_2d.cpp index dadd8173c6..2b3f496fc0 100644 --- a/servers/physics_2d/godot_shape_2d.cpp +++ b/servers/physics_2d/godot_shape_2d.cpp @@ -373,8 +373,7 @@ void GodotCapsuleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_suppo if (h > 0 && Math::abs(n.x) > segment_is_valid_support_threshold) { // make it flat n.y = 0.0; - n.normalize(); - n *= radius; + n.x = SIGN(n.x) * radius; r_amount = 2; r_supports[0] = n; diff --git a/servers/physics_3d/godot_area_3d.h b/servers/physics_3d/godot_area_3d.h index f05d0f9019..c3c9e494a4 100644 --- a/servers/physics_3d/godot_area_3d.h +++ b/servers/physics_3d/godot_area_3d.h @@ -204,7 +204,7 @@ void GodotArea3D::add_body_to_query(GodotBody3D *p_body, uint32_t p_body_shape, void GodotArea3D::remove_body_from_query(GodotBody3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) { BodyKey bk(p_body, p_body_shape, p_area_shape); monitored_bodies[bk].dec(); - if (!monitor_query_list.in_list()) { + if (get_space() && !monitor_query_list.in_list()) { _queue_monitor_update(); } } @@ -220,7 +220,7 @@ void GodotArea3D::add_area_to_query(GodotArea3D *p_area, uint32_t p_area_shape, void GodotArea3D::remove_area_from_query(GodotArea3D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) { BodyKey bk(p_area, p_area_shape, p_self_shape); monitored_areas[bk].dec(); - if (!monitor_query_list.in_list()) { + if (get_space() && !monitor_query_list.in_list()) { _queue_monitor_update(); } } diff --git a/servers/physics_3d/godot_area_pair_3d.cpp b/servers/physics_3d/godot_area_pair_3d.cpp index 5561210f2d..aaa96f5a28 100644 --- a/servers/physics_3d/godot_area_pair_3d.cpp +++ b/servers/physics_3d/godot_area_pair_3d.cpp @@ -67,6 +67,7 @@ bool GodotAreaPair3D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; body->add_area(area); } @@ -75,6 +76,7 @@ bool GodotAreaPair3D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; body->remove_area(area); } @@ -104,7 +106,8 @@ GodotAreaPair3D::GodotAreaPair3D(GodotBody3D *p_body, int p_body_shape, GodotAre GodotAreaPair3D::~GodotAreaPair3D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; body->remove_area(area); } if (area->has_monitor_callback()) { @@ -242,6 +245,7 @@ bool GodotAreaSoftBodyPair3D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; soft_body->add_area(area); } @@ -250,6 +254,7 @@ bool GodotAreaSoftBodyPair3D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; soft_body->remove_area(area); } @@ -276,7 +281,8 @@ GodotAreaSoftBodyPair3D::GodotAreaSoftBodyPair3D(GodotSoftBody3D *p_soft_body, i GodotAreaSoftBodyPair3D::~GodotAreaSoftBodyPair3D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; soft_body->remove_area(area); } if (area->has_monitor_callback()) { diff --git a/servers/physics_3d/godot_area_pair_3d.h b/servers/physics_3d/godot_area_pair_3d.h index c11fa07827..a2c5df0f7a 100644 --- a/servers/physics_3d/godot_area_pair_3d.h +++ b/servers/physics_3d/godot_area_pair_3d.h @@ -44,6 +44,7 @@ class GodotAreaPair3D : public GodotConstraint3D { bool colliding = false; bool process_collision = false; bool has_space_override = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; @@ -83,6 +84,7 @@ class GodotAreaSoftBodyPair3D : public GodotConstraint3D { bool colliding = false; bool process_collision = false; bool has_space_override = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/physics_3d/godot_body_3d.cpp b/servers/physics_3d/godot_body_3d.cpp index e102d0f3c9..407957b904 100644 --- a/servers/physics_3d/godot_body_3d.cpp +++ b/servers/physics_3d/godot_body_3d.cpp @@ -454,7 +454,8 @@ void GodotBody3D::set_space(GodotSpace3D *p_space) { if (get_space()) { _mass_properties_changed(); - if (active) { + + if (active && !active_list.in_list()) { get_space()->body_add_to_active_list(&active_list); } } diff --git a/servers/physics_3d/godot_collision_object_3d.cpp b/servers/physics_3d/godot_collision_object_3d.cpp index 0d7fcb67f6..283614a43d 100644 --- a/servers/physics_3d/godot_collision_object_3d.cpp +++ b/servers/physics_3d/godot_collision_object_3d.cpp @@ -210,20 +210,21 @@ void GodotCollisionObject3D::_update_shapes_with_motion(const Vector3 &p_motion) } void GodotCollisionObject3D::_set_space(GodotSpace3D *p_space) { - if (space) { - space->remove_object(this); + GodotSpace3D *old_space = space; + space = p_space; + + if (old_space) { + old_space->remove_object(this); for (int i = 0; i < shapes.size(); i++) { Shape &s = shapes.write[i]; if (s.bpid) { - space->get_broadphase()->remove(s.bpid); + old_space->get_broadphase()->remove(s.bpid); s.bpid = 0; } } } - space = p_space; - if (space) { space->add_object(this); _update_shapes(); diff --git a/servers/physics_server_2d_wrap_mt.cpp b/servers/physics_server_2d_wrap_mt.cpp index f4682b7c14..a23bb5e701 100644 --- a/servers/physics_server_2d_wrap_mt.cpp +++ b/servers/physics_server_2d_wrap_mt.cpp @@ -120,8 +120,6 @@ PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool physics_server_2d = p_contained; create_thread = p_create_thread; - pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc"); - if (!p_create_thread) { server_thread = Thread::get_caller_id(); } else { diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h index 0ae004da19..3bebe5df85 100644 --- a/servers/physics_server_2d_wrap_mt.h +++ b/servers/physics_server_2d_wrap_mt.h @@ -66,7 +66,6 @@ class PhysicsServer2DWrapMT : public PhysicsServer2D { bool first_frame = true; Mutex alloc_mutex; - int pool_max_size = 0; public: #define ServerName PhysicsServer2D diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index d523f4b1ec..addd1a5663 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -343,7 +343,7 @@ void PhysicsShapeQueryParameters3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_exclude", "get_exclude"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "set_motion", "get_motion"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion"), "set_motion", "get_motion"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::RID, "shape_rid"), "set_shape_rid", "get_shape_rid"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform"), "set_transform", "get_transform"); diff --git a/servers/physics_server_3d_wrap_mt.cpp b/servers/physics_server_3d_wrap_mt.cpp index 59db4617d0..feb17cad84 100644 --- a/servers/physics_server_3d_wrap_mt.cpp +++ b/servers/physics_server_3d_wrap_mt.cpp @@ -120,8 +120,6 @@ PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool physics_server_3d = p_contained; create_thread = p_create_thread; - pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc"); - if (!p_create_thread) { server_thread = Thread::get_caller_id(); } else { diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h index 99179d8248..fc8930977d 100644 --- a/servers/physics_server_3d_wrap_mt.h +++ b/servers/physics_server_3d_wrap_mt.h @@ -65,7 +65,6 @@ class PhysicsServer3DWrapMT : public PhysicsServer3D { bool first_frame = true; Mutex alloc_mutex; - int pool_max_size = 0; public: #define ServerName PhysicsServer3D diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 50e14a1f37..2beab44f91 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -117,6 +117,8 @@ static MovieWriterMJPEG *writer_mjpeg = nullptr; static MovieWriterPNGWAV *writer_pngwav = nullptr; void register_server_types() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Register Extensions"); + shader_types = memnew(ShaderTypes); GDREGISTER_CLASS(TextServerManager); @@ -293,16 +295,24 @@ void register_server_types() { writer_pngwav = memnew(MovieWriterPNGWAV); MovieWriter::add_writer(writer_pngwav); + + OS::get_singleton()->benchmark_end_measure("Servers", "Register Extensions"); } void unregister_server_types() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Unregister Extensions"); + ServersDebugger::deinitialize(); memdelete(shader_types); memdelete(writer_mjpeg); memdelete(writer_pngwav); + + OS::get_singleton()->benchmark_end_measure("Servers", "Unregister Extensions"); } void register_server_singletons() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Register Singletons"); + Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton(), "DisplayServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer")); @@ -312,4 +322,6 @@ void register_server_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D")); Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer")); + + OS::get_singleton()->benchmark_end_measure("Servers", "Register Singletons"); } diff --git a/servers/rendering/dummy/environment/gi.h b/servers/rendering/dummy/environment/gi.h index a26938c740..5d0e84ae43 100644 --- a/servers/rendering/dummy/environment/gi.h +++ b/servers/rendering/dummy/environment/gi.h @@ -78,6 +78,8 @@ public: virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override { return false; } virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override { return 0; } + + virtual void sdfgi_reset() override {} }; } // namespace RendererDummy diff --git a/servers/rendering/dummy/rasterizer_canvas_dummy.h b/servers/rendering/dummy/rasterizer_canvas_dummy.h index 862b941a73..a450b2a21d 100644 --- a/servers/rendering/dummy/rasterizer_canvas_dummy.h +++ b/servers/rendering/dummy/rasterizer_canvas_dummy.h @@ -38,7 +38,7 @@ public: PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override { return 0; } void free_polygon(PolygonID p_polygon) override {} - void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override {} + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override {} RID light_create() override { return RID(); } void light_set_texture(RID p_rid, RID p_texture) override {} diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 2a72547810..b3a5323e66 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -177,6 +177,9 @@ public: virtual void render_target_set_as_unused(RID p_render_target) override {} virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override {} virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override { return RS::VIEWPORT_MSAA_DISABLED; } + virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override {} + virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override { return false; } + virtual void render_target_do_msaa_resolve(RID p_render_target) override {} virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {} virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; } diff --git a/servers/rendering/environment/renderer_gi.h b/servers/rendering/environment/renderer_gi.h index 6eff319882..94e2c1afda 100644 --- a/servers/rendering/environment/renderer_gi.h +++ b/servers/rendering/environment/renderer_gi.h @@ -79,6 +79,8 @@ public: virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const = 0; virtual uint32_t voxel_gi_get_version(RID p_probe) const = 0; + + virtual void sdfgi_reset() = 0; }; #endif // RENDERER_GI_H diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index c5206017f7..d21a985a60 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -37,17 +37,17 @@ #include "rendering_server_globals.h" #include "servers/rendering/storage/texture_storage.h" -void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t canvas_cull_mask) { +void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) { RENDER_TIMESTAMP("Cull CanvasItem Tree"); memset(z_list, 0, z_range * sizeof(RendererCanvasRender::Item *)); memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *)); for (int i = 0; i < p_child_item_count; i++) { - _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, canvas_cull_mask); + _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask); } if (p_canvas_item) { - _cull_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, canvas_cull_mask); + _cull_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask); } RendererCanvasRender::Item *list = nullptr; @@ -69,7 +69,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas RENDER_TIMESTAMP("Render CanvasItems"); bool sdf_flag; - RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, sdf_flag); + RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, sdf_flag, r_render_info); if (sdf_flag) { sdf_used = true; } @@ -114,29 +114,29 @@ void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner, RID_Owner<Renderer } while (ysort_owner && ysort_owner->sort_y); } -void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *ci, RendererCanvasCull::Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &xform, const Rect2 &p_clip_rect, Rect2 global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *canvas_group_from, const Transform2D &p_xform) { +void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *ci, RendererCanvasCull::Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &p_transform, const Rect2 &p_clip_rect, Rect2 p_global_rect, const Color &p_modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *r_canvas_group_from) { if (ci->copy_back_buffer) { - ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).intersection(p_clip_rect); + ci->copy_back_buffer->screen_rect = p_transform.xform(ci->copy_back_buffer->rect).intersection(p_clip_rect); } if (p_use_canvas_group) { int zidx = p_z - RS::CANVAS_ITEM_Z_MIN; - if (canvas_group_from == nullptr) { + if (r_canvas_group_from == nullptr) { // no list before processing this item, means must put stuff in group from the beginning of list. - canvas_group_from = r_z_list[zidx]; + r_canvas_group_from = r_z_list[zidx]; } else { // there was a list before processing, so begin group from this one. - canvas_group_from = canvas_group_from->next; + r_canvas_group_from = r_canvas_group_from->next; } - if (canvas_group_from) { + if (r_canvas_group_from) { // Has a place to begin the group from! //compute a global rect (in global coords) for children in the same z layer Rect2 rect_accum; - RendererCanvasRender::Item *c = canvas_group_from; + RendererCanvasRender::Item *c = r_canvas_group_from; while (c) { - if (c == canvas_group_from) { + if (c == r_canvas_group_from) { rect_accum = c->global_rect_cache; } else { rect_accum = rect_accum.merge(c->global_rect_cache); @@ -161,28 +161,28 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * RendererCanvasRender::Item::CommandRect *crect = ci->alloc_command<RendererCanvasRender::Item::CommandRect>(); crect->flags = RendererCanvasRender::CANVAS_RECT_IS_GROUP; // so we can recognize it later - crect->rect = xform.affine_inverse().xform(rect_accum); + crect->rect = p_transform.affine_inverse().xform(rect_accum); crect->modulate = Color(1, 1, 1, 1); //the global rect is used to do the copying, so update it - global_rect = rect_accum.grow(ci->canvas_group->clear_margin); //grow again by clear margin - global_rect.position += p_clip_rect.position; + p_global_rect = rect_accum.grow(ci->canvas_group->clear_margin); //grow again by clear margin + p_global_rect.position += p_clip_rect.position; } else { - global_rect.position -= p_clip_rect.position; + p_global_rect.position -= p_clip_rect.position; - global_rect = global_rect.merge(rect_accum); //must use both rects for this - global_rect = global_rect.grow(ci->canvas_group->clear_margin); //grow by clear margin + p_global_rect = p_global_rect.merge(rect_accum); //must use both rects for this + p_global_rect = p_global_rect.grow(ci->canvas_group->clear_margin); //grow by clear margin - global_rect.position += p_clip_rect.position; + p_global_rect.position += p_clip_rect.position; } // Very important that this is cleared after used in RendererCanvasRender to avoid // potential crashes. - canvas_group_from->canvas_group_owner = ci; + r_canvas_group_from->canvas_group_owner = ci; } } - if (((ci->commands != nullptr || ci->visibility_notifier) && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { + if (((ci->commands != nullptr || ci->visibility_notifier) && p_clip_rect.intersects(p_global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { //something to draw? if (ci->update_when_visible) { @@ -190,9 +190,9 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * } if (ci->commands != nullptr || ci->copy_back_buffer) { - ci->final_transform = xform; - ci->final_modulate = modulate * ci->self_modulate; - ci->global_rect_cache = global_rect; + ci->final_transform = p_transform; + ci->final_modulate = p_modulate * ci->self_modulate; + ci->global_rect_cache = p_global_rect; ci->global_rect_cache.position -= p_clip_rect.position; ci->light_masked = false; @@ -223,14 +223,14 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * } } -void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool allow_y_sort, uint32_t canvas_cull_mask) { +void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask) { Item *ci = p_canvas_item; if (!ci->visible) { return; } - if (!(ci->visibility_layer & canvas_cull_mask)) { + if (!(ci->visibility_layer & p_canvas_cull_mask)) { return; } @@ -298,7 +298,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 } if (ci->sort_y) { - if (allow_y_sort) { + if (p_allow_y_sort) { if (ci->ysort_children_count == -1) { ci->ysort_children_count = 0; _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), nullptr, ci->ysort_children_count, p_z); @@ -318,7 +318,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 sorter.sort(child_items, child_item_count); for (i = 0; i < child_item_count; i++) { - _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, canvas_cull_mask); + _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask); } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; @@ -328,7 +328,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 canvas_group_from = r_z_last_list[zidx]; } - _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from, xform); + _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from); } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; @@ -342,19 +342,19 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 if (!child_items[i]->behind && !use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, canvas_cull_mask); + _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask); } - _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from, xform); + _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from); for (int i = 0; i < child_item_count; i++) { if (child_items[i]->behind || use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, canvas_cull_mask); + _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask); } } } -void RendererCanvasCull::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel, uint32_t canvas_cull_mask) { +void RendererCanvasCull::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel, uint32_t canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) { RENDER_TIMESTAMP("> Render Canvas"); sdf_used = false; @@ -377,26 +377,26 @@ void RendererCanvasCull::render_canvas(RID p_render_target, Canvas *p_canvas, co } if (!has_mirror) { - _render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask); + _render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info); } else { //used for parallaxlayer mirroring for (int i = 0; i < l; i++) { const Canvas::ChildItem &ci2 = p_canvas->child_items[i]; - _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask); + _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info); //mirroring (useful for scrolling backgrounds) if (ci2.mirror.x != 0) { Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0)); - _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask); + _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info); } if (ci2.mirror.y != 0) { Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y)); - _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask); + _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info); } if (ci2.mirror.y != 0 && ci2.mirror.x != 0) { Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror); - _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask); + _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info); } } } @@ -1396,14 +1396,12 @@ void RendererCanvasCull::canvas_item_add_triangle_array(RID p_item, const Vector ERR_FAIL_COND(!p_bones.is_empty() && p_bones.size() != vertex_count * 4); ERR_FAIL_COND(!p_weights.is_empty() && p_weights.size() != vertex_count * 4); - Vector<int> indices = p_indices; - Item::CommandPolygon *polygon = canvas_item->alloc_command<Item::CommandPolygon>(); ERR_FAIL_NULL(polygon); polygon->texture = p_texture; - polygon->polygon.create(indices, p_points, p_colors, p_uvs, p_bones, p_weights); + polygon->polygon.create(p_indices, p_points, p_colors, p_uvs, p_bones, p_weights); polygon->primitive = RS::PRIMITIVE_TRIANGLES; } @@ -2041,6 +2039,12 @@ void RendererCanvasCull::update_visibility_notifiers() { } } +Rect2 RendererCanvasCull::_debug_canvas_item_get_rect(RID p_item) { + Item *canvas_item = canvas_item_owner.get_or_null(p_item); + ERR_FAIL_NULL_V(canvas_item, Rect2()); + return canvas_item->get_rect(); +} + bool RendererCanvasCull::free(RID p_rid) { if (canvas_owner.owns(p_rid)) { Canvas *canvas = canvas_owner.get_or_null(p_rid); diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 0f51abbb26..ae0f29f10e 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -181,11 +181,11 @@ public: PagedAllocator<Item::VisibilityNotifierData> visibility_notifier_allocator; SelfList<Item::VisibilityNotifierData>::List visibility_notifier_list; - _FORCE_INLINE_ void _attach_canvas_item_for_draw(Item *ci, Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &xform, const Rect2 &p_clip_rect, Rect2 global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *canvas_group_from, const Transform2D &p_xform); + _FORCE_INLINE_ void _attach_canvas_item_for_draw(Item *ci, Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &p_transform, const Rect2 &p_clip_rect, Rect2 p_global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *r_canvas_group_from); private: - void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t canvas_cull_mask); - void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool allow_y_sort, uint32_t canvas_cull_mask); + void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask); static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1; @@ -193,7 +193,7 @@ private: RendererCanvasRender::Item **z_last_list; public: - void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel, uint32_t canvas_cull_mask); + void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr); bool was_sdf_used(); @@ -326,6 +326,8 @@ public: void update_visibility_notifiers(); + Rect2 _debug_canvas_item_get_rect(RID p_item); + bool free(RID p_rid); RendererCanvasCull(); ~RendererCanvasCull(); diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index ef4de9ce54..3b78df5fab 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -31,6 +31,7 @@ #ifndef RENDERER_CANVAS_RENDER_H #define RENDERER_CANVAS_RENDER_H +#include "servers/rendering/rendering_method.h" #include "servers/rendering_server.h" class RendererCanvasRender { @@ -479,7 +480,7 @@ public: } }; - virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) = 0; + virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) = 0; struct LightOccluderInstance { bool enabled; diff --git a/servers/rendering/renderer_rd/api_context_rd.cpp b/servers/rendering/renderer_rd/api_context_rd.cpp new file mode 100644 index 0000000000..b5b3cdd88c --- /dev/null +++ b/servers/rendering/renderer_rd/api_context_rd.cpp @@ -0,0 +1,33 @@ +/**************************************************************************/ +/* api_context_rd.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "api_context_rd.h" + +ApiContextRD::~ApiContextRD() {} diff --git a/servers/rendering/renderer_rd/api_context_rd.h b/servers/rendering/renderer_rd/api_context_rd.h new file mode 100644 index 0000000000..22167be3c1 --- /dev/null +++ b/servers/rendering/renderer_rd/api_context_rd.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* api_context_rd.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef API_CONTEXT_RD_H +#define API_CONTEXT_RD_H + +#include "servers/rendering/rendering_device.h" +#include "servers/rendering/rendering_device_driver.h" + +class ApiContextRD { +public: + virtual const char *get_api_name() const = 0; + virtual RenderingDevice::Capabilities get_device_capabilities() const = 0; + virtual const RDD::MultiviewCapabilities &get_multiview_capabilities() const = 0; + + virtual int get_swapchain_image_count() const = 0; + + virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) = 0; + virtual void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) = 0; + virtual int window_get_width(DisplayServer::WindowID p_window = 0) = 0; + virtual int window_get_height(DisplayServer::WindowID p_window = 0) = 0; + virtual bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0) = 0; + virtual void window_destroy(DisplayServer::WindowID p_window_id) = 0; + virtual RDD::RenderPassID window_get_render_pass(DisplayServer::WindowID p_window = 0) = 0; + virtual RDD::FramebufferID window_get_framebuffer(DisplayServer::WindowID p_window = 0) = 0; + + virtual RID local_device_create() = 0; + virtual void local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) = 0; + virtual void local_device_sync(RID p_local_device) = 0; + virtual void local_device_free(RID p_local_device) = 0; + + virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) = 0; + virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) = 0; + virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false) = 0; + virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) = 0; + virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) = 0; + virtual Error swap_buffers() = 0; + virtual Error initialize() = 0; + + virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) = 0; + virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) = 0; + virtual void command_end_label(RDD::CommandBufferID p_command_buffer) = 0; + + virtual String get_device_vendor_name() const = 0; + virtual String get_device_name() const = 0; + virtual RDD::DeviceType get_device_type() const = 0; + virtual String get_device_api_version() const = 0; + virtual String get_device_pipeline_cache_uuid() const = 0; + + virtual void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) = 0; + virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const = 0; + + virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) = 0; + + virtual ~ApiContextRD(); +}; + +#endif // API_CONTEXT_RD_H diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 7554e478bb..0d1721039c 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -266,8 +266,8 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID screen_size = p_screen_size; - cluster_screen_size.width = (p_screen_size.width - 1) / cluster_size + 1; - cluster_screen_size.height = (p_screen_size.height - 1) / cluster_size + 1; + cluster_screen_size.width = Math::division_round_up((uint32_t)p_screen_size.width, cluster_size); + cluster_screen_size.height = Math::division_round_up((uint32_t)p_screen_size.height, cluster_size); max_elements_by_type = p_max_elements; if (max_elements_by_type % 32) { // Needs to be aligned to 32. @@ -503,7 +503,8 @@ void ClusterBuilderRD::bake_cluster() { push_constant.max_render_element_count_div_32 = render_element_max / 32; push_constant.cluster_screen_size[0] = cluster_screen_size.x; push_constant.cluster_screen_size[1] = cluster_screen_size.y; - push_constant.render_element_count_div_32 = render_element_count > 0 ? (render_element_count - 1) / 32 + 1 : 0; + + push_constant.render_element_count_div_32 = Math::division_round_up(render_element_count, 32U); push_constant.max_cluster_element_count_div_32 = max_elements_by_type / 32; push_constant.pad1 = 0; push_constant.pad2 = 0; diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index aa1a87cdd7..bb584beb52 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -71,7 +71,9 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) { for (int i = 0; i < BLUR_MODE_MAX; i++) { blur_raster.pipelines[i].clear(); } + } + { Vector<String> copy_modes; copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n"); @@ -104,9 +106,9 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) { copy_modes.push_back("\n"); // COPY_TO_FB_COPY copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n"); // COPY_TO_FB_COPY_PANORAMA_TO_DP copy_modes.push_back("\n#define MODE_TWO_SOURCES\n"); // COPY_TO_FB_COPY2 + copy_modes.push_back("\n#define MODE_SET_COLOR\n"); // COPY_TO_FB_SET_COLOR copy_modes.push_back("\n#define MULTIVIEW\n"); // COPY_TO_FB_MULTIVIEW copy_modes.push_back("\n#define MULTIVIEW\n#define MODE_TWO_SOURCES\n"); // COPY_TO_FB_MULTIVIEW_WITH_DEPTH - copy_modes.push_back("\n#define MODE_SET_COLOR\n"); // COPY_TO_FB_SET_COLOR copy_to_fb.shader.initialize(copy_modes); @@ -310,12 +312,12 @@ CopyEffects::~CopyEffects() { filter.raster_shader.version_free(filter.shader_version); roughness.raster_shader.version_free(roughness.shader_version); } else { - copy.shader.version_free(copy.shader_version); cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version); filter.compute_shader.version_free(filter.shader_version); roughness.compute_shader.version_free(roughness.shader_version); } + copy.shader.version_free(copy.shader_version); specular_merge.shader.version_free(specular_merge.shader_version); RD::get_singleton()->free(filter.coefficient_buffer); @@ -335,8 +337,6 @@ CopyEffects::~CopyEffects() { } void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_to_rect shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -386,8 +386,6 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons } void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_cubemap_to_panorama shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -423,8 +421,6 @@ void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panoram } void CopyEffects::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -462,8 +458,6 @@ void CopyEffects::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture } void CopyEffects::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect_and_linearize shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -539,7 +533,7 @@ void CopyEffects::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuff RD::get_singleton()->draw_list_draw(draw_list, true); } -void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one, bool p_linear) { +void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one, bool p_linear, bool p_normal) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -570,6 +564,10 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe copy_to_fb.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0; } + if (p_normal) { + copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_NORMAL; + } + // setup our uniforms RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -599,6 +597,40 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe RD::get_singleton()->draw_list_end(); } +void CopyEffects::copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + memset(©_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); + copy_to_fb.push_constant.luminance_multiplier = 1.0; + + if (p_linear) { + // Used for copying to a linear buffer. In the mobile renderer we divide the contents of the linear buffer + // to allow for a wider effective range. + copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_LINEAR; + copy_to_fb.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0; + } + + // setup our uniforms + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture })); + + // Multiview not supported here! + CopyToFBMode mode = COPY_TO_FB_COPY; + + RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode); + ERR_FAIL_COND(shader.is_null()); + + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_fb_format)); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, material_storage->get_quad_index_array()); + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, ©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, true); +} + void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the copy with the clustered renderer."); @@ -1018,8 +1050,8 @@ void CopyEffects::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, c RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_cubemap), 1); - int x_groups = (p_size.x - 1) / 8 + 1; - int y_groups = (p_size.y - 1) / 8 + 1; + int x_groups = Math::division_round_up(p_size.x, 8); + int y_groups = Math::division_round_up(p_size.y, 8); RD::get_singleton()->compute_list_set_push_constant(compute_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant)); @@ -1172,8 +1204,8 @@ void CopyEffects::cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); - int x_groups = (p_size - 1) / 8 + 1; - int y_groups = (p_size - 1) / 8 + 1; + int x_groups = Math::division_round_up(p_size, 8); + int y_groups = x_groups; RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, p_face_id > 9 ? 6 : 1); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index 470ac1acee..60272a2eab 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -172,11 +172,13 @@ private: COPY_TO_FB_COPY, COPY_TO_FB_COPY_PANORAMA_TO_DP, COPY_TO_FB_COPY2, + COPY_TO_FB_SET_COLOR, + // These variants are disabled unless XR shaders are enabled. + // They should be listed last. COPY_TO_FB_MULTIVIEW, COPY_TO_FB_MULTIVIEW_WITH_DEPTH, - COPY_TO_FB_SET_COLOR, COPY_TO_FB_MAX, }; @@ -188,6 +190,7 @@ private: COPY_TO_FB_FLAG_SRGB = (1 << 4), COPY_TO_FB_FLAG_ALPHA_TO_ONE = (1 << 5), COPY_TO_FB_FLAG_LINEAR = (1 << 6), + COPY_TO_FB_FLAG_NORMAL = (1 << 7), }; struct CopyToFbPushConstant { @@ -326,8 +329,9 @@ public: void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array); void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false); void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far); - void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false); + void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false, bool p_normal = false); void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false); + void copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear = false); void copy_raster(RID p_source_texture, RID p_dest_framebuffer); void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst = false); diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index abcd9bbfae..3033d42375 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -108,7 +108,7 @@ void DebugEffects::_create_frustum_arrays() { // Create our index_array PackedByteArray data; - data.resize(6 * 2 * 3 * 4); + data.resize(6 * 2 * 3 * 2); { uint8_t *w = data.ptrw(); uint16_t *p16 = (uint16_t *)w; @@ -142,7 +142,7 @@ void DebugEffects::_create_frustum_arrays() { // Create our lines_array PackedByteArray data; - data.resize(12 * 2 * 4); + data.resize(12 * 2 * 2); { uint8_t *w = data.ptrw(); uint16_t *p16 = (uint16_t *)w; diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index d123f24865..628edc0127 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -950,7 +950,7 @@ void SSEffects::screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_rende RD::get_singleton()->draw_command_end_label(); // SSIL - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. int zero[1] = { 0 }; RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier @@ -1332,7 +1332,7 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe RD::get_singleton()->draw_command_end_label(); // Interleave } RD::get_singleton()->draw_command_end_label(); //SSAO - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //wait for upcoming transfer + RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. int zero[1] = { 0 }; RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 7d4eb0e038..2befb194f7 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -1069,8 +1069,8 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P uint32_t cluster_size = p_settings.cluster_builder->get_cluster_size(); params.cluster_shift = get_shift_from_power_of_2(cluster_size); - uint32_t cluster_screen_width = (p_settings.rb_size.x - 1) / cluster_size + 1; - uint32_t cluster_screen_height = (p_settings.rb_size.y - 1) / cluster_size + 1; + uint32_t cluster_screen_width = Math::division_round_up((uint32_t)p_settings.rb_size.x, cluster_size); + uint32_t cluster_screen_height = Math::division_round_up((uint32_t)p_settings.rb_size.y, cluster_size); params.max_cluster_element_count_div_32 = p_settings.max_cluster_elements / 32; params.cluster_type_size = cluster_screen_width * cluster_screen_height * (params.max_cluster_element_count_div_32 + 32); params.cluster_width = cluster_screen_width; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 0667ae87e5..d968736037 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -392,6 +392,10 @@ Dependency *GI::voxel_gi_get_dependency(RID p_voxel_gi) const { return &voxel_gi->dependency; } +void GI::sdfgi_reset() { + sdfgi_current_version++; +} + //////////////////////////////////////////////////////////////////////////////// // SDFGI @@ -416,6 +420,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re y_scale_mode = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_y_scale(p_env); static const float y_scale[3] = { 2.0, 1.5, 1.0 }; y_mult = y_scale[y_scale_mode]; + version = gi->sdfgi_current_version; cascades.resize(num_cascades); probe_axis_count = SDFGI::PROBE_DIVISOR + 1; solid_cell_ratio = gi->sdfgi_solid_cell_ratio; @@ -3138,7 +3143,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->voxel_gi_lighting_shader_version_pipelines[VOXEL_GI_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, dynamic_maps[0].uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIDynamicPushConstant)); - RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1); + RD::get_singleton()->compute_list_dispatch(compute_list, Math::division_round_up(rect.size.x, 8), Math::division_round_up(rect.size.y, 8), 1); //print_line("rect: " + itos(i) + ": " + rect); for (int k = 1; k < dynamic_maps.size(); k++) { @@ -3200,7 +3205,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID } RD::get_singleton()->compute_list_bind_uniform_set(compute_list, dynamic_maps[k].uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIDynamicPushConstant)); - RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1); + RD::get_singleton()->compute_list_dispatch(compute_list, Math::division_round_up(rect.size.x, 8), Math::division_round_up(rect.size.y, 8), 1); } RD::get_singleton()->compute_list_end(); diff --git a/servers/rendering/renderer_rd/environment/gi.h b/servers/rendering/renderer_rd/environment/gi.h index 9a45919a2f..c46d4cbd25 100644 --- a/servers/rendering/renderer_rd/environment/gi.h +++ b/servers/rendering/renderer_rd/environment/gi.h @@ -667,6 +667,7 @@ public: float y_mult = 1.0; + uint32_t version = 0; uint32_t render_pass = 0; int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically @@ -701,11 +702,14 @@ public: Vector3 sdfgi_debug_probe_dir; bool sdfgi_debug_probe_enabled = false; Vector3i sdfgi_debug_probe_index; + uint32_t sdfgi_current_version = 0; /* SDFGI UPDATE */ int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; } + virtual void sdfgi_reset() override; + struct SDFGIData { float grid_size[3]; uint32_t max_cascades; 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 efec3b5072..76e814e1ee 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -626,8 +626,8 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat scene_state.ubo.cluster_shift = get_shift_from_power_of_2(p_render_data->cluster_size); scene_state.ubo.max_cluster_element_count_div_32 = p_render_data->cluster_max_elements / 32; { - uint32_t cluster_screen_width = (p_screen_size.width - 1) / p_render_data->cluster_size + 1; - uint32_t cluster_screen_height = (p_screen_size.height - 1) / p_render_data->cluster_size + 1; + uint32_t cluster_screen_width = Math::division_round_up((uint32_t)p_screen_size.width, p_render_data->cluster_size); + uint32_t cluster_screen_height = Math::division_round_up((uint32_t)p_screen_size.height, p_render_data->cluster_size); scene_state.ubo.cluster_type_size = cluster_screen_width * cluster_screen_height * (scene_state.ubo.max_cluster_element_count_div_32 + 32); scene_state.ubo.cluster_width = cluster_screen_width; } @@ -961,13 +961,17 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con inst->gi_offset_cache = 0xFFFFFFFF; } } - - if (p_pass_mode == PASS_MODE_COLOR && p_using_motion_pass) { + if (p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI || p_pass_mode == PASS_MODE_COLOR) { bool transform_changed = inst->prev_transform_change_frame == frame; bool has_mesh_instance = inst->mesh_instance.is_valid(); bool uses_particles = inst->base_flags & INSTANCE_DATA_FLAG_PARTICLES; bool is_multimesh_with_motion = !uses_particles && (inst->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) && mesh_storage->_multimesh_uses_motion_vectors_offsets(inst->data->base); - uses_motion = transform_changed || has_mesh_instance || uses_particles || is_multimesh_with_motion; + bool is_dynamic = transform_changed || has_mesh_instance || uses_particles || is_multimesh_with_motion; + if (p_pass_mode == PASS_MODE_COLOR && p_using_motion_pass) { + uses_motion = is_dynamic; + } else if (is_dynamic) { + flags |= INSTANCE_DATA_FLAGS_DYNAMIC; + } } } inst->flags_cache = flags; @@ -1390,6 +1394,11 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo sdfgi->store_probes(); } + Size2i viewport_size = Size2i(1, 1); + if (rb.is_valid()) { + viewport_size = rb->get_internal_size(); + } + p_render_data->cube_shadows.clear(); p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); @@ -1412,7 +1421,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo //cube shadows are rendered in their own way for (const int &index : p_render_data->cube_shadows) { - _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info); + _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size); } if (p_render_data->directional_shadows.size()) { @@ -1442,11 +1451,11 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo //render directional shadows for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size); } //render positional shadows for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size); } _render_shadow_process(); @@ -1667,6 +1676,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID color_framebuffer; RID color_only_framebuffer; RID depth_framebuffer; + RendererRD::MaterialStorage::Samplers samplers; PassMode depth_pass_mode = PASS_MODE_DEPTH; uint32_t color_pass_flags = 0; @@ -1693,6 +1703,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } reverse_cull = true; // for some reason our views are inverted + samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(); } else { screen_size = rb->get_internal_size(); @@ -1724,6 +1735,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); color_only_framebuffer = rb_data->get_color_only_fb(); + samplers = rb->get_samplers(); } p_render_data->scene_data->emissive_exposure_normalization = -1.0; @@ -1734,7 +1746,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _setup_voxelgis(*p_render_data->voxel_gi_instances); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); - _update_render_base_uniform_set(rb->get_samplers()); // May have changed due to the above (light buffer enlarged, as an example). + // May have changed due to the above (light buffer enlarged, as an example). + _update_render_base_uniform_set(); _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi, using_motion_pass); render_list[RENDER_LIST_OPAQUE].sort_by_key(); @@ -1770,11 +1783,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS); - depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI); - depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + depth_pass_clear.push_back(Color(0, 0, 0, 0)); depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; default: { @@ -1918,7 +1931,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Render Depth Pre-Pass"); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID(), samplers); bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); @@ -1965,13 +1978,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co p_render_data->scene_data->opaque_prepass_threshold = 0.0f; // Shadow pass can change the base uniform set samplers. - _update_render_base_uniform_set(rb->get_samplers()); + _update_render_base_uniform_set(); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, true, using_motion_pass); RENDER_TIMESTAMP("Render Opaque Pass"); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, samplers, true); bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss; bool can_continue_depth = !(scene_state.used_depth_texture || scene_state.used_normal_texture) && !using_ssr && !using_sss; @@ -2018,11 +2031,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("Render Motion Pass"); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, true); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true); RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_CONTINUE, final_color_action, RD::INITIAL_ACTION_CONTINUE, final_depth_action); + RD::get_singleton()->draw_command_end_label(); + if (will_continue_color) { // Close the motion vectors framebuffer as it'll no longer be used. RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE); @@ -2037,8 +2052,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } } - RD::get_singleton()->draw_command_end_label(); - if (debug_voxelgis) { //debug voxelgis bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only); @@ -2137,6 +2150,12 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_list_end(); } + if (rb_data.is_valid() && using_fsr2) { + // Make sure the upscaled texture is initialized, but not necessarily filled, before running screen copies + // so it properly detect if a dedicated copy texture should be used. + rb->ensure_upscaled(); + } + if (scene_state.used_screen_texture) { RENDER_TIMESTAMP("Copy Screen Texture"); @@ -2155,7 +2174,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Render 3D Transparent Pass"); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); @@ -2200,7 +2219,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (rb_data.is_valid() && (using_fsr2 || using_taa)) { if (using_fsr2) { - rb->ensure_upscaled(); rb_data->ensure_fsr2(fsr2_effect); RID exposure; @@ -2305,7 +2323,7 @@ void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_re } } -void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info) { +void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); ERR_FAIL_COND(!light_storage->owns_light_instance(p_light)); @@ -2460,7 +2478,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas if (render_cubemap) { //rendering to cubemap - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size); if (finalize_cubemap) { _render_shadow_process(); _render_shadow_end(); @@ -2478,20 +2496,20 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas } else { //render shadow - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size); } } void RenderForwardClustered::_render_shadow_begin() { scene_state.shadow_passes.clear(); RD::get_singleton()->draw_command_begin_label("Shadow Setup"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); render_list[RENDER_LIST_SECONDARY].clear(); scene_state.instance_data[RENDER_LIST_SECONDARY].clear(); } -void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info) { +void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size) { uint32_t shadow_pass_index = scene_state.shadow_passes.size(); SceneState::ShadowPass shadow_pass; @@ -2515,7 +2533,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page render_data.instances = &p_instances; render_data.render_info = p_render_info; - _setup_environment(&render_data, true, Vector2(1, 1), !p_flip_y, Color(), false, false, p_use_pancake, shadow_pass_index); + _setup_environment(&render_data, true, p_viewport_size, !p_flip_y, Color(), false, false, p_use_pancake, shadow_pass_index); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { scene_data.screen_mesh_lod_threshold = 0.0; @@ -2568,7 +2586,7 @@ void RenderForwardClustered::_render_shadow_process() { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { //render passes need to be configured after instance buffer is done, since they need the latest version SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), false, i); + shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), false, i); } RD::get_singleton()->draw_command_end_label(); @@ -2609,7 +2627,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con render_data.cluster_max_elements = 32; render_data.instances = &p_instances; - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), true, Color(), false, false, false); @@ -2619,7 +2637,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render Collider Heightfield"); @@ -2655,7 +2673,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform scene_shader.enable_advanced_shader_group(); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2664,7 +2682,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -2706,7 +2724,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance scene_shader.enable_advanced_shader_group(); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2715,7 +2733,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -2775,7 +2793,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu render_data.cluster_max_elements = 32; render_data.instances = &p_instances; - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); PassMode pass_mode = PASS_MODE_SDF; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -2833,7 +2851,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu scene_data.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); - RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture); + RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); HashMap<Size2i, RID>::Iterator E = sdfgi_framebuffer_size_cache.find(fb_size); if (!E) { @@ -2855,7 +2873,7 @@ void RenderForwardClustered::base_uniforms_changed() { render_base_uniform_set = RID(); } -void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers) { +void RenderForwardClustered::_update_render_base_uniform_set() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != light_storage->lightmap_array_get_version())) { @@ -2878,73 +2896,13 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (decals_get_filter()) { - case RS::DECAL_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (light_projectors_get_filter()) { - case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_omni_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 6; + u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_spot_light_buffer()); uniforms.push_back(u); @@ -2952,35 +2910,35 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; - u.binding = 7; + u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_reflection_probe_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 8; + u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_directional_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 9; + u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 10; + u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_capture_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 11; + u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); u.append_id(decal_atlas); @@ -2988,7 +2946,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M } { RD::Uniform u; - u.binding = 12; + u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_srgb(); u.append_id(decal_atlas); @@ -2996,7 +2954,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M } { RD::Uniform u; - u.binding = 13; + u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::TextureStorage::get_singleton()->get_decal_buffer()); uniforms.push_back(u); @@ -3005,7 +2963,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 14; + u.binding = 12; u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); uniforms.push_back(u); } @@ -3013,18 +2971,32 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 15; + u.binding = 13; u.append_id(sdfgi_get_ubo()); uniforms.push_back(u); } - uniforms.append_array(p_samplers.get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); + { + RD::Uniform u; + u.binding = 14; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + u.append_id(RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 15; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.append_id(best_fit_normal.texture); + uniforms.push_back(u); + } render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); } } -RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas, int p_index) { +RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas, int p_index) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); @@ -3172,6 +3144,68 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; u.binding = 10; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(12)); + + { + RD::Uniform u; + u.binding = 24; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture; if (rb.is_valid() && rb->has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH)) { @@ -3184,7 +3218,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 11; + u.binding = 25; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID bbt = rb_data.is_valid() ? rb->get_back_buffer_texture() : RID(); RID texture = bbt.is_valid() ? bbt : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3194,7 +3228,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 12; + u.binding = 26; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb_data->has_normal_roughness() ? rb_data->get_normal_roughness() : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_NORMAL : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_NORMAL); u.append_id(texture); @@ -3203,7 +3237,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 13; + u.binding = 27; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID aot = rb.is_valid() && rb->has_texture(RB_SCOPE_SSAO, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSAO, RB_FINAL) : RID(); RID texture = aot.is_valid() ? aot : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3213,7 +3247,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 14; + u.binding = 28; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); @@ -3222,7 +3256,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 15; + u.binding = 29; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); @@ -3230,7 +3264,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 16; + u.binding = 30; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { @@ -3245,7 +3279,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 17; + u.binding = 31; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { @@ -3260,7 +3294,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 18; + u.binding = 32; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; RID voxel_gi; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_GI)) { @@ -3272,7 +3306,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 19; + u.binding = 33; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID vfog; if (rb_data.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) { @@ -3289,7 +3323,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 20; + u.binding = 34; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssil = rb.is_valid() && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSIL, RB_FINAL) : RID(); RID texture = ssil.is_valid() ? ssil : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3300,7 +3334,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_rd, RENDER_PASS_UNIFORM_SET, uniforms); } -RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture) { +RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); Vector<RD::Uniform> uniforms; @@ -3406,33 +3440,95 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 10; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(12)); + // actual sdfgi stuff { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 10; + u.binding = 24; u.append_id(p_albedo_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 11; + u.binding = 25; u.append_id(p_emission_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 12; + u.binding = 26; u.append_id(p_emission_aniso_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 13; + u.binding = 27; u.append_id(p_geom_facing_texture); uniforms.push_back(u); } @@ -3481,14 +3577,18 @@ void RenderForwardClustered::sdfgi_update(const Ref<RenderSceneBuffers> &p_rende } bool needs_sdfgi = p_environment.is_valid() && environment_get_sdfgi_enabled(p_environment); + bool needs_reset = sdfgi.is_valid() ? sdfgi->version != gi.sdfgi_current_version : false; - if (!needs_sdfgi) { + if (!needs_sdfgi || needs_reset) { if (sdfgi.is_valid()) { // delete it sdfgi.unref(); rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } - return; + + if (!needs_sdfgi) { + return; + } } // Ensure advanced shaders are available if SDFGI is used. @@ -3693,6 +3793,11 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet sdcache->sort.priority = p_material->priority; sdcache->sort.uses_projector = ginstance->using_projectors; sdcache->sort.uses_softshadow = ginstance->using_softshadows; + + uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); + if (p_material->shader_data->uses_tangent && !(format & RS::ARRAY_FORMAT_TANGENT)) { + WARN_PRINT_ED("Attempting to use a shader that requires tangents with a mesh that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool)."); + } } void RenderForwardClustered::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh) { @@ -4117,7 +4222,6 @@ RenderForwardClustered::RenderForwardClustered() { } { defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n"; - defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; } #ifdef REAL_T_IS_DOUBLE { @@ -4138,6 +4242,44 @@ RenderForwardClustered::RenderForwardClustered() { shadow_sampler = RD::get_singleton()->sampler_create(sampler); } + { + Vector<String> modes; + modes.push_back("\n"); + best_fit_normal.shader.initialize(modes); + best_fit_normal.shader_version = best_fit_normal.shader.version_create(); + best_fit_normal.pipeline = RD::get_singleton()->compute_pipeline_create(best_fit_normal.shader.version_get_shader(best_fit_normal.shader_version, 0)); + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8_UNORM; + tformat.width = 1024; + tformat.height = 1024; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + tformat.texture_type = RD::TEXTURE_TYPE_2D; + best_fit_normal.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + + RID shader = best_fit_normal.shader.version_get_shader(best_fit_normal.shader_version, 0); + ERR_FAIL_COND(shader.is_null()); + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.binding = 0; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.append_id(best_fit_normal.texture); + uniforms.push_back(u); + } + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader, 0); + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, best_fit_normal.pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, tformat.width, tformat.height, 1); + RD::get_singleton()->compute_list_end(); + + best_fit_normal.shader.version_free(best_fit_normal.shader_version); + } + render_list_thread_threshold = GLOBAL_GET("rendering/limits/forward_renderer/threaded_render_minimum_instances"); _update_shader_quality_settings(); @@ -4171,6 +4313,7 @@ RenderForwardClustered::~RenderForwardClustered() { RD::get_singleton()->free(shadow_sampler); RSG::light_storage->directional_shadow_atlas_set_size(0); + RD::get_singleton()->free(best_fit_normal.texture); { for (const RID &rid : scene_state.uniform_buffers) { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 46deb30cde..5ff3d9f52a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -40,6 +40,7 @@ #include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h" #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" +#include "servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl.gen.h" #include "servers/rendering/renderer_rd/storage_rd/utilities.h" @@ -64,8 +65,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { MATERIAL_UNIFORM_SET = 3, }; - const int SAMPLERS_BINDING_FIRST_INDEX = 16; - enum { SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6, SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7, @@ -162,9 +161,16 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint64_t lightmap_texture_array_version = 0xFFFFFFFF; - void _update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers); - RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture); - RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas = false, int p_index = 0); + void _update_render_base_uniform_set(); + RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers); + RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0); + + struct BestFitNormal { + BestFitNormalShaderRD shader; + RID shader_version; + RID pipeline; + RID texture; + } best_fit_normal; enum PassMode { PASS_MODE_COLOR, @@ -238,6 +244,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { + INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6, @@ -593,9 +600,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { /* Render shadows */ - void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr); + void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_begin(); - void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr); + void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_process(); void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 9676474a66..bba1f62023 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -65,6 +65,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_discard = false; uses_roughness = false; uses_normal = false; + uses_tangent = false; + bool uses_normal_map = false; bool wireframe = false; unshaded = false; @@ -121,11 +123,16 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; actions.usage_flag_pointers["NORMAL"] = &uses_normal; - actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal; + actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map; actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; + actions.usage_flag_pointers["TANGENT"] = &uses_tangent; + actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent; + actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["VERTEX"] = &uses_vertex; @@ -150,6 +157,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_normal_texture = gen_code.uses_normal_roughness_texture; uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; + uses_normal |= uses_normal_map; + uses_tangent |= uses_normal_map; #if 0 print_line("**compiling shader:"); @@ -844,7 +853,6 @@ void SceneShaderForwardClustered::set_default_specialization_constants(const Vec void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_multiview) { if (p_needs_multiview || RendererCompositorRD::get_singleton()->is_xr_enabled()) { shader.enable_group(SHADER_GROUP_ADVANCED_MULTIVIEW); - } else { - shader.enable_group(SHADER_GROUP_ADVANCED); } + shader.enable_group(SHADER_GROUP_ADVANCED); } diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 0739cd9f86..3b83b2b582 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -165,6 +165,7 @@ public: bool uses_discard = false; bool uses_roughness = false; bool uses_normal = false; + bool uses_tangent = false; bool uses_particle_trails = false; bool unshaded = false; 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 8a672d8628..b7d7105daa 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -53,6 +53,7 @@ RendererRD::ForwardID RenderForwardMobile::ForwardIDStorageMobile::allocate_forw index = forward_id_allocators[p_type].allocations.size(); forward_id_allocators[p_type].allocations.push_back(true); forward_id_allocators[p_type].map.push_back(0xFF); + forward_id_allocators[p_type].last_pass.push_back(0); } else { forward_id_allocators[p_type].allocations[index] = true; } @@ -64,44 +65,72 @@ void RenderForwardMobile::ForwardIDStorageMobile::free_forward_id(RendererRD::Fo forward_id_allocators[p_type].allocations[p_id] = false; } -void RenderForwardMobile::ForwardIDStorageMobile::map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index) { +void RenderForwardMobile::ForwardIDStorageMobile::map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index, uint64_t p_last_pass) { forward_id_allocators[p_type].map[p_id] = p_index; + forward_id_allocators[p_type].last_pass[p_id] = p_last_pass; } void RenderForwardMobile::fill_push_constant_instance_indices(SceneState::InstanceData *p_instance_data, const GeometryInstanceForwardMobile *p_instance) { - // First zero out our indices. + uint64_t current_frame = RSG::rasterizer->get_frame_number(); p_instance_data->omni_lights[0] = 0xFFFFFFFF; p_instance_data->omni_lights[1] = 0xFFFFFFFF; - p_instance_data->spot_lights[0] = 0xFFFFFFFF; - p_instance_data->spot_lights[1] = 0xFFFFFFFF; - - p_instance_data->decals[0] = 0xFFFFFFFF; - p_instance_data->decals[1] = 0xFFFFFFFF; - - p_instance_data->reflection_probes[0] = 0xFFFFFFFF; - p_instance_data->reflection_probes[1] = 0xFFFFFFFF; - - for (uint32_t i = 0; i < MAX_RDL_CULL; i++) { - uint32_t ofs = i < 4 ? 0 : 1; - uint32_t shift = (i & 0x3) << 3; + uint32_t idx = 0; + for (uint32_t i = 0; i < p_instance->omni_light_count; i++) { + uint32_t ofs = idx < 4 ? 0 : 1; + uint32_t shift = (idx & 0x3) << 3; uint32_t mask = ~(0xFF << shift); - if (i < p_instance->omni_light_count) { + + if (forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT].last_pass[p_instance->omni_lights[i]] == current_frame) { p_instance_data->omni_lights[ofs] &= mask; p_instance_data->omni_lights[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT].map[p_instance->omni_lights[i]]) << shift; + idx++; } - if (i < p_instance->spot_light_count) { + } + + p_instance_data->spot_lights[0] = 0xFFFFFFFF; + p_instance_data->spot_lights[1] = 0xFFFFFFFF; + + idx = 0; + for (uint32_t i = 0; i < p_instance->spot_light_count; i++) { + uint32_t ofs = idx < 4 ? 0 : 1; + uint32_t shift = (idx & 0x3) << 3; + uint32_t mask = ~(0xFF << shift); + if (forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT].last_pass[p_instance->spot_lights[i]] == current_frame) { p_instance_data->spot_lights[ofs] &= mask; p_instance_data->spot_lights[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT].map[p_instance->spot_lights[i]]) << shift; + idx++; } - if (i < p_instance->decals_count) { + } + + p_instance_data->decals[0] = 0xFFFFFFFF; + p_instance_data->decals[1] = 0xFFFFFFFF; + + idx = 0; + for (uint32_t i = 0; i < p_instance->decals_count; i++) { + uint32_t ofs = idx < 4 ? 0 : 1; + uint32_t shift = (idx & 0x3) << 3; + uint32_t mask = ~(0xFF << shift); + if (forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_DECAL].last_pass[p_instance->decals[i]] == current_frame) { p_instance_data->decals[ofs] &= mask; p_instance_data->decals[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_DECAL].map[p_instance->decals[i]]) << shift; + idx++; } - if (i < p_instance->reflection_probe_count) { + } + + p_instance_data->reflection_probes[0] = 0xFFFFFFFF; + p_instance_data->reflection_probes[1] = 0xFFFFFFFF; + + idx = 0; + for (uint32_t i = 0; i < p_instance->reflection_probe_count; i++) { + uint32_t ofs = idx < 4 ? 0 : 1; + uint32_t shift = (idx & 0x3) << 3; + uint32_t mask = ~(0xFF << shift); + if (forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_REFLECTION_PROBE].last_pass[p_instance->reflection_probes[i]] == current_frame) { p_instance_data->reflection_probes[ofs] &= mask; p_instance_data->reflection_probes[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_REFLECTION_PROBE].map[p_instance->reflection_probes[i]]) << shift; + idx++; } } } @@ -163,82 +192,54 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe // Now define our subpasses Vector<RD::FramebufferPass> passes; - // Define our base pass, we'll be re-using this - RD::FramebufferPass pass; - pass.color_attachments.push_back(0); - pass.depth_attachment = 1; - if (vrs_texture.is_valid()) { - pass.vrs_attachment = 2; - } - switch (p_config_type) { - case FB_CONFIG_ONE_PASS: { - // just one pass - if (use_msaa) { - // Add resolve - pass.resolve_attachments.push_back(color_buffer_id); + case FB_CONFIG_RENDER_PASS: { + RD::FramebufferPass pass; + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + if (vrs_texture.is_valid()) { + pass.vrs_attachment = 2; } - passes.push_back(pass); - return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); - } break; - case FB_CONFIG_TWO_SUBPASSES: { - // - opaque pass - passes.push_back(pass); - - // - add sky pass if (use_msaa) { - // add resolve + // Add resolve pass.resolve_attachments.push_back(color_buffer_id); } passes.push_back(pass); return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); } break; - case FB_CONFIG_THREE_SUBPASSES: { - // - opaque pass - passes.push_back(pass); - - // - add sky pass - passes.push_back(pass); - - // - add alpha pass - if (use_msaa) { - // add resolve - pass.resolve_attachments.push_back(color_buffer_id); - } - passes.push_back(pass); - return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); - } break; - case FB_CONFIG_FOUR_SUBPASSES: { + case FB_CONFIG_RENDER_AND_POST_PASS: { Size2i target_size = render_buffers->get_target_size(); Size2i internal_size = render_buffers->get_internal_size(); // can't do our blit pass if resolutions don't match, this should already have been checked. ERR_FAIL_COND_V(target_size != internal_size, RID()); - // - opaque pass - passes.push_back(pass); - - // - add sky pass - passes.push_back(pass); + RD::FramebufferPass pass; + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + if (vrs_texture.is_valid()) { + pass.vrs_attachment = 2; + } - // - add alpha pass if (use_msaa) { // add resolve pass.resolve_attachments.push_back(color_buffer_id); } + passes.push_back(pass); // - add blit to 2D pass RID render_target = render_buffers->get_render_target(); ERR_FAIL_COND_V(render_target.is_null(), RID()); RID target_buffer; - if (texture_storage->render_target_get_msaa(render_target) == RS::VIEWPORT_MSAA_DISABLED) { + if (view_count > 1 || texture_storage->render_target_get_msaa(render_target) == RS::VIEWPORT_MSAA_DISABLED) { target_buffer = texture_storage->render_target_get_rd_texture(render_target); } else { target_buffer = texture_storage->render_target_get_rd_texture_msaa(render_target); + texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved. } ERR_FAIL_COND_V(target_buffer.is_null(), RID()); @@ -319,7 +320,7 @@ bool RenderForwardMobile::_render_buffers_can_be_storage() { return false; } -RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas, int p_index) { +RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas, int p_index) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); @@ -493,6 +494,68 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 12; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(13)); + if (p_index >= (int)render_pass_uniform_sets.size()) { render_pass_uniform_sets.resize(p_index + 1); } @@ -539,7 +602,6 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); p_render_data->cube_shadows.clear(); p_render_data->shadows.clear(); @@ -598,28 +660,11 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { //full barrier here, we need raster, transfer and compute and it depends from the previous work RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS); - - bool using_shadows = true; - - if (p_render_data->reflection_probe.is_valid()) { - if (!RSG::light_storage->reflection_probe_renders_shadows(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe))) { - using_shadows = false; - } - } else { - //do not render reflections when rendering a reflection probe - light_storage->update_reflection_probe_buffer(p_render_data, *p_render_data->reflection_probes, p_render_data->scene_data->cam_transform.affine_inverse(), p_render_data->environment); - } - - uint32_t directional_light_count = 0; - uint32_t positional_light_count = 0; - light_storage->update_light_buffers(p_render_data, *p_render_data->lights, p_render_data->scene_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); - texture_storage->update_decal_buffer(*p_render_data->decals, p_render_data->scene_data->cam_transform); - - p_render_data->directional_light_count = directional_light_count; } void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); ERR_FAIL_NULL(p_render_data); @@ -665,8 +710,28 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Size2i screen_size; RID framebuffer; bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; - bool using_subpass_transparent = true; - bool using_subpass_post_process = true; + bool merge_transparent_pass = true; // If true: we can do our transparent pass in the same pass as our opaque pass. + bool using_subpass_post_process = true; // If true: we can do our post processing in a subpass + RendererRD::MaterialStorage::Samplers samplers; + + bool using_shadows = true; + + if (p_render_data->reflection_probe.is_valid()) { + if (!RSG::light_storage->reflection_probe_renders_shadows(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe))) { + using_shadows = false; + } + } else { + //do not render reflections when rendering a reflection probe + light_storage->update_reflection_probe_buffer(p_render_data, *p_render_data->reflection_probes, p_render_data->scene_data->cam_transform.affine_inverse(), p_render_data->environment); + } + + // Update light and decal buffer first so we know what lights and decals are safe to pair with. + uint32_t directional_light_count = 0; + uint32_t positional_light_count = 0; + light_storage->update_light_buffers(p_render_data, *p_render_data->lights, p_render_data->scene_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); + texture_storage->update_decal_buffer(*p_render_data->decals, p_render_data->scene_data->cam_transform); + + p_render_data->directional_light_count = directional_light_count; // fill our render lists early so we can find out if we use various features _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR); @@ -692,8 +757,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } reverse_cull = true; - using_subpass_transparent = true; // we ignore our screen/depth texture here + merge_transparent_pass = true; // we ignore our screen/depth texture here using_subpass_post_process = false; // not applicable at all for reflection probes. + samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(); } else if (rb_data.is_valid()) { // setup rendering to render buffer screen_size = p_render_data->render_buffers->get_internal_size(); @@ -708,20 +774,18 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (scene_state.used_screen_texture || scene_state.used_depth_texture) { // can't use our last two subpasses because we're reading from screen texture or depth texture - using_subpass_transparent = false; + merge_transparent_pass = false; using_subpass_post_process = false; } if (using_subpass_post_process) { - // all as subpasses - framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES); - } else if (using_subpass_transparent) { - // our tonemap pass is separate - framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_THREE_SUBPASSES); + // We can do all in one go. + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_RENDER_AND_POST_PASS); } else { - // only opaque and sky as subpasses - framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_TWO_SUBPASSES); + // We separate things out. + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_RENDER_PASS); } + samplers = rb->get_samplers(); } else { ERR_FAIL(); //bug? } @@ -733,7 +797,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); - _update_render_base_uniform_set(rb->get_samplers()); //may have changed due to the above (light buffer enlarged, as an example) + // May have changed due to the above (light buffer enlarged, as an example). + _update_render_base_uniform_set(); RD::get_singleton()->draw_command_end_label(); // Render Setup @@ -747,6 +812,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Color clear_color = p_default_bg_color; bool keep_color = false; + bool copy_canvas = false; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black @@ -785,12 +851,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } break; case RS::ENV_BG_CANVAS: { if (rb_data.is_valid()) { - RID dest_framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS); - RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); - bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); - copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear); + copy_canvas = true; } - keep_color = true; } break; case RS::ENV_BG_KEEP: { keep_color = true; @@ -858,6 +920,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG; } } + { if (rb_data.is_valid()) { RD::get_singleton()->draw_command_begin_label("Render 3D Pass"); @@ -866,130 +929,93 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } // opaque pass - - RD::get_singleton()->draw_command_begin_label("Render Opaque Subpass"); + RD::get_singleton()->draw_command_begin_label("Render Opaque"); p_render_data->scene_data->directional_light_count = p_render_data->directional_light_count; // Shadow pass can change the base uniform set samplers. - _update_render_base_uniform_set(rb->get_samplers()); + _update_render_base_uniform_set(); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, p_render_data->render_buffers.is_valid()); - if (using_subpass_transparent && using_subpass_post_process) { + if (merge_transparent_pass && using_subpass_post_process) { RENDER_TIMESTAMP("Render Opaque + Transparent + Tonemap"); - } else if (using_subpass_transparent) { + } else if (merge_transparent_pass) { RENDER_TIMESTAMP("Render Opaque + Transparent"); } else { RENDER_TIMESTAMP("Render Opaque"); } - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); - - bool can_continue_color = !using_subpass_transparent && !scene_state.used_screen_texture; - bool can_continue_depth = !using_subpass_transparent && !scene_state.used_depth_texture; + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, samplers, true); + // Set clear colors. + Vector<Color> c; { - // regular forward for now - Vector<Color> c; - { - Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier; - if (rb_data.is_valid()) { - cc.a = 0; // For transparent viewport backgrounds. + Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier; + if (rb_data.is_valid()) { + cc.a = 0; // For transparent viewport backgrounds. + } + c.push_back(cc); // Our render buffer. + if (rb_data.is_valid()) { + if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + c.push_back(clear_color.srgb_to_linear() * inverse_luminance_multiplier); // Our resolve buffer. } - c.push_back(cc); // Our render buffer. - if (rb_data.is_valid()) { - if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { - c.push_back(clear_color.srgb_to_linear() * inverse_luminance_multiplier); // Our resolve buffer. - } - if (using_subpass_post_process) { - c.push_back(Color()); // Our 2D buffer we're copying into. - } + if (using_subpass_post_process) { + c.push_back(Color()); // Our 2D buffer we're copying into. } } + } - RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - render_list_params.framebuffer_format = fb_format; - if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { - // secondary command buffers need more testing at this time - //multi threaded - thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count()); - RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); - - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardMobile::_render_list_thread_function, &render_list_params, thread_draw_lists.size(), -1, true, SNAME("ForwardMobileRenderList")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, merge_transparent_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, merge_transparent_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, c, 1.0, 0); + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + if (copy_canvas) { + if (p_render_data->scene_data->view_count > 1) { + WARN_PRINT_ONCE("Canvas background is not supported in multiview!"); } else { - //single threaded - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); - _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); + RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); + bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); + + copy_effects->copy_to_drawlist(draw_list, fb_format, texture, convert_to_linear); } } - RD::get_singleton()->draw_command_end_label(); //Render Opaque Subpass + if (render_list[RENDER_LIST_OPAQUE].elements.size() > 0) { + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + render_list_params.framebuffer_format = fb_format; + render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. + + _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); + } + + RD::get_singleton()->draw_command_end_label(); //Render Opaque if (draw_sky || draw_sky_fog_only) { - RD::get_singleton()->draw_command_begin_label("Draw Sky Subpass"); + RD::get_singleton()->draw_command_begin_label("Draw Sky"); // Note, sky.setup should have been called up above and setup stuff we need. - RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); - sky.draw_sky(draw_list, rb, p_render_data->environment, framebuffer, time, sky_energy_multiplier); - RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass - - // note, if MSAA is used in 2-subpass approach we should get an automatic resolve here - } else { - // switch to subpass but we do nothing here so basically we skip (though this should trigger resolve with 2-subpass MSAA). - RD::get_singleton()->draw_list_switch_to_next_pass(); - } - - if (!using_subpass_transparent) { - // We're done with our subpasses so end our container pass - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); - - RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass - } - - if (scene_state.used_screen_texture) { - // Copy screen texture to backbuffer so we can read from it - _render_buffers_copy_screen_texture(p_render_data); + RD::get_singleton()->draw_command_end_label(); // Draw Sky } - if (scene_state.used_depth_texture) { - // Copy depth texture to backbuffer so we can read from it - _render_buffers_copy_depth_texture(p_render_data); - } + if (merge_transparent_pass) { + if (render_list[RENDER_LIST_ALPHA].element_info.size() > 0) { + // transparent pass - // transparent pass + RD::get_singleton()->draw_command_begin_label("Render Transparent"); - RD::get_singleton()->draw_command_begin_label("Render Transparent Subpass"); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR_TRANSPARENT, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + render_list_params.framebuffer_format = fb_format; + render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. - if (using_subpass_transparent) { - RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR_TRANSPARENT, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - render_list_params.framebuffer_format = fb_format; - if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { - // secondary command buffers need more testing at this time - //multi threaded - thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count()); - RD::get_singleton()->draw_list_switch_to_next_pass_split(thread_draw_lists.size(), thread_draw_lists.ptr()); - render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardMobile::_render_list_thread_function, &render_list_params, thread_draw_lists.size(), -1, true, SNAME("ForwardMobileRenderSubpass")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); - - } else { - //single threaded - RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); - render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); - } - RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass + RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass + } // note if we are using MSAA we should get an automatic resolve through our subpass configuration. @@ -1002,35 +1028,46 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); } else { - RENDER_TIMESTAMP("Render Transparent"); + // We're done with our subpasses so end our container pass + // note, if MSAA is used we should get an automatic resolve here - if (rb_data.is_valid()) { - framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS); + RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); + + RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass + + if (scene_state.used_screen_texture) { + // Copy screen texture to backbuffer so we can read from it + _render_buffers_copy_screen_texture(p_render_data); } - // this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation - // _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + if (scene_state.used_depth_texture) { + // Copy depth texture to backbuffer so we can read from it + _render_buffers_copy_depth_texture(p_render_data); + } - RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - render_list_params.framebuffer_format = fb_format; - if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { - // secondary command buffers need more testing at this time - //multi threaded - thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count()); - RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardMobile::_render_list_thread_function, &render_list_params, thread_draw_lists.size(), -1, true, SNAME("ForwardMobileRenderSubpass")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); + if (render_list[RENDER_LIST_ALPHA].element_info.size() > 0) { + RD::get_singleton()->draw_command_begin_label("Render Transparent Pass"); + RENDER_TIMESTAMP("Render Transparent"); - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); - } else { - //single threaded - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); + + if (rb_data.is_valid()) { + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_RENDER_PASS); + } + + // this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation + //_setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + render_list_params.framebuffer_format = fb_format; + render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. + + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); - } - RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass + RD::get_singleton()->draw_command_end_label(); // Render Transparent Pass + } } } @@ -1234,7 +1271,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i void RenderForwardMobile::_render_shadow_begin() { scene_state.shadow_passes.clear(); RD::get_singleton()->draw_command_begin_label("Shadow Setup"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); render_list[RENDER_LIST_SECONDARY].clear(); } @@ -1314,7 +1351,7 @@ void RenderForwardMobile::_render_shadow_process() { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { //render passes need to be configured after instance buffer is done, since they need the latest version SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), false, i); + shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), false, i); } RD::get_singleton()->draw_command_end_label(); @@ -1341,7 +1378,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c RD::get_singleton()->draw_command_begin_label("Render 3D Material"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; @@ -1365,7 +1402,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -1392,7 +1429,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> RD::get_singleton()->draw_command_begin_label("Render UV2"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.dual_paraboloid_side = 0; @@ -1410,7 +1447,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -1466,7 +1503,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const RD::get_singleton()->draw_command_begin_label("Render Collider Heightfield"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; @@ -1491,7 +1528,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render Collider Heightfield"); @@ -1510,7 +1547,7 @@ void RenderForwardMobile::base_uniforms_changed() { render_base_uniform_set = RID(); } -void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers) { +void RenderForwardMobile::_update_render_base_uniform_set() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != light_storage->lightmap_array_get_version())) { @@ -1518,8 +1555,6 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate RD::get_singleton()->free(render_base_uniform_set); } - // This is all loaded into set 0 - lightmap_texture_array_version = light_storage->lightmap_array_get_version(); Vector<RD::Uniform> uniforms; @@ -1535,73 +1570,13 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (decals_get_filter()) { - case RS::DECAL_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (light_projectors_get_filter()) { - case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_omni_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 6; + u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_spot_light_buffer()); uniforms.push_back(u); @@ -1609,35 +1584,35 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; - u.binding = 7; + u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_reflection_probe_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 8; + u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_directional_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 9; + u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 10; + u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_capture_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 11; + u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); u.append_id(decal_atlas); @@ -1645,7 +1620,7 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate } { RD::Uniform u; - u.binding = 12; + u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_srgb(); u.append_id(decal_atlas); @@ -1653,7 +1628,7 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate } { RD::Uniform u; - u.binding = 13; + u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::TextureStorage::get_singleton()->get_decal_buffer()); uniforms.push_back(u); @@ -1662,12 +1637,18 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 14; + u.binding = 12; u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); uniforms.push_back(u); } - uniforms.append_array(p_samplers.get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); + { + RD::Uniform u; + u.binding = 13; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + u.append_id(RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); } @@ -2478,6 +2459,11 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI sdcache->sort.geometry_id = p_mesh.get_local_index(); // sdcache->sort.uses_forward_gi = ginstance->can_sdfgi; sdcache->sort.priority = p_material->priority; + + uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); + if (p_material->shader_data->uses_tangent && !(format & RS::ARRAY_FORMAT_TANGENT)) { + WARN_PRINT_ED("Attempting to use a shader that requires tangents with a mesh that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool)."); + } } void RenderForwardMobile::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, RID p_mat_src, RID p_mesh) { @@ -2818,7 +2804,6 @@ RenderForwardMobile::RenderForwardMobile() { } { defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n"; - defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; } #ifdef REAL_T_IS_DOUBLE { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 50bf83b612..da96ca2124 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -59,8 +59,6 @@ private: MATERIAL_UNIFORM_SET = 3, }; - const int SAMPLERS_BINDING_FIRST_INDEX = 15; - enum { SPEC_CONSTANT_USING_PROJECTOR = 0, @@ -108,15 +106,9 @@ private: GDCLASS(RenderBufferDataForwardMobile, RenderBufferCustomDataRD); public: - // We can have: - // - 4 subpasses combining the full render cycle - // - 3 subpasses + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer) - // - 2 subpasses + 1 normal pass for transparent + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer) enum FramebufferConfigType { - FB_CONFIG_ONE_PASS, // Single pass frame buffer for alpha pass - FB_CONFIG_TWO_SUBPASSES, // Opaque + Sky sub pass - FB_CONFIG_THREE_SUBPASSES, // Opaque + Sky + Alpha sub pass - FB_CONFIG_FOUR_SUBPASSES, // Opaque + Sky + Alpha sub pass + Tonemap pass + FB_CONFIG_RENDER_PASS, // Single pass framebuffer for normal rendering. + FB_CONFIG_RENDER_AND_POST_PASS, // Two subpasses, one for normal rendering, one for post processing. FB_CONFIG_MAX }; @@ -195,12 +187,12 @@ private: /* Render Scene */ - RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas = false, int p_index = 0); + RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0); void _pre_opaque_render(RenderDataRD *p_render_data); uint64_t lightmap_texture_array_version = 0xFFFFFFFF; - void _update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers); + void _update_render_base_uniform_set(); void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); @@ -394,6 +386,7 @@ protected: // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { + INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6, @@ -535,6 +528,7 @@ protected: struct ForwardIDAllocator { LocalVector<bool> allocations; LocalVector<uint8_t> map; + LocalVector<uint64_t> last_pass; }; ForwardIDAllocator forward_id_allocators[RendererRD::FORWARD_ID_MAX]; @@ -542,7 +536,7 @@ protected: public: virtual RendererRD::ForwardID allocate_forward_id(RendererRD::ForwardIDType p_type) override; virtual void free_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id) override; - virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index) override; + virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index, uint64_t p_last_pass) override; virtual bool uses_forward_ids() const override { return true; } }; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 9a3556ce35..f1cec0e07c 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -67,6 +67,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_discard = false; uses_roughness = false; uses_normal = false; + uses_tangent = false; + bool uses_normal_map = false; bool wireframe = false; unshaded = false; @@ -122,7 +124,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; actions.usage_flag_pointers["NORMAL"] = &uses_normal; - actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal; + actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map; + + actions.usage_flag_pointers["TANGENT"] = &uses_tangent; + actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent; actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; @@ -150,6 +157,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_screen_texture = gen_code.uses_screen_texture; uses_depth_texture = gen_code.uses_depth_texture; uses_normal_texture = gen_code.uses_normal_roughness_texture; + uses_normal |= uses_normal_map; + uses_tangent |= uses_normal_map; #ifdef DEBUG_ENABLED if (uses_sss) { @@ -621,7 +630,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; actions.global_buffer_array_variable = "global_shader_uniforms.data"; - actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs"; + actions.instance_uniform_index_variable = "instances.data[draw_call.instance_index].instance_uniforms_ofs"; actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture actions.check_multiview_samplers = RendererCompositorRD::get_singleton()->is_xr_enabled(); // Make sure we check sampling multiview textures. diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 5c76d89247..da189c6f67 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -119,6 +119,7 @@ public: bool uses_discard = false; bool uses_roughness = false; bool uses_normal = false; + bool uses_tangent = false; bool uses_particle_trails = false; bool unshaded = false; diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h index 2f1e79b397..0ebebd0540 100644 --- a/servers/rendering/renderer_rd/pipeline_cache_rd.h +++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h @@ -38,7 +38,7 @@ class PipelineCacheRD { SpinLock spin_lock; RID shader; - uint32_t input_mask; + uint64_t input_mask; RD::RenderPrimitive render_primitive; RD::PipelineRasterizationState rasterization_state; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index ecf2c29956..657628111a 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -305,6 +305,7 @@ RendererCanvasRender::PolygonID RendererCanvasRenderRD::request_polygon(const Ve } pb.vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), vertex_id, buffers); + pb.primitive_count = vertex_count; if (p_indices.size()) { //create indices, as indices were requested @@ -316,6 +317,7 @@ RendererCanvasRender::PolygonID RendererCanvasRenderRD::request_polygon(const Ve } pb.index_buffer = RD::get_singleton()->index_buffer_create(p_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, index_buffer); pb.indices = RD::get_singleton()->index_array_create(pb.index_buffer, 0, p_indices.size()); + pb.primitive_count = p_indices.size(); } pb.vertex_format_id = vertex_id; @@ -398,7 +400,13 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI r_last_texture = p_texture; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used) { +_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) { + static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; + static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; + return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; +} + +void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { //create an empty push constant RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); @@ -592,6 +600,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); RD::get_singleton()->draw_list_draw(p_draw_list, true); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; case Item::Command::TYPE_NINEPATCH: { @@ -661,6 +675,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); RD::get_singleton()->draw_list_draw(p_draw_list, true); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + // Restore if overridden. push_constant.color_texture_pixel_size[0] = texpixel_size.x; push_constant.color_texture_pixel_size[1] = texpixel_size.y; @@ -711,6 +731,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend } RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->primitive_count); + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; case Item::Command::TYPE_PRIMITIVE: { const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); @@ -744,6 +770,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); RD::get_singleton()->draw_list_draw(p_draw_list, true); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + if (primitive->point_count == 4) { for (uint32_t j = 1; j < 3; j++) { //second half of triangle @@ -761,6 +793,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); RD::get_singleton()->draw_list_draw(p_draw_list, true); + + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } } } break; @@ -918,6 +956,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), instance_count); + + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)) * instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } } for (int j = 0; j < 6; j++) { @@ -1098,7 +1142,7 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo return uniform_set; } -void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer) { +void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); @@ -1116,6 +1160,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target); } else { framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target); + texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target, false); // If MSAA is enabled, our framebuffer will be resolved! if (texture_storage->render_target_is_clear_requested(p_to_render_target)) { clear = true; @@ -1195,7 +1240,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co } } - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used); + _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, r_render_info); prev_material = material; } @@ -1203,7 +1248,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co RD::get_singleton()->draw_list_end(); } -void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { +void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); @@ -1494,7 +1539,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { @@ -1526,7 +1571,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -1550,7 +1595,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -1580,7 +1625,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); //then reset item_count = 0; } @@ -1980,8 +2025,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve if (oc->vertex_array.is_null()) { //create from scratch //vertices - // TODO: geometry is always of length lc * 6 * sizeof(float), so in doubles builds this will receive half the data it needs - oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry); + oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(float), geometry); Vector<RID> buffer; buffer.push_back(oc->vertex_buffer); @@ -2042,7 +2086,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve if (oc->sdf_vertex_array.is_null()) { //create from scratch //vertices - oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(real_t), p_points.to_byte_array()); +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; + } + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), float_points.to_byte_array()); +#else + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), p_points.to_byte_array()); +#endif oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array()); oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size()); @@ -2053,7 +2108,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve } else { //update existing - RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(real_t) * 2 * p_points.size(), p_points.ptr()); +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; + } + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), float_points.ptr()); +#else + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), p_points.ptr()); +#endif RD::get_singleton()->buffer_update(oc->sdf_index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr()); } } @@ -2577,15 +2643,15 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { //pipelines Vector<RD::VertexAttribute> vf; RD::VertexAttribute vd; - vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32B32_SFLOAT : RD::DATA_FORMAT_R64G64B64_SFLOAT; + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + vd.stride = sizeof(float) * 3; vd.location = 0; vd.offset = 0; - vd.stride = sizeof(real_t) * 3; vf.push_back(vd); shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); - vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32_SFLOAT : RD::DATA_FORMAT_R64G64_SFLOAT; - vd.stride = sizeof(real_t) * 2; + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.stride = sizeof(float) * 2; vf.write[0] = vd; shadow_render.sdf_vertex_format = RD::get_singleton()->vertex_format_create(vf); @@ -2759,7 +2825,7 @@ bool RendererCanvasRenderRD::free(RID p_rid) { } void RendererCanvasRenderRD::set_shadow_texture_size(int p_size) { - p_size = nearest_power_of_2_templated(p_size); + p_size = MAX(1, nearest_power_of_2_templated(p_size)); if (p_size == state.shadow_texture_size) { return; } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 7dbcd903e6..6da3774fc2 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -229,6 +229,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID vertex_array; RID index_buffer; RID indices; + uint32_t primitive_count = 0; }; struct { @@ -423,8 +424,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender { double debug_redraw_time = 1.0; inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used); - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); + void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); @@ -435,30 +436,30 @@ class RendererCanvasRenderRD : public RendererCanvasRender { void _update_shadow_atlas(); public: - PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()); - void free_polygon(PolygonID p_polygon); + PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; + void free_polygon(PolygonID p_polygon) override; - RID light_create(); - void light_set_texture(RID p_rid, RID p_texture); - void light_set_use_shadow(RID p_rid, bool p_enable); - void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders); - void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders); + RID light_create() override; + void light_set_texture(RID p_rid, RID p_texture) override; + void light_set_use_shadow(RID p_rid, bool p_enable) override; + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override; + void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override; - virtual void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders); + virtual void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override; - RID occluder_polygon_create(); - void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed); - void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode); + RID occluder_polygon_create() override; + void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override; + void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override; - void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used); + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override; - virtual void set_shadow_texture_size(int p_size); + virtual void set_shadow_texture_size(int p_size) override; - void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color); + void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override; void set_time(double p_time); - void update(); - bool free(RID p_rid); + void update() override; + bool free(RID p_rid) override; RendererCanvasRenderRD(); ~RendererCanvasRenderRD(); }; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index b9bda9329e..a69877e680 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -306,19 +306,20 @@ RendererCompositorRD::RendererCompositorRD() { fog = memnew(RendererRD::Fog); canvas = memnew(RendererCanvasRenderRD()); - String rendering_method = GLOBAL_GET("rendering/renderer/rendering_method"); + String rendering_method = OS::get_singleton()->get_current_rendering_method(); uint64_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); if (rendering_method == "mobile" || textures_per_stage < 48) { - scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); if (rendering_method == "forward_plus") { WARN_PRINT_ONCE("Platform supports less than 48 textures per stage which is less than required by the Clustered renderer. Defaulting to Mobile renderer."); } + scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); } else if (rendering_method == "forward_plus") { - // default to our high end renderer scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } else { - ERR_FAIL_MSG("Cannot instantiate RenderingDevice-based renderer with renderer type " + rendering_method); + // Fall back to our high end renderer. + ERR_PRINT(vformat("Cannot instantiate RenderingDevice-based renderer with renderer type '%s'. Defaulting to Forward+ renderer.", rendering_method)); + scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } scene->init(); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 07d56eae0c..30c9b97aa4 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -260,15 +260,29 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData RD::get_singleton()->draw_command_begin_label("Copy screen texture"); - rb->allocate_blur_textures(); - + StringName texture_name; bool can_use_storage = _render_buffers_can_be_storage(); Size2i size = rb->get_internal_size(); + // When upscaling, the blur texture needs to be at the target size for post-processing to work. We prefer to use a + // dedicated backbuffer copy texture instead if the blur texture is not an option so shader effects work correctly. + Size2i target_size = rb->get_target_size(); + bool internal_size_matches = (size.width == target_size.width) && (size.height == target_size.height); + bool reuse_blur_texture = !rb->has_upscaled_texture() || internal_size_matches; + if (reuse_blur_texture) { + rb->allocate_blur_textures(); + texture_name = RB_TEX_BLUR_0; + } else { + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + usage_bits |= can_use_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + rb->create_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR, rb->get_base_data_format(), usage_bits); + texture_name = RB_TEX_BACK_COLOR; + } + for (uint32_t v = 0; v < rb->get_view_count(); v++) { RID texture = rb->get_internal_texture(v); - int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0).mipmaps); - RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, 0); + int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, texture_name).mipmaps); + RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, texture_name, v, 0); if (can_use_storage) { copy_effects->copy_to_rect(texture, dest, Rect2i(0, 0, size.x, size.y)); @@ -279,8 +293,8 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData for (int i = 1; i < mipmaps; i++) { RID source = dest; - dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, i); - Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, i); + dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, texture_name, v, i); + Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, texture_name, i); if (can_use_storage) { copy_effects->make_mipmap(source, dest, msize); @@ -351,6 +365,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RID color_texture = use_upscaled_texture ? rb->get_upscaled_texture() : rb->get_internal_texture(); Size2i color_size = use_upscaled_texture ? target_size : rb->get_internal_size(); + bool dest_is_msaa_2d = rb->get_view_count() == 1 && texture_storage->render_target_get_msaa(render_target) != RS::VIEWPORT_MSAA_DISABLED; + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes)) { RENDER_TIMESTAMP("Depth of Field"); RD::get_singleton()->draw_command_begin_label("DOF"); @@ -567,7 +583,13 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // If we do a bilinear upscale we just render into our render target and our shader will upscale automatically. // Target size in this case is lying as we never get our real target size communicated. // Bit nasty but... - dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target); + + if (dest_is_msaa_2d) { + dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target)); + texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved. + } else { + dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target); + } } tone_mapper->tonemapper(color_texture, dest_fb, tonemap); @@ -585,6 +607,15 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende fsr->fsr_upscale(rb, source_texture, dest_texture); } + if (dest_is_msaa_2d) { + // We can't upscale directly into our MSAA buffer so we need to do a copy + RID source_texture = texture_storage->render_target_get_rd_texture(render_target); + RID dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target)); + copy_effects->copy_to_fb_rect(source_texture, dest_fb, Rect2i(Point2i(), rb->get_target_size())); + + texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved. + } + RD::get_singleton()->draw_command_end_label(); } @@ -742,7 +773,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(rb).is_valid()) { Size2 rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); + copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, false, RID(), false, false, false, true); } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index 10e37c7da8..db2ca29474 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -45,7 +45,7 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { String text; for (int i = 0; i < lines.size(); i++) { - String l = lines[i]; + const String &l = lines[i]; bool push_chunk = false; StageTemplate::Chunk chunk; @@ -242,8 +242,8 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) { current_source = builder.as_string(); RD::ShaderStageSPIRVData stage; - stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error); - if (stage.spir_v.size() == 0) { + stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spirv.size() == 0) { build_ok = false; } else { stage.shader_stage = RD::SHADER_STAGE_VERTEX; @@ -260,8 +260,8 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) { current_source = builder.as_string(); RD::ShaderStageSPIRVData stage; - stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error); - if (stage.spir_v.size() == 0) { + stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spirv.size() == 0) { build_ok = false; } else { stage.shader_stage = RD::SHADER_STAGE_FRAGMENT; @@ -279,8 +279,8 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) { current_source = builder.as_string(); RD::ShaderStageSPIRVData stage; - stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); - if (stage.spir_v.size() == 0) { + stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spirv.size() == 0) { build_ok = false; } else { stage.shader_stage = RD::SHADER_STAGE_COMPUTE; @@ -480,6 +480,7 @@ void ShaderRD::_save_to_cache(Version *p_version, int p_group) { } void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) { + ERR_FAIL_NULL(p_version->variants); for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { int variant_id = group_to_variant_map[p_group][i]; RID shader = RD::get_singleton()->shader_create_placeholder(); diff --git a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl index 6137224162..1b065a8dd3 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl @@ -20,6 +20,7 @@ #define FLAG_SRGB (1 << 4) #define FLAG_ALPHA_TO_ONE (1 << 5) #define FLAG_LINEAR (1 << 6) +#define FLAG_NORMAL (1 << 7) #ifdef MULTIVIEW layout(location = 0) out vec3 uv_interp; @@ -77,6 +78,7 @@ void main() { #define FLAG_SRGB (1 << 4) #define FLAG_ALPHA_TO_ONE (1 << 5) #define FLAG_LINEAR (1 << 6) +#define FLAG_NORMAL (1 << 7) layout(push_constant, std430) uniform Params { vec4 section; @@ -192,6 +194,9 @@ void main() { if (bool(params.flags & FLAG_LINEAR)) { color.rgb = srgb_to_linear(color.rgb); } + if (bool(params.flags & FLAG_NORMAL)) { + color.rgb = normalize(color.rgb * 2.0 - 1.0) * 0.5 + 0.5; + } frag_color = color / params.luminance_multiplier; #endif // MODE_SET_COLOR diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl index 7b3c2f1c3b..6c77ab4a91 100644 --- a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl @@ -33,7 +33,7 @@ layout(location = 0) out vec2 uv_interp; void main() { vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); - uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0 * float(params.face_size); // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index fecf70bd01..8618f083b3 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -65,8 +65,12 @@ void main() { vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth); vec4 normal_roughness = imageLoad(source_normal_roughness, ssC); - vec3 normal = normal_roughness.xyz * 2.0 - 1.0; + vec3 normal = normalize(normal_roughness.xyz * 2.0 - 1.0); float roughness = normal_roughness.w; + if (roughness > 0.5) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); // The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828. if (roughness > 0.6) { diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl index 4df74b8626..51caa67d3c 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl @@ -59,8 +59,13 @@ void main() { color += texelFetch(source_ssr, ofs, 0); float d = texelFetch(source_depth, ofs, 0).r; vec4 nr = texelFetch(source_normal, ofs, 0); - normal.xyz += nr.xyz * 2.0 - 1.0; - normal.w += nr.w; + normal.xyz += normalize(nr.xyz * 2.0 - 1.0); + float roughness = normal.w; + if (roughness > 0.5) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); + normal.w += roughness; if (sc_multiview) { // we're doing a full unproject so we need the value as is. @@ -81,6 +86,7 @@ void main() { depth /= 4.0; normal.xyz = normalize(normal.xyz / 4.0) * 0.5 + 0.5; normal.w /= 4.0; + normal.w = normal.w * (127.0 / 255.0); } else { ivec2 ofs = ssC << 1; diff --git a/servers/rendering/renderer_rd/shaders/effects/ssao.glsl b/servers/rendering/renderer_rd/shaders/effects/ssao.glsl index 58efc68bc7..8c50bb544d 100644 --- a/servers/rendering/renderer_rd/shaders/effects/ssao.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/ssao.glsl @@ -158,21 +158,16 @@ vec4 calculate_edges(const float p_center_z, const float p_left_z, const float p return clamp((1.3 - edgesLRTB / (p_center_z * 0.040)), 0.0, 1.0); } -vec3 decode_normal(vec3 p_encoded_normal) { - vec3 normal = p_encoded_normal * 2.0 - 1.0; - return normal; -} - vec3 load_normal(ivec2 p_pos) { - vec3 encoded_normal = imageLoad(source_normal, p_pos).xyz; - encoded_normal.z = 1.0 - encoded_normal.z; - return decode_normal(encoded_normal); + vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos).xyz * 2.0 - 1.0); + encoded_normal.z = -encoded_normal.z; + return encoded_normal; } vec3 load_normal(ivec2 p_pos, ivec2 p_offset) { - vec3 encoded_normal = imageLoad(source_normal, p_pos + p_offset).xyz; - encoded_normal.z = 1.0 - encoded_normal.z; - return decode_normal(encoded_normal); + vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos + p_offset).xyz * 2.0 - 1.0); + encoded_normal.z = -encoded_normal.z; + return encoded_normal; } // all vectors in viewspace diff --git a/servers/rendering/renderer_rd/shaders/effects/ssil.glsl b/servers/rendering/renderer_rd/shaders/effects/ssil.glsl index fac13b0e3f..0b623c1d4e 100644 --- a/servers/rendering/renderer_rd/shaders/effects/ssil.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/ssil.glsl @@ -159,21 +159,16 @@ vec4 calculate_edges(const float p_center_z, const float p_left_z, const float p return clamp((1.3 - edgesLRTB / (p_center_z * 0.040)), 0.0, 1.0); } -vec3 decode_normal(vec3 p_encoded_normal) { - vec3 normal = p_encoded_normal * 2.0 - 1.0; - return normal; -} - vec3 load_normal(ivec2 p_pos) { - vec3 encoded_normal = imageLoad(source_normal, p_pos).xyz; - encoded_normal.z = 1.0 - encoded_normal.z; - return decode_normal(encoded_normal); + vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos).xyz * 2.0 - 1.0); + encoded_normal.z = -encoded_normal.z; + return encoded_normal; } vec3 load_normal(ivec2 p_pos, ivec2 p_offset) { - vec3 encoded_normal = imageLoad(source_normal, p_pos + p_offset).xyz; - encoded_normal.z = 1.0 - encoded_normal.z; - return decode_normal(encoded_normal); + vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos + p_offset).xyz * 2.0 - 1.0); + encoded_normal.z = -encoded_normal.z; + return encoded_normal; } // all vectors in viewspace diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index 59af9501ba..80ed34cda1 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -618,6 +618,11 @@ void process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 ref if (normal.length() > 0.5) { //valid normal, can do GI float roughness = normal_roughness.w; + bool dynamic_object = roughness > 0.5; + if (dynamic_object) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); vec3 view = -normalize(mat3(scene_data.cam_transform) * (vertex - scene_data.eye_offset[gl_GlobalInvocationID.z].xyz)); vertex = mat3(scene_data.cam_transform) * vertex; normal = normalize(mat3(scene_data.cam_transform) * normal); diff --git a/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl b/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl index 577c6d0cd0..6e9e0f82b3 100644 --- a/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl @@ -492,7 +492,7 @@ void main() { ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(z); - vec3 normal = imageLoad(source_normal, uv_xy).xyz * 2.0 - 1.0; + vec3 normal = normalize(imageLoad(source_normal, uv_xy).xyz * 2.0 - 1.0); normal = vec3(params.x_dir) * normal.x * mix(1.0, -1.0, params.flip_x) + vec3(params.y_dir) * normal.y * mix(1.0, -1.0, params.flip_y) - vec3(params.z_dir) * normal.z; vec4 albedo = imageLoad(source_albedo, uv_xy); diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl new file mode 100644 index 0000000000..8280d192e3 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl @@ -0,0 +1,43 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(r8, set = 0, binding = 0) uniform restrict writeonly image2D current_image; + +// This shader is used to generate a "best fit normal texture" as described by: +// https://advances.realtimerendering.com/s2010/Kaplanyan-CryEngine3(SIGGRAPH%202010%20Advanced%20RealTime%20Rendering%20Course).pdf +// This texture tells you what length of normal can be used to store a unit vector +// with the lest amount of error. + +vec3 quantize(vec3 c) { + return round(clamp(c * 0.5 + 0.5, 0.0, 1.0) * 255.0) * (1.0 / 255.0) * 2.0 - 1.0; +} + +float find_minimum_error(vec3 normal) { + float min_error = 100000.0; + float t_best = 0.0; + for (float nstep = 1.5; nstep < 127.5; ++nstep) { + float t = nstep / 127.5; + vec3 vp = normal * t; + vec3 quantizedp = quantize(vp); + vec3 vdiff = (quantizedp - vp) / t; + float error = max(abs(vdiff.x), max(abs(vdiff.y), abs(vdiff.z))); + if (error < min_error) { + min_error = error; + t_best = t; + } + } + return t_best; +} + +void main() { + vec2 uv = vec2(gl_GlobalInvocationID.xy) * vec2(1.0 / 1024.0) + vec2(0.5 / 1024.0); + uv.y *= uv.x; + + vec3 dir = vec3(uv.x, uv.y, 1.0); + imageStore(current_image, ivec2(gl_GlobalInvocationID.xy), vec4(find_minimum_error(dir), 1.0, 1.0, 1.0)); +} diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 8e6db7583e..930d981494 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -816,10 +816,10 @@ vec4 fog_process(vec3 vertex) { #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod, blend; blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod); - sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; - sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); + sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; + sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); #else - sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; + sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective); } @@ -868,6 +868,28 @@ uint cluster_get_range_clip_mask(uint i, uint z_min, uint z_max) { #endif //!MODE_RENDER DEPTH +#if defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_MATERIAL) +// https://advances.realtimerendering.com/s2010/Kaplanyan-CryEngine3(SIGGRAPH%202010%20Advanced%20RealTime%20Rendering%20Course).pdf +vec3 encode24(vec3 v) { + // Unsigned normal (handles most symmetry) + vec3 vNormalUns = abs(v); + // Get the major axis for our collapsed cubemap lookup + float maxNAbs = max(vNormalUns.z, max(vNormalUns.x, vNormalUns.y)); + // Get the collapsed cubemap texture coordinates + vec2 vTexCoord = vNormalUns.z < maxNAbs ? (vNormalUns.y < maxNAbs ? vNormalUns.yz : vNormalUns.xz) : vNormalUns.xy; + vTexCoord /= maxNAbs; + vTexCoord = vTexCoord.x < vTexCoord.y ? vTexCoord.yx : vTexCoord.xy; + // Stretch: + vTexCoord.y /= vTexCoord.x; + float fFittingScale = texture(sampler2D(best_fit_normal_texture, SAMPLER_NEAREST_CLAMP), vTexCoord).r; + // Make vector touch unit cube + vec3 result = v / maxNAbs; + // scale the normal to get the best fit + result *= fFittingScale; + return result; +} +#endif // MODE_RENDER_NORMAL_ROUGHNESS + void fragment_shader(in SceneData scene_data) { uint instance_index = instance_index_interp; @@ -1019,13 +1041,11 @@ void fragment_shader(in SceneData scene_data) { #endif // ALPHA_ANTIALIASING_EDGE_USED #ifdef MODE_RENDER_DEPTH -#ifdef USE_OPAQUE_PREPASS -#ifndef ALPHA_SCISSOR_USED +#if defined(USE_OPAQUE_PREPASS) || defined(ALPHA_ANTIALIASING_EDGE_USED) if (alpha < scene_data.opaque_prepass_threshold) { discard; } -#endif // !ALPHA_SCISSOR_USED -#endif // USE_OPAQUE_PREPASS +#endif // USE_OPAQUE_PREPASS || ALPHA_ANTIALIASING_EDGE_USED #endif // MODE_RENDER_DEPTH #endif // !USE_SHADOW_TO_OPACITY @@ -1273,11 +1293,11 @@ void fragment_shader(in SceneData scene_data) { float lod, blend; blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod); - specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; + specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light *= scene_data.IBL_exposure_normalization; @@ -1297,9 +1317,9 @@ void fragment_shader(in SceneData scene_data) { if (scene_data.use_ambient_cubemap) { vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; #ifdef USE_RADIANCE_CUBEMAP_ARRAY - vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; + vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; #else - vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; + vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); @@ -1330,11 +1350,11 @@ void fragment_shader(in SceneData scene_data) { float lod, blend; blend = modf(roughness_lod, lod); - vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; + vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; @@ -1521,18 +1541,18 @@ void fragment_shader(in SceneData scene_data) { vec2 base_coord = screen_uv; vec2 closest_coord = base_coord; #ifdef USE_MULTIVIEW - float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); + float closest_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0)); #else // USE_MULTIVIEW - float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0); + float closest_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0)); #endif // USE_MULTIVIEW for (int i = 0; i < 4; i++) { const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1)); vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size; #ifdef USE_MULTIVIEW - float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); + float neighbour_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0)); #else // USE_MULTIVIEW - float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0); + float neighbour_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0)); #endif // USE_MULTIVIEW if (neighbour_ang > closest_ang) { closest_ang = neighbour_ang; @@ -1862,7 +1882,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1872,21 +1892,21 @@ void fragment_shader(in SceneData scene_data) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1896,7 +1916,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } } @@ -2304,7 +2324,7 @@ void fragment_shader(in SceneData scene_data) { albedo_output_buffer.rgb = albedo; albedo_output_buffer.a = alpha; - normal_output_buffer.rgb = normal * 0.5 + 0.5; + normal_output_buffer.rgb = encode24(normal) * 0.5 + 0.5; normal_output_buffer.a = 0.0; depth_output_buffer.r = -vertex.z; @@ -2318,7 +2338,15 @@ void fragment_shader(in SceneData scene_data) { #endif #ifdef MODE_RENDER_NORMAL_ROUGHNESS - normal_roughness_output_buffer = vec4(normal * 0.5 + 0.5, roughness); + normal_roughness_output_buffer = vec4(encode24(normal) * 0.5 + 0.5, roughness); + + // We encode the dynamic static into roughness. + // Values over 0.5 are dynamic, under 0.5 are static. + normal_roughness_output_buffer.w = normal_roughness_output_buffer.w * (127.0 / 255.0); + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_DYNAMIC)) { + normal_roughness_output_buffer.w = 1.0 - normal_roughness_output_buffer.w; + } + normal_roughness_output_buffer.w = normal_roughness_output_buffer.w; #ifdef MODE_RENDER_VOXEL_GI if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index bfd87b4ea1..441cf3c80c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -46,14 +46,9 @@ draw_call; #include "../light_data_inc.glsl" -#include "../samplers_inc.glsl" - layout(set = 0, binding = 2) uniform sampler shadow_sampler; -layout(set = 0, binding = 3) uniform sampler decal_sampler; - -layout(set = 0, binding = 4) uniform sampler light_projector_sampler; - +#define INSTANCE_FLAGS_DYNAMIC (1 << 3) #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) #define INSTANCE_FLAGS_USE_SDFGI (1 << 6) @@ -74,22 +69,22 @@ layout(set = 0, binding = 4) uniform sampler light_projector_sampler; #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO 1 #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL 2 -layout(set = 0, binding = 5, std430) restrict readonly buffer OmniLights { +layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { LightData data[]; } omni_lights; -layout(set = 0, binding = 6, std430) restrict readonly buffer SpotLights { +layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights { LightData data[]; } spot_lights; -layout(set = 0, binding = 7, std430) restrict readonly buffer ReflectionProbeData { +layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData { ReflectionData data[]; } reflections; -layout(set = 0, binding = 8, std140) uniform DirectionalLights { +layout(set = 0, binding = 6, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } directional_lights; @@ -103,7 +98,7 @@ struct Lightmap { float exposure_normalization; }; -layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { +layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { Lightmap data[]; } lightmaps; @@ -112,20 +107,20 @@ struct LightmapCapture { vec4 sh[9]; }; -layout(set = 0, binding = 10, std140) restrict readonly buffer LightmapCaptures { +layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures { LightmapCapture data[]; } lightmap_captures; -layout(set = 0, binding = 11) uniform texture2D decal_atlas; -layout(set = 0, binding = 12) uniform texture2D decal_atlas_srgb; +layout(set = 0, binding = 9) uniform texture2D decal_atlas; +layout(set = 0, binding = 10) uniform texture2D decal_atlas_srgb; -layout(set = 0, binding = 13, std430) restrict readonly buffer Decals { +layout(set = 0, binding = 11, std430) restrict readonly buffer Decals { DecalData data[]; } decals; -layout(set = 0, binding = 14, std430) restrict readonly buffer GlobalShaderUniformData { +layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData { vec4 data[]; } global_shader_uniforms; @@ -139,7 +134,7 @@ struct SDFVoxelGICascadeData { float exposure_normalization; }; -layout(set = 0, binding = 15, std140) uniform SDFGI { +layout(set = 0, binding = 13, std140) uniform SDFGI { vec3 grid_size; uint max_cascades; @@ -167,6 +162,10 @@ layout(set = 0, binding = 15, std140) uniform SDFGI { } sdfgi; +layout(set = 0, binding = 14) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; + +layout(set = 0, binding = 15) uniform texture2D best_fit_normal_texture; + /* Set 1: Render Pass (changes per render pass) */ layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -250,12 +249,29 @@ layout(set = 1, binding = 9, std430) buffer restrict readonly ClusterBuffer { } cluster_buffer; +layout(set = 1, binding = 10) uniform sampler decal_sampler; + +layout(set = 1, binding = 11) uniform sampler light_projector_sampler; + +layout(set = 1, binding = 12 + 0) uniform sampler SAMPLER_NEAREST_CLAMP; +layout(set = 1, binding = 12 + 1) uniform sampler SAMPLER_LINEAR_CLAMP; +layout(set = 1, binding = 12 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 12 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 12 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 12 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 12 + 6) uniform sampler SAMPLER_NEAREST_REPEAT; +layout(set = 1, binding = 12 + 7) uniform sampler SAMPLER_LINEAR_REPEAT; +layout(set = 1, binding = 12 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 12 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 12 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 1, binding = 12 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + #ifdef MODE_RENDER_SDF -layout(r16ui, set = 1, binding = 10) uniform restrict writeonly uimage3D albedo_volume_grid; -layout(r32ui, set = 1, binding = 11) uniform restrict writeonly uimage3D emission_grid; -layout(r32ui, set = 1, binding = 12) uniform restrict writeonly uimage3D emission_aniso_grid; -layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; +layout(r16ui, set = 1, binding = 24) uniform restrict writeonly uimage3D albedo_volume_grid; +layout(r32ui, set = 1, binding = 25) uniform restrict writeonly uimage3D emission_grid; +layout(r32ui, set = 1, binding = 26) uniform restrict writeonly uimage3D emission_aniso_grid; +layout(r32ui, set = 1, binding = 27) uniform restrict uimage3D geom_facing_grid; //still need to be present for shaders that use it, so remap them to something #define depth_buffer shadow_atlas @@ -266,24 +282,24 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; #else #ifdef USE_MULTIVIEW -layout(set = 1, binding = 10) uniform texture2DArray depth_buffer; -layout(set = 1, binding = 11) uniform texture2DArray color_buffer; -layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer; -layout(set = 1, binding = 13) uniform texture2DArray ao_buffer; -layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer; -layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer; +layout(set = 1, binding = 24) uniform texture2DArray depth_buffer; +layout(set = 1, binding = 25) uniform texture2DArray color_buffer; +layout(set = 1, binding = 26) uniform texture2DArray normal_roughness_buffer; +layout(set = 1, binding = 27) uniform texture2DArray ao_buffer; +layout(set = 1, binding = 28) uniform texture2DArray ambient_buffer; +layout(set = 1, binding = 29) uniform texture2DArray reflection_buffer; #define multiviewSampler sampler2DArray #else // USE_MULTIVIEW -layout(set = 1, binding = 10) uniform texture2D depth_buffer; -layout(set = 1, binding = 11) uniform texture2D color_buffer; -layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer; -layout(set = 1, binding = 13) uniform texture2D ao_buffer; -layout(set = 1, binding = 14) uniform texture2D ambient_buffer; -layout(set = 1, binding = 15) uniform texture2D reflection_buffer; +layout(set = 1, binding = 24) uniform texture2D depth_buffer; +layout(set = 1, binding = 25) uniform texture2D color_buffer; +layout(set = 1, binding = 26) uniform texture2D normal_roughness_buffer; +layout(set = 1, binding = 27) uniform texture2D ao_buffer; +layout(set = 1, binding = 28) uniform texture2D ambient_buffer; +layout(set = 1, binding = 29) uniform texture2D reflection_buffer; #define multiviewSampler sampler2D #endif -layout(set = 1, binding = 16) uniform texture2DArray sdfgi_lightprobe_texture; -layout(set = 1, binding = 17) uniform texture3D sdfgi_occlusion_cascades; +layout(set = 1, binding = 30) uniform texture2DArray sdfgi_lightprobe_texture; +layout(set = 1, binding = 31) uniform texture3D sdfgi_occlusion_cascades; struct VoxelGIData { mat4 xform; // 64 - 64 @@ -300,21 +316,30 @@ struct VoxelGIData { float exposure_normalization; // 4 - 112 }; -layout(set = 1, binding = 18, std140) uniform VoxelGIs { +layout(set = 1, binding = 32, std140) uniform VoxelGIs { VoxelGIData data[MAX_VOXEL_GI_INSTANCES]; } voxel_gi_instances; -layout(set = 1, binding = 19) uniform texture3D volumetric_fog_texture; +layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture; #ifdef USE_MULTIVIEW -layout(set = 1, binding = 20) uniform texture2DArray ssil_buffer; +layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer; #else -layout(set = 1, binding = 20) uniform texture2D ssil_buffer; +layout(set = 1, binding = 34) uniform texture2D ssil_buffer; #endif // USE_MULTIVIEW #endif +vec4 normal_roughness_compatibility(vec4 p_normal_roughness) { + float roughness = p_normal_roughness.w; + if (roughness > 0.5) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); + return vec4(normalize(p_normal_roughness.xyz * 2.0 - 1.0) * 0.5 + 0.5, roughness); +} + /* Set 2 Skeleton & Instancing (can change per item) */ layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms { diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index a9e9a617d6..03737e087c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -658,10 +658,10 @@ vec4 fog_process(vec3 vertex) { #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod, blend; blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod); - sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; - sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); + sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; + sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); #else - sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; + sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective); } @@ -854,13 +854,11 @@ void main() { #endif // ALPHA_ANTIALIASING_EDGE_USED #ifdef MODE_RENDER_DEPTH -#ifdef USE_OPAQUE_PREPASS -#ifndef ALPHA_SCISSOR_USED +#if defined(USE_OPAQUE_PREPASS) || defined(ALPHA_ANTIALIASING_EDGE_USED) if (alpha < scene_data.opaque_prepass_threshold) { discard; } -#endif // !ALPHA_SCISSOR_USED -#endif // USE_OPAQUE_PREPASS +#endif // USE_OPAQUE_PREPASS || ALPHA_ANTIALIASING_EDGE_USED #endif // MODE_RENDER_DEPTH #endif // !USE_SHADOW_TO_OPACITY @@ -1053,11 +1051,11 @@ void main() { float lod, blend; blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod); - specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else // USE_RADIANCE_CUBEMAP_ARRAY - specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; + specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light *= sc_luminance_multiplier; @@ -1078,9 +1076,9 @@ void main() { if (scene_data.use_ambient_cubemap) { vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; #ifdef USE_RADIANCE_CUBEMAP_ARRAY - vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; + vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; #else - vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; + vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY cubemap_ambient *= sc_luminance_multiplier; cubemap_ambient *= scene_data.IBL_exposure_normalization; @@ -1112,11 +1110,11 @@ void main() { float lod, blend; blend = modf(roughness_lod, lod); - vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; + vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; @@ -1508,7 +1506,7 @@ void main() { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1518,21 +1516,21 @@ void main() { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1542,7 +1540,7 @@ void main() { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 3de5e76970..7674e905e1 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -27,13 +27,9 @@ draw_call; #include "../light_data_inc.glsl" -#include "../samplers_inc.glsl" - layout(set = 0, binding = 2) uniform sampler shadow_sampler; -layout(set = 0, binding = 3) uniform sampler decal_sampler; -layout(set = 0, binding = 4) uniform sampler light_projector_sampler; - +#define INSTANCE_FLAGS_DYNAMIC (1 << 3) #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) #define INSTANCE_FLAGS_USE_SDFGI (1 << 6) @@ -50,22 +46,22 @@ layout(set = 0, binding = 4) uniform sampler light_projector_sampler; //3 bits of stride #define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF -layout(set = 0, binding = 5, std430) restrict readonly buffer OmniLights { +layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { LightData data[]; } omni_lights; -layout(set = 0, binding = 6, std430) restrict readonly buffer SpotLights { +layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights { LightData data[]; } spot_lights; -layout(set = 0, binding = 7, std430) restrict readonly buffer ReflectionProbeData { +layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData { ReflectionData data[]; } reflections; -layout(set = 0, binding = 8, std140) uniform DirectionalLights { +layout(set = 0, binding = 6, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } directional_lights; @@ -79,7 +75,7 @@ struct Lightmap { float exposure_normalization; }; -layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { +layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { Lightmap data[]; } lightmaps; @@ -88,24 +84,26 @@ struct LightmapCapture { mediump vec4 sh[9]; }; -layout(set = 0, binding = 10, std140) restrict readonly buffer LightmapCaptures { +layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures { LightmapCapture data[]; } lightmap_captures; -layout(set = 0, binding = 11) uniform mediump texture2D decal_atlas; -layout(set = 0, binding = 12) uniform mediump texture2D decal_atlas_srgb; +layout(set = 0, binding = 9) uniform mediump texture2D decal_atlas; +layout(set = 0, binding = 10) uniform mediump texture2D decal_atlas_srgb; -layout(set = 0, binding = 13, std430) restrict readonly buffer Decals { +layout(set = 0, binding = 11, std430) restrict readonly buffer Decals { DecalData data[]; } decals; -layout(set = 0, binding = 14, std430) restrict readonly buffer GlobalShaderUniformData { +layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData { highp vec4 data[]; } global_shader_uniforms; +layout(set = 0, binding = 13) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; + /* Set 1: Render Pass (changes per render pass) */ layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -166,6 +164,23 @@ layout(set = 1, binding = 10) uniform mediump texture2D color_buffer; #define multiviewSampler sampler2D #endif // USE_MULTIVIEW +layout(set = 1, binding = 11) uniform sampler decal_sampler; + +layout(set = 1, binding = 12) uniform sampler light_projector_sampler; + +layout(set = 1, binding = 13 + 0) uniform sampler SAMPLER_NEAREST_CLAMP; +layout(set = 1, binding = 13 + 1) uniform sampler SAMPLER_LINEAR_CLAMP; +layout(set = 1, binding = 13 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 13 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 13 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 13 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 13 + 6) uniform sampler SAMPLER_NEAREST_REPEAT; +layout(set = 1, binding = 13 + 7) uniform sampler SAMPLER_LINEAR_REPEAT; +layout(set = 1, binding = 13 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 13 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 13 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 1, binding = 13 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + /* Set 2 Skeleton & Instancing (can change per item) */ layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms { diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl index 8f68030cba..1f618eb34e 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl @@ -13,7 +13,7 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); + vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); float a = (1.0 - color.a); color += a * scolor; dist += half_diameter; @@ -35,7 +35,7 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); + vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); lod_level += 1.0; float a = (1.0 - color.a); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 4fb577d697..e9722bad1f 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -932,7 +932,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 reflection; - reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; + reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; reflection.rgb *= reflections.data[ref_index].exposure_normalization; if (reflections.data[ref_index].exterior) { reflection.rgb = mix(specular_light, reflection.rgb, blend); @@ -955,7 +955,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 ambient_out; - ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; + ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; ambient_out.rgb *= reflections.data[ref_index].exposure_normalization; ambient_out.a = blend; if (reflections.data[ref_index].exterior) { diff --git a/servers/rendering/renderer_rd/storage_rd/forward_id_storage.h b/servers/rendering/renderer_rd/storage_rd/forward_id_storage.h index bedf5e80c7..c8f8d4f7f2 100644 --- a/servers/rendering/renderer_rd/storage_rd/forward_id_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/forward_id_storage.h @@ -59,7 +59,7 @@ public: virtual RendererRD::ForwardID allocate_forward_id(RendererRD::ForwardIDType p_type) { return -1; } virtual void free_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id) {} - virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index) {} + virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index, uint64_t p_last_pass) {} virtual bool uses_forward_ids() const { return false; } }; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 4fd33ad71a..5f4bf6c8ed 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -793,7 +793,7 @@ void LightStorage::update_light_buffers(RenderDataRD *p_render_data, const Paged real_t distance = (i < omni_light_count) ? omni_light_sort[index].depth : spot_light_sort[index].depth; if (using_forward_ids) { - forward_id_storage->map_forward_id(type == RS::LIGHT_OMNI ? RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT : RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT, light_instance->forward_id, index); + forward_id_storage->map_forward_id(type == RS::LIGHT_OMNI ? RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT : RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT, light_instance->forward_id, index, light_instance->last_pass); } Transform3D light_transform = light_instance->transform; @@ -1669,8 +1669,10 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c for (uint32_t i = 0; i < reflection_count; i++) { ReflectionProbeInstance *rpi = reflection_sort[i].probe_instance; + rpi->last_pass = RSG::rasterizer->get_frame_number(); + if (using_forward_ids) { - forward_id_storage->map_forward_id(FORWARD_ID_TYPE_REFLECTION_PROBE, rpi->forward_id, i); + forward_id_storage->map_forward_id(FORWARD_ID_TYPE_REFLECTION_PROBE, rpi->forward_id, i, rpi->last_pass); } ReflectionProbe *probe = reflection_probe_owner.get_or_null(rpi->probe); @@ -1717,8 +1719,6 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c // hook for subclass to do further processing. RendererSceneRenderRD::get_singleton()->setup_added_reflection_probe(transform, extents); - - rpi->last_pass = RSG::rasterizer->get_frame_number(); } if (reflection_count) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 14605b308e..876bdf5c71 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -356,14 +356,12 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) #else if (surface_version != uint64_t(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION)) { - RS::_fix_surface_compatibility(new_surface); + RS::get_singleton()->fix_surface_compatibility(new_surface); surface_version = new_surface.format & (RS::ARRAY_FLAG_FORMAT_VERSION_MASK << RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT); ERR_FAIL_COND_MSG(surface_version != RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION, - "Surface version provided (" + - itos((surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + - ") does not match current version (" + - itos((RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + - ")"); + vformat("Surface version provided (%d) does not match current version (%d).", + (surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK, + (RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK)); } #endif @@ -432,6 +430,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->aabb = new_surface.aabb; s->bone_aabbs = new_surface.bone_aabbs; //only really useful for returning them. + s->mesh_to_skeleton_xform = p_surface.mesh_to_skeleton_xform; s->uv_scale = new_surface.uv_scale; @@ -593,7 +592,6 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer); // When using an uncompressed buffer with normals, but without tangents, we have to trim the padding. if (!(s.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (s.format & RS::ARRAY_FORMAT_NORMAL) && !(s.format & RS::ARRAY_FORMAT_TANGENT)) { - Vector<uint8_t> new_vertex_data; sd.vertex_data.resize(sd.vertex_data.size() - sizeof(uint16_t) * 2); } } @@ -620,6 +618,7 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; + sd.mesh_to_skeleton_xform = s.mesh_to_skeleton_xform; if (s.blend_shape_buffer.is_valid()) { sd.blend_shape_data = RD::get_singleton()->buffer_get_data(s.blend_shape_buffer); @@ -666,15 +665,16 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { for (uint32_t i = 0; i < mesh->surface_count; i++) { AABB laabb; - if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { - int bs = mesh->surfaces[i]->bone_aabbs.size(); - const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + const Mesh::Surface &surface = *mesh->surfaces[i]; + if ((surface.format & RS::ARRAY_FORMAT_BONES) && surface.bone_aabbs.size()) { + int bs = surface.bone_aabbs.size(); + const AABB *skbones = surface.bone_aabbs.ptr(); int sbs = skeleton->size; ERR_CONTINUE(bs > sbs); const float *baseptr = skeleton->data.ptr(); - bool first = true; + bool found_bone_aabb = false; if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { @@ -694,11 +694,13 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[1][1] = dataptr[5]; mtx.origin.y = dataptr[7]; - AABB baabb = mtx.xform(skbones[j]); + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); - if (first) { + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } @@ -726,21 +728,29 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[2][2] = dataptr[10]; mtx.origin.z = dataptr[11]; - AABB baabb = mtx.xform(skbones[j]); - if (first) { + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); + + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } } } + if (found_bone_aabb) { + // Transform skeleton bounds back to mesh's space if any animated AABB applied. + laabb = surface.mesh_to_skeleton_xform.affine_inverse().xform(laabb); + } + if (laabb.size == Vector3()) { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } } else { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } if (i == 0) { @@ -1543,7 +1553,7 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { memset(w, 0, buffer_size * sizeof(float)); } } - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); memset(multimesh->data_cache_dirty_regions, 0, data_cache_dirty_region_count * sizeof(bool)); multimesh->data_cache_dirty_region_count = 0; @@ -1571,7 +1581,7 @@ void MeshStorage::_multimesh_update_motion_vectors_data_cache(MultiMesh *multime uint32_t current_ofs = multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float); uint32_t previous_ofs = multimesh->motion_vectors_previous_offset * multimesh->stride_cache * sizeof(float); uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; - uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t visible_region_count = visible_instances == 0 ? 0 : Math::division_round_up(visible_instances, (uint32_t)MULTIMESH_DIRTY_REGION_SIZE); uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); uint32_t size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); for (uint32_t i = 0; i < visible_region_count; i++) { @@ -1591,7 +1601,7 @@ bool MeshStorage::_multimesh_uses_motion_vectors(MultiMesh *multimesh) { void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { uint32_t region_index = p_index / MULTIMESH_DIRTY_REGION_SIZE; #ifdef DEBUG_ENABLED - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); ERR_FAIL_UNSIGNED_INDEX(region_index, data_cache_dirty_region_count); //bug #endif if (!multimesh->data_cache_dirty_regions[region_index]) { @@ -1612,7 +1622,7 @@ void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb) { if (p_data) { - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { if (!multimesh->data_cache_dirty_regions[i]) { @@ -2011,8 +2021,8 @@ void MeshStorage::_update_dirty_multimeshes() { uint32_t total_dirty_regions = multimesh->data_cache_dirty_region_count + multimesh->previous_data_cache_dirty_region_count; if (total_dirty_regions != 0) { - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; - uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, (int)MULTIMESH_DIRTY_REGION_SIZE); + uint32_t visible_region_count = visible_instances == 0 ? 0 : Math::division_round_up(visible_instances, (uint32_t)MULTIMESH_DIRTY_REGION_SIZE); uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); if (total_dirty_regions > 32 || total_dirty_regions > visible_region_count / 2) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index f03334baac..0fc1a6f320 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -120,6 +120,10 @@ private: Vector<AABB> bone_aabbs; + // Transform used in runtime bone AABBs compute. + // As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + Vector4 uv_scale; RID blend_shape_buffer; @@ -469,7 +473,7 @@ public: } } - _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { + _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); s->version_lock.lock(); @@ -501,7 +505,7 @@ public: s->version_lock.unlock(); } - _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint64_t p_surface_index, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { + _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint64_t p_surface_index, uint64_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); ERR_FAIL_NULL(mi); Mesh *mesh = mi->mesh; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 5ff5adc59a..6a99eb4108 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -148,9 +148,6 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co update_samplers(); - // Notify the renderer the base uniform needs to be recreated. - RendererSceneRenderRD::get_singleton()->base_uniforms_changed(); - // cleanout any old buffers we had. cleanup(); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index 43704119e7..b2946e6bbc 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -58,6 +58,7 @@ #define RB_TEX_BLUR_1 SNAME("blur_1") #define RB_TEX_HALF_BLUR SNAME("half_blur") // only for raster! +#define RB_TEX_BACK_COLOR SNAME("back_color") #define RB_TEX_BACK_DEPTH SNAME("back_depth") class RenderSceneBuffersRD : public RenderSceneBuffers { @@ -267,7 +268,16 @@ public: } // back buffer (color) - RID get_back_buffer_texture() const { return has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) ? get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) : RID(); } // We (re)use our blur texture here. + RID get_back_buffer_texture() const { + // Prefer returning the dedicated backbuffer color texture if it was created. Return the reused blur texture otherwise. + if (has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR)) { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR); + } else if (has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0)) { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0); + } else { + return RID(); + } + } // Upscaled. void ensure_upscaled(); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 166b850864..380e325ffa 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1557,9 +1557,9 @@ uint64_t TextureStorage::texture_get_native_handle(RID p_texture, bool p_srgb) c ERR_FAIL_NULL_V(tex, 0); if (p_srgb && tex->rd_texture_srgb.is_valid()) { - return RD::get_singleton()->texture_get_native_handle(tex->rd_texture_srgb); + return RD::get_singleton()->get_driver_resource(RD::DRIVER_RESOURCE_TEXTURE, tex->rd_texture_srgb); } else { - return RD::get_singleton()->texture_get_native_handle(tex->rd_texture); + return RD::get_singleton()->get_driver_resource(RD::DRIVER_RESOURCE_TEXTURE, tex->rd_texture); } } @@ -2862,7 +2862,7 @@ void TextureStorage::update_decal_buffer(const PagedArray<RID> &p_decals, const Decal *decal = decal_sort[i].decal; if (using_forward_ids) { - forward_id_storage->map_forward_id(FORWARD_ID_TYPE_DECAL, decal_instance->forward_id, i); + forward_id_storage->map_forward_id(FORWARD_ID_TYPE_DECAL, decal_instance->forward_id, i, RSG::rasterizer->get_frame_number()); } decal_instance->cull_mask = decal->cull_mask; @@ -3364,6 +3364,31 @@ RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) con return rt->msaa; } +void TextureStorage::render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + + rt->msaa_needs_resolve = p_needs_resolve; +} + +bool TextureStorage::render_target_get_msaa_needs_resolve(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, false); + + return rt->msaa_needs_resolve; +} + +void TextureStorage::render_target_do_msaa_resolve(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + if (!rt->msaa_needs_resolve) { + return; + } + RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_end(); + rt->msaa_needs_resolve = false; +} + void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); @@ -3479,6 +3504,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); RD::get_singleton()->draw_list_end(); rt->clear_requested = false; + rt->msaa_needs_resolve = false; } void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { @@ -3511,7 +3537,9 @@ Rect2i TextureStorage::_render_target_get_sdf_rect(const RenderTarget *rt) const scale = 200; } break; default: { - } + ERR_PRINT("Invalid viewport SDF oversize, defaulting to 100%."); + scale = 100; + } break; } margin = (rt->size * scale / 100) - rt->size; @@ -3603,6 +3631,7 @@ void TextureStorage::_render_target_allocate_sdf(RenderTarget *rt) { scale = 25; } break; default: { + ERR_PRINT("Invalid viewport SDF scale, defaulting to 100%."); scale = 100; } break; } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 276c8c4ce2..c665b02e14 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -331,9 +331,10 @@ private: uint32_t view_count; RID color; Vector<RID> color_slices; - RID color_multisample; // Needed when MSAA is enabled. + RID color_multisample; // Needed when 2D MSAA is enabled. - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; // 2D MSAA mode + bool msaa_needs_resolve = false; // 2D MSAA needs resolved //used for retrieving from CPU RD::DataFormat color_format = RD::DATA_FORMAT_R4G4_UNORM_PACK8; @@ -718,6 +719,9 @@ public: virtual void render_target_set_as_unused(RID p_render_target) override; virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; + virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override; + virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override; + virtual void render_target_do_msaa_resolve(RID p_render_target) override; virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) override; virtual bool render_target_is_using_hdr(RID p_render_target) const override; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 1c5cbe9612..73aacf311f 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -2239,7 +2239,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in // This trick here is what stabilizes the shadow (make potential jaggies to not move) // at the cost of some wasted resolution. Still, the quality increase is very well worth it. - const real_t unit = (radius + soft_shadow_expand) * 2.0 / texture_size; + const real_t unit = (radius + soft_shadow_expand) * 4.0 / texture_size; x_max_cam = Math::snapped(x_vec.dot(center) + radius + soft_shadow_expand, unit); x_min_cam = Math::snapped(x_vec.dot(center) - radius - soft_shadow_expand, unit); y_max_cam = Math::snapped(y_vec.dot(center) + radius + soft_shadow_expand, unit); diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index a389e3e767..cd89bb314b 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -47,6 +47,11 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, taa_jitter = p_taa_jitter; } +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect) { ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view"); diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 34d6a93e36..97e9694fe1 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -116,19 +116,30 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { if (p_viewport->size.width == 0 || p_viewport->size.height == 0) { p_viewport->render_buffers.unref(); } else { + const float EPSILON = 0.0001; float scaling_3d_scale = p_viewport->scaling_3d_scale; RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode; + bool upscaler_available = p_viewport->fsr_enabled; + + if ((!upscaler_available || scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_BILINEAR || scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) && scaling_3d_scale >= (1.0 - EPSILON) && scaling_3d_scale <= (1.0 + EPSILON)) { + // No 3D scaling on bilinear or FSR? Ignore scaling mode, this just introduces overhead. + // - Mobile can't perform optimal path + // - FSR does an extra pass (or 2 extra passes if 2D-MSAA is enabled) + // Scaling = 1.0 on FSR2 has benefits + scaling_3d_scale = 1.0; + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } + bool scaling_3d_is_fsr = (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) || (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2); bool use_taa = p_viewport->use_taa; - if (scaling_3d_is_fsr && (scaling_3d_scale > 1.0)) { + if (scaling_3d_is_fsr && (scaling_3d_scale >= (1.0 + EPSILON))) { // FSR is not designed for downsampling. // Fall back to bilinear scaling. WARN_PRINT_ONCE("FSR 3D resolution scaling is not designed for downsampling. Falling back to bilinear 3D resolution scaling."); scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; } - bool upscaler_available = p_viewport->fsr_enabled; if (scaling_3d_is_fsr && !upscaler_available) { // FSR is not actually available. // Fall back to bilinear scaling. @@ -143,8 +154,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { use_taa = false; } - int width; - int height; + int target_width; + int target_height; int render_width; int render_height; @@ -152,40 +163,40 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR: // Clamp 3D rendering resolution to reasonable values supported on most hardware. // This prevents freezing the engine or outright crashing on lower-end GPUs. - width = CLAMP(p_viewport->size.width * scaling_3d_scale, 1, 16384); - height = CLAMP(p_viewport->size.height * scaling_3d_scale, 1, 16384); - render_width = width; - render_height = height; + target_width = p_viewport->size.width; + target_height = p_viewport->size.height; + render_width = CLAMP(target_width * scaling_3d_scale, 1, 16384); + render_height = CLAMP(target_height * scaling_3d_scale, 1, 16384); break; case RS::VIEWPORT_SCALING_3D_MODE_FSR: case RS::VIEWPORT_SCALING_3D_MODE_FSR2: - width = p_viewport->size.width; - height = p_viewport->size.height; - render_width = MAX(width * scaling_3d_scale, 1.0); // width / (width * scaling) - render_height = MAX(height * scaling_3d_scale, 1.0); + target_width = p_viewport->size.width; + target_height = p_viewport->size.height; + render_width = MAX(target_width * scaling_3d_scale, 1.0); // target_width / (target_width * scaling) + render_height = MAX(target_height * scaling_3d_scale, 1.0); break; case RS::VIEWPORT_SCALING_3D_MODE_OFF: - width = p_viewport->size.width; - height = p_viewport->size.height; - render_width = width; - render_height = height; + target_width = p_viewport->size.width; + target_height = p_viewport->size.height; + render_width = target_width; + render_height = target_height; break; default: // This is an unknown mode. WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling.", scaling_3d_mode)); scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; scaling_3d_scale = 1.0; - width = p_viewport->size.width; - height = p_viewport->size.height; - render_width = width; - render_height = height; + target_width = p_viewport->size.width; + target_height = p_viewport->size.height; + render_width = target_width; + render_height = target_height; break; } uint32_t jitter_phase_count = 0; if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2) { // Implementation has been copied from ffxFsr2GetJitterPhaseCount. - jitter_phase_count = uint32_t(8.0f * pow(float(width) / render_width, 2.0f)); + jitter_phase_count = uint32_t(8.0f * pow(float(target_width) / render_width, 2.0f)); } else if (use_taa) { // Default jitter count for TAA. jitter_phase_count = 16; @@ -201,7 +212,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { RenderSceneBuffersConfiguration rb_config; rb_config.set_render_target(p_viewport->render_target); rb_config.set_internal_size(Size2i(render_width, render_height)); - rb_config.set_target_size(Size2(width, height)); + rb_config.set_target_size(Size2(target_width, target_height)); rb_config.set_view_count(p_viewport->view_count); rb_config.set_scaling_3d_mode(scaling_3d_mode); rb_config.set_msaa_3d(p_viewport->msaa_3d); @@ -259,6 +270,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { /* Camera should always be BEFORE any other 3D */ + bool can_draw_2d = !p_viewport->disable_2d && p_viewport->view_count == 1; // Stereo rendering does not support 2D, no depth data bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front int scenario_canvas_max_layer = 0; bool force_clear_render_target = false; @@ -272,7 +284,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { if (RSG::scene->is_scenario(p_viewport->scenario)) { RID environment = RSG::scene->scenario_get_environment(p_viewport->scenario); if (RSG::scene->is_environment(environment)) { - if (!p_viewport->disable_2d && !viewport_is_environment_disabled(p_viewport)) { + if (can_draw_2d && !viewport_is_environment_disabled(p_viewport)) { scenario_draw_canvas_bg = RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS; scenario_canvas_max_layer = RSG::scene->environment_get_canvas_max_layer(environment); } else if (RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS) { @@ -307,7 +319,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { _draw_3d(p_viewport); } - if (!p_viewport->disable_2d) { + if (can_draw_2d) { RBMap<Viewport::CanvasKey, Viewport::CanvasData *> canvas_map; Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y); @@ -574,7 +586,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { ptr = ptr->filter_next_ptr; } - RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel, p_viewport->canvas_cull_mask); + RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel, p_viewport->canvas_cull_mask, &p_viewport->render_info); if (RSG::canvas->was_sdf_used()) { p_viewport->sdf_active = true; } @@ -610,6 +622,11 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); } + if (RSG::texture_storage->render_target_get_msaa_needs_resolve(p_viewport->render_target)) { + WARN_PRINT_ONCE("2D MSAA is enabled while there is no 2D content. Disable 2D MSAA for better performance."); + RSG::texture_storage->render_target_do_msaa_resolve(p_viewport->render_target); + } + if (p_viewport->measure_render_time) { String rt_id = "vp_end_" + itos(p_viewport->self.get_id()); RSG::utilities->capture_timestamp(rt_id); @@ -789,9 +806,14 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { RENDER_TIMESTAMP("< Render Viewport " + itos(i)); + // 3D render info. objects_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]; vertices_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]; draw_calls_used += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]; + // 2D render info. + objects_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]; + vertices_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]; + draw_calls_used += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]; } RSG::scene->set_debug_draw_mode(RS::VIEWPORT_DEBUG_DRAW_DISABLED); @@ -869,9 +891,7 @@ void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpn ERR_FAIL_NULL(viewport); viewport->fsr_sharpness = p_sharpness; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_fsr_sharpness(p_sharpness); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) { @@ -879,9 +899,7 @@ void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_ ERR_FAIL_NULL(viewport); viewport->texture_mipmap_bias = p_mipmap_bias; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_texture_mipmap_bias(p_mipmap_bias); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) { @@ -1251,9 +1269,7 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb return; } viewport->use_debanding = p_use_debanding; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_use_debanding(p_use_debanding); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { @@ -1375,7 +1391,7 @@ void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::V RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const { RID *rids = nullptr; uint32_t rid_count = viewport_owner.get_rid_count(); - rids = (RID *)alloca(sizeof(RID *) * rid_count); + rids = (RID *)alloca(sizeof(RID) * rid_count); viewport_owner.fill_owned_buffer(rids); for (uint32_t i = 0; i < rid_count; i++) { Viewport *viewport = viewport_owner.get_or_null(rids[i]); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index c2fa232eac..30bf47a096 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -33,17 +33,13 @@ #include "rendering_device_binds.h" -#include "thirdparty/spirv-reflect/spirv_reflect.h" +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "servers/rendering/renderer_rd/api_context_rd.h" -RenderingDevice *RenderingDevice::singleton = nullptr; +//#define FORCE_FULL_BARRIER -const char *RenderingDevice::shader_stage_names[RenderingDevice::SHADER_STAGE_MAX] = { - "Vertex", - "Fragment", - "TesselationControl", - "TesselationEvaluation", - "Compute", -}; +RenderingDevice *RenderingDevice::singleton = nullptr; RenderingDevice *RenderingDevice::get_singleton() { return singleton; @@ -53,6 +49,50 @@ RenderingDevice::ShaderCompileToSPIRVFunction RenderingDevice::compile_to_spirv_ RenderingDevice::ShaderCacheFunction RenderingDevice::cache_function = nullptr; RenderingDevice::ShaderSPIRVGetCacheKeyFunction RenderingDevice::get_spirv_cache_key_function = nullptr; +/***************************/ +/**** ID INFRASTRUCTURE ****/ +/***************************/ + +void RenderingDevice::_add_dependency(RID p_id, RID p_depends_on) { + if (!dependency_map.has(p_depends_on)) { + dependency_map[p_depends_on] = HashSet<RID>(); + } + + dependency_map[p_depends_on].insert(p_id); + + if (!reverse_dependency_map.has(p_id)) { + reverse_dependency_map[p_id] = HashSet<RID>(); + } + + reverse_dependency_map[p_id].insert(p_depends_on); +} + +void RenderingDevice::_free_dependencies(RID p_id) { + // Direct dependencies must be freed. + + HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); + if (E) { + while (E->value.size()) { + free(*E->value.begin()); + } + dependency_map.remove(E); + } + + // Reverse dependencies must be unreferenced. + E = reverse_dependency_map.find(p_id); + + if (E) { + for (const RID &F : E->value) { + HashMap<RID, HashSet<RID>>::Iterator G = dependency_map.find(F); + ERR_CONTINUE(!G); + ERR_CONTINUE(!G->value.has(p_id)); + G->value.erase(p_id); + } + + reverse_dependency_map.remove(E); + } +} + void RenderingDevice::shader_set_compile_to_spirv_function(ShaderCompileToSPIRVFunction p_function) { compile_to_spirv_function = p_function; } @@ -91,625 +131,5969 @@ RID RenderingDevice::shader_create_from_spirv(const Vector<ShaderStageSPIRVData> return shader_create_from_bytecode(bytecode); } -RID RenderingDevice::_texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data) { - ERR_FAIL_COND_V(p_format.is_null(), RID()); - ERR_FAIL_COND_V(p_view.is_null(), RID()); - Vector<Vector<uint8_t>> data; - for (int i = 0; i < p_data.size(); i++) { - Vector<uint8_t> byte_slice = p_data[i]; - ERR_FAIL_COND_V(byte_slice.is_empty(), RID()); - data.push_back(byte_slice); +/******************/ +/**** BARRIERS ****/ +/******************/ + +void RenderingDevice::_full_barrier(bool p_sync_with_draw) { + // Used for debug. + + RDD::MemoryBarrier mb; + mb.src_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | + RDD::BARRIER_ACCESS_INDEX_READ_BIT | + RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | + RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | + RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | + RDD::BARRIER_ACCESS_HOST_READ_BIT | + RDD::BARRIER_ACCESS_HOST_WRITE_BIT); + mb.dst_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | + RDD::BARRIER_ACCESS_INDEX_READ_BIT | + RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | + RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | + RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | + RDD::BARRIER_ACCESS_HOST_READ_BIT | + RDD::BARRIER_ACCESS_HOST_WRITE_BIT); + + RDD::CommandBufferID cmd_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + driver->command_pipeline_barrier(cmd_buffer, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, mb, {}, {}); +} + +/***************************/ +/**** BUFFER MANAGEMENT ****/ +/***************************/ + +RenderingDevice::Buffer *RenderingDevice::_get_buffer_from_owner(RID p_buffer, BitField<RDD::PipelineStageBits> &r_stages, BitField<RDD::BarrierAccessBits> &r_access, BitField<BarrierMask> p_post_barrier) { + Buffer *buffer = nullptr; + r_stages.clear(); + r_access.clear(); + if (vertex_buffer_owner.owns(p_buffer)) { + buffer = vertex_buffer_owner.get_or_null(p_buffer); + + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); + if (buffer->usage & RDD::BUFFER_USAGE_STORAGE_BIT) { + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + } + } else if (index_buffer_owner.owns(p_buffer)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT); + buffer = index_buffer_owner.get_or_null(p_buffer); + } else if (uniform_buffer_owner.owns(p_buffer)) { + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + r_access.set_flag(RDD::BARRIER_ACCESS_UNIFORM_READ_BIT); + buffer = uniform_buffer_owner.get_or_null(p_buffer); + } else if (texture_buffer_owner.owns(p_buffer)) { + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); + } + + // FIXME: Broken. + //buffer = texture_buffer_owner.get_or_null(p_buffer)->buffer; + } else if (storage_buffer_owner.owns(p_buffer)) { + buffer = storage_buffer_owner.get_or_null(p_buffer); + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + + if (buffer->usage.has_flag(RDD::BUFFER_USAGE_INDIRECT_BIT)) { + r_stages.set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); + r_access.set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); + } } - return texture_create(p_format->base, p_view->base, data); + return buffer; } -RID RenderingDevice::_texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture) { - ERR_FAIL_COND_V(p_view.is_null(), RID()); +Error RenderingDevice::_insert_staging_block() { + StagingBufferBlock block; - return texture_create_shared(p_view->base, p_with_texture); + block.driver_id = driver->buffer_create(staging_buffer_block_size, RDD::BUFFER_USAGE_TRANSFER_FROM_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU); + ERR_FAIL_COND_V(!block.driver_id, ERR_CANT_CREATE); + + block.frame_used = 0; + block.fill_amount = 0; + + staging_buffer_blocks.insert(staging_buffer_current, block); + return OK; } -RID RenderingDevice::_texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type) { - ERR_FAIL_COND_V(p_view.is_null(), RID()); +Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment) { + // Determine a block to use. - return texture_create_shared_from_slice(p_view->base, p_with_texture, p_layer, p_mipmap, p_mipmaps, p_slice_type); + r_alloc_size = p_amount; + + while (true) { + r_alloc_offset = 0; + + // See if we can use current block. + if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { + // We used this block this frame, let's see if there is still room. + + uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; + + { + uint32_t align_remainder = write_from % p_required_align; + if (align_remainder != 0) { + write_from += p_required_align - align_remainder; + } + } + + int32_t available_bytes = int32_t(staging_buffer_block_size) - int32_t(write_from); + + if ((int32_t)p_amount < available_bytes) { + // All is good, we should be ok, all will fit. + r_alloc_offset = write_from; + } else if (p_can_segment && available_bytes >= (int32_t)p_required_align) { + // Ok all won't fit but at least we can fit a chunkie. + // All is good, update what needs to be written to. + r_alloc_offset = write_from; + r_alloc_size = available_bytes - (available_bytes % p_required_align); + + } else { + // Can't fit it into this buffer. + // Will need to try next buffer. + + staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); + + // Before doing anything, though, let's check that we didn't manage to fill all blocks. + // Possible in a single frame. + if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { + // Guess we did.. ok, let's see if we can insert a new block. + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + // We can, so we are safe. + Error err = _insert_staging_block(); + if (err) { + return err; + } + // Claim for this frame. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } else { + // Ok, worst case scenario, all the staging buffers belong to this frame + // and this frame is not even done. + // If this is the main thread, it means the user is likely loading a lot of resources at once,. + // Otherwise, the thread should just be blocked until the next frame (currently unimplemented). + + if (false) { // Separate thread from render. + + //block_until_next_frame() + continue; + } else { + // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. + _flush(true); + + // Clear the whole staging buffer. + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + staging_buffer_blocks.write[i].frame_used = 0; + staging_buffer_blocks.write[i].fill_amount = 0; + } + // Claim current. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } + } + + } else { + // Not from current frame, so continue and try again. + continue; + } + } + + } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frame_count) { + // This is an old block, which was already processed, let's reuse. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; + } else { + // This block may still be in use, let's not touch it unless we have to, so.. can we create a new one? + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + // We are still allowed to create a new block, so let's do that and insert it for current pos. + Error err = _insert_staging_block(); + if (err) { + return err; + } + // Claim for this frame. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } else { + // Oops, we are out of room and we can't create more. + // Let's flush older frames. + // The logic here is that if a game is loading a lot of data from the main thread, it will need to be stalled anyway. + // If loading from a separate thread, we can block that thread until next frame when more room is made (not currently implemented, though). + + if (false) { + // Separate thread from render. + //block_until_next_frame() + continue; // And try again. + } else { + _flush(false); + + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + // Clear all blocks but the ones from this frame. + int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); + if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { + break; // Ok, we reached something from this frame, abort. + } + + staging_buffer_blocks.write[block_idx].frame_used = 0; + staging_buffer_blocks.write[block_idx].fill_amount = 0; + } + + // Claim for current frame. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } + } + } + + // All was good, break. + break; + } + + staging_buffer_used = true; + + return OK; } -Ref<RDTextureFormat> RenderingDevice::_texture_get_format(RID p_rd_texture) { - Ref<RDTextureFormat> rtf; - rtf.instantiate(); - rtf->base = texture_get_format(p_rd_texture); +Error RenderingDevice::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer, uint32_t p_required_align) { + // Submitting may get chunked for various reasons, so convert this to a task. + size_t to_submit = p_data_size; + size_t submit_from = 0; - return rtf; + while (to_submit > 0) { + uint32_t block_write_offset; + uint32_t block_write_amount; + + Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount); + if (err) { + return err; + } + + // Map staging buffer (It's CPU and coherent). + uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); + ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); + + // Copy to staging buffer. + memcpy(data_ptr + block_write_offset, p_data + submit_from, block_write_amount); + + // Unmap. + driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id); + + // Insert a command to copy this. + + RDD::BufferCopyRegion region; + region.src_offset = block_write_offset; + region.dst_offset = submit_from + p_offset; + region.size = block_write_amount; + driver->command_copy_buffer(p_use_draw_command_buffer ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, p_buffer->driver_id, region); + + staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount; + + to_submit -= block_write_amount; + submit_from += block_write_amount; + } + + return OK; } -RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count) { - Vector<AttachmentFormat> attachments; - attachments.resize(p_attachments.size()); +Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a compute list"); + + // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed + // for the source or destination buffers before performing the copy. These masks are effectively ignored. + BitField<RDD::PipelineStageBits> src_stages; + BitField<RDD::BarrierAccessBits> src_access; + Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stages, src_access, BARRIER_MASK_NO_BARRIER); + if (!src_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); + } - for (int i = 0; i < p_attachments.size(); i++) { - Ref<RDAttachmentFormat> af = p_attachments[i]; - ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); - attachments.write[i] = af->base; + BitField<RDD::PipelineStageBits> dst_stages; + BitField<RDD::BarrierAccessBits> dst_access; + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set + // after the copy command is queued. + dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); } - return framebuffer_format_create(attachments, p_view_count); + + Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stages, dst_access, p_post_barrier); + if (!dst_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type."); + } + + // Validate the copy's dimensions for both buffers. + ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer."); + ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer."); + + // Perform the copy. + RDD::BufferCopyRegion region; + region.src_offset = p_src_offset; + region.dst_offset = p_dst_offset; + region.size = p_size; + driver->command_copy_buffer(frames[frame].draw_command_buffer, src_buffer->driver_id, dst_buffer->driver_id, region); + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS) && p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { + if (dst_stages.is_empty()) { + dst_stages = RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + + // As indicated by the post barrier mask, push a new barrier. + RDD::BufferBarrier bb; + bb.buffer = dst_buffer->driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = dst_access; + bb.offset = p_dst_offset; + bb.size = p_size; + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); + } +#endif + + return OK; } -RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count) { - Vector<AttachmentFormat> attachments; - attachments.resize(p_attachments.size()); +Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Updating buffers is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Updating buffers is forbidden during creation of a compute list"); + + BitField<RDD::PipelineStageBits> dst_stages; + BitField<RDD::BarrierAccessBits> dst_access; + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + // Protect subsequent updates. + dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stages, dst_access, p_post_barrier); + if (!buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); + } + + ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, + "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); + + Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, true); + if (err) { + return err; + } + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + if (dst_stages.is_empty()) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS) && p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { + RDD::BufferBarrier bb; + bb.buffer = buffer->driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = dst_access; + bb.offset = p_offset; + bb.size = p_size; + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); + } + +#endif + return err; +} + +Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG((p_size % 4) != 0, ERR_INVALID_PARAMETER, + "Size must be a multiple of four"); + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Updating buffers in is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Updating buffers is forbidden during creation of a compute list"); + + BitField<RDD::PipelineStageBits> dst_stages; + BitField<RDD::BarrierAccessBits> dst_access; + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + // Protect subsequent updates. + dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + + Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stages, dst_access, p_post_barrier); + if (!buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); + } + + ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, + "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); + + driver->command_clear_buffer(frames[frame].draw_command_buffer, buffer->driver_id, p_offset, p_size); + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + if (dst_stages.is_empty()) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = buffer->driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = dst_access; + bb.offset = p_offset; + bb.size = p_size; + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); + } + +#endif + return OK; +} + +Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset, uint32_t p_size) { + _THREAD_SAFE_METHOD_ + + // It could be this buffer was just created. + BitField<RDD::PipelineStageBits> src_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + BitField<RDD::BarrierAccessBits> src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + // Get the vulkan buffer and the potential stage/access possible. + Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stages, src_access, BARRIER_MASK_ALL_BARRIERS); + if (!buffer) { + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); + } + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // Make sure no one is using the buffer -- the "true" gets us to the same command buffer as below. + RDD::BufferBarrier bb; + bb.buffer = buffer->driver_id; + bb.src_access = src_access; + bb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + bb.size = buffer->size; + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stages, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, bb, {}); + } + + // Size of buffer to retrieve. + if (!p_size) { + p_size = buffer->size; + } else { + ERR_FAIL_COND_V_MSG(p_size + p_offset > buffer->size, Vector<uint8_t>(), + "Size is larger than the buffer."); + } + + RDD::BufferID tmp_buffer = driver->buffer_create(buffer->size, RDD::BUFFER_USAGE_TRANSFER_TO_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU); + ERR_FAIL_COND_V(!tmp_buffer, Vector<uint8_t>()); + + RDD::BufferCopyRegion region; + region.src_offset = p_offset; + region.size = p_size; + driver->command_copy_buffer(frames[frame].draw_command_buffer, buffer->driver_id, tmp_buffer, region); + // Flush everything so memory can be safely mapped. + _flush(true); + + uint8_t *buffer_mem = driver->buffer_map(tmp_buffer); + ERR_FAIL_COND_V(!buffer_mem, Vector<uint8_t>()); + + Vector<uint8_t> buffer_data; + { + buffer_data.resize(p_size); + uint8_t *w = buffer_data.ptrw(); + memcpy(w, buffer_mem, p_size); + } + + driver->buffer_unmap(tmp_buffer); + + driver->buffer_free(tmp_buffer); + + return buffer_data; +} + +RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + buffer.size = p_size_bytes; + buffer.usage = (RDD::BUFFER_USAGE_TRANSFER_FROM_BIT | RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_STORAGE_BIT); + if (p_usage.has_flag(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT)) { + buffer.usage.set_flag(RDD::BUFFER_USAGE_INDIRECT_BIT); + } + buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); + ERR_FAIL_COND_V(!buffer.driver_id, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + const uint8_t *r = p_data.ptr(); + _buffer_update(&buffer, 0, r, data_size); + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = buffer.driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + bb.size = data_size; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, bb, {}); + } + } + + buffer_memory += buffer.size; + + return storage_buffer_owner.make_rid(buffer); +} + +RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) { + _THREAD_SAFE_METHOD_ + + uint32_t element_size = get_format_vertex_size(p_format); + ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers"); + uint64_t size_bytes = uint64_t(element_size) * p_size_elements; + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != size_bytes, RID()); + + Buffer texture_buffer; + texture_buffer.size = size_bytes; + BitField<RDD::BufferUsageBits> usage = (RDD::BUFFER_USAGE_TRANSFER_FROM_BIT | RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_TEXEL_BIT); + texture_buffer.driver_id = driver->buffer_create(size_bytes, usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); + ERR_FAIL_COND_V(!texture_buffer.driver_id, RID()); + + bool ok = driver->buffer_set_texel_format(texture_buffer.driver_id, p_format); + if (!ok) { + driver->buffer_free(texture_buffer.driver_id); + ERR_FAIL_V(RID()); + } + + if (p_data.size()) { + _buffer_update(&texture_buffer, 0, p_data.ptr(), p_data.size()); + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = texture_buffer.driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; + bb.size = size_bytes; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, (RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT), {}, bb, {}); + } + } + + buffer_memory += size_bytes; + + RID id = texture_buffer_owner.make_rid(texture_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; +} + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +RID RenderingDevice::texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data) { + _THREAD_SAFE_METHOD_ + + // Some adjustments will happen. + TextureFormat format = p_format; + + if (format.shareable_formats.size()) { + ERR_FAIL_COND_V_MSG(format.shareable_formats.find(format.format) == -1, RID(), + "If supplied a list of shareable formats, the current format must be present in the list"); + ERR_FAIL_COND_V_MSG(p_view.format_override != DATA_FORMAT_MAX && format.shareable_formats.find(p_view.format_override) == -1, RID(), + "If supplied a list of shareable formats, the current view format override must be present in the list"); + } + + ERR_FAIL_INDEX_V(format.texture_type, RDD::TEXTURE_TYPE_MAX, RID()); + + ERR_FAIL_COND_V_MSG(format.width < 1, RID(), "Width must be equal or greater than 1 for all textures"); + + if (format.texture_type != TEXTURE_TYPE_1D && format.texture_type != TEXTURE_TYPE_1D_ARRAY) { + ERR_FAIL_COND_V_MSG(format.height < 1, RID(), "Height must be equal or greater than 1 for 2D and 3D textures"); + } + + if (format.texture_type == TEXTURE_TYPE_3D) { + ERR_FAIL_COND_V_MSG(format.depth < 1, RID(), "Depth must be equal or greater than 1 for 3D textures"); + } + + ERR_FAIL_COND_V(format.mipmaps < 1, RID()); + + if (format.texture_type == TEXTURE_TYPE_1D_ARRAY || format.texture_type == TEXTURE_TYPE_2D_ARRAY || format.texture_type == TEXTURE_TYPE_CUBE_ARRAY || format.texture_type == TEXTURE_TYPE_CUBE) { + ERR_FAIL_COND_V_MSG(format.array_layers < 1, RID(), + "Amount of layers must be equal or greater than 1 for arrays and cubemaps."); + ERR_FAIL_COND_V_MSG((format.texture_type == TEXTURE_TYPE_CUBE_ARRAY || format.texture_type == TEXTURE_TYPE_CUBE) && (format.array_layers % 6) != 0, RID(), + "Cubemap and cubemap array textures must provide a layer number that is multiple of 6"); + } else { + format.array_layers = 1; + } + + ERR_FAIL_INDEX_V(format.samples, TEXTURE_SAMPLES_MAX, RID()); + + format.height = format.texture_type != TEXTURE_TYPE_1D && format.texture_type != TEXTURE_TYPE_1D_ARRAY ? format.height : 1; + format.depth = format.texture_type == TEXTURE_TYPE_3D ? format.depth : 1; + + uint32_t required_mipmaps = get_image_required_mipmaps(format.width, format.height, format.depth); + + ERR_FAIL_COND_V_MSG(required_mipmaps < format.mipmaps, RID(), + "Too many mipmaps requested for texture format and dimensions (" + itos(format.mipmaps) + "), maximum allowed: (" + itos(required_mipmaps) + ")."); + + if (p_data.size()) { + ERR_FAIL_COND_V_MSG(!(format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT), RID(), + "Texture needs the TEXTURE_USAGE_CAN_UPDATE_BIT usage flag in order to be updated at initialization or later"); + + ERR_FAIL_COND_V_MSG(p_data.size() != (int)format.array_layers, RID(), + "Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(format.array_layers) + ")."); + + for (uint32_t i = 0; i < format.array_layers; i++) { + uint32_t required_size = get_image_format_required_size(format.format, format.width, format.height, format.depth, format.mipmaps); + ERR_FAIL_COND_V_MSG((uint32_t)p_data[i].size() != required_size, RID(), + "Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ")."); + } + } + + { + // Validate that this image is supported for the intended use. + bool cpu_readable = (format.usage_bits & RDD::TEXTURE_USAGE_CPU_READ_BIT); + BitField<RDD::TextureUsageBits> supported_usage = driver->texture_get_usages_supported_by_format(format.format, cpu_readable); + + String format_text = "'" + String(FORMAT_NAMES[format.format]) + "'"; + + if ((format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_SAMPLING_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as sampling texture."); + } + if ((format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as color attachment."); + } + if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as depth-stencil attachment."); + } + if ((format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_STORAGE_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as storage image."); + } + if ((format.usage_bits & TEXTURE_USAGE_STORAGE_ATOMIC_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_STORAGE_ATOMIC_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image."); + } + if ((format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment."); + } + } + + // Transfer and validate view info. + + RDD::TextureView tv; + if (p_view.format_override == DATA_FORMAT_MAX) { + tv.format = format.format; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + tv.format = p_view.format_override; + } + ERR_FAIL_INDEX_V(p_view.swizzle_r, TEXTURE_SWIZZLE_MAX, RID()); + ERR_FAIL_INDEX_V(p_view.swizzle_g, TEXTURE_SWIZZLE_MAX, RID()); + ERR_FAIL_INDEX_V(p_view.swizzle_b, TEXTURE_SWIZZLE_MAX, RID()); + ERR_FAIL_INDEX_V(p_view.swizzle_a, TEXTURE_SWIZZLE_MAX, RID()); + tv.swizzle_r = p_view.swizzle_r; + tv.swizzle_g = p_view.swizzle_g; + tv.swizzle_b = p_view.swizzle_b; + tv.swizzle_a = p_view.swizzle_a; + + // Create. + + Texture texture; + + texture.driver_id = driver->texture_create(format, tv); + ERR_FAIL_COND_V(!texture.driver_id, RID()); + texture.type = format.texture_type; + texture.format = format.format; + texture.width = format.width; + texture.height = format.height; + texture.depth = format.depth; + texture.layers = format.array_layers; + texture.mipmaps = format.mipmaps; + texture.base_mipmap = 0; + texture.base_layer = 0; + texture.is_resolve_buffer = format.is_resolve_buffer; + texture.usage_flags = format.usage_bits; + texture.samples = format.samples; + texture.allowed_shared_formats = format.shareable_formats; + + // Set base layout based on usage priority. + + if ((format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT)) { + // First priority, readable. + texture.layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } else if ((format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) { + // Second priority, storage. + texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; + } else if ((format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + // Third priority, color or depth. + texture.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture.layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; + } + + if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); + if (format_has_stencil(format.format)) { + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_STENCIL_BIT); + } + } else { + texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); + } + + texture.bound = false; + + // Barrier to set layout. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = texture.driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; + tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + tb.next_layout = texture.layout; + tb.subresources.aspect = texture.barrier_aspect_flags; + tb.subresources.mipmap_count = format.mipmaps; + tb.subresources.layer_count = format.array_layers; + + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, tb); + } + + texture_memory += driver->texture_get_allocation_size(texture.driver_id); + + RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + + if (p_data.size()) { + for (uint32_t i = 0; i < p_format.array_layers; i++) { + _texture_update(id, i, p_data[i], BARRIER_MASK_ALL_BARRIERS, true); + } + } + return id; +} + +RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with_texture) { + _THREAD_SAFE_METHOD_ + + Texture *src_texture = texture_owner.get_or_null(p_with_texture); + ERR_FAIL_COND_V(!src_texture, RID()); + + if (src_texture->owner.is_valid()) { // Ahh this is a share. The RenderingDeviceDriver needs the actual owner. + p_with_texture = src_texture->owner; + src_texture = texture_owner.get_or_null(src_texture->owner); + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. + } + + // Create view. + + Texture texture = *src_texture; + + RDD::TextureView tv; + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { + tv.format = texture.format; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + + ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), + "Format override is not in the list of allowed shareable formats for original texture."); + tv.format = p_view.format_override; + } + tv.swizzle_r = p_view.swizzle_r; + tv.swizzle_g = p_view.swizzle_g; + tv.swizzle_b = p_view.swizzle_b; + tv.swizzle_a = p_view.swizzle_a; + + texture.driver_id = driver->texture_create_shared(texture.driver_id, tv); + ERR_FAIL_COND_V(!texture.driver_id, RID()); + + texture.owner = p_with_texture; + RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + _add_dependency(id, p_with_texture); + + return id; +} + +RID RenderingDevice::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_usage, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) { + _THREAD_SAFE_METHOD_ + // This method creates a texture object using a VkImage created by an extension, module or other external source (OpenXR uses this). + + Texture texture; + texture.type = p_type; + texture.format = p_format; + texture.samples = p_samples; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.mipmaps = 1; + texture.usage_flags = p_usage; + texture.base_mipmap = 0; + texture.base_layer = 0; + texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); + texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); + + // Set base layout based on usage priority. + + if (p_usage.has_flag(TEXTURE_USAGE_SAMPLING_BIT)) { + // First priority, readable. + texture.layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } else if (p_usage.has_flag(TEXTURE_USAGE_STORAGE_BIT)) { + // Second priority, storage. + texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; + } else if (p_usage.has_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + // Third priority, color or depth. + texture.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if (p_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture.layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; + } + + if (p_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); + /*if (format_has_stencil(p_format.format)) { + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_STENCIL_BIT); + }*/ + } else { + texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); + texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); + } + + texture.driver_id = driver->texture_create_from_extension(p_image, p_type, p_format, p_layers, (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)); + ERR_FAIL_COND_V(!texture.driver_id, RID()); + + // Barrier to set layout. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = texture.driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; + tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + tb.next_layout = texture.layout; + tb.subresources.aspect = texture.barrier_aspect_flags; + tb.subresources.mipmap_count = texture.mipmaps; + tb.subresources.layer_count = texture.layers; + + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT, {}, {}, tb); + } + + RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + + return id; +} + +RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type, uint32_t p_layers) { + _THREAD_SAFE_METHOD_ + + Texture *src_texture = texture_owner.get_or_null(p_with_texture); + ERR_FAIL_COND_V(!src_texture, RID()); + + if (src_texture->owner.is_valid()) { // // Ahh this is a share. The RenderingDeviceDriver needs the actual owner. + p_with_texture = src_texture->owner; + src_texture = texture_owner.get_or_null(src_texture->owner); + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. + } + + ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_CUBEMAP && (src_texture->type != TEXTURE_TYPE_CUBE && src_texture->type != TEXTURE_TYPE_CUBE_ARRAY), RID(), + "Can only create a cubemap slice from a cubemap or cubemap array mipmap"); + + ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_3D && src_texture->type != TEXTURE_TYPE_3D, RID(), + "Can only create a 3D slice from a 3D texture"); + + ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_2D_ARRAY && (src_texture->type != TEXTURE_TYPE_2D_ARRAY), RID(), + "Can only create an array slice from a 2D array mipmap"); + + // Create view. + + ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); + ERR_FAIL_COND_V(p_mipmap + p_mipmaps > src_texture->mipmaps, RID()); + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, src_texture->layers, RID()); + + int slice_layers = 1; + if (p_layers != 0) { + ERR_FAIL_COND_V_MSG(p_layers > 1 && p_slice_type != TEXTURE_SLICE_2D_ARRAY, RID(), "layer slicing only supported for 2D arrays"); + ERR_FAIL_COND_V_MSG(p_layer + p_layers > src_texture->layers, RID(), "layer slice is out of bounds"); + slice_layers = p_layers; + } else if (p_slice_type == TEXTURE_SLICE_2D_ARRAY) { + ERR_FAIL_COND_V_MSG(p_layer != 0, RID(), "layer must be 0 when obtaining a 2D array mipmap slice"); + slice_layers = src_texture->layers; + } else if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { + slice_layers = 6; + } + + Texture texture = *src_texture; + get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height); + texture.mipmaps = p_mipmaps; + texture.layers = slice_layers; + texture.base_mipmap = p_mipmap; + texture.base_layer = p_layer; + + if (p_slice_type == TEXTURE_SLICE_2D) { + texture.type = TEXTURE_TYPE_2D; + } else if (p_slice_type == TEXTURE_SLICE_3D) { + texture.type = TEXTURE_TYPE_3D; + } + + RDD::TextureView tv; + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { + tv.format = texture.format; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + + ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), + "Format override is not in the list of allowed shareable formats for original texture."); + tv.format = p_view.format_override; + } + tv.swizzle_r = p_view.swizzle_r; + tv.swizzle_g = p_view.swizzle_g; + tv.swizzle_b = p_view.swizzle_b; + tv.swizzle_a = p_view.swizzle_a; + + if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { + ERR_FAIL_COND_V_MSG(p_layer >= src_texture->layers, RID(), + "Specified layer is invalid for cubemap"); + ERR_FAIL_COND_V_MSG((p_layer % 6) != 0, RID(), + "Specified layer must be a multiple of 6."); + } + + texture.driver_id = driver->texture_create_shared_from_slice(src_texture->driver_id, tv, p_slice_type, p_layer, slice_layers, p_mipmap, p_mipmaps); + ERR_FAIL_COND_V(!texture.driver_id, RID()); + + texture.owner = p_with_texture; + RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + _add_dependency(id, p_with_texture); + + return id; +} + +Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { + return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); +} + +static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_dst_pitch, uint32_t p_unit_size) { + uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size; + uint32_t dst_offset = 0; + for (uint32_t y = p_src_h; y > 0; y--) { + uint8_t const *__restrict src = p_src + src_offset; + uint8_t *__restrict dst = p_dst + dst_offset; + for (uint32_t x = p_src_w * p_unit_size; x > 0; x--) { + *dst = *src; + src++; + dst++; + } + src_offset += p_src_full_w * p_unit_size; + dst_offset += p_dst_pitch; + } +} + +Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER, + "Updating textures is forbidden during creation of a draw or compute list"); + + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(texture, ERR_INVALID_PARAMETER); + + if (texture->owner != RID()) { + p_texture = texture->owner; + texture = texture_owner.get_or_null(texture->owner); + ERR_FAIL_NULL_V(texture, ERR_BUG); // This is a bug. + } + + ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, + "Texture can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, + "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable."); + + uint32_t layer_count = texture->layers; + if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) { + layer_count *= 6; + } + ERR_FAIL_COND_V(p_layer >= layer_count, ERR_INVALID_PARAMETER); + + uint32_t width, height; + uint32_t tight_mip_size = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, texture->mipmaps, &width, &height); + uint32_t required_size = tight_mip_size; + uint32_t required_align = get_compressed_image_format_block_byte_size(texture->format); + if (required_align == 1) { + required_align = get_image_format_pixel_size(texture->format); + } + required_align = STEPIFY(required_align, driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT)); + + ERR_FAIL_COND_V_MSG(required_size != (uint32_t)p_data.size(), ERR_INVALID_PARAMETER, + "Required size for texture update (" + itos(required_size) + ") does not match data supplied size (" + itos(p_data.size()) + ")."); + + uint32_t region_size = texture_upload_region_size_px; + + const uint8_t *r = p_data.ptr(); + + RDD::CommandBufferID command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; + + // Barrier to transfer. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = texture->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.prev_layout = texture->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.subresources.aspect = texture->barrier_aspect_flags; + tb.subresources.mipmap_count = texture->mipmaps; + tb.subresources.base_layer = p_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + + uint32_t mipmap_offset = 0; + + uint32_t logic_width = texture->width; + uint32_t logic_height = texture->height; + + for (uint32_t mm_i = 0; mm_i < texture->mipmaps; mm_i++) { + uint32_t depth = 0; + uint32_t image_total = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, mm_i + 1, &width, &height, &depth); + + const uint8_t *read_ptr_mipmap = r + mipmap_offset; + tight_mip_size = image_total - mipmap_offset; + + for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0. + + const uint8_t *read_ptr = read_ptr_mipmap + (tight_mip_size / depth) * z; + + for (uint32_t y = 0; y < height; y += region_size) { + for (uint32_t x = 0; x < width; x += region_size) { + uint32_t region_w = MIN(region_size, width - x); + uint32_t region_h = MIN(region_size, height - y); + + uint32_t region_logic_w = MIN(region_size, logic_width - x); + uint32_t region_logic_h = MIN(region_size, logic_height - y); + + uint32_t pixel_size = get_image_format_pixel_size(texture->format); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(texture->format, block_w, block_h); + + uint32_t region_pitch = (region_w * pixel_size * block_w) >> get_compressed_image_format_pixel_rshift(texture->format); + uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP); + region_pitch = STEPIFY(region_pitch, pitch_step); + uint32_t to_allocate = region_pitch * region_h; + + uint32_t alloc_offset = 0, alloc_size = 0; + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + uint8_t *write_ptr = nullptr; + { // Map. + uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); + ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); + write_ptr = data_ptr; + write_ptr += alloc_offset; + } + + ERR_FAIL_COND_V(region_w % block_w, ERR_BUG); + ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); + + if (block_w != 1 || block_h != 1) { + // Compressed image (blocks). + // Must copy a block region. + + uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format); + // Re-create current variables in blocky format. + uint32_t xb = x / block_w; + uint32_t yb = y / block_h; + uint32_t wb = width / block_w; + //uint32_t hb = height / block_h; + uint32_t region_wb = region_w / block_w; + uint32_t region_hb = region_h / block_h; + _copy_region(read_ptr, write_ptr, xb, yb, region_wb, region_hb, wb, region_pitch, block_size); + } else { + // Regular image (pixels). + // Must copy a pixel region. + _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, region_pitch, pixel_size); + } + + { // Unmap. + driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id); + } + + RDD::BufferTextureCopyRegion copy_region; + copy_region.buffer_offset = alloc_offset; + copy_region.texture_subresources.aspect = texture->read_aspect_flags; + copy_region.texture_subresources.mipmap = mm_i; + copy_region.texture_subresources.base_layer = p_layer; + copy_region.texture_subresources.layer_count = 1; + copy_region.texture_offset = Vector3i(x, y, z); + copy_region.texture_region_size = Vector3i(region_logic_w, region_logic_h, 1); + + driver->command_copy_buffer_to_texture(command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, texture->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, copy_region); + + staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; + } + } + } + + mipmap_offset = image_total; + logic_width = MAX(1u, logic_width >> 1); + logic_height = MAX(1u, logic_height >> 1); + } + + // Barrier to restore layout. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + BitField<RDD::PipelineStageBits> stages; + BitField<RDD::BarrierAccessBits> access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + + if (stages.is_empty()) { + stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + RDD::TextureBarrier tb; + tb.texture = texture->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.dst_access = access; + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.next_layout = texture->layout; + tb.subresources.aspect = texture->barrier_aspect_flags; + tb.subresources.mipmap_count = texture->mipmaps; + tb.subresources.base_layer = p_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); + + if (texture->used_in_frame != frames_drawn) { + texture->used_in_raster = false; + texture->used_in_compute = false; + texture->used_in_frame = frames_drawn; + } + texture->used_in_transfer = true; + } + + return OK; +} + +Vector<uint8_t> RenderingDevice::_texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d) { + uint32_t width, height, depth; + uint32_t tight_mip_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth); + + Vector<uint8_t> image_data; + image_data.resize(tight_mip_size); + + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(tex->format, blockw, blockh); + uint32_t block_size = get_compressed_image_format_block_byte_size(tex->format); + uint32_t pixel_size = get_image_format_pixel_size(tex->format); + + { + uint8_t *w = image_data.ptrw(); + + uint32_t mipmap_offset = 0; + for (uint32_t mm_i = 0; mm_i < tex->mipmaps; mm_i++) { + uint32_t image_total = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, mm_i + 1, &width, &height, &depth); + + uint8_t *write_ptr_mipmap = w + mipmap_offset; + tight_mip_size = image_total - mipmap_offset; + + RDD::TextureSubresource subres; + subres.aspect = RDD::TEXTURE_ASPECT_COLOR; + subres.layer = p_layer; + subres.mipmap = mm_i; + RDD::TextureCopyableLayout layout; + driver->texture_get_copyable_layout(tex->driver_id, subres, &layout); + + uint8_t *img_mem = driver->texture_map(tex->driver_id, subres); + ERR_FAIL_NULL_V(img_mem, Vector<uint8_t>()); + + for (uint32_t z = 0; z < depth; z++) { + uint8_t *write_ptr = write_ptr_mipmap + z * tight_mip_size / depth; + const uint8_t *slice_read_ptr = img_mem + z * layout.depth_pitch; + + if (block_size > 1) { + // Compressed. + uint32_t line_width = (block_size * (width / blockw)); + for (uint32_t y = 0; y < height / blockh; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * line_width; + + memcpy(wptr, rptr, line_width); + } + + } else { + // Uncompressed. + for (uint32_t y = 0; y < height; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * pixel_size * width; + memcpy(wptr, rptr, (uint64_t)pixel_size * width); + } + } + } + + driver->texture_unmap(tex->driver_id); + + mipmap_offset = image_total; + } + } + + return image_data; +} + +Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_layer) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, Vector<uint8_t>()); + + ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(), + "Texture can't be retrieved while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to retrieve this texture."); + ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(), + "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); + + uint32_t layer_count = tex->layers; + if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + layer_count *= 6; + } + ERR_FAIL_COND_V(p_layer >= layer_count, Vector<uint8_t>()); + + if ((tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT)) { + // Does not need anything fancy, map and read. + return _texture_get_data(tex, p_layer); + } else { + LocalVector<RDD::TextureCopyableLayout> mip_layouts; + uint32_t work_mip_alignment = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT); + uint32_t work_buffer_size = 0; + mip_layouts.resize(tex->mipmaps); + for (uint32_t i = 0; i < tex->mipmaps; i++) { + RDD::TextureSubresource subres; + subres.aspect = RDD::TEXTURE_ASPECT_COLOR; + subres.layer = p_layer; + subres.mipmap = i; + driver->texture_get_copyable_layout(tex->driver_id, subres, &mip_layouts[i]); + + // Assuming layers are tightly packed. If this is not true on some driver, we must modify the copy algorithm. + DEV_ASSERT(mip_layouts[i].layer_pitch == mip_layouts[i].size / layer_count); + + work_buffer_size = STEPIFY(work_buffer_size, work_mip_alignment) + mip_layouts[i].size; + } + + RDD::BufferID tmp_buffer = driver->buffer_create(work_buffer_size, RDD::BUFFER_USAGE_TRANSFER_TO_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU); + ERR_FAIL_COND_V(!tmp_buffer, Vector<uint8_t>()); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; // Makes more sense to retrieve. + + // Pre-copy barrier. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = tex->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.prev_layout = tex->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.subresources.aspect = tex->barrier_aspect_flags; + tb.subresources.mipmap_count = tex->mipmaps; + tb.subresources.base_layer = p_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + + { + uint32_t w = tex->width; + uint32_t h = tex->height; + uint32_t d = tex->depth; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + RDD::BufferTextureCopyRegion copy_region; + copy_region.buffer_offset = mip_layouts[i].offset; + copy_region.texture_subresources.aspect = tex->read_aspect_flags; + copy_region.texture_subresources.mipmap = i; + copy_region.texture_subresources.base_layer = p_layer; + copy_region.texture_subresources.layer_count = 1; + copy_region.texture_region_size.x = w; + copy_region.texture_region_size.y = h; + copy_region.texture_region_size.z = d; + driver->command_copy_texture_to_buffer(command_buffer, tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer, copy_region); + + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); + } + } + + // Post-copy barrier. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; + if ((tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { + tb.dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.next_layout = tex->layout; + tb.subresources.aspect = tex->barrier_aspect_flags; + tb.subresources.mipmap_count = tex->mipmaps; + tb.subresources.base_layer = p_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, tb); + } + + _flush(true); + + const uint8_t *read_ptr = driver->buffer_map(tmp_buffer); + ERR_FAIL_NULL_V(read_ptr, Vector<uint8_t>()); + + Vector<uint8_t> buffer_data; + { + uint32_t tight_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps); + buffer_data.resize(tight_buffer_size); + + uint8_t *write_ptr = buffer_data.ptrw(); + + uint32_t w = tex->width; + uint32_t h = tex->height; + uint32_t d = tex->depth; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + uint32_t width = 0, height = 0, depth = 0; + uint32_t tight_mip_size = get_image_format_required_size(tex->format, w, h, d, 1, &width, &height, &depth); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + uint32_t tight_row_pitch = tight_mip_size / ((height / block_h) * depth); + + { + // Copy row-by-row to erase padding due to alignments. + const uint8_t *rp = read_ptr; + uint8_t *wp = write_ptr; + for (uint32_t row = h * d / block_h; row != 0; row--) { + memcpy(wp, rp, tight_row_pitch); + rp += mip_layouts[i].row_pitch; + wp += tight_row_pitch; + } + } + + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); + read_ptr += mip_layouts[i].size; + write_ptr += tight_mip_size; + } + } + + driver->buffer_unmap(tmp_buffer); + driver->buffer_free(tmp_buffer); + + return buffer_data; + } +} + +bool RenderingDevice::texture_is_shared(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, false); + return tex->owner.is_valid(); +} + +bool RenderingDevice::texture_is_valid(RID p_texture) { + return texture_owner.owns(p_texture); +} + +RD::TextureFormat RenderingDevice::texture_get_format(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, TextureFormat()); + + TextureFormat tf; + + tf.format = tex->format; + tf.width = tex->width; + tf.height = tex->height; + tf.depth = tex->depth; + tf.array_layers = tex->layers; + tf.mipmaps = tex->mipmaps; + tf.texture_type = tex->type; + tf.samples = tex->samples; + tf.usage_bits = tex->usage_flags; + tf.shareable_formats = tex->allowed_shared_formats; + tf.is_resolve_buffer = tex->is_resolve_buffer; + + return tf; +} + +Size2i RenderingDevice::texture_size(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, Size2i()); + return Size2i(tex->width, tex->height); +} + +#ifndef DISABLE_DEPRECATED +uint64_t RenderingDevice::texture_get_native_handle(RID p_texture) { + return get_driver_resource(DRIVER_RESOURCE_TEXTURE, p_texture); +} +#endif + +Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + Texture *src_tex = texture_owner.get_or_null(p_from_texture); + ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, + "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); + ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); + + uint32_t src_layer_count = src_tex->layers; + uint32_t src_width, src_height, src_depth; + get_image_format_required_size(src_tex->format, src_tex->width, src_tex->height, src_tex->depth, p_src_mipmap + 1, &src_width, &src_height, &src_depth); + if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + src_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_from.x < 0 || p_from.x + p_size.x > src_width, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_from.y < 0 || p_from.y + p_size.y > src_height, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_from.z < 0 || p_from.z + p_size.z > src_depth, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER); + + Texture *dst_tex = texture_owner.get_or_null(p_to_texture); + ERR_FAIL_NULL_V(dst_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, + "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); + ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, + "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); + + uint32_t dst_layer_count = dst_tex->layers; + uint32_t dst_width, dst_height, dst_depth; + get_image_format_required_size(dst_tex->format, dst_tex->width, dst_tex->height, dst_tex->depth, p_dst_mipmap + 1, &dst_width, &dst_height, &dst_depth); + if (dst_tex->type == TEXTURE_TYPE_CUBE || dst_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + dst_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_to.x < 0 || p_to.x + p_size.x > dst_width, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_to.y < 0 || p_to.y + p_size.y > dst_height, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_to.z < 0 || p_to.z + p_size.z > dst_depth, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_dst_mipmap >= dst_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_dst_layer >= dst_layer_count, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(src_tex->read_aspect_flags != dst_tex->read_aspect_flags, ERR_INVALID_PARAMETER, + "Source and destination texture must be of the same type (color or depth)."); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + + // PRE Copy the image. + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + { // Source. + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.prev_layout = src_tex->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.subresources.aspect = src_tex->barrier_aspect_flags; + tb.subresources.base_mipmap = p_src_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = p_src_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + { // Dest. + RDD::TextureBarrier tb; + tb.texture = dst_tex->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.prev_layout = dst_tex->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.subresources.aspect = dst_tex->read_aspect_flags; + tb.subresources.base_mipmap = p_dst_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = p_dst_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + } + + // COPY. + + { + RDD::TextureCopyRegion copy_region; + copy_region.src_subresources.aspect = src_tex->read_aspect_flags; + copy_region.src_subresources.mipmap = p_src_mipmap; + copy_region.src_subresources.base_layer = p_src_layer; + copy_region.src_subresources.layer_count = 1; + copy_region.src_offset = p_from; + + copy_region.dst_subresources.aspect = dst_tex->read_aspect_flags; + copy_region.dst_subresources.mipmap = p_dst_mipmap; + copy_region.dst_subresources.base_layer = p_dst_layer; + copy_region.dst_subresources.layer_count = 1; + copy_region.dst_offset = p_to; + + copy_region.size = p_size; + + driver->command_copy_texture(command_buffer, src_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, copy_region); + } + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // RESTORE LAYOUT for SRC and DST. + + BitField<RDD::PipelineStageBits> stages; + BitField<RDD::BarrierAccessBits> access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + + if (stages.is_empty()) { + stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + { // Restore src. + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.dst_access = access; + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.next_layout = src_tex->layout; + tb.subresources.aspect = src_tex->barrier_aspect_flags; + tb.subresources.base_mipmap = p_src_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = p_src_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); + } + + { // Make dst readable. + + RDD::TextureBarrier tb; + tb.texture = dst_tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.dst_access = access; + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.next_layout = dst_tex->layout; + tb.subresources.aspect = dst_tex->read_aspect_flags; + tb.subresources.base_mipmap = p_dst_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = p_dst_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); + } + + if (dst_tex->used_in_frame != frames_drawn) { + dst_tex->used_in_raster = false; + dst_tex->used_in_compute = false; + dst_tex->used_in_frame = frames_drawn; + } + dst_tex->used_in_transfer = true; + } + + return OK; +} + +Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + Texture *src_tex = texture_owner.get_or_null(p_from_texture); + ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, + "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); + ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); + + ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)"); + ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled."); + + Texture *dst_tex = texture_owner.get_or_null(p_to_texture); + ERR_FAIL_NULL_V(dst_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, + "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); + ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, + "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); + + ERR_FAIL_COND_V_MSG(dst_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Destination texture must be 2D (or a slice of a 3D/Cube texture)."); + ERR_FAIL_COND_V_MSG(dst_tex->samples != TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Destination texture must not be multisampled."); + + ERR_FAIL_COND_V_MSG(src_tex->format != dst_tex->format, ERR_INVALID_PARAMETER, "Source and Destination textures must be the same format."); + ERR_FAIL_COND_V_MSG(src_tex->width != dst_tex->width && src_tex->height != dst_tex->height && src_tex->depth != dst_tex->depth, ERR_INVALID_PARAMETER, "Source and Destination textures must have the same dimensions."); + + ERR_FAIL_COND_V_MSG(src_tex->read_aspect_flags != dst_tex->read_aspect_flags, ERR_INVALID_PARAMETER, + "Source and destination texture must be of the same type (color or depth)."); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // PRE Copy the image. + + { // Source. + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.prev_layout = src_tex->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.subresources.aspect = src_tex->barrier_aspect_flags; + tb.subresources.base_mipmap = src_tex->base_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = src_tex->base_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + { // Dest. + RDD::TextureBarrier tb; + tb.texture = dst_tex->driver_id; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.prev_layout = dst_tex->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.subresources.aspect = dst_tex->barrier_aspect_flags; + tb.subresources.base_mipmap = dst_tex->base_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = dst_tex->base_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + } + + // RESOLVE. + driver->command_resolve_texture(command_buffer, src_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_tex->base_layer, src_tex->base_mipmap, dst_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_tex->base_layer, dst_tex->base_mipmap); + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // RESTORE LAYOUT for SRC and DST. + + BitField<RDD::PipelineStageBits> stages; + BitField<RDD::BarrierAccessBits> access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + + if (stages.is_empty()) { + stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + { // Restore src. + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + tb.dst_access = access; + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + tb.next_layout = src_tex->layout; + tb.subresources.aspect = src_tex->barrier_aspect_flags; + tb.subresources.base_mipmap = src_tex->base_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = src_tex->base_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT, stages, {}, {}, tb); + } + + { // Make dst readable. + + RDD::TextureBarrier tb; + tb.texture = dst_tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.dst_access = access; + tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + tb.next_layout = dst_tex->layout; + tb.subresources.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; + tb.subresources.base_mipmap = dst_tex->base_mipmap; + tb.subresources.mipmap_count = 1; + tb.subresources.base_layer = dst_tex->base_layer; + tb.subresources.layer_count = 1; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); + } + } + + return OK; +} + +Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + Texture *src_tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, + "Source texture can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to clear this texture."); + + ERR_FAIL_COND_V(p_layers == 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_mipmaps == 0, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be cleared."); + + uint32_t src_layer_count = src_tex->layers; + if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + src_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + + RDD::TextureLayout clear_layout = (src_tex->layout == RDD::TEXTURE_LAYOUT_GENERAL) ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + + // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner.) + const BitField<RDD::PipelineStageBits> valid_texture_stages = RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT; + constexpr BitField<RDD::BarrierAccessBits> read_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; + constexpr BitField<RDD::BarrierAccessBits> read_write_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; + const BitField<RDD::BarrierAccessBits> valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; + + // Barrier from previous access with optional layout change (see clear_layout logic above). + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.src_access = valid_texture_access; + tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.prev_layout = src_tex->layout; + tb.next_layout = clear_layout; + tb.subresources.aspect = src_tex->read_aspect_flags; + tb.subresources.base_mipmap = src_tex->base_mipmap + p_base_mipmap; + tb.subresources.mipmap_count = p_mipmaps; + tb.subresources.base_layer = src_tex->base_layer + p_base_layer; + tb.subresources.layer_count = p_layers; + + driver->command_pipeline_barrier(command_buffer, valid_texture_stages, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + } + + RDD::TextureSubresourceRange range; + range.aspect = src_tex->read_aspect_flags; + range.base_mipmap = src_tex->base_mipmap + p_base_mipmap; + range.mipmap_count = p_mipmaps; + range.base_layer = src_tex->base_layer + p_base_layer; + range.layer_count = p_layers; + + driver->command_clear_color_texture(command_buffer, src_tex->driver_id, clear_layout, p_color, range); + + // Barrier to post clear accesses (changing back the layout if needed). + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + BitField<RDD::PipelineStageBits> stages; + BitField<RDD::BarrierAccessBits> access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + + if (stages.is_empty()) { + stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + RDD::TextureBarrier tb; + tb.texture = src_tex->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + tb.dst_access = access; + tb.prev_layout = clear_layout; + tb.next_layout = src_tex->layout; + tb.subresources.aspect = src_tex->read_aspect_flags; + tb.subresources.base_mipmap = src_tex->base_mipmap + p_base_mipmap; + tb.subresources.mipmap_count = p_mipmaps; + tb.subresources.base_layer = src_tex->base_layer + p_base_layer; + tb.subresources.layer_count = p_layers; + + driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); + + if (src_tex->used_in_frame != frames_drawn) { + src_tex->used_in_raster = false; + src_tex->used_in_compute = false; + src_tex->used_in_frame = frames_drawn; + } + src_tex->used_in_transfer = true; + } + + return OK; +} + +bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const { + ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); + + _THREAD_SAFE_METHOD_ + + bool cpu_readable = (p_usage & RDD::TEXTURE_USAGE_CPU_READ_BIT); + BitField<TextureUsageBits> supported = driver->texture_get_usages_supported_by_format(p_format, cpu_readable); + bool any_unsupported = (((int64_t)supported) | ((int64_t)p_usage)) != ((int64_t)supported); + return !any_unsupported; +} + +/*********************/ +/**** FRAMEBUFFER ****/ +/*********************/ + +RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { + // NOTE: + // Before the refactor to RenderingDevice-RenderingDeviceDriver, there was commented out code to + // specify dependencies to external subpasses. Since it had been unused for a long timel it wasn't ported + // to the new architecture. + + LocalVector<int32_t> attachment_last_pass; + attachment_last_pass.resize(p_attachments.size()); + + if (p_view_count > 1) { + const RDD::MultiviewCapabilities &capabilities = driver->get_multiview_capabilities(); + + // This only works with multiview! + ERR_FAIL_COND_V_MSG(!capabilities.is_supported, RDD::RenderPassID(), "Multiview not supported"); + + // Make sure we limit this to the number of views we support. + ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, RDD::RenderPassID(), "Hardware does not support requested number of views for Multiview render pass"); + } + + LocalVector<RDD::Attachment> attachments; + LocalVector<int> attachment_remap; for (int i = 0; i < p_attachments.size(); i++) { - Ref<RDAttachmentFormat> af = p_attachments[i]; - ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); - attachments.write[i] = af->base; + if (p_attachments[i].usage_flags == AttachmentFormat::UNUSED_ATTACHMENT) { + attachment_remap.push_back(RDD::AttachmentReference::UNUSED); + continue; + } + + ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, RDD::RenderPassID()); + ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, RDD::RenderPassID()); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT)), + RDD::RenderPassID(), "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set."); + + RDD::Attachment description; + description.format = p_attachments[i].format; + description.samples = p_attachments[i].samples; + + bool is_sampled = (p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT); + bool is_storage = (p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT); + bool is_depth = (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + + // We can setup a framebuffer where we write to our VRS texture to set it up. + // We make the assumption here that if our texture is actually used as our VRS attachment. + // It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses. + bool is_vrs = (p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && i == p_passes[0].vrs_attachment; + + if (is_vrs) { + // For VRS we only read, there is no writing to this texture. + description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + description.initial_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + } else { + // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write. + // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs + // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that + // stage. + + switch (is_depth ? p_initial_depth_action : p_initial_action) { + case INITIAL_ACTION_CLEAR_REGION: + case INITIAL_ACTION_CLEAR: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; + description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; + description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case INITIAL_ACTION_KEEP: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case INITIAL_ACTION_DROP: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case INITIAL_ACTION_CLEAR_REGION_CONTINUE: + case INITIAL_ACTION_CONTINUE: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + description.initial_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + description.initial_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + default: { + ERR_FAIL_V(RDD::RenderPassID()); // Should never reach here. + } + } + } + + bool used_last = false; + + { + int last_pass = p_passes.size() - 1; + + if (is_depth) { + // Likely missing depth resolve? + if (p_passes[last_pass].depth_attachment == i) { + used_last = true; + } + } else if (is_vrs) { + if (p_passes[last_pass].vrs_attachment == i) { + used_last = true; + } + } else { + if (p_passes[last_pass].resolve_attachments.size()) { + // If using resolve attachments, check resolve attachments. + for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { + if (p_passes[last_pass].resolve_attachments[j] == i) { + used_last = true; + break; + } + } + } + if (!used_last) { + for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) { + if (p_passes[last_pass].color_attachments[j] == i) { + used_last = true; + break; + } + } + } + } + + if (!used_last) { + for (int j = 0; j < p_passes[last_pass].preserve_attachments.size(); j++) { + if (p_passes[last_pass].preserve_attachments[j] == i) { + used_last = true; + break; + } + } + } + } + + FinalAction final_action = p_final_action; + FinalAction final_depth_action = p_final_depth_action; + + if (!used_last) { + if (is_depth) { + final_depth_action = FINAL_ACTION_DISCARD; + + } else { + final_action = FINAL_ACTION_DISCARD; + } + } + + if (is_vrs) { + // We don't change our VRS texture during this process. + + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } else { + switch (is_depth ? final_depth_action : final_action) { + case FINAL_ACTION_READ: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + // TODO: What does this mean about the next usage (and thus appropriate dependency masks. + } + } break; + case FINAL_ACTION_DISCARD: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + } else { + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case FINAL_ACTION_CONTINUE: { + if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_STORE; + description.final_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. + } + + } break; + default: { + ERR_FAIL_V(RDD::RenderPassID()); // Should never reach here. + } + } + } + + attachment_last_pass[i] = -1; + attachment_remap.push_back(attachments.size()); + attachments.push_back(description); } - Vector<FramebufferPass> passes; + LocalVector<RDD::Subpass> subpasses; + subpasses.resize(p_passes.size()); + LocalVector<RDD::SubpassDependency> subpass_dependencies; + for (int i = 0; i < p_passes.size(); i++) { - Ref<RDFramebufferPass> pass = p_passes[i]; - ERR_CONTINUE(pass.is_null()); - passes.push_back(pass->base); + const FramebufferPass *pass = &p_passes[i]; + RDD::Subpass &subpass = subpasses[i]; + + TextureSamples texture_samples = TEXTURE_SAMPLES_1; + bool is_multisample_first = true; + + for (int j = 0; j < pass->color_attachments.size(); j++) { + int32_t attachment = pass->color_attachments[j]; + RDD::AttachmentReference reference; + if (attachment == ATTACHMENT_UNUSED) { + reference.attachment = RDD::AttachmentReference::UNUSED; + reference.layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); + } + reference.attachment = attachment_remap[attachment]; + reference.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + } + reference.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; + subpass.color_references.push_back(reference); + } + + for (int j = 0; j < pass->input_attachments.size(); j++) { + int32_t attachment = pass->input_attachments[j]; + RDD::AttachmentReference reference; + if (attachment == ATTACHMENT_UNUSED) { + reference.attachment = RDD::AttachmentReference::UNUSED; + reference.layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + reference.attachment = attachment_remap[attachment]; + reference.layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } + reference.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; + subpass.input_references.push_back(reference); + } + + if (pass->resolve_attachments.size() > 0) { + ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), RDD::RenderPassID(), "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); + ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, RDD::RenderPassID(), "Resolve attachments specified, but color attachments are not multisample."); + } + for (int j = 0; j < pass->resolve_attachments.size(); j++) { + int32_t attachment = pass->resolve_attachments[j]; + RDD::AttachmentReference reference; + if (attachment == ATTACHMENT_UNUSED) { + reference.attachment = RDD::AttachmentReference::UNUSED; + reference.layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == ATTACHMENT_UNUSED, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color texture."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; + ERR_FAIL_COND_V_MSG(multisample, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); + reference.attachment = attachment_remap[attachment]; + reference.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + attachment_last_pass[attachment] = i; + } + reference.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; + subpass.resolve_references.push_back(reference); + } + + if (pass->depth_attachment != ATTACHMENT_UNUSED) { + int32_t attachment = pass->depth_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + subpass.depth_stencil_reference.attachment = attachment_remap[attachment]; + subpass.depth_stencil_reference.layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, RDD::RenderPassID(), "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); + } + + } else { + subpass.depth_stencil_reference.attachment = RDD::AttachmentReference::UNUSED; + subpass.depth_stencil_reference.layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + } + + if (pass->vrs_attachment != ATTACHMENT_UNUSED) { + int32_t attachment = pass->vrs_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + subpass.vrs_reference.attachment = attachment_remap[attachment]; + subpass.vrs_reference.layout = RDD::TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL; + + attachment_last_pass[attachment] = i; + } + + for (int j = 0; j < pass->preserve_attachments.size(); j++) { + int32_t attachment = pass->preserve_attachments[j]; + + ERR_FAIL_COND_V_MSG(attachment == ATTACHMENT_UNUSED, RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused."); + + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); + + if (attachment_last_pass[attachment] != i) { + // Preserve can still be used to keep depth or color from being discarded after use. + attachment_last_pass[attachment] = i; + subpasses[i].preserve_attachments.push_back(attachment); + } + } + + if (r_samples) { + r_samples->push_back(texture_samples); + } + + if (i > 0) { + RDD::SubpassDependency dependency; + dependency.src_subpass = i - 1; + dependency.dst_subpass = i; + dependency.src_stages = (RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); + dependency.dst_stages = (RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + dependency.src_access = (RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + dependency.dst_access = (RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT); + subpass_dependencies.push_back(dependency); + } } - return framebuffer_format_create_multipass(attachments, passes, p_view_count); -} + RDD::RenderPassID render_pass = driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count); + ERR_FAIL_COND_V(!render_pass, RDD::RenderPassID()); -RID RenderingDevice::_framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check, uint32_t p_view_count) { - Vector<RID> textures = Variant(p_textures); - return framebuffer_create(textures, p_format_check, p_view_count); + return render_pass; } -RID RenderingDevice::_framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { - Vector<RID> textures = Variant(p_textures); +RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { + FramebufferPass pass; + for (int i = 0; i < p_format.size(); i++) { + if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + Vector<FramebufferPass> passes; - for (int i = 0; i < p_passes.size(); i++) { - Ref<RDFramebufferPass> pass = p_passes[i]; - ERR_CONTINUE(pass.is_null()); - passes.push_back(pass->base); + passes.push_back(pass); + return framebuffer_format_create_multipass(p_format, passes, p_view_count); +} +RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ + + FramebufferFormatKey key; + key.attachments = p_attachments; + key.passes = p_passes; + key.view_count = p_view_count; + + const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + if (E) { + // Exists, return. + return E->get(); } - return framebuffer_create_multipass(textures, passes, p_format_check, p_view_count); + + Vector<TextureSamples> samples; + RDD::RenderPassID render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); // Actions don't matter for this use case. + + if (!render_pass) { // Was likely invalid. + return INVALID_ID; + } + FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); + + E = framebuffer_format_cache.insert(key, id); + FramebufferFormat fb_format; + fb_format.E = E; + fb_format.render_pass = render_pass; + fb_format.pass_samples = samples; + fb_format.view_count = p_view_count; + framebuffer_formats[id] = fb_format; + return id; } -RID RenderingDevice::_sampler_create(const Ref<RDSamplerState> &p_state) { - ERR_FAIL_COND_V(p_state.is_null(), RID()); +RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_empty(TextureSamples p_samples) { + FramebufferFormatKey key; + key.passes.push_back(FramebufferPass()); - return sampler_create(p_state->base); + const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + if (E) { + // Exists, return. + return E->get(); + } + + LocalVector<RDD::Subpass> subpass; + subpass.resize(1); + + RDD::RenderPassID render_pass = driver->render_pass_create({}, subpass, {}, 1); + ERR_FAIL_COND_V(!render_pass, FramebufferFormatID()); + + FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); + + E = framebuffer_format_cache.insert(key, id); + + FramebufferFormat fb_format; + fb_format.E = E; + fb_format.render_pass = render_pass; + fb_format.pass_samples.push_back(p_samples); + framebuffer_formats[id] = fb_format; + return id; } -RenderingDevice::VertexFormatID RenderingDevice::_vertex_format_create(const TypedArray<RDVertexAttribute> &p_vertex_formats) { - Vector<VertexAttribute> descriptions; - descriptions.resize(p_vertex_formats.size()); +RenderingDevice::TextureSamples RenderingDevice::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { + HashMap<FramebufferFormatID, FramebufferFormat>::Iterator E = framebuffer_formats.find(p_format); + ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->value.pass_samples.size()), TEXTURE_SAMPLES_1); - for (int i = 0; i < p_vertex_formats.size(); i++) { - Ref<RDVertexAttribute> af = p_vertex_formats[i]; - ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); - descriptions.write[i] = af->base; + return E->value.pass_samples[p_pass]; +} + +RID RenderingDevice::framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples, FramebufferFormatID p_format_check) { + _THREAD_SAFE_METHOD_ + Framebuffer framebuffer; + framebuffer.format_id = framebuffer_format_create_empty(p_samples); + ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); + framebuffer.size = p_size; + framebuffer.view_count = 1; + + RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; +} + +RID RenderingDevice::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ + + FramebufferPass pass; + + for (int i = 0; i < p_texture_attachments.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); + + ERR_FAIL_COND_V_MSG(texture && texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + pass.vrs_attachment = i; + } else { + if (texture && texture->is_resolve_buffer) { + pass.resolve_attachments.push_back(i); + } else { + pass.color_attachments.push_back(texture ? i : ATTACHMENT_UNUSED); + } + } } - return vertex_format_create(descriptions); + + Vector<FramebufferPass> passes; + passes.push_back(pass); + + return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); } -RID RenderingDevice::_vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const TypedArray<RID> &p_src_buffers, const Vector<int64_t> &p_offsets) { - Vector<RID> buffers = Variant(p_src_buffers); +RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ - Vector<uint64_t> offsets; - offsets.resize(p_offsets.size()); - for (int i = 0; i < p_offsets.size(); i++) { - offsets.write[i] = p_offsets[i]; + Vector<AttachmentFormat> attachments; + attachments.resize(p_texture_attachments.size()); + Size2i size; + bool size_set = false; + for (int i = 0; i < p_texture_attachments.size(); i++) { + AttachmentFormat af; + Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); + if (!texture) { + af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT; + } else { + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (!size_set) { + size.width = texture->width; + size.height = texture->height; + size_set = true; + } else if (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + // If this is not the first attachment we assume this is used as the VRS attachment. + // In this case this texture will be 1/16th the size of the color attachment. + // So we skip the size check. + } else { + ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), + "All textures in a framebuffer should be the same size."); + } + + af.format = texture->format; + af.samples = texture->samples; + af.usage_flags = texture->usage_flags; + } + attachments.write[i] = af; } - return vertex_array_create(p_vertex_count, p_vertex_format, buffers, offsets); + ERR_FAIL_COND_V_MSG(!size_set, RID(), "All attachments unused."); + + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); + if (format_id == INVALID_ID) { + return RID(); + } + + ERR_FAIL_COND_V_MSG(p_format_check != INVALID_ID && format_id != p_format_check, RID(), + "The format used to check this framebuffer differs from the intended framebuffer format."); + + Framebuffer framebuffer; + framebuffer.format_id = format_id; + framebuffer.texture_ids = p_texture_attachments; + framebuffer.size = size; + framebuffer.view_count = p_view_count; + + RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + + for (int i = 0; i < p_texture_attachments.size(); i++) { + if (p_texture_attachments[i].is_valid()) { + _add_dependency(id, p_texture_attachments[i]); + } + } + + return id; } -Ref<RDShaderSPIRV> RenderingDevice::_shader_compile_spirv_from_source(const Ref<RDShaderSource> &p_source, bool p_allow_cache) { - ERR_FAIL_COND_V(p_source.is_null(), Ref<RDShaderSPIRV>()); +RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_get_format(RID p_framebuffer) { + _THREAD_SAFE_METHOD_ - Ref<RDShaderSPIRV> bytecode; - bytecode.instantiate(); - for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { - String error; + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_NULL_V(framebuffer, INVALID_ID); - ShaderStage stage = ShaderStage(i); - String source = p_source->get_stage_source(stage); + return framebuffer->format_id; +} - if (!source.is_empty()) { - Vector<uint8_t> spirv = shader_compile_spirv_from_source(stage, source, p_source->get_language(), &error, p_allow_cache); - bytecode->set_stage_bytecode(stage, spirv); - bytecode->set_stage_compile_error(stage, error); +bool RenderingDevice::framebuffer_is_valid(RID p_framebuffer) const { + _THREAD_SAFE_METHOD_ + + return framebuffer_owner.owns(p_framebuffer); +} + +void RenderingDevice::framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) { + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_COND(!framebuffer); + + framebuffer->invalidated_callback = p_callback; + framebuffer->invalidated_callback_userdata = p_userdata; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +RID RenderingDevice::sampler_create(const SamplerState &p_state) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_INDEX_V(p_state.repeat_u, SAMPLER_REPEAT_MODE_MAX, RID()); + ERR_FAIL_INDEX_V(p_state.repeat_v, SAMPLER_REPEAT_MODE_MAX, RID()); + ERR_FAIL_INDEX_V(p_state.repeat_w, SAMPLER_REPEAT_MODE_MAX, RID()); + ERR_FAIL_INDEX_V(p_state.compare_op, COMPARE_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_state.border_color, SAMPLER_BORDER_COLOR_MAX, RID()); + + RDD::SamplerID sampler = driver->sampler_create(p_state); + ERR_FAIL_COND_V(!sampler, RID()); + + RID id = sampler_owner.make_rid(sampler); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; +} + +bool RenderingDevice::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const { + ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); + + _THREAD_SAFE_METHOD_ + + return driver->sampler_is_format_supported_for_filter(p_format, p_sampler_filter); +} + +/***********************/ +/**** VERTEX BUFFER ****/ +/***********************/ + +RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, bool p_use_as_storage) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + buffer.size = p_size_bytes; + buffer.usage = RDD::BUFFER_USAGE_TRANSFER_FROM_BIT | RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_VERTEX_BIT; + if (p_use_as_storage) { + buffer.usage.set_flag(RDD::BUFFER_USAGE_STORAGE_BIT); + } + buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); + ERR_FAIL_COND_V(!buffer.driver_id, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + const uint8_t *r = p_data.ptr(); + _buffer_update(&buffer, 0, r, data_size); + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = buffer.driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + bb.size = data_size; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT, {}, bb, {}); } } - return bytecode; + + buffer_memory += buffer.size; + + RID id = vertex_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } -Vector<uint8_t> RenderingDevice::_shader_compile_binary_from_spirv(const Ref<RDShaderSPIRV> &p_spirv, const String &p_shader_name) { - ERR_FAIL_COND_V(p_spirv.is_null(), Vector<uint8_t>()); +// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. +RenderingDevice::VertexFormatID RenderingDevice::vertex_format_create(const Vector<VertexAttribute> &p_vertex_descriptions) { + _THREAD_SAFE_METHOD_ - Vector<ShaderStageSPIRVData> stage_data; - for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { - ShaderStage stage = ShaderStage(i); - ShaderStageSPIRVData sd; - sd.shader_stage = stage; - String error = p_spirv->get_stage_compile_error(stage); - ERR_FAIL_COND_V_MSG(!error.is_empty(), Vector<uint8_t>(), "Can't create a shader from an errored bytecode. Check errors in source bytecode."); - sd.spir_v = p_spirv->get_stage_bytecode(stage); - if (sd.spir_v.is_empty()) { - continue; + VertexDescriptionKey key; + key.vertex_formats = p_vertex_descriptions; + + VertexFormatID *idptr = vertex_format_cache.getptr(key); + if (idptr) { + return *idptr; + } + + HashSet<int> used_locations; + for (int i = 0; i < p_vertex_descriptions.size(); i++) { + ERR_CONTINUE(p_vertex_descriptions[i].format >= DATA_FORMAT_MAX); + ERR_FAIL_COND_V(used_locations.has(p_vertex_descriptions[i].location), INVALID_ID); + + ERR_FAIL_COND_V_MSG(get_format_vertex_size(p_vertex_descriptions[i].format) == 0, INVALID_ID, + "Data format for attachment (" + itos(i) + "), '" + FORMAT_NAMES[p_vertex_descriptions[i].format] + "', is not valid for a vertex array."); + + used_locations.insert(p_vertex_descriptions[i].location); + } + + RDD::VertexFormatID driver_id = driver->vertex_format_create(p_vertex_descriptions); + ERR_FAIL_COND_V(!driver_id, 0); + + VertexFormatID id = (vertex_format_cache.size() | ((int64_t)ID_TYPE_VERTEX_FORMAT << ID_BASE_SHIFT)); + vertex_format_cache[key] = id; + vertex_formats[id].vertex_formats = p_vertex_descriptions; + vertex_formats[id].driver_id = driver_id; + return id; +} + +RID RenderingDevice::vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers, const Vector<uint64_t> &p_offsets) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); + const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; + + ERR_FAIL_COND_V(vd.vertex_formats.size() != p_src_buffers.size(), RID()); + + for (int i = 0; i < p_src_buffers.size(); i++) { + ERR_FAIL_COND_V(!vertex_buffer_owner.owns(p_src_buffers[i]), RID()); + } + + VertexArray vertex_array; + + if (p_offsets.is_empty()) { + vertex_array.offsets.resize_zeroed(p_src_buffers.size()); + } else { + ERR_FAIL_COND_V(p_offsets.size() != p_src_buffers.size(), RID()); + vertex_array.offsets = p_offsets; + } + + vertex_array.vertex_count = p_vertex_count; + vertex_array.description = p_vertex_format; + vertex_array.max_instances_allowed = 0xFFFFFFFF; // By default as many as you want. + for (int i = 0; i < p_src_buffers.size(); i++) { + Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); + + // Validate with buffer. + { + const VertexAttribute &atf = vd.vertex_formats[i]; + + uint32_t element_size = get_format_vertex_size(atf.format); + ERR_FAIL_COND_V(element_size == 0, RID()); // Should never happens since this was prevalidated. + + if (atf.frequency == VERTEX_FREQUENCY_VERTEX) { + // Validate size for regular drawing. + uint64_t total_size = uint64_t(atf.stride) * (p_vertex_count - 1) + atf.offset + element_size; + ERR_FAIL_COND_V_MSG(total_size > buffer->size, RID(), + "Attachment (" + itos(i) + ") will read past the end of the buffer."); + + } else { + // Validate size for instances drawing. + uint64_t available = buffer->size - atf.offset; + ERR_FAIL_COND_V_MSG(available < element_size, RID(), + "Attachment (" + itos(i) + ") uses instancing, but it's just too small."); + + uint32_t instances_allowed = available / atf.stride; + vertex_array.max_instances_allowed = MIN(instances_allowed, vertex_array.max_instances_allowed); + } } - stage_data.push_back(sd); + + vertex_array.buffers.push_back(buffer->driver_id); } - return shader_compile_binary_from_spirv(stage_data, p_shader_name); + RID id = vertex_array_owner.make_rid(vertex_array); + for (int i = 0; i < p_src_buffers.size(); i++) { + _add_dependency(id, p_src_buffers[i]); + } + + return id; } -RID RenderingDevice::_shader_create_from_spirv(const Ref<RDShaderSPIRV> &p_spirv, const String &p_shader_name) { - ERR_FAIL_COND_V(p_spirv.is_null(), RID()); +RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_index_count == 0, RID()); + + IndexBuffer index_buffer; + index_buffer.format = p_format; + index_buffer.supports_restart_indices = p_use_restart_indices; + index_buffer.index_count = p_index_count; + uint32_t size_bytes = p_index_count * ((p_format == INDEX_BUFFER_FORMAT_UINT16) ? 2 : 4); +#ifdef DEBUG_ENABLED + if (p_data.size()) { + index_buffer.max_index = 0; + ERR_FAIL_COND_V_MSG((uint32_t)p_data.size() != size_bytes, RID(), + "Default index buffer initializer array size (" + itos(p_data.size()) + ") does not match format required size (" + itos(size_bytes) + ")."); + const uint8_t *r = p_data.ptr(); + if (p_format == INDEX_BUFFER_FORMAT_UINT16) { + const uint16_t *index16 = (const uint16_t *)r; + for (uint32_t i = 0; i < p_index_count; i++) { + if (p_use_restart_indices && index16[i] == 0xFFFF) { + continue; // Restart index, ignore. + } + index_buffer.max_index = MAX(index16[i], index_buffer.max_index); + } + } else { + const uint32_t *index32 = (const uint32_t *)r; + for (uint32_t i = 0; i < p_index_count; i++) { + if (p_use_restart_indices && index32[i] == 0xFFFFFFFF) { + continue; // Restart index, ignore. + } + index_buffer.max_index = MAX(index32[i], index_buffer.max_index); + } + } + } else { + index_buffer.max_index = 0xFFFFFFFF; + } +#else + index_buffer.max_index = 0xFFFFFFFF; +#endif + index_buffer.size = size_bytes; + index_buffer.usage = (RDD::BUFFER_USAGE_TRANSFER_FROM_BIT | RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_INDEX_BIT); + index_buffer.driver_id = driver->buffer_create(index_buffer.size, index_buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); + ERR_FAIL_COND_V(!index_buffer.driver_id, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + const uint8_t *r = p_data.ptr(); + _buffer_update(&index_buffer, 0, r, data_size); + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = index_buffer.driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = RDD::BARRIER_ACCESS_INDEX_READ_BIT; + bb.size = data_size; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT, {}, bb, {}); + } + } - Vector<ShaderStageSPIRVData> stage_data; - for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { - ShaderStage stage = ShaderStage(i); - ShaderStageSPIRVData sd; - sd.shader_stage = stage; - String error = p_spirv->get_stage_compile_error(stage); - ERR_FAIL_COND_V_MSG(!error.is_empty(), RID(), "Can't create a shader from an errored bytecode. Check errors in source bytecode."); - sd.spir_v = p_spirv->get_stage_bytecode(stage); - if (sd.spir_v.is_empty()) { + buffer_memory += index_buffer.size; + + RID id = index_buffer_owner.make_rid(index_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; +} + +RID RenderingDevice::index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!index_buffer_owner.owns(p_index_buffer), RID()); + + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_index_buffer); + + ERR_FAIL_COND_V(p_index_count == 0, RID()); + ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, RID()); + + IndexArray index_array; + index_array.max_index = index_buffer->max_index; + index_array.driver_id = index_buffer->driver_id; + index_array.offset = p_index_offset; + index_array.indices = p_index_count; + index_array.format = index_buffer->format; + index_array.supports_restart_indices = index_buffer->supports_restart_indices; + + RID id = index_array_owner.make_rid(index_array); + _add_dependency(id, p_index_buffer); + return id; +} + +/****************/ +/**** SHADER ****/ +/****************/ + +static const char *SHADER_UNIFORM_NAMES[RenderingDevice::UNIFORM_TYPE_MAX] = { + "Sampler", "CombinedSampler", "Texture", "Image", "TextureBuffer", "SamplerTextureBuffer", "ImageBuffer", "UniformBuffer", "StorageBuffer", "InputAttachment" +}; + +String RenderingDevice::_shader_uniform_debug(RID p_shader, int p_set) { + String ret; + const Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_NULL_V(shader, String()); + for (int i = 0; i < shader->uniform_sets.size(); i++) { + if (p_set >= 0 && i != p_set) { continue; } - stage_data.push_back(sd); + for (int j = 0; j < shader->uniform_sets[i].size(); j++) { + const ShaderUniform &ui = shader->uniform_sets[i][j]; + if (!ret.is_empty()) { + ret += "\n"; + } + ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + SHADER_UNIFORM_NAMES[ui.type] + " Writable: " + (ui.writable ? "Y" : "N") + " Length: " + itos(ui.length); + } } - return shader_create_from_spirv(stage_data); + return ret; } -RID RenderingDevice::_uniform_set_create(const TypedArray<RDUniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) { - Vector<Uniform> uniforms; - uniforms.resize(p_uniforms.size()); - for (int i = 0; i < p_uniforms.size(); i++) { - Ref<RDUniform> uniform = p_uniforms[i]; - ERR_FAIL_COND_V(!uniform.is_valid(), RID()); - uniforms.write[i] = uniform->base; +String RenderingDevice::shader_get_binary_cache_key() const { + return driver->shader_get_binary_cache_key(); +} + +Vector<uint8_t> RenderingDevice::shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name) { + return driver->shader_compile_binary_from_spirv(p_spirv, p_shader_name); +} + +RID RenderingDevice::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder) { + _THREAD_SAFE_METHOD_ + + ShaderDescription shader_desc; + String name; + RDD::ShaderID shader_id = driver->shader_create_from_bytecode(p_shader_binary, shader_desc, name); + ERR_FAIL_COND_V(!shader_id, RID()); + + // All good, let's create modules. + + RID id; + if (p_placeholder.is_null()) { + id = shader_owner.make_rid(); + } else { + id = p_placeholder; } - return uniform_set_create(uniforms, p_shader, p_shader_set); + + Shader *shader = shader_owner.get_or_null(id); + ERR_FAIL_NULL_V(shader, RID()); + + *((ShaderDescription *)shader) = shader_desc; // ShaderDescription bundle. + shader->name = name; + shader->driver_id = shader_id; + shader->layout_hash = driver->shader_get_layout_hash(shader_id); + + for (int i = 0; i < shader->uniform_sets.size(); i++) { + uint32_t format = 0; // No format, default. + + if (shader->uniform_sets[i].size()) { + // Sort and hash. + + shader->uniform_sets.write[i].sort(); + + UniformSetFormat usformat; + usformat.uniforms = shader->uniform_sets[i]; + RBMap<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); + if (E) { + format = E->get(); + } else { + format = uniform_set_format_cache.size() + 1; + uniform_set_format_cache.insert(usformat, format); + } + } + + shader->set_formats.push_back(format); + } + +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } -Error RenderingDevice::_buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { - return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier); +RID RenderingDevice::shader_create_placeholder() { + Shader shader; + return shader_owner.make_rid(shader); } -static Vector<RenderingDevice::PipelineSpecializationConstant> _get_spec_constants(const TypedArray<RDPipelineSpecializationConstant> &p_constants) { - Vector<RenderingDevice::PipelineSpecializationConstant> ret; - ret.resize(p_constants.size()); - for (int i = 0; i < p_constants.size(); i++) { - Ref<RDPipelineSpecializationConstant> c = p_constants[i]; - ERR_CONTINUE(c.is_null()); - RenderingDevice::PipelineSpecializationConstant &sc = ret.write[i]; - Variant value = c->get_value(); - switch (value.get_type()) { - case Variant::BOOL: { - sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; - sc.bool_value = value; +uint64_t RenderingDevice::shader_get_vertex_input_attribute_mask(RID p_shader) { + _THREAD_SAFE_METHOD_ + + const Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_NULL_V(shader, 0); + return shader->vertex_input_mask; +} + +/******************/ +/**** UNIFORMS ****/ +/******************/ + +RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + buffer.size = p_size_bytes; + buffer.usage = (RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_UNIFORM_BIT); + buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); + ERR_FAIL_COND_V(!buffer.driver_id, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + const uint8_t *r = p_data.ptr(); + _buffer_update(&buffer, 0, r, data_size); + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::BufferBarrier bb; + bb.buffer = buffer.driver_id; + bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + bb.dst_access = RDD::BARRIER_ACCESS_UNIFORM_READ_BIT; + bb.size = data_size; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, bb, {}); + } + } + + buffer_memory += buffer.size; + + RID id = uniform_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; +} + +RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_uniforms.size() == 0, RID()); + + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_NULL_V(shader, RID()); + + ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->uniform_sets.size() || shader->uniform_sets[p_shader_set].size() == 0, RID(), + "Desired set (" + itos(p_shader_set) + ") not used by shader."); + // See that all sets in shader are satisfied. + + const Vector<ShaderUniform> &set = shader->uniform_sets[p_shader_set]; + + uint32_t uniform_count = p_uniforms.size(); + const Uniform *uniforms = p_uniforms.ptr(); + + uint32_t set_uniform_count = set.size(); + const ShaderUniform *set_uniforms = set.ptr(); + + LocalVector<RDD::BoundUniform> driver_uniforms; + driver_uniforms.resize(set_uniform_count); + + // Used for verification to make sure a uniform set does not use a framebuffer bound texture. + LocalVector<UniformSet::AttachableTexture> attachable_textures; + Vector<Texture *> mutable_sampled_textures; + Vector<Texture *> mutable_storage_textures; + + for (uint32_t i = 0; i < set_uniform_count; i++) { + const ShaderUniform &set_uniform = set_uniforms[i]; + int uniform_idx = -1; + for (int j = 0; j < (int)uniform_count; j++) { + if (uniforms[j].binding == set_uniform.binding) { + uniform_idx = j; + } + } + ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(), + "All the shader bindings for the given set must be covered by the uniforms provided. Binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + ") was not provided."); + + const Uniform &uniform = uniforms[uniform_idx]; + + ERR_FAIL_COND_V_MSG(uniform.uniform_type != set_uniform.type, RID(), + "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + "). Expected '" + SHADER_UNIFORM_NAMES[set_uniform.type] + "', supplied: '" + SHADER_UNIFORM_NAMES[uniform.uniform_type] + "'."); + + RDD::BoundUniform &driver_uniform = driver_uniforms[i]; + driver_uniform.type = uniform.uniform_type; + driver_uniform.binding = uniform.binding; + + switch (uniform.uniform_type) { + case UNIFORM_TYPE_SAMPLER: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(uniform.get_id(j)); + ERR_FAIL_COND_V_MSG(!sampler_driver_id, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); + + driver_uniform.ids.push_back(*sampler_driver_id); + } } break; - case Variant::INT: { - sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; - sc.int_value = value; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(uniform.get_id(j + 0)); + ERR_FAIL_COND_V_MSG(!sampler_driver_id, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); + + Texture *texture = texture_owner.get_or_null(uniform.get_id(j + 1)); + ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + if ((texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT))) { + UniformSet::AttachableTexture attachable_texture; + attachable_texture.bind = set_uniform.binding; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j + 1); + attachable_textures.push_back(attachable_texture); + } + + if ((texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { + mutable_sampled_textures.push_back(texture); + } + + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); + + driver_uniform.ids.push_back(*sampler_driver_id); + driver_uniform.ids.push_back(texture->driver_id); + } } break; - case Variant::FLOAT: { - sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; - sc.float_value = value; + case UNIFORM_TYPE_TEXTURE: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + if ((texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT))) { + UniformSet::AttachableTexture attachable_texture; + attachable_texture.bind = set_uniform.binding; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j); + attachable_textures.push_back(attachable_texture); + } + + if ((texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { + mutable_sampled_textures.push_back(texture); + } + + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); + + driver_uniform.ids.push_back(texture->driver_id); + } + } break; + case UNIFORM_TYPE_IMAGE: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + + ERR_FAIL_NULL_V_MSG(texture, RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_STORAGE_BIT usage flag set in order to be used as uniform."); + + if ((texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT)) { + mutable_storage_textures.push_back(texture); + } + + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); + + driver_uniform.ids.push_back(texture->driver_id); + } + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Buffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j)); + ERR_FAIL_NULL_V_MSG(buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); + + driver_uniform.ids.push_back(buffer->driver_id); + } + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(uniform.get_id(j + 0)); + ERR_FAIL_COND_V_MSG(!sampler_driver_id, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); + + Buffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); + ERR_FAIL_NULL_V_MSG(buffer, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid texture buffer."); + + driver_uniform.ids.push_back(*sampler_driver_id); + driver_uniform.ids.push_back(buffer->driver_id); + } + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + // Todo. + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); + + Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.get_id(0)); + ERR_FAIL_NULL_V_MSG(buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); + + ERR_FAIL_COND_V_MSG(buffer->size < (uint32_t)set_uniform.length, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " is smaller than size of shader uniform: (" + itos(set_uniform.length) + ")."); + + driver_uniform.ids.push_back(buffer->driver_id); + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); + + Buffer *buffer = nullptr; + + if (storage_buffer_owner.owns(uniform.get_id(0))) { + buffer = storage_buffer_owner.get_or_null(uniform.get_id(0)); + } else if (vertex_buffer_owner.owns(uniform.get_id(0))) { + buffer = vertex_buffer_owner.get_or_null(uniform.get_id(0)); + + ERR_FAIL_COND_V_MSG(!(buffer->usage.has_flag(RDD::BUFFER_USAGE_STORAGE_BIT)), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); + } + ERR_FAIL_NULL_V_MSG(buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); + + // If 0, then it's sized on link time. + ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); + + driver_uniform.ids.push_back(buffer->driver_id); + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); + + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + + ERR_FAIL_NULL_V_MSG(texture, RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); + + driver_uniform.ids.push_back(texture->driver_id); + } } break; default: { } } + } - sc.constant_id = c->get_constant_id(); + RDD::UniformSetID driver_uniform_set = driver->uniform_set_create(driver_uniforms, shader->driver_id, p_shader_set); + ERR_FAIL_COND_V(!driver_uniform_set, RID()); + + UniformSet uniform_set; + uniform_set.driver_id = driver_uniform_set; + uniform_set.format = shader->set_formats[p_shader_set]; + uniform_set.attachable_textures = attachable_textures; + uniform_set.mutable_sampled_textures = mutable_sampled_textures; + uniform_set.mutable_storage_textures = mutable_storage_textures; + uniform_set.shader_set = p_shader_set; + uniform_set.shader_id = p_shader; + + RID id = uniform_set_owner.make_rid(uniform_set); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Add dependencies. + _add_dependency(id, p_shader); + for (uint32_t i = 0; i < uniform_count; i++) { + const Uniform &uniform = uniforms[i]; + int id_count = uniform.get_id_count(); + for (int j = 0; j < id_count; j++) { + _add_dependency(id, uniform.get_id(j)); + } } - return ret; + + return id; } -RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants) { - PipelineRasterizationState rasterization_state; - if (p_rasterization_state.is_valid()) { - rasterization_state = p_rasterization_state->base; +bool RenderingDevice::uniform_set_is_valid(RID p_uniform_set) { + return uniform_set_owner.owns(p_uniform_set); +} + +void RenderingDevice::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) { + UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); + ERR_FAIL_NULL(us); + us->invalidated_callback = p_callback; + us->invalidated_callback_userdata = p_userdata; +} + +/*******************/ +/**** PIPELINES ****/ +/*******************/ + +RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { + _THREAD_SAFE_METHOD_ + + // Needs a shader. + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_NULL_V(shader, RID()); + + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), + "Compute shaders can't be used in render pipelines"); + + if (p_framebuffer_format == INVALID_ID) { + // If nothing provided, use an empty one (no attachments). + p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>()); + } + ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID()); + const FramebufferFormat &fb_format = framebuffer_formats[p_framebuffer_format]; + + // Validate shader vs. framebuffer. + { + ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + uint32_t output_mask = 0; + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != ATTACHMENT_UNUSED) { + output_mask |= 1 << i; + } + } + ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), + "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); } - PipelineMultisampleState multisample_state; - if (p_multisample_state.is_valid()) { - multisample_state = p_multisample_state->base; - for (int i = 0; i < p_multisample_state->sample_masks.size(); i++) { - int64_t mask = p_multisample_state->sample_masks[i]; - multisample_state.sample_mask.push_back(mask); + RDD::VertexFormatID driver_vertex_format; + if (p_vertex_format != INVALID_ID) { + // Uses vertices, else it does not. + ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); + const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; + driver_vertex_format = vertex_formats[p_vertex_format].driver_id; + + // Validate with inputs. + for (uint32_t i = 0; i < 64; i++) { + if (!(shader->vertex_input_mask & ((uint64_t)1) << i)) { + continue; + } + bool found = false; + for (int j = 0; j < vd.vertex_formats.size(); j++) { + if (vd.vertex_formats[j].location == i) { + found = true; + } + } + + ERR_FAIL_COND_V_MSG(!found, RID(), + "Shader vertex input location (" + itos(i) + ") not provided in vertex input description for pipeline creation."); } + + } else { + ERR_FAIL_COND_V_MSG(shader->vertex_input_mask != 0, RID(), + "Shader contains vertex inputs, but no vertex input description was provided for pipeline creation."); } - PipelineDepthStencilState depth_stencil_state; - if (p_depth_stencil_state.is_valid()) { - depth_stencil_state = p_depth_stencil_state->base; + ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); + + ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); + + if (p_multisample_state.sample_mask.size()) { + // Use sample mask. + ERR_FAIL_COND_V((int)TEXTURE_SAMPLES_COUNT[p_multisample_state.sample_count] != p_multisample_state.sample_mask.size(), RID()); } - PipelineColorBlendState color_blend_state; - if (p_blend_state.is_valid()) { - color_blend_state = p_blend_state->base; - for (int i = 0; i < p_blend_state->attachments.size(); i++) { - Ref<RDPipelineColorBlendStateAttachment> attachment = p_blend_state->attachments[i]; - if (attachment.is_valid()) { - color_blend_state.attachments.push_back(attachment->base); + ERR_FAIL_INDEX_V(p_depth_stencil_state.depth_compare_operator, COMPARE_OP_MAX, RID()); + + ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.fail, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.pass, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.depth_fail, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.compare, COMPARE_OP_MAX, RID()); + + ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.fail, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.pass, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.depth_fail, STENCIL_OP_MAX, RID()); + ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.compare, COMPARE_OP_MAX, RID()); + + ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); + + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + ERR_FAIL_COND_V(p_blend_state.attachments.size() < pass.color_attachments.size(), RID()); + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != ATTACHMENT_UNUSED) { + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); + + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); + } + } + + for (int i = 0; i < shader->specialization_constants.size(); i++) { + const ShaderSpecializationConstant &sc = shader->specialization_constants[i]; + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.type, RID(), "Specialization constant provided for id (" + itos(sc.constant_id) + ") is of the wrong type."); + break; } } } - return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass, _get_spec_constants(p_specialization_constants)); + RenderPipeline pipeline; + pipeline.driver_id = driver->render_pipeline_create( + shader->driver_id, + driver_vertex_format, + p_render_primitive, + p_rasterization_state, + p_multisample_state, + p_depth_stencil_state, + p_blend_state, + pass.color_attachments, + p_dynamic_state_flags, + fb_format.render_pass, + p_for_render_pass, + p_specialization_constants); + ERR_FAIL_COND_V(!pipeline.driver_id, RID()); + + if (pipelines_cache_enabled) { + _update_pipeline_cache(); + } + + pipeline.shader = p_shader; + pipeline.shader_driver_id = shader->driver_id; + pipeline.shader_layout_hash = shader->layout_hash; + pipeline.set_formats = shader->set_formats; + pipeline.push_constant_size = shader->push_constant_size; + +#ifdef DEBUG_ENABLED + pipeline.validation.dynamic_state = p_dynamic_state_flags; + pipeline.validation.framebuffer_format = p_framebuffer_format; + pipeline.validation.render_pass = p_for_render_pass; + pipeline.validation.vertex_format = p_vertex_format; + pipeline.validation.uses_restart_indices = p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX; + + static const uint32_t primitive_divisor[RENDER_PRIMITIVE_MAX] = { + 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1 + }; + pipeline.validation.primitive_divisor = primitive_divisor[p_render_primitive]; + static const uint32_t primitive_minimum[RENDER_PRIMITIVE_MAX] = { + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + }; + pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; +#endif + // Create ID to associate with this pipeline. + RID id = render_pipeline_owner.make_rid(pipeline); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. + _add_dependency(id, p_shader); + return id; } -RID RenderingDevice::_compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants = TypedArray<RDPipelineSpecializationConstant>()) { - return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants)); +bool RenderingDevice::render_pipeline_is_valid(RID p_pipeline) { + _THREAD_SAFE_METHOD_ + return render_pipeline_owner.owns(p_pipeline); } -RenderingDevice::DrawListID RenderingDevice::_draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { - Vector<RID> stextures; - for (int i = 0; i < p_storage_textures.size(); i++) { - stextures.push_back(p_storage_textures[i]); +RID RenderingDevice::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { + _THREAD_SAFE_METHOD_ + + // Needs a shader. + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_NULL_V(shader, RID()); + + ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), + "Non-compute shaders can't be used in compute pipelines"); + + for (int i = 0; i < shader->specialization_constants.size(); i++) { + const ShaderSpecializationConstant &sc = shader->specialization_constants[i]; + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.type, RID(), "Specialization constant provided for id (" + itos(sc.constant_id) + ") is of the wrong type."); + break; + } + } } - return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); + + ComputePipeline pipeline; + pipeline.driver_id = driver->compute_pipeline_create(shader->driver_id, p_specialization_constants); + ERR_FAIL_COND_V(!pipeline.driver_id, RID()); + + if (pipelines_cache_enabled) { + _update_pipeline_cache(); + } + + pipeline.shader = p_shader; + pipeline.shader_driver_id = shader->driver_id; + pipeline.shader_layout_hash = shader->layout_hash; + pipeline.set_formats = shader->set_formats; + pipeline.push_constant_size = shader->push_constant_size; + pipeline.local_group_size[0] = shader->compute_local_size[0]; + pipeline.local_group_size[1] = shader->compute_local_size[1]; + pipeline.local_group_size[2] = shader->compute_local_size[2]; + + // Create ID to associate with this pipeline. + RID id = compute_pipeline_owner.make_rid(pipeline); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. + _add_dependency(id, p_shader); + return id; } -Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { - Vector<DrawListID> splits; - splits.resize(p_splits); - Vector<RID> stextures; +bool RenderingDevice::compute_pipeline_is_valid(RID p_pipeline) { + return compute_pipeline_owner.owns(p_pipeline); +} + +/****************/ +/**** SCREEN ****/ +/****************/ + +int RenderingDevice::screen_get_width(DisplayServer::WindowID p_screen) const { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(local_device.is_valid(), -1, "Local devices have no screen"); + return context->window_get_width(p_screen); +} + +int RenderingDevice::screen_get_height(DisplayServer::WindowID p_screen) const { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(local_device.is_valid(), -1, "Local devices have no screen"); + return context->window_get_height(p_screen); +} + +RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_format() const { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); + + DataFormat format = driver->screen_get_format(); + ERR_FAIL_COND_V(format == DATA_FORMAT_MAX, INVALID_ID); + + AttachmentFormat attachment; + attachment.format = format; + attachment.samples = TEXTURE_SAMPLES_1; + attachment.usage_flags = TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + Vector<AttachmentFormat> screen_attachment; + screen_attachment.push_back(attachment); + return const_cast<RenderingDevice *>(this)->framebuffer_format_create(screen_attachment); +} + +/*******************/ +/**** DRAW LIST ****/ +/*******************/ + +RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayServer::WindowID p_screen, const Color &p_clear_color) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); + + ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + + if (!context->window_is_valid_swapchain(p_screen)) { + return INVALID_ID; + } + + Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); + + _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); +#ifdef DEBUG_ENABLED + draw_list_framebuffer_format = screen_get_framebuffer_format(); +#endif + draw_list_subpass_count = 1; + + RDD::RenderPassClearValue clear_value; + clear_value.color = p_clear_color; + driver->command_begin_render_pass( + command_buffer, + context->window_get_render_pass(p_screen), + context->window_get_framebuffer(p_screen), + RDD::COMMAND_BUFFER_TYPE_PRIMARY, + Rect2i(0, 0, size.width, size.height), + VectorView(&clear_value, 1)); + + driver->command_render_set_viewport(command_buffer, Rect2i(Point2i(), size)); + driver->command_render_set_scissor(command_buffer, Rect2i(Point2i(), size)); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; +} + +Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count) { + Framebuffer::VersionKey vk; + vk.initial_color_action = p_initial_color_action; + vk.final_color_action = p_final_color_action; + vk.initial_depth_action = p_initial_depth_action; + vk.final_depth_action = p_final_depth_action; + vk.view_count = p_framebuffer->view_count; + + if (!p_framebuffer->framebuffers.has(vk)) { + // Need to create this version. + Framebuffer::Version version; + + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); + + LocalVector<RDD::TextureID> attachments; + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); + if (texture) { + attachments.push_back(texture->driver_id); + if (!(texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { // VRS attachment will be a different size. + ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); + ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + } + } + } + + version.framebuffer = driver->framebuffer_create(version.render_pass, attachments, p_framebuffer->size.width, p_framebuffer->size.height); + ERR_FAIL_COND_V(!version.framebuffer, ERR_CANT_CREATE); + + version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); + + p_framebuffer->framebuffers.insert(vk, version); + } + const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; + *r_framebuffer = version.framebuffer; + *r_render_pass = version.render_pass; + *r_subpass_count = version.subpass_count; + + return OK; +} + +Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, RDD::CommandBufferID p_command_buffer, RDD::CommandBufferType p_cmd_buffer_mode, const Vector<RID> &p_storage_textures, bool p_constrained_to_region) { + LocalVector<RDD::RenderPassClearValue> clear_values; + clear_values.resize(p_framebuffer->texture_ids.size()); + int clear_values_count = 0; + { + int color_index = 0; + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + RDD::RenderPassClearValue clear_value; + + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); + if (!texture) { + color_index++; + continue; + } + + if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. + clear_value.color = p_clear_colors[color_index]; + color_index++; + } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + clear_value.depth = p_clear_depth; + clear_value.stencil = p_clear_stencil; + } + + clear_values[clear_values_count++] = clear_value; + } + } + for (int i = 0; i < p_storage_textures.size(); i++) { - stextures.push_back(p_storage_textures[i]); + Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); + if (!texture) { + continue; + } + ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); + + if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // Must change layout to general. + RDD::TextureBarrier tb; + tb.texture = texture->driver_id; + tb.src_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + tb.prev_layout = texture->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_GENERAL; + tb.subresources.aspect = texture->read_aspect_flags; + tb.subresources.base_mipmap = texture->base_mipmap; + tb.subresources.mipmap_count = texture->mipmaps; + tb.subresources.base_layer = texture->base_layer; + tb.subresources.layer_count = texture->layers; + + driver->command_pipeline_barrier(p_command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, {}, {}, tb); + + texture->layout = RDD::TEXTURE_LAYOUT_GENERAL; + } + + draw_list_storage_textures.push_back(p_storage_textures[i]); + } } - draw_list_begin_split(p_framebuffer, p_splits, splits.ptrw(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); - Vector<int64_t> split_ids; - split_ids.resize(splits.size()); - for (int i = 0; i < splits.size(); i++) { - split_ids.write[i] = splits[i]; + Rect2i region; + if (p_constrained_to_region) { + region = Rect2i(p_viewport_offset, p_viewport_size); + } else { + region = Rect2i(Point2i(), p_framebuffer->size); } - return split_ids; + driver->command_begin_render_pass( + p_command_buffer, + p_render_pass, + p_framebuffer_driver_id, + p_cmd_buffer_mode, + region, + clear_values); + + // Mark textures as bound. + draw_list_bound_textures.clear(); + draw_list_unbind_color_textures = p_final_color_action != FINAL_ACTION_CONTINUE; + draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; + + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); + if (!texture) { + continue; + } + texture->bound = true; + draw_list_bound_textures.push_back(p_framebuffer->texture_ids[i]); + } + + return OK; } -Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p_splits) { - Vector<DrawListID> splits; - splits.resize(p_splits); +void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { + LocalVector<RDD::AttachmentClear> clear_attachments; + int color_index = 0; + int texture_index = 0; + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - Error err = draw_list_switch_to_next_pass_split(p_splits, splits.ptrw()); - ERR_FAIL_COND_V(err != OK, Vector<int64_t>()); + if (!texture) { + texture_index++; + continue; + } - Vector<int64_t> split_ids; - split_ids.resize(splits.size()); - for (int i = 0; i < splits.size(); i++) { - split_ids.write[i] = splits[i]; + RDD::AttachmentClear clear_at; + if (p_clear_color && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + Color clear_color = p_clear_colors[texture_index++]; + clear_at.value.color = clear_color; + clear_at.color_attachment = color_index++; + clear_at.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; + } else if (p_clear_depth && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + clear_at.value.depth = p_depth; + clear_at.value.stencil = p_stencil; + clear_at.color_attachment = 0; + clear_at.aspect = RDD::TEXTURE_ASPECT_DEPTH_BIT; + if (format_has_stencil(texture->format)) { + clear_at.aspect.set_flag(RDD::TEXTURE_ASPECT_STENCIL_BIT); + } + } else { + ERR_CONTINUE(true); + } + clear_attachments.push_back(clear_at); } - return split_ids; + Rect2i rect = Rect2i(p_viewport_offset, p_viewport_size); + + driver->command_render_clear_attachments(p_draw_list->command_buffer, clear_attachments, rect); } -void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); - draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size); +RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_NULL_V(framebuffer, INVALID_ID); + + Point2i viewport_offset; + Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; + bool needs_clear_color = false; + bool needs_clear_depth = false; + + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. + Rect2i viewport(viewport_offset, viewport_size); + Rect2i regioni = p_region; + if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && + ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && + ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { + ERR_FAIL_V_MSG(INVALID_ID, "When supplying a custom region, it must be contained within the framebuffer rectangle"); + } + + viewport_offset = regioni.position; + viewport_size = regioni.size; + + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } + } + } + + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + // We only check for our VRS usage bit if this is not the first texture id. + // If it is the first we're likely populating our VRS texture. + // Bit dirty but... + if (!texture || (!(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) { + if (!texture || !texture->is_resolve_buffer) { + color_count++; + } + } + } + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); + } + + RDD::FramebufferID fb_driver_id; + RDD::RenderPassID render_pass; + + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); + ERR_FAIL_COND_V(err != OK, INVALID_ID); + + RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY, p_storage_textures, constrained_to_region); + + if (err != OK) { + return INVALID_ID; + } + + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = fb_driver_id; + + _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); +#ifdef DEBUG_ENABLED + draw_list_framebuffer_format = framebuffer->format_id; +#endif + draw_list_current_subpass = 0; + + if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); + _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + driver->command_render_set_viewport(command_buffer, Rect2i(viewport_offset, viewport_size)); + driver->command_render_set_scissor(command_buffer, Rect2i(viewport_offset, viewport_size)); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); - compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); +Error RenderingDevice::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list != nullptr, ERR_BUSY, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, ERR_BUSY, "Only one draw/compute list can be active at the same time."); + + ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_NULL_V(framebuffer, ERR_INVALID_DECLARATION); + + Point2i viewport_offset; + Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; + bool needs_clear_color = false; + bool needs_clear_depth = false; + + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. + Rect2i viewport(viewport_offset, viewport_size); + Rect2i regioni = p_region; + if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && + ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && + ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "When supplying a custom region, it must be contained within the framebuffer rectangle"); + } + + viewport_offset = regioni.position; + viewport_size = regioni.size; + + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } + } + } + + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. + + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + + if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + color_count++; + } + } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); + } + + RDD::FramebufferID fb_driver_id; + RDD::RenderPassID render_pass; + + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + RDD::CommandBufferID frame_command_buffer = frames[frame].draw_command_buffer; + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, frame_command_buffer, RDD::COMMAND_BUFFER_TYPE_SECONDARY, p_storage_textures, constrained_to_region); + + if (err != OK) { + return ERR_CANT_CREATE; + } + + draw_list_current_subpass = 0; + +#ifdef DEBUG_ENABLED + draw_list_framebuffer_format = framebuffer->format_id; +#endif + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = fb_driver_id; + + err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); + if (err != OK) { + return err; + } + + if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); + _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + bool secondary_viewport_scissor = driver->api_trait_get(RDD::API_TRAIT_SECONDARY_VIEWPORT_SCISSOR); + for (uint32_t i = 0; i < p_splits; i++) { + if (secondary_viewport_scissor) { + driver->command_render_set_viewport(draw_list[i].command_buffer, Rect2i(viewport_offset, viewport_size)); + driver->command_render_set_scissor(draw_list[i].command_buffer, Rect2i(viewport_offset, viewport_size)); + } + r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; + } + + return OK; } -Error RenderingDevice::_reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, SpirvReflectionData &r_reflection_data) { - r_reflection_data = {}; +RenderingDevice::DrawList *RenderingDevice::_get_draw_list_ptr(DrawListID p_id) { + if (p_id < 0) { + return nullptr; + } + + if (!draw_list) { + return nullptr; + } else if (p_id == (int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT)) { + if (draw_list_split) { + return nullptr; + } + return draw_list; + } else if (p_id >> DrawListID(ID_BASE_SHIFT) == ID_TYPE_SPLIT_DRAW_LIST) { + if (!draw_list_split) { + return nullptr; + } - for (int i = 0; i < p_spirv.size(); i++) { - ShaderStage stage = p_spirv[i].shader_stage; - ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage); + uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); // Mask. - if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) { - r_reflection_data.is_compute = true; - ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, FAILED, - "Compute shaders can only receive one stage, dedicated to compute."); + if (index >= draw_list_count) { + return nullptr; } - ERR_FAIL_COND_V_MSG(r_reflection_data.stages_mask.has_flag(stage_flag), FAILED, - "Stage " + String(shader_stage_names[p_spirv[i].shader_stage]) + " submitted more than once."); - { - SpvReflectShaderModule module; - const uint8_t *spirv = p_spirv[i].spir_v.ptr(); - SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spir_v.size(), spirv, &module); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed parsing shader."); - - if (r_reflection_data.is_compute) { - r_reflection_data.compute_local_size[0] = module.entry_points->local_size.x; - r_reflection_data.compute_local_size[1] = module.entry_points->local_size.y; - r_reflection_data.compute_local_size[2] = module.entry_points->local_size.z; - } - uint32_t binding_count = 0; - result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings."); - - if (binding_count > 0) { - // Parse bindings. - - Vector<SpvReflectDescriptorBinding *> bindings; - bindings.resize(binding_count); - result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); - - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings."); - - for (uint32_t j = 0; j < binding_count; j++) { - const SpvReflectDescriptorBinding &binding = *bindings[j]; - - SpirvReflectionData::Uniform info{}; - - bool need_array_dimensions = false; - bool need_block_size = false; - bool may_be_writable = false; - - switch (binding.descriptor_type) { - case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { - info.type = UNIFORM_TYPE_SAMPLER; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { - info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { - info.type = UNIFORM_TYPE_TEXTURE; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { - info.type = UNIFORM_TYPE_IMAGE; - need_array_dimensions = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { - info.type = UNIFORM_TYPE_TEXTURE_BUFFER; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { - info.type = UNIFORM_TYPE_IMAGE_BUFFER; - need_array_dimensions = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - info.type = UNIFORM_TYPE_UNIFORM_BUFFER; - need_block_size = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - info.type = UNIFORM_TYPE_STORAGE_BUFFER; - need_block_size = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { - ERR_PRINT("Dynamic uniform buffer not supported."); - continue; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { - ERR_PRINT("Dynamic storage buffer not supported."); - continue; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { - ERR_PRINT("Acceleration structure not supported."); - continue; - } break; - } + return &draw_list[index]; + } else { + return nullptr; + } +} - if (need_array_dimensions) { - if (binding.array.dims_count == 0) { - info.length = 1; - } else { - for (uint32_t k = 0; k < binding.array.dims_count; k++) { - if (k == 0) { - info.length = binding.array.dims[0]; - } else { - info.length *= binding.array.dims[k]; - } - } - } +void RenderingDevice::draw_list_set_blend_constants(DrawListID p_list, const Color &p_color) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif - } else if (need_block_size) { - info.length = binding.block.size; - } else { - info.length = 0; - } + driver->command_render_set_blend_constants(dl->command_buffer, p_color); +} - if (may_be_writable) { - info.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); - } else { - info.writable = false; - } +void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif - info.binding = binding.binding; - uint32_t set = binding.set; - - ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); - - if (set < (uint32_t)r_reflection_data.uniforms.size()) { - // Check if this already exists. - bool exists = false; - for (int k = 0; k < r_reflection_data.uniforms[set].size(); k++) { - if (r_reflection_data.uniforms[set][k].binding == (uint32_t)info.binding) { - // Already exists, verify that it's the same type. - ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].type != info.type, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); - - // Also, verify that it's the same size. - ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].length != info.length, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); - - // Also, verify that it has the same writability. - ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].writable != info.writable, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); - - // Just append stage mask and return. - r_reflection_data.uniforms.write[set].write[k].stages_mask.set_flag(stage_flag); - exists = true; - break; - } - } + const RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_render_pipeline); + ERR_FAIL_NULL(pipeline); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass); +#endif - if (exists) { - continue; // Merged. - } - } + if (p_render_pipeline == dl->state.pipeline) { + return; // Redundant state, return. + } - info.stages_mask.set_flag(stage_flag); + dl->state.pipeline = p_render_pipeline; - if (set >= (uint32_t)r_reflection_data.uniforms.size()) { - r_reflection_data.uniforms.resize(set + 1); + driver->command_bind_render_pipeline(dl->command_buffer, pipeline->driver_id); + + if (dl->state.pipeline_shader != pipeline->shader) { + // Shader changed, so descriptor sets may become incompatible. + + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. + dl->state.set_count = MAX(dl->state.set_count, pcount); + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. + + uint32_t first_invalid_set = UINT32_MAX; // All valid by default. + switch (driver->api_trait_get(RDD::API_TRAIT_SHADER_CHANGE_INVALIDATION)) { + case RDD::SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS: { + first_invalid_set = 0; + } break; + case RDD::SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE: { + for (uint32_t i = 0; i < pcount; i++) { + if (dl->state.sets[i].pipeline_expected_format != pformats[i]) { + first_invalid_set = i; + break; } + } + } break; + case RDD::SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH: { + if (dl->state.pipeline_shader_layout_hash != pipeline->shader_layout_hash) { + first_invalid_set = 0; + } + } break; + } + + for (uint32_t i = 0; i < pcount; i++) { + dl->state.sets[i].bound = dl->state.sets[i].bound && i < first_invalid_set; + dl->state.sets[i].pipeline_expected_format = pformats[i]; + } + + for (uint32_t i = pcount; i < dl->state.set_count; i++) { + // Unbind the ones above (not used) if exist. + dl->state.sets[i].bound = false; + } + + dl->state.set_count = pcount; // Update set count. + + if (pipeline->push_constant_size) { +#ifdef DEBUG_ENABLED + dl->validation.pipeline_push_constant_supplied = false; +#endif + } + + dl->state.pipeline_shader = pipeline->shader; + dl->state.pipeline_shader_driver_id = pipeline->shader_driver_id; + dl->state.pipeline_shader_layout_hash = pipeline->shader_layout_hash; + } + +#ifdef DEBUG_ENABLED + // Update render pass pipeline info. + dl->validation.pipeline_active = true; + dl->validation.pipeline_dynamic_state = pipeline->validation.dynamic_state; + dl->validation.pipeline_vertex_format = pipeline->validation.vertex_format; + dl->validation.pipeline_uses_restart_indices = pipeline->validation.uses_restart_indices; + dl->validation.pipeline_primitive_divisor = pipeline->validation.primitive_divisor; + dl->validation.pipeline_primitive_minimum = pipeline->validation.primitive_minimum; + dl->validation.pipeline_push_constant_size = pipeline->push_constant_size; +#endif +} + +void RenderingDevice::draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_index >= driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS) || p_index >= MAX_UNIFORM_SETS, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS)) + ")."); +#endif + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); + ERR_FAIL_NULL(uniform_set); + + if (p_index > dl->state.set_count) { + dl->state.set_count = p_index; + } - r_reflection_data.uniforms.write[set].push_back(info); + dl->state.sets[p_index].uniform_set_driver_id = uniform_set->driver_id; // Update set pointer. + dl->state.sets[p_index].bound = false; // Needs rebind. + dl->state.sets[p_index].uniform_set_format = uniform_set->format; + dl->state.sets[p_index].uniform_set = p_uniform_set; + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + uint32_t mst_count = uniform_set->mutable_storage_textures.size(); + if (mst_count) { + Texture **mst_textures = const_cast<UniformSet *>(uniform_set)->mutable_storage_textures.ptrw(); + for (uint32_t i = 0; i < mst_count; i++) { + if (mst_textures[i]->used_in_frame != frames_drawn) { + mst_textures[i]->used_in_frame = frames_drawn; + mst_textures[i]->used_in_transfer = false; + mst_textures[i]->used_in_compute = false; } + mst_textures[i]->used_in_raster = true; } + } + } - { - // Specialization constants. - - uint32_t sc_count = 0; - result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants."); - - if (sc_count) { - Vector<SpvReflectSpecializationConstant *> spec_constants; - spec_constants.resize(sc_count); - - result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants."); - - for (uint32_t j = 0; j < sc_count; j++) { - int32_t existing = -1; - SpirvReflectionData::SpecializationConstant sconst{}; - SpvReflectSpecializationConstant *spc = spec_constants[j]; - - sconst.constant_id = spc->constant_id; - sconst.int_value = 0; // Clear previous value JIC. - switch (spc->constant_type) { - case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; - sconst.bool_value = spc->default_value.int_bool_value != 0; - } break; - case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; - sconst.int_value = spc->default_value.int_bool_value; - } break; - case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; - sconst.float_value = spc->default_value.float_value; - } break; - } - sconst.stages_mask.set_flag(stage_flag); - - for (int k = 0; k < r_reflection_data.specialization_constants.size(); k++) { - if (r_reflection_data.specialization_constants[k].constant_id == sconst.constant_id) { - ERR_FAIL_COND_V_MSG(r_reflection_data.specialization_constants[k].type != sconst.type, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ."); - ERR_FAIL_COND_V_MSG(r_reflection_data.specialization_constants[k].int_value != sconst.int_value, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ."); - existing = k; - break; - } - } +#ifdef DEBUG_ENABLED + { // Validate that textures bound are not attached as framebuffer bindings. + uint32_t attachable_count = uniform_set->attachable_textures.size(); + const UniformSet::AttachableTexture *attachable_ptr = uniform_set->attachable_textures.ptr(); + uint32_t bound_count = draw_list_bound_textures.size(); + const RID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < attachable_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(attachable_ptr[i].texture == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform (set: " + itos(p_index) + ", binding: " + itos(attachable_ptr[i].bind) + "), this is not allowed."); + } + } + } +#endif +} - if (existing > 0) { - r_reflection_data.specialization_constants.write[existing].stages_mask.set_flag(stage_flag); - } else { - r_reflection_data.specialization_constants.push_back(sconst); - } - } +void RenderingDevice::draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array); + ERR_FAIL_NULL(vertex_array); + + if (dl->state.vertex_array == p_vertex_array) { + return; // Already set. + } + + dl->state.vertex_array = p_vertex_array; + +#ifdef DEBUG_ENABLED + dl->validation.vertex_format = vertex_array->description; + dl->validation.vertex_max_instances_allowed = vertex_array->max_instances_allowed; +#endif + dl->validation.vertex_array_size = vertex_array->vertex_count; + driver->command_render_bind_vertex_buffers(dl->command_buffer, vertex_array->buffers.size(), vertex_array->buffers.ptr(), vertex_array->offsets.ptr()); +} + +void RenderingDevice::draw_list_bind_index_array(DrawListID p_list, RID p_index_array) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const IndexArray *index_array = index_array_owner.get_or_null(p_index_array); + ERR_FAIL_NULL(index_array); + + if (dl->state.index_array == p_index_array) { + return; // Already set. + } + + dl->state.index_array = p_index_array; +#ifdef DEBUG_ENABLED + dl->validation.index_array_max_index = index_array->max_index; +#endif + dl->validation.index_array_size = index_array->indices; + dl->validation.index_array_offset = index_array->offset; + + driver->command_render_bind_index_buffer(dl->command_buffer, index_array->driver_id, index_array->format, index_array->offset); +} + +void RenderingDevice::draw_list_set_line_width(DrawListID p_list, float p_width) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + driver->command_render_set_line_width(dl->command_buffer, p_width); +} + +void RenderingDevice::draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_data_size != dl->validation.pipeline_push_constant_size, + "This render pipeline requires (" + itos(dl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); +#endif + driver->command_bind_push_constants(dl->command_buffer, dl->state.pipeline_shader_driver_id, 0, VectorView((const uint32_t *)p_data, p_data_size / sizeof(uint32_t))); +#ifdef DEBUG_ENABLED + dl->validation.pipeline_push_constant_supplied = true; +#endif +} + +void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances, uint32_t p_procedural_vertices) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.pipeline_active, + "No render pipeline was set before attempting to draw."); + if (dl->validation.pipeline_vertex_format != INVALID_ID) { + // Pipeline uses vertices, validate format. + ERR_FAIL_COND_MSG(dl->validation.vertex_format == INVALID_ID, + "No vertex array was bound, and render pipeline expects vertices."); + // Make sure format is right. + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != dl->validation.vertex_format, + "The vertex format used to create the pipeline does not match the vertex format bound."); + // Make sure number of instances is valid. + ERR_FAIL_COND_MSG(p_instances > dl->validation.vertex_max_instances_allowed, + "Number of instances requested (" + itos(p_instances) + " is larger than the maximum number supported by the bound vertex array (" + itos(dl->validation.vertex_max_instances_allowed) + ")."); + } + + if (dl->validation.pipeline_push_constant_size > 0) { + // Using push constants, check that they were supplied. + ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_supplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + // Bind descriptor sets. + + for (uint32_t i = 0; i < dl->state.set_count; i++) { + if (dl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } +#ifdef DEBUG_ENABLED + if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { + if (dl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(dl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); + } + } +#endif + driver->command_uniform_set_prepare_for_use(dl->command_buffer, dl->state.sets[i].uniform_set_driver_id, dl->state.pipeline_shader_driver_id, i); + } + for (uint32_t i = 0; i < dl->state.set_count; i++) { + if (dl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } + if (!dl->state.sets[i].bound) { + driver->command_bind_render_uniform_set(dl->command_buffer, dl->state.sets[i].uniform_set_driver_id, dl->state.pipeline_shader_driver_id, i); + dl->state.sets[i].bound = true; + } + } + + if (p_use_indices) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_procedural_vertices > 0, + "Procedural vertices can't be used together with indices."); + + ERR_FAIL_COND_MSG(!dl->validation.index_array_size, + "Draw command requested indices, but no index buffer was set."); + + ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, + "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); +#endif + uint32_t to_draw = dl->validation.index_array_size; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, + "Too few indices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); + + ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, + "Index amount (" + itos(to_draw) + ") must be a multiple of the amount of indices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); +#endif + driver->command_render_draw_indexed(dl->command_buffer, to_draw, p_instances, dl->validation.index_array_offset, 0, 0); + } else { + uint32_t to_draw; + + if (p_procedural_vertices > 0) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != INVALID_ID, + "Procedural vertices requested, but pipeline expects a vertex array."); +#endif + to_draw = p_procedural_vertices; + } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format == INVALID_ID, + "Draw command lacks indices, but pipeline format does not use vertices."); +#endif + to_draw = dl->validation.vertex_array_size; + } + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, + "Too few vertices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); + + ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, + "Vertex amount (" + itos(to_draw) + ") must be a multiple of the amount of vertices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); +#endif + + driver->command_render_draw(dl->command_buffer, to_draw, p_instances, 0, 0); + } +} + +void RenderingDevice::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) { + DrawList *dl = _get_draw_list_ptr(p_list); + + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + Rect2i rect = p_rect; + rect.position += dl->viewport.position; + + rect = dl->viewport.intersection(rect); + + if (rect.get_area() == 0) { + return; + } + + driver->command_render_set_scissor(dl->command_buffer, rect); +} + +void RenderingDevice::draw_list_disable_scissor(DrawListID p_list) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_NULL(dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + driver->command_render_set_scissor(dl->command_buffer, dl->viewport); +} + +uint32_t RenderingDevice::draw_list_get_current_pass() { + return draw_list_current_subpass; +} + +RenderingDevice::DrawListID RenderingDevice::draw_list_switch_to_next_pass() { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + driver->command_next_render_subpass(frames[frame].draw_command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY); + + _draw_list_allocate(viewport, 0, draw_list_current_subpass); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; +} +Error RenderingDevice::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + driver->command_next_render_subpass(frames[frame].draw_command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY); + + _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); + + for (uint32_t i = 0; i < p_splits; i++) { + r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; + } + + return OK; +} + +Error RenderingDevice::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { + // Lock while draw_list is active. + _THREAD_SAFE_LOCK_ + + if (p_splits == 0) { + draw_list = memnew(DrawList); + draw_list->command_buffer = frames[frame].draw_command_buffer; + draw_list->viewport = p_viewport; + draw_list_count = 0; + draw_list_split = false; + } else { + if (p_splits > (uint32_t)split_draw_list_allocators.size()) { + uint32_t from = split_draw_list_allocators.size(); + split_draw_list_allocators.resize(p_splits); + for (uint32_t i = from; i < p_splits; i++) { + RDD::CommandPoolID cmd_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY); + ERR_FAIL_COND_V(!cmd_pool, ERR_CANT_CREATE); + split_draw_list_allocators.write[i].command_pool = cmd_pool; + + for (int j = 0; j < frame_count; j++) { + RDD::CommandBufferID cmd_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY, cmd_pool); + ERR_FAIL_COND_V(!cmd_buffer, ERR_CANT_CREATE); + split_draw_list_allocators.write[i].command_buffers.push_back(cmd_buffer); } } + } + draw_list = memnew_arr(DrawList, p_splits); + draw_list_count = p_splits; + draw_list_split = true; + + for (uint32_t i = 0; i < p_splits; i++) { + // Take a command buffer and initialize it. + RDD::CommandBufferID cmd_buffer = split_draw_list_allocators[i].command_buffers[frame]; + + bool ok = driver->command_buffer_begin_secondary(cmd_buffer, draw_list_render_pass, p_subpass, draw_list_vkframebuffer); + if (!ok) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V(ERR_CANT_CREATE); + } + + draw_list[i].command_buffer = cmd_buffer; + draw_list[i].viewport = p_viewport; + } + } + + return OK; +} - if (stage == SHADER_STAGE_VERTEX) { - uint32_t iv_count = 0; - result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating input variables."); +void RenderingDevice::_draw_list_free(Rect2i *r_last_viewport) { + if (draw_list_split) { + // Send all command buffers. + RDD::CommandBufferID *command_buffers = (RDD::CommandBufferID *)alloca(sizeof(RDD::CommandBufferID) * draw_list_count); + for (uint32_t i = 0; i < draw_list_count; i++) { + driver->command_buffer_end(draw_list[i].command_buffer); + command_buffers[i] = draw_list[i].command_buffer; + if (r_last_viewport) { + if (i == 0 || draw_list[i].viewport_set) { + *r_last_viewport = draw_list[i].viewport; + } + } + } - if (iv_count) { - Vector<SpvReflectInterfaceVariable *> input_vars; - input_vars.resize(iv_count); + driver->command_buffer_execute_secondary(frames[frame].draw_command_buffer, VectorView(command_buffers, draw_list_count)); + memdelete_arr(draw_list); + draw_list = nullptr; - result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); + } else { + if (r_last_viewport) { + *r_last_viewport = draw_list->viewport; + } + // Just end the list. + memdelete(draw_list); + draw_list = nullptr; + } - for (uint32_t j = 0; j < iv_count; j++) { - if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. - r_reflection_data.vertex_input_mask |= (1ULL << uint32_t(input_vars[j]->location)); - } + // Draw_list is no longer active. + _THREAD_SAFE_UNLOCK_ +} + +void RenderingDevice::draw_list_end(BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + + _draw_list_free(); + + driver->command_end_render_pass(frames[frame].draw_command_buffer); + + for (int i = 0; i < draw_list_bound_textures.size(); i++) { + Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); + ERR_CONTINUE(!texture); // Wtf. + if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + texture->bound = false; + } + if (draw_list_unbind_depth_textures && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture->bound = false; + } + } + draw_list_bound_textures.clear(); + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + BitField<RDD::PipelineStageBits> dst_stages; + BitField<RDD::BarrierAccessBits> dst_access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); // RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); // RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); // RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); // RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); + } + + if (dst_stages.is_empty()) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } + + RDD::TextureBarrier *texture_barriers = nullptr; + + uint32_t texture_barrier_count = draw_list_storage_textures.size(); + + if (texture_barrier_count) { + texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * draw_list_storage_textures.size()); + } + + BitField<RDD::PipelineStageBits> src_stage(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); + BitField<RDD::BarrierAccessBits> src_access( + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + if (texture_barrier_count) { + src_stage.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + + for (uint32_t i = 0; i < texture_barrier_count; i++) { + Texture *texture = texture_owner.get_or_null(draw_list_storage_textures[i]); + + RDD::TextureBarrier &tb = texture_barriers[i]; + tb.texture = texture->driver_id; + tb.src_access = src_access; + tb.dst_access = dst_access; + tb.prev_layout = texture->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tb.subresources.aspect = texture->read_aspect_flags; + tb.subresources.base_mipmap = texture->base_mipmap; + tb.subresources.mipmap_count = texture->mipmaps; + tb.subresources.base_layer = texture->base_layer; + tb.subresources.layer_count = texture->layers; + + texture->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + // To ensure proper synchronization, we must make sure rendering is done before: + // * Some buffer is copied. + // * Another render pass happens (since we may be done). + + RDD::MemoryBarrier mb; + mb.src_access = src_access; + mb.dst_access = dst_access; + + if (texture_barrier_count > 0 || p_post_barrier != BARRIER_MASK_NO_BARRIER) { + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stage, dst_stages, mb, {}, VectorView(texture_barriers, texture_barrier_count)); + } + } + + draw_list_storage_textures.clear(); + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#endif +} + +/***********************/ +/**** COMPUTE LISTS ****/ +/***********************/ + +RenderingDevice::ComputeListID RenderingDevice::compute_list_begin(bool p_allow_draw_overlap) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + // Lock while compute_list is active. + _THREAD_SAFE_LOCK_ + + compute_list = memnew(ComputeList); + compute_list->command_buffer = frames[frame].draw_command_buffer; + compute_list->state.allow_draw_overlap = p_allow_draw_overlap; + + return ID_TYPE_COMPUTE_LIST; +} + +void RenderingDevice::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) { + // Must be called within a compute list, the class mutex is locked during that time + + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); + + ComputeList *cl = compute_list; + + const ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_compute_pipeline); + ERR_FAIL_NULL(pipeline); + + if (p_compute_pipeline == cl->state.pipeline) { + return; // Redundant state, return. + } + + cl->state.pipeline = p_compute_pipeline; + + driver->command_bind_compute_pipeline(cl->command_buffer, pipeline->driver_id); + + if (cl->state.pipeline_shader != pipeline->shader) { + // Shader changed, so descriptor sets may become incompatible. + + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. + cl->state.set_count = MAX(cl->state.set_count, pcount); + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. + + uint32_t first_invalid_set = UINT32_MAX; // All valid by default. + switch (driver->api_trait_get(RDD::API_TRAIT_SHADER_CHANGE_INVALIDATION)) { + case RDD::SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS: { + first_invalid_set = 0; + } break; + case RDD::SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE: { + for (uint32_t i = 0; i < pcount; i++) { + if (cl->state.sets[i].pipeline_expected_format != pformats[i]) { + first_invalid_set = i; + break; } } + } break; + case RDD::SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH: { + if (cl->state.pipeline_shader_layout_hash != pipeline->shader_layout_hash) { + first_invalid_set = 0; + } + } break; + } + + for (uint32_t i = 0; i < pcount; i++) { + cl->state.sets[i].bound = cl->state.sets[i].bound && i < first_invalid_set; + cl->state.sets[i].pipeline_expected_format = pformats[i]; + } + + for (uint32_t i = pcount; i < cl->state.set_count; i++) { + // Unbind the ones above (not used) if exist. + cl->state.sets[i].bound = false; + } + + cl->state.set_count = pcount; // Update set count. + + if (pipeline->push_constant_size) { +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_supplied = false; +#endif + } + + cl->state.pipeline_shader = pipeline->shader; + cl->state.pipeline_shader_driver_id = pipeline->shader_driver_id; + cl->state.pipeline_shader_layout_hash = pipeline->shader_layout_hash; + cl->state.local_group_size[0] = pipeline->local_group_size[0]; + cl->state.local_group_size[1] = pipeline->local_group_size[1]; + cl->state.local_group_size[2] = pipeline->local_group_size[2]; + } + +#ifdef DEBUG_ENABLED + // Update compute pass pipeline info. + cl->validation.pipeline_active = true; + cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; +#endif +} + +void RenderingDevice::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) { + // Must be called within a compute list, the class mutex is locked during that time + + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_index >= driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS) || p_index >= MAX_UNIFORM_SETS, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS)) + ")."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); + ERR_FAIL_NULL(uniform_set); + + if (p_index > cl->state.set_count) { + cl->state.set_count = p_index; + } + + cl->state.sets[p_index].uniform_set_driver_id = uniform_set->driver_id; // Update set pointer. + cl->state.sets[p_index].bound = false; // Needs rebind. + cl->state.sets[p_index].uniform_set_format = uniform_set->format; + cl->state.sets[p_index].uniform_set = p_uniform_set; + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + uint32_t textures_to_sampled_count = uniform_set->mutable_sampled_textures.size(); + uint32_t textures_to_storage_count = uniform_set->mutable_storage_textures.size(); + + Texture **textures_to_sampled = uniform_set->mutable_sampled_textures.ptrw(); + + RDD::TextureBarrier *texture_barriers = nullptr; + + if (textures_to_sampled_count + textures_to_storage_count) { + texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * (textures_to_sampled_count + textures_to_storage_count)); + } + uint32_t texture_barrier_count = 0; + + BitField<RDD::PipelineStageBits> src_stages; + + for (uint32_t i = 0; i < textures_to_sampled_count; i++) { + if (textures_to_sampled[i]->layout != RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; + tb.texture = textures_to_sampled[i]->driver_id; + tb.src_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + tb.prev_layout = textures_to_sampled[i]->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tb.subresources.aspect = textures_to_sampled[i]->read_aspect_flags; + tb.subresources.base_mipmap = textures_to_sampled[i]->base_mipmap; + tb.subresources.mipmap_count = textures_to_sampled[i]->mipmaps; + tb.subresources.base_layer = textures_to_sampled[i]->base_layer; + tb.subresources.layer_count = textures_to_sampled[i]->layers; + + textures_to_sampled[i]->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + cl->state.textures_to_sampled_layout.erase(textures_to_sampled[i]); } - if (stage == SHADER_STAGE_FRAGMENT) { - uint32_t ov_count = 0; - result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating output variables."); + if (textures_to_sampled[i]->used_in_frame != frames_drawn) { + textures_to_sampled[i]->used_in_frame = frames_drawn; + textures_to_sampled[i]->used_in_transfer = false; + textures_to_sampled[i]->used_in_raster = false; + } + textures_to_sampled[i]->used_in_compute = true; + } - if (ov_count) { - Vector<SpvReflectInterfaceVariable *> output_vars; - output_vars.resize(ov_count); + Texture **textures_to_storage = uniform_set->mutable_storage_textures.ptrw(); - result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining output variables."); + for (uint32_t i = 0; i < textures_to_storage_count; i++) { + if (textures_to_storage[i]->layout != RDD::TEXTURE_LAYOUT_GENERAL) { + BitField<RDD::BarrierAccessBits> src_access; - for (uint32_t j = 0; j < ov_count; j++) { - const SpvReflectInterfaceVariable *refvar = output_vars[j]; - if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { - r_reflection_data.fragment_output_mask |= 1 << refvar->location; - } + if (textures_to_storage[i]->used_in_frame == frames_drawn) { + if (textures_to_storage[i]->used_in_compute) { + src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (textures_to_storage[i]->used_in_raster) { + src_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); } + if (textures_to_storage[i]->used_in_transfer) { + src_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); + } + + textures_to_storage[i]->used_in_compute = false; + textures_to_storage[i]->used_in_raster = false; + textures_to_storage[i]->used_in_transfer = false; + + } else { + src_access.clear(); + textures_to_storage[i]->used_in_compute = false; + textures_to_storage[i]->used_in_raster = false; + textures_to_storage[i]->used_in_transfer = false; + textures_to_storage[i]->used_in_frame = frames_drawn; } + + RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; + tb.texture = textures_to_storage[i]->driver_id; + tb.src_access = src_access; + tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + tb.prev_layout = textures_to_storage[i]->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_GENERAL; + tb.subresources.aspect = textures_to_storage[i]->read_aspect_flags; + tb.subresources.base_mipmap = textures_to_storage[i]->base_mipmap; + tb.subresources.mipmap_count = textures_to_storage[i]->mipmaps; + tb.subresources.base_layer = textures_to_storage[i]->base_layer; + tb.subresources.layer_count = textures_to_storage[i]->layers; + + textures_to_storage[i]->layout = RDD::TEXTURE_LAYOUT_GENERAL; + + cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); // Needs to go back to sampled layout afterwards. } + } - uint32_t pc_count = 0; - result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating push constants."); + if (texture_barrier_count) { + if (src_stages.is_empty()) { + src_stages.set_flag(RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } - if (pc_count) { - ERR_FAIL_COND_V_MSG(pc_count > 1, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + driver->command_pipeline_barrier(cl->command_buffer, src_stages, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, VectorView(texture_barriers, texture_barrier_count)); + } + } - Vector<SpvReflectBlockVariable *> pconstants; - pconstants.resize(pc_count); - result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining push constants."); #if 0 - if (pconstants[0] == nullptr) { - Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); - f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); - } + { // Validate that textures bound are not attached as framebuffer bindings. + uint32_t attachable_count = uniform_set->attachable_textures.size(); + const RID *attachable_ptr = uniform_set->attachable_textures.ptr(); + uint32_t bound_count = draw_list_bound_textures.size(); + const RID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < attachable_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(attachable_ptr[i] == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); + } + } + } #endif +} + +void RenderingDevice::compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_data_size != cl->validation.pipeline_push_constant_size, + "This compute pipeline requires (" + itos(cl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); +#endif + driver->command_bind_push_constants(cl->command_buffer, cl->state.pipeline_shader_driver_id, 0, VectorView((const uint32_t *)p_data, p_data_size / sizeof(uint32_t))); +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_supplied = true; +#endif +} + +void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + // Must be called within a compute list, the class mutex is locked during that time + + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); - ERR_FAIL_COND_V_MSG(r_reflection_data.push_constant_size && r_reflection_data.push_constant_size != pconstants[0]->size, FAILED, - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + ComputeList *cl = compute_list; - r_reflection_data.push_constant_size = pconstants[0]->size; - r_reflection_data.push_constant_stages_mask.set_flag(stage_flag); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_x_groups == 0, "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is zero."); + ERR_FAIL_COND_MSG(p_z_groups == 0, "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is zero."); + ERR_FAIL_COND_MSG(p_y_groups == 0, "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is zero."); + ERR_FAIL_COND_MSG(p_x_groups > driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X), + "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X)) + ")"); + ERR_FAIL_COND_MSG(p_y_groups > driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y), + "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is larger than device limit (" + itos(driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y)) + ")"); + ERR_FAIL_COND_MSG(p_z_groups > driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z), + "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is larger than device limit (" + itos(driver->limit_get(LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z)) + ")"); - //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + + ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); + + if (cl->validation.pipeline_push_constant_size > 0) { + // Using push constants, check that they were supplied. + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + // Bind descriptor sets. + + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } +#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { + if (cl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); } + } +#endif + driver->command_uniform_set_prepare_for_use(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + } + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } + if (!cl->state.sets[i].bound) { + driver->command_bind_compute_uniform_set(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + cl->state.sets[i].bound = true; + } + } - // Destroy the reflection data when no longer required. - spvReflectDestroyShaderModule(&module); + driver->command_compute_dispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups); +} + +void RenderingDevice::compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_x_threads == 0, "Dispatch amount of X compute threads (" + itos(p_x_threads) + ") is zero."); + ERR_FAIL_COND_MSG(p_y_threads == 0, "Dispatch amount of Y compute threads (" + itos(p_y_threads) + ") is zero."); + ERR_FAIL_COND_MSG(p_z_threads == 0, "Dispatch amount of Z compute threads (" + itos(p_z_threads) + ") is zero."); +#endif + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + + ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); + + if (cl->validation.pipeline_push_constant_size > 0) { + // Using push constants, check that they were supplied. + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + compute_list_dispatch(p_list, Math::division_round_up(p_x_threads, cl->state.local_group_size[0]), Math::division_round_up(p_y_threads, cl->state.local_group_size[1]), Math::division_round_up(p_z_threads, cl->state.local_group_size[2])); +} + +void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_NULL(compute_list); + + ComputeList *cl = compute_list; + Buffer *buffer = storage_buffer_owner.get_or_null(p_buffer); + ERR_FAIL_COND(!buffer); + + ERR_FAIL_COND_MSG(!buffer->usage.has_flag(RDD::BUFFER_USAGE_INDIRECT_BIT), "Buffer provided was not created to do indirect dispatch."); + + ERR_FAIL_COND_MSG(p_offset + 12 > buffer->size, "Offset provided (+12) is past the end of buffer."); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + + ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); + + if (cl->validation.pipeline_push_constant_size > 0) { + // Using push constants, check that they were supplied. + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + // Bind descriptor sets. + + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } +#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { + if (cl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } + } +#endif + driver->command_uniform_set_prepare_for_use(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + } + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + continue; // Nothing expected by this pipeline. + } + if (!cl->state.sets[i].bound) { + driver->command_bind_compute_uniform_set(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + cl->state.sets[i].bound = true; } + } + + driver->command_compute_dispatch_indirect(cl->command_buffer, buffer->driver_id, p_offset); +} + +void RenderingDevice::compute_list_add_barrier(ComputeListID p_list) { + // Must be called within a compute list, the class mutex is locked during that time + + BitField<RDD::PipelineStageBits> stages(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + BitField<RDD::BarrierAccessBits> access(RDD::BARRIER_ACCESS_SHADER_READ_BIT); + _compute_list_add_barrier(BARRIER_MASK_COMPUTE, stages, access); +} + +void RenderingDevice::_compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, BitField<RDD::PipelineStageBits> p_stages, BitField<RDD::BarrierAccessBits> p_access) { + ERR_FAIL_NULL(compute_list); + + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::TextureBarrier *texture_barriers = nullptr; + + uint32_t texture_barrier_count = compute_list->state.textures_to_sampled_layout.size(); + + if (texture_barrier_count) { + texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * texture_barrier_count); + } + + texture_barrier_count = 0; // We'll count how many we end up issuing. + + for (Texture *E : compute_list->state.textures_to_sampled_layout) { + if (E->layout != RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; + tb.texture = E->driver_id; + tb.src_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; + tb.dst_access = p_access; + tb.prev_layout = E->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tb.subresources.aspect = E->read_aspect_flags; + tb.subresources.base_mipmap = E->base_mipmap; + tb.subresources.mipmap_count = E->mipmaps; + tb.subresources.base_layer = E->base_layer; + tb.subresources.layer_count = E->layers; + + E->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + if (E->used_in_frame != frames_drawn) { + E->used_in_transfer = false; + E->used_in_raster = false; + E->used_in_compute = false; + E->used_in_frame = frames_drawn; + } + } + + if (p_stages) { + RDD::MemoryBarrier mb; + mb.src_access = RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; + mb.dst_access = p_access; + driver->command_pipeline_barrier(compute_list->command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, p_stages, mb, {}, VectorView(texture_barriers, texture_barrier_count)); - r_reflection_data.stages_mask.set_flag(stage_flag); + } else if (texture_barrier_count) { + driver->command_pipeline_barrier(compute_list->command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, {}, VectorView(texture_barriers, texture_barrier_count)); + } } - return OK; +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#endif +} + +void RenderingDevice::compute_list_end(BitField<BarrierMask> p_post_barrier) { + ERR_FAIL_NULL(compute_list); + + BitField<RDD::PipelineStageBits> stages; + BitField<RDD::BarrierAccessBits> access; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); + access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); + } + _compute_list_add_barrier(p_post_barrier, stages, access); + + memdelete(compute_list); + compute_list = nullptr; + + // Compute_list is no longer active. + _THREAD_SAFE_UNLOCK_ +} + +void RenderingDevice::barrier(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { + if (!driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + return; + } + + BitField<RDD::PipelineStageBits> src_stages; + BitField<RDD::BarrierAccessBits> src_access; + + if (p_from == 0) { + src_stages.set_flag(RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } else { + if (p_from.has_flag(BARRIER_MASK_COMPUTE)) { + src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_from.has_flag(BARRIER_MASK_FRAGMENT)) { + src_stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT).set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT).set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + } + if (p_from.has_flag(BARRIER_MASK_TRANSFER)) { + src_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + src_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + } + } + + BitField<RDD::PipelineStageBits> dst_stages; + BitField<RDD::BarrierAccessBits> dst_access; + + if (p_to == 0) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + } else { + if (p_to.has_flag(BARRIER_MASK_COMPUTE)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + } + if (p_to.has_flag(BARRIER_MASK_VERTEX)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); + } + if (p_to.has_flag(BARRIER_MASK_FRAGMENT)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); + } + if (p_to.has_flag(BARRIER_MASK_TRANSFER)) { + dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); + dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); + } + } + + RDD::MemoryBarrier mb; + mb.src_access = src_access; + mb.dst_access = dst_access; + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stages, dst_stages, mb, {}, {}); +} + +void RenderingDevice::full_barrier() { +#ifndef DEBUG_ENABLED + ERR_PRINT("Full barrier is debug-only, should not be used in production"); +#endif + _full_barrier(true); +} + +/**************************/ +/**** FRAME MANAGEMENT ****/ +/**************************/ + +void RenderingDevice::free(RID p_id) { + _THREAD_SAFE_METHOD_ + + _free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems. + _free_internal(p_id); +} + +void RenderingDevice::_free_internal(RID p_id) { +#ifdef DEV_ENABLED + String resource_name; + if (resource_names.has(p_id)) { + resource_name = resource_names[p_id]; + resource_names.erase(p_id); + } +#endif + + // Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it). + if (texture_owner.owns(p_id)) { + Texture *texture = texture_owner.get_or_null(p_id); + frames[frame].textures_to_dispose_of.push_back(*texture); + texture_owner.free(p_id); + } else if (framebuffer_owner.owns(p_id)) { + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); + frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); + + if (framebuffer->invalidated_callback != nullptr) { + framebuffer->invalidated_callback(framebuffer->invalidated_callback_userdata); + } + + framebuffer_owner.free(p_id); + } else if (sampler_owner.owns(p_id)) { + RDD::SamplerID sampler_driver_id = *sampler_owner.get_or_null(p_id); + frames[frame].samplers_to_dispose_of.push_back(sampler_driver_id); + sampler_owner.free(p_id); + } else if (vertex_buffer_owner.owns(p_id)) { + Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); + frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer); + vertex_buffer_owner.free(p_id); + } else if (vertex_array_owner.owns(p_id)) { + vertex_array_owner.free(p_id); + } else if (index_buffer_owner.owns(p_id)) { + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); + frames[frame].buffers_to_dispose_of.push_back(*index_buffer); + index_buffer_owner.free(p_id); + } else if (index_array_owner.owns(p_id)) { + index_array_owner.free(p_id); + } else if (shader_owner.owns(p_id)) { + Shader *shader = shader_owner.get_or_null(p_id); + if (shader->driver_id) { // Not placeholder? + frames[frame].shaders_to_dispose_of.push_back(*shader); + } + shader_owner.free(p_id); + } else if (uniform_buffer_owner.owns(p_id)) { + Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); + frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer); + uniform_buffer_owner.free(p_id); + } else if (texture_buffer_owner.owns(p_id)) { + Buffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); + frames[frame].buffers_to_dispose_of.push_back(*texture_buffer); + texture_buffer_owner.free(p_id); + } else if (storage_buffer_owner.owns(p_id)) { + Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); + frames[frame].buffers_to_dispose_of.push_back(*storage_buffer); + storage_buffer_owner.free(p_id); + } else if (uniform_set_owner.owns(p_id)) { + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); + frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); + uniform_set_owner.free(p_id); + + if (uniform_set->invalidated_callback != nullptr) { + uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata); + } + } else if (render_pipeline_owner.owns(p_id)) { + RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); + frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); + render_pipeline_owner.free(p_id); + } else if (compute_pipeline_owner.owns(p_id)) { + ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); + frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); + compute_pipeline_owner.free(p_id); + } else { +#ifdef DEV_ENABLED + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id()) + " " + resource_name); +#else + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); +#endif + } +} + +// The full list of resources that can be named is in the VkObjectType enum. +// We just expose the resources that are owned and can be accessed easily. +void RenderingDevice::set_resource_name(RID p_id, const String &p_name) { + if (texture_owner.owns(p_id)) { + Texture *texture = texture_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_TEXTURE, texture->driver_id, p_name); + } else if (framebuffer_owner.owns(p_id)) { + //Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); + // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex. + } else if (sampler_owner.owns(p_id)) { + RDD::SamplerID sampler_driver_id = *sampler_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_SAMPLER, sampler_driver_id, p_name); + } else if (vertex_buffer_owner.owns(p_id)) { + Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_BUFFER, vertex_buffer->driver_id, p_name); + } else if (index_buffer_owner.owns(p_id)) { + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_BUFFER, index_buffer->driver_id, p_name); + } else if (shader_owner.owns(p_id)) { + Shader *shader = shader_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_SHADER, shader->driver_id, p_name); + } else if (uniform_buffer_owner.owns(p_id)) { + Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_BUFFER, uniform_buffer->driver_id, p_name); + } else if (texture_buffer_owner.owns(p_id)) { + Buffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_BUFFER, texture_buffer->driver_id, p_name); + } else if (storage_buffer_owner.owns(p_id)) { + Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_BUFFER, storage_buffer->driver_id, p_name); + } else if (uniform_set_owner.owns(p_id)) { + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_UNIFORM_SET, uniform_set->driver_id, p_name); + } else if (render_pipeline_owner.owns(p_id)) { + RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_PIPELINE, pipeline->driver_id, p_name); + } else if (compute_pipeline_owner.owns(p_id)) { + ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); + driver->set_object_name(RDD::OBJECT_TYPE_PIPELINE, pipeline->driver_id, p_name); + } else { + ERR_PRINT("Attempted to name invalid ID: " + itos(p_id.get_id())); + return; + } +#ifdef DEV_ENABLED + resource_names[p_id] = p_name; +#endif +} + +void RenderingDevice::draw_command_begin_label(String p_label_name, const Color &p_color) { + _THREAD_SAFE_METHOD_ + context->command_begin_label(frames[frame].draw_command_buffer, p_label_name, p_color); +} + +void RenderingDevice::draw_command_insert_label(String p_label_name, const Color &p_color) { + _THREAD_SAFE_METHOD_ + context->command_insert_label(frames[frame].draw_command_buffer, p_label_name, p_color); +} + +void RenderingDevice::draw_command_end_label() { + _THREAD_SAFE_METHOD_ + context->command_end_label(frames[frame].draw_command_buffer); +} + +String RenderingDevice::get_device_vendor_name() const { + return context->get_device_vendor_name(); +} + +String RenderingDevice::get_device_name() const { + return context->get_device_name(); +} + +RenderingDevice::DeviceType RenderingDevice::get_device_type() const { + return context->get_device_type(); +} + +String RenderingDevice::get_device_api_version() const { + return context->get_device_api_version(); +} + +String RenderingDevice::get_device_pipeline_cache_uuid() const { + return context->get_device_pipeline_cache_uuid(); +} + +void RenderingDevice::_finalize_command_bufers() { + if (draw_list) { + ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); + } + + if (compute_list) { + ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); + } + + { + driver->end_segment(); + driver->command_buffer_end(frames[frame].setup_command_buffer); + driver->command_buffer_end(frames[frame].draw_command_buffer); + } +} + +void RenderingDevice::_begin_frame() { + // Erase pending resources. + _free_pending_resources(frame); + + // Create setup command buffer and set as the setup buffer. + + { + bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); + ERR_FAIL_COND(!ok); + ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); + ERR_FAIL_COND(!ok); + + if (local_device.is_null()) { + context->append_command_buffer(frames[frame].draw_command_buffer); + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. + } + + driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); + } + + // Advance current frame. + frames_drawn++; + // Advance staging buffer if used. + if (staging_buffer_used) { + staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); + staging_buffer_used = false; + } + + if (frames[frame].timestamp_count) { + driver->timestamp_query_pool_get_results(frames[frame].timestamp_pool, frames[frame].timestamp_count, frames[frame].timestamp_result_values.ptr()); + driver->command_timestamp_query_pool_reset(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count); + SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); + SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); + } + + frames[frame].timestamp_result_count = frames[frame].timestamp_count; + frames[frame].timestamp_count = 0; + frames[frame].index = Engine::get_singleton()->get_frames_drawn(); +} + +void RenderingDevice::swap_buffers() { + ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); + _THREAD_SAFE_METHOD_ + + context->postpare_buffers(frames[frame].draw_command_buffer); + _finalize_command_bufers(); + + screen_prepared = false; + // Swap buffers. + context->swap_buffers(); + + frame = (frame + 1) % frame_count; + + _begin_frame(); +} + +void RenderingDevice::submit() { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done."); + + _finalize_command_bufers(); + + RDD::CommandBufferID command_buffers[2] = { frames[frame].setup_command_buffer, frames[frame].draw_command_buffer }; + context->local_device_push_command_buffers(local_device, command_buffers, 2); + local_device_processing = true; +} + +void RenderingDevice::sync() { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(!local_device_processing, "sync can only be called after a submit"); + + context->local_device_sync(local_device); + _begin_frame(); + local_device_processing = false; +} + +void RenderingDevice::_free_pending_resources(int p_frame) { + // Free in dependency usage order, so nothing weird happens. + // Pipelines. + while (frames[p_frame].render_pipelines_to_dispose_of.front()) { + RenderPipeline *pipeline = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); + + driver->pipeline_free(pipeline->driver_id); + + frames[p_frame].render_pipelines_to_dispose_of.pop_front(); + } + + while (frames[p_frame].compute_pipelines_to_dispose_of.front()) { + ComputePipeline *pipeline = &frames[p_frame].compute_pipelines_to_dispose_of.front()->get(); + + driver->pipeline_free(pipeline->driver_id); + + frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); + } + + // Uniform sets. + while (frames[p_frame].uniform_sets_to_dispose_of.front()) { + UniformSet *uniform_set = &frames[p_frame].uniform_sets_to_dispose_of.front()->get(); + + driver->uniform_set_free(uniform_set->driver_id); + + frames[p_frame].uniform_sets_to_dispose_of.pop_front(); + } + + // Shaders. + while (frames[p_frame].shaders_to_dispose_of.front()) { + Shader *shader = &frames[p_frame].shaders_to_dispose_of.front()->get(); + + driver->shader_free(shader->driver_id); + + frames[p_frame].shaders_to_dispose_of.pop_front(); + } + + // Samplers. + while (frames[p_frame].samplers_to_dispose_of.front()) { + RDD::SamplerID sampler = frames[p_frame].samplers_to_dispose_of.front()->get(); + + driver->sampler_free(sampler); + + frames[p_frame].samplers_to_dispose_of.pop_front(); + } + + // Framebuffers. + while (frames[p_frame].framebuffers_to_dispose_of.front()) { + Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); + + for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) { + // First framebuffer, then render pass because it depends on it. + driver->framebuffer_free(E.value.framebuffer); + driver->render_pass_free(E.value.render_pass); + } + + frames[p_frame].framebuffers_to_dispose_of.pop_front(); + } + + // Textures. + while (frames[p_frame].textures_to_dispose_of.front()) { + Texture *texture = &frames[p_frame].textures_to_dispose_of.front()->get(); + if (texture->bound) { + WARN_PRINT("Deleted a texture while it was bound."); + } + + texture_memory -= driver->texture_get_allocation_size(texture->driver_id); + driver->texture_free(texture->driver_id); + + frames[p_frame].textures_to_dispose_of.pop_front(); + } + + // Buffers. + while (frames[p_frame].buffers_to_dispose_of.front()) { + Buffer &buffer = frames[p_frame].buffers_to_dispose_of.front()->get(); + driver->buffer_free(buffer.driver_id); + buffer_memory -= buffer.size; + + frames[p_frame].buffers_to_dispose_of.pop_front(); + } +} + +void RenderingDevice::prepare_screen_for_drawing() { + _THREAD_SAFE_METHOD_ + context->prepare_buffers(frames[frame].draw_command_buffer); + screen_prepared = true; +} + +uint32_t RenderingDevice::get_frame_delay() const { + return frame_count; +} + +uint64_t RenderingDevice::get_memory_usage(MemoryType p_type) const { + switch (p_type) { + case MEMORY_BUFFERS: { + return buffer_memory; + } + case MEMORY_TEXTURES: { + return texture_memory; + } + case MEMORY_TOTAL: { + return driver->get_total_memory_used(); + } + default: { + DEV_ASSERT(false); + return 0; + } + } +} + +void RenderingDevice::_flush(bool p_current_frame) { + if (local_device.is_valid() && !p_current_frame) { + return; // Flushing previous frames has no effect with local device. + } + // Not doing this crashes RADV (undefined behavior). + if (p_current_frame) { + driver->end_segment(); + driver->command_buffer_end(frames[frame].setup_command_buffer); + driver->command_buffer_end(frames[frame].draw_command_buffer); + } + + if (local_device.is_valid()) { + RDD::CommandBufferID command_buffers[2] = { frames[frame].setup_command_buffer, frames[frame].draw_command_buffer }; + context->local_device_push_command_buffers(local_device, command_buffers, 2); + context->local_device_sync(local_device); + + bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); + ERR_FAIL_COND(!ok); + ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); + ERR_FAIL_COND(!ok); + + driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); + } else { + context->flush(p_current_frame, p_current_frame); + // Re-create the setup command. + if (p_current_frame) { + bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); + ERR_FAIL_COND(!ok); + + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. + ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); + ERR_FAIL_COND(!ok); + context->append_command_buffer(frames[frame].draw_command_buffer); + + driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); + } + } +} + +void RenderingDevice::initialize(ApiContextRD *p_context, bool p_local_device) { + context = p_context; + + device_capabilities = p_context->get_device_capabilities(); + + if (p_local_device) { + frame_count = 1; + local_device = context->local_device_create(); + } else { + frame_count = context->get_swapchain_image_count() + 1; // Always need one extra to ensure it's unused at any time, without having to use a fence for this. + } + driver = context->get_driver(local_device); + max_timestamp_query_elements = 256; + + frames.resize(frame_count); + frame = 0; + // Create setup and frame buffers. + for (int i = 0; i < frame_count; i++) { + frames[i].index = 0; + + // Create command pool, one per frame is recommended. + frames[i].command_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY); + ERR_FAIL_COND(!frames[i].command_pool); + + // Create command buffers. + frames[i].setup_command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY, frames[i].command_pool); + ERR_CONTINUE(!frames[i].setup_command_buffer); + frames[i].draw_command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY, frames[i].command_pool); + ERR_CONTINUE(!frames[i].draw_command_buffer); + + { + // Create query pool. + frames[i].timestamp_pool = driver->timestamp_query_pool_create(max_timestamp_query_elements); + frames[i].timestamp_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); + frames[i].timestamp_count = 0; + frames[i].timestamp_result_names.resize(max_timestamp_query_elements); + 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; + } + } + + { + // Begin the first command buffer for the first frame, so + // setting up things can be done in the meantime until swap_buffers(), which is called before advance. + bool ok = driver->command_buffer_begin(frames[0].setup_command_buffer); + ERR_FAIL_COND(!ok); + + ok = driver->command_buffer_begin(frames[0].draw_command_buffer); + ERR_FAIL_COND(!ok); + if (local_device.is_null()) { + context->set_setup_buffer(frames[0].setup_command_buffer); // Append now so it's added before everything else. + context->append_command_buffer(frames[0].draw_command_buffer); + } + } + + for (int i = 0; i < frame_count; i++) { + // Reset all queries in a query pool before doing any operations with them. + driver->command_timestamp_query_pool_reset(frames[0].setup_command_buffer, frames[i].timestamp_pool, max_timestamp_query_elements); + } + + staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); + staging_buffer_block_size = MAX(4u, staging_buffer_block_size); + staging_buffer_block_size *= 1024; // Kb -> bytes. + staging_buffer_max_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/max_size_mb"); + staging_buffer_max_size = MAX(1u, staging_buffer_max_size); + staging_buffer_max_size *= 1024 * 1024; + + if (staging_buffer_max_size < staging_buffer_block_size * 4) { + // Validate enough blocks. + staging_buffer_max_size = staging_buffer_block_size * 4; + } + texture_upload_region_size_px = GLOBAL_GET("rendering/rendering_device/staging_buffer/texture_upload_region_size_px"); + texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); + + frames_drawn = frame_count; // Start from frame count, so everything else is immediately old. + + // Ensure current staging block is valid and at least one per frame exists. + staging_buffer_current = 0; + staging_buffer_used = false; + + for (int i = 0; i < frame_count; i++) { + // Staging was never used, create a block. + Error err = _insert_staging_block(); + ERR_CONTINUE(err != OK); + } + + draw_list = nullptr; + draw_list_count = 0; + draw_list_split = false; + + compute_list = nullptr; + + pipelines_cache_file_path = "user://vulkan/pipelines"; + pipelines_cache_file_path += "." + context->get_device_name().validate_filename().replace(" ", "_").to_lower(); + if (Engine::get_singleton()->is_editor_hint()) { + pipelines_cache_file_path += ".editor"; + } + pipelines_cache_file_path += ".cache"; + + Vector<uint8_t> cache_data = _load_pipeline_cache(); + pipelines_cache_enabled = driver->pipeline_cache_create(cache_data); + if (pipelines_cache_enabled) { + pipelines_cache_size = driver->pipeline_cache_query_size(); + print_verbose(vformat("Startup PSO cache (%.1f MiB)", pipelines_cache_size / (1024.0f * 1024.0f))); + } +} + +Vector<uint8_t> RenderingDevice::_load_pipeline_cache() { + DirAccess::make_dir_recursive_absolute(pipelines_cache_file_path.get_base_dir()); + + if (FileAccess::exists(pipelines_cache_file_path)) { + Error file_error; + Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(pipelines_cache_file_path, &file_error); + return file_data; + } else { + return Vector<uint8_t>(); + } +} + +void RenderingDevice::_update_pipeline_cache(bool p_closing) { + { + bool still_saving = pipelines_cache_save_task != WorkerThreadPool::INVALID_TASK_ID && !WorkerThreadPool::get_singleton()->is_task_completed(pipelines_cache_save_task); + if (still_saving) { + if (p_closing) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(pipelines_cache_save_task); + pipelines_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; + } else { + // We can't save until the currently running save is done. We'll retry next time; worst case, we'll save when exiting. + return; + } + } + } + + { + size_t new_pipelines_cache_size = driver->pipeline_cache_query_size(); + ERR_FAIL_COND(!new_pipelines_cache_size); + size_t difference = new_pipelines_cache_size - pipelines_cache_size; + + bool must_save = false; + + if (p_closing) { + must_save = difference > 0; + } else { + float save_interval = GLOBAL_GET("rendering/rendering_device/pipeline_cache/save_chunk_size_mb"); + must_save = difference > 0 && difference / (1024.0f * 1024.0f) >= save_interval; + } + + if (must_save) { + pipelines_cache_size = new_pipelines_cache_size; + } else { + return; + } + } + + if (p_closing) { + _save_pipeline_cache(this); + } else { + pipelines_cache_save_task = WorkerThreadPool::get_singleton()->add_native_task(&_save_pipeline_cache, this, false, "PipelineCacheSave"); + } +} + +void RenderingDevice::_save_pipeline_cache(void *p_data) { + RenderingDevice *self = static_cast<RenderingDevice *>(p_data); + + self->_thread_safe_.lock(); + Vector<uint8_t> cache_blob = self->driver->pipeline_cache_serialize(); + self->_thread_safe_.unlock(); + + if (cache_blob.size() == 0) { + return; + } + print_verbose(vformat("Updated PSO cache (%.1f MiB)", cache_blob.size() / (1024.0f * 1024.0f))); + + Ref<FileAccess> f = FileAccess::open(self->pipelines_cache_file_path, FileAccess::WRITE, nullptr); + if (f.is_valid()) { + f->store_buffer(cache_blob); + } +} + +template <class T> +void RenderingDevice::_free_rids(T &p_owner, const char *p_type) { + List<RID> owned; + p_owner.get_owned_list(&owned); + if (owned.size()) { + if (owned.size() == 1) { + WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type)); + } else { + WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); + } + for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif + free(E); + } + } +} + +void RenderingDevice::capture_timestamp(const String &p_name) { + ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); + ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); + + // This should be optional for profiling, else it will slow things down. + if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + RDD::MemoryBarrier mb; + mb.src_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | + RDD::BARRIER_ACCESS_INDEX_READ_BIT | + RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | + RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | + RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | + RDD::BARRIER_ACCESS_HOST_READ_BIT | + RDD::BARRIER_ACCESS_HOST_WRITE_BIT); + mb.dst_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | + RDD::BARRIER_ACCESS_INDEX_READ_BIT | + RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | + RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_READ_BIT | + RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | + RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | + RDD::BARRIER_ACCESS_HOST_READ_BIT | + RDD::BARRIER_ACCESS_HOST_WRITE_BIT); + + driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, mb, {}, {}); + } + + driver->command_timestamp_write(frames[frame].draw_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count); + frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name; + frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec(); + frames[frame].timestamp_count++; +} + +uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) { + _THREAD_SAFE_METHOD_ + + uint64_t driver_id = 0; + switch (p_resource) { + case DRIVER_RESOURCE_LOGICAL_DEVICE: + case DRIVER_RESOURCE_PHYSICAL_DEVICE: + case DRIVER_RESOURCE_TOPMOST_OBJECT: + case DRIVER_RESOURCE_COMMAND_QUEUE: + case DRIVER_RESOURCE_QUEUE_FAMILY: + break; + case DRIVER_RESOURCE_TEXTURE: + case DRIVER_RESOURCE_TEXTURE_VIEW: + case DRIVER_RESOURCE_TEXTURE_DATA_FORMAT: { + Texture *tex = texture_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + driver_id = tex->driver_id; + } break; + case DRIVER_RESOURCE_SAMPLER: { + RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(sampler_driver_id, 0); + + driver_id = *sampler_driver_id; + } break; + case DRIVER_RESOURCE_UNIFORM_SET: { + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(uniform_set, 0); + + driver_id = uniform_set->driver_id; + } break; + case DRIVER_RESOURCE_BUFFER: { + Buffer *buffer = nullptr; + if (vertex_buffer_owner.owns(p_rid)) { + buffer = vertex_buffer_owner.get_or_null(p_rid); + } else if (index_buffer_owner.owns(p_rid)) { + buffer = index_buffer_owner.get_or_null(p_rid); + } else if (uniform_buffer_owner.owns(p_rid)) { + buffer = uniform_buffer_owner.get_or_null(p_rid); + } else if (texture_buffer_owner.owns(p_rid)) { + buffer = texture_buffer_owner.get_or_null(p_rid); + } else if (storage_buffer_owner.owns(p_rid)) { + buffer = storage_buffer_owner.get_or_null(p_rid); + } + ERR_FAIL_NULL_V(buffer, 0); + + driver_id = buffer->driver_id; + } break; + case DRIVER_RESOURCE_COMPUTE_PIPELINE: { + ComputePipeline *compute_pipeline = compute_pipeline_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(compute_pipeline, 0); + + driver_id = compute_pipeline->driver_id; + } break; + case DRIVER_RESOURCE_RENDER_PIPELINE: { + RenderPipeline *render_pipeline = render_pipeline_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(render_pipeline, 0); + + driver_id = render_pipeline->driver_id; + } break; + default: { + ERR_FAIL_V(0); + } break; + } + + return driver->get_resource_native_handle(p_resource, driver_id); +} + +uint32_t RenderingDevice::get_captured_timestamps_count() const { + return frames[frame].timestamp_result_count; +} + +uint64_t RenderingDevice::get_captured_timestamps_frame() const { + return frames[frame].index; +} + +uint64_t RenderingDevice::get_captured_timestamp_gpu_time(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return driver->timestamp_query_result_to_time(frames[frame].timestamp_result_values[p_index]); +} + +uint64_t RenderingDevice::get_captured_timestamp_cpu_time(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return frames[frame].timestamp_cpu_result_values[p_index]; +} + +String RenderingDevice::get_captured_timestamp_name(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String()); + return frames[frame].timestamp_result_names[p_index]; +} + +uint64_t RenderingDevice::limit_get(Limit p_limit) const { + return driver->limit_get(p_limit); +} + +void RenderingDevice::finalize() { + // Free all resources. + + _flush(false); + + _free_rids(render_pipeline_owner, "Pipeline"); + _free_rids(compute_pipeline_owner, "Compute"); + _free_rids(uniform_set_owner, "UniformSet"); + _free_rids(texture_buffer_owner, "TextureBuffer"); + _free_rids(storage_buffer_owner, "StorageBuffer"); + _free_rids(uniform_buffer_owner, "UniformBuffer"); + _free_rids(shader_owner, "Shader"); + _free_rids(index_array_owner, "IndexArray"); + _free_rids(index_buffer_owner, "IndexBuffer"); + _free_rids(vertex_array_owner, "VertexArray"); + _free_rids(vertex_buffer_owner, "VertexBuffer"); + _free_rids(framebuffer_owner, "Framebuffer"); + _free_rids(sampler_owner, "Sampler"); + { + // For textures it's a bit more difficult because they may be shared. + List<RID> owned; + texture_owner.get_owned_list(&owned); + if (owned.size()) { + if (owned.size() == 1) { + WARN_PRINT("1 RID of type \"Texture\" was leaked."); + } else { + WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); + } + // Free shared first. + for (List<RID>::Element *E = owned.front(); E;) { + List<RID>::Element *N = E->next(); + if (texture_is_shared(E->get())) { +#ifdef DEV_ENABLED + if (resource_names.has(E->get())) { + print_line(String(" - ") + resource_names[E->get()]); + } +#endif + free(E->get()); + owned.erase(E); + } + E = N; + } + // Free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. + for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif + free(E); + } + } + } + + // Free everything pending. + for (uint32_t i = 0; i < frames.size(); i++) { + int f = (frame + i) % frames.size(); + _free_pending_resources(f); + driver->command_pool_free(frames[i].command_pool); + driver->timestamp_query_pool_free(frames[i].timestamp_pool); + } + + if (pipelines_cache_enabled) { + _update_pipeline_cache(true); + driver->pipeline_cache_free(); + } + + for (int i = 0; i < split_draw_list_allocators.size(); i++) { + driver->command_pool_free(split_draw_list_allocators[i].command_pool); + } + + frames.clear(); + + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + driver->buffer_free(staging_buffer_blocks[i].driver_id); + } + + while (vertex_formats.size()) { + HashMap<VertexFormatID, VertexDescriptionCache>::Iterator temp = vertex_formats.begin(); + driver->vertex_format_free(temp->value.driver_id); + vertex_formats.remove(temp); + } + + for (KeyValue<FramebufferFormatID, FramebufferFormat> &E : framebuffer_formats) { + driver->render_pass_free(E.value.render_pass); + } + framebuffer_formats.clear(); + + // All these should be clear at this point. + ERR_FAIL_COND(dependency_map.size()); + ERR_FAIL_COND(reverse_dependency_map.size()); +} + +RenderingDevice *RenderingDevice::create_local_device() { + RenderingDevice *rd = memnew(RenderingDevice); + rd->initialize(context, true); + return rd; +} + +bool RenderingDevice::has_feature(const Features p_feature) const { + return driver->has_feature(p_feature); } void RenderingDevice::_bind_methods() { @@ -731,7 +6115,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::texture_resolve_multisample, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); ClassDB::bind_method(D_METHOD("texture_get_format", "texture"), &RenderingDevice::_texture_get_format); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("texture_get_native_handle", "texture"), &RenderingDevice::texture_get_native_handle); +#endif ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments", "view_count"), &RenderingDevice::_framebuffer_format_create, DEFVAL(1)); ClassDB::bind_method(D_METHOD("framebuffer_format_create_multipass", "attachments", "passes", "view_count"), &RenderingDevice::_framebuffer_format_create_multipass, DEFVAL(1)); @@ -768,7 +6154,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("uniform_set_create", "uniforms", "shader", "shader_set"), &RenderingDevice::_uniform_set_create); ClassDB::bind_method(D_METHOD("uniform_set_is_valid", "uniform_set"), &RenderingDevice::uniform_set_is_valid); - ClassDB::bind_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data", "post_barrier"), &RenderingDevice::_buffer_update, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data", "post_barrier"), &RenderingDevice::_buffer_update_bind, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::buffer_clear, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data, DEFVAL(0), DEFVAL(0)); @@ -852,6 +6238,20 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(DEVICE_TYPE_CPU); BIND_ENUM_CONSTANT(DEVICE_TYPE_MAX); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_LOGICAL_DEVICE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_PHYSICAL_DEVICE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_TOPMOST_OBJECT); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_COMMAND_QUEUE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_QUEUE_FAMILY); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_TEXTURE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_TEXTURE_VIEW); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_TEXTURE_DATA_FORMAT); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_SAMPLER); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_UNIFORM_SET); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_BUFFER); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_COMPUTE_PIPELINE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_RENDER_PIPELINE); +#ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_DEVICE); BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE); BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_INSTANCE); @@ -865,6 +6265,7 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_BUFFER); BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE); BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE); +#endif BIND_ENUM_CONSTANT(DATA_FORMAT_R4G4_UNORM_PACK8); BIND_ENUM_CONSTANT(DATA_FORMAT_R4G4B4A4_UNORM_PACK16); @@ -1345,8 +6746,326 @@ void RenderingDevice::_bind_methods() { BIND_CONSTANT(INVALID_FORMAT_ID); } +RenderingDevice::~RenderingDevice() { + if (local_device.is_valid()) { + finalize(); + context->local_device_free(local_device); + } + if (singleton == this) { + singleton = nullptr; + } +} + RenderingDevice::RenderingDevice() { if (singleton == nullptr) { // there may be more rendering devices later singleton = this; } } + +/*****************/ +/**** BINDERS ****/ +/*****************/ + +RID RenderingDevice::_texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data) { + ERR_FAIL_COND_V(p_format.is_null(), RID()); + ERR_FAIL_COND_V(p_view.is_null(), RID()); + Vector<Vector<uint8_t>> data; + for (int i = 0; i < p_data.size(); i++) { + Vector<uint8_t> byte_slice = p_data[i]; + ERR_FAIL_COND_V(byte_slice.is_empty(), RID()); + data.push_back(byte_slice); + } + return texture_create(p_format->base, p_view->base, data); +} + +RID RenderingDevice::_texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture) { + ERR_FAIL_COND_V(p_view.is_null(), RID()); + + return texture_create_shared(p_view->base, p_with_texture); +} + +RID RenderingDevice::_texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type) { + ERR_FAIL_COND_V(p_view.is_null(), RID()); + + return texture_create_shared_from_slice(p_view->base, p_with_texture, p_layer, p_mipmap, p_mipmaps, p_slice_type); +} + +Ref<RDTextureFormat> RenderingDevice::_texture_get_format(RID p_rd_texture) { + Ref<RDTextureFormat> rtf; + rtf.instantiate(); + rtf->base = texture_get_format(p_rd_texture); + + return rtf; +} + +RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count) { + Vector<AttachmentFormat> attachments; + attachments.resize(p_attachments.size()); + + for (int i = 0; i < p_attachments.size(); i++) { + Ref<RDAttachmentFormat> af = p_attachments[i]; + ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); + attachments.write[i] = af->base; + } + return framebuffer_format_create(attachments, p_view_count); +} + +RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count) { + Vector<AttachmentFormat> attachments; + attachments.resize(p_attachments.size()); + + for (int i = 0; i < p_attachments.size(); i++) { + Ref<RDAttachmentFormat> af = p_attachments[i]; + ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); + attachments.write[i] = af->base; + } + + Vector<FramebufferPass> passes; + for (int i = 0; i < p_passes.size(); i++) { + Ref<RDFramebufferPass> pass = p_passes[i]; + ERR_CONTINUE(pass.is_null()); + passes.push_back(pass->base); + } + + return framebuffer_format_create_multipass(attachments, passes, p_view_count); +} + +RID RenderingDevice::_framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check, uint32_t p_view_count) { + Vector<RID> textures = Variant(p_textures); + return framebuffer_create(textures, p_format_check, p_view_count); +} + +RID RenderingDevice::_framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { + Vector<RID> textures = Variant(p_textures); + Vector<FramebufferPass> passes; + for (int i = 0; i < p_passes.size(); i++) { + Ref<RDFramebufferPass> pass = p_passes[i]; + ERR_CONTINUE(pass.is_null()); + passes.push_back(pass->base); + } + return framebuffer_create_multipass(textures, passes, p_format_check, p_view_count); +} + +RID RenderingDevice::_sampler_create(const Ref<RDSamplerState> &p_state) { + ERR_FAIL_COND_V(p_state.is_null(), RID()); + + return sampler_create(p_state->base); +} + +RenderingDevice::VertexFormatID RenderingDevice::_vertex_format_create(const TypedArray<RDVertexAttribute> &p_vertex_formats) { + Vector<VertexAttribute> descriptions; + descriptions.resize(p_vertex_formats.size()); + + for (int i = 0; i < p_vertex_formats.size(); i++) { + Ref<RDVertexAttribute> af = p_vertex_formats[i]; + ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); + descriptions.write[i] = af->base; + } + return vertex_format_create(descriptions); +} + +RID RenderingDevice::_vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const TypedArray<RID> &p_src_buffers, const Vector<int64_t> &p_offsets) { + Vector<RID> buffers = Variant(p_src_buffers); + + Vector<uint64_t> offsets; + offsets.resize(p_offsets.size()); + for (int i = 0; i < p_offsets.size(); i++) { + offsets.write[i] = p_offsets[i]; + } + + return vertex_array_create(p_vertex_count, p_vertex_format, buffers, offsets); +} + +Ref<RDShaderSPIRV> RenderingDevice::_shader_compile_spirv_from_source(const Ref<RDShaderSource> &p_source, bool p_allow_cache) { + ERR_FAIL_COND_V(p_source.is_null(), Ref<RDShaderSPIRV>()); + + Ref<RDShaderSPIRV> bytecode; + bytecode.instantiate(); + for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { + String error; + + ShaderStage stage = ShaderStage(i); + String source = p_source->get_stage_source(stage); + + if (!source.is_empty()) { + Vector<uint8_t> spirv = shader_compile_spirv_from_source(stage, source, p_source->get_language(), &error, p_allow_cache); + bytecode->set_stage_bytecode(stage, spirv); + bytecode->set_stage_compile_error(stage, error); + } + } + return bytecode; +} + +Vector<uint8_t> RenderingDevice::_shader_compile_binary_from_spirv(const Ref<RDShaderSPIRV> &p_spirv, const String &p_shader_name) { + ERR_FAIL_COND_V(p_spirv.is_null(), Vector<uint8_t>()); + + Vector<ShaderStageSPIRVData> stage_data; + for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { + ShaderStage stage = ShaderStage(i); + ShaderStageSPIRVData sd; + sd.shader_stage = stage; + String error = p_spirv->get_stage_compile_error(stage); + ERR_FAIL_COND_V_MSG(!error.is_empty(), Vector<uint8_t>(), "Can't create a shader from an errored bytecode. Check errors in source bytecode."); + sd.spirv = p_spirv->get_stage_bytecode(stage); + if (sd.spirv.is_empty()) { + continue; + } + stage_data.push_back(sd); + } + + return shader_compile_binary_from_spirv(stage_data, p_shader_name); +} + +RID RenderingDevice::_shader_create_from_spirv(const Ref<RDShaderSPIRV> &p_spirv, const String &p_shader_name) { + ERR_FAIL_COND_V(p_spirv.is_null(), RID()); + + Vector<ShaderStageSPIRVData> stage_data; + for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { + ShaderStage stage = ShaderStage(i); + ShaderStageSPIRVData sd; + sd.shader_stage = stage; + String error = p_spirv->get_stage_compile_error(stage); + ERR_FAIL_COND_V_MSG(!error.is_empty(), RID(), "Can't create a shader from an errored bytecode. Check errors in source bytecode."); + sd.spirv = p_spirv->get_stage_bytecode(stage); + if (sd.spirv.is_empty()) { + continue; + } + stage_data.push_back(sd); + } + return shader_create_from_spirv(stage_data); +} + +RID RenderingDevice::_uniform_set_create(const TypedArray<RDUniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) { + Vector<Uniform> uniforms; + uniforms.resize(p_uniforms.size()); + for (int i = 0; i < p_uniforms.size(); i++) { + Ref<RDUniform> uniform = p_uniforms[i]; + ERR_FAIL_COND_V(!uniform.is_valid(), RID()); + uniforms.write[i] = uniform->base; + } + return uniform_set_create(uniforms, p_shader, p_shader_set); +} + +Error RenderingDevice::_buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { + return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier); +} + +static Vector<RenderingDevice::PipelineSpecializationConstant> _get_spec_constants(const TypedArray<RDPipelineSpecializationConstant> &p_constants) { + Vector<RenderingDevice::PipelineSpecializationConstant> ret; + ret.resize(p_constants.size()); + for (int i = 0; i < p_constants.size(); i++) { + Ref<RDPipelineSpecializationConstant> c = p_constants[i]; + ERR_CONTINUE(c.is_null()); + RenderingDevice::PipelineSpecializationConstant &sc = ret.write[i]; + Variant value = c->get_value(); + switch (value.get_type()) { + case Variant::BOOL: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sc.bool_value = value; + } break; + case Variant::INT: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sc.int_value = value; + } break; + case Variant::FLOAT: { + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sc.float_value = value; + } break; + default: { + } + } + + sc.constant_id = c->get_constant_id(); + } + return ret; +} + +RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants) { + PipelineRasterizationState rasterization_state; + if (p_rasterization_state.is_valid()) { + rasterization_state = p_rasterization_state->base; + } + + PipelineMultisampleState multisample_state; + if (p_multisample_state.is_valid()) { + multisample_state = p_multisample_state->base; + for (int i = 0; i < p_multisample_state->sample_masks.size(); i++) { + int64_t mask = p_multisample_state->sample_masks[i]; + multisample_state.sample_mask.push_back(mask); + } + } + + PipelineDepthStencilState depth_stencil_state; + if (p_depth_stencil_state.is_valid()) { + depth_stencil_state = p_depth_stencil_state->base; + } + + PipelineColorBlendState color_blend_state; + if (p_blend_state.is_valid()) { + color_blend_state = p_blend_state->base; + for (int i = 0; i < p_blend_state->attachments.size(); i++) { + Ref<RDPipelineColorBlendStateAttachment> attachment = p_blend_state->attachments[i]; + if (attachment.is_valid()) { + color_blend_state.attachments.push_back(attachment->base); + } + } + } + + return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass, _get_spec_constants(p_specialization_constants)); +} + +RID RenderingDevice::_compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants = TypedArray<RDPipelineSpecializationConstant>()) { + return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants)); +} + +RenderingDevice::DrawListID RenderingDevice::_draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { + Vector<RID> stextures; + for (int i = 0; i < p_storage_textures.size(); i++) { + stextures.push_back(p_storage_textures[i]); + } + return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); +} + +Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { + Vector<DrawListID> splits; + splits.resize(p_splits); + Vector<RID> stextures; + for (int i = 0; i < p_storage_textures.size(); i++) { + stextures.push_back(p_storage_textures[i]); + } + draw_list_begin_split(p_framebuffer, p_splits, splits.ptrw(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); + + Vector<int64_t> split_ids; + split_ids.resize(splits.size()); + for (int i = 0; i < splits.size(); i++) { + split_ids.write[i] = splits[i]; + } + + return split_ids; +} + +Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p_splits) { + Vector<DrawListID> splits; + splits.resize(p_splits); + + Error err = draw_list_switch_to_next_pass_split(p_splits, splits.ptrw()); + ERR_FAIL_COND_V(err != OK, Vector<int64_t>()); + + Vector<int64_t> split_ids; + split_ids.resize(splits.size()); + for (int i = 0; i < splits.size(); i++) { + split_ids.write[i] = splits[i]; + } + + return split_ids; +} + +void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { + ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size); +} + +void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { + ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); +} diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index b95640acc6..90ed2828c0 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -32,8 +32,16 @@ #define RENDERING_DEVICE_H #include "core/object/class_db.h" +#include "core/object/worker_thread_pool.h" +#include "core/os/thread_safe.h" +#include "core/templates/local_vector.h" +#include "core/templates/oa_hash_map.h" +#include "core/templates/rid_owner.h" #include "core/variant/typed_array.h" #include "servers/display_server.h" +#include "servers/rendering/rendering_device.h" +#include "servers/rendering/rendering_device_commons.h" +#include "servers/rendering/rendering_device_driver.h" class RDTextureFormat; class RDTextureView; @@ -50,56 +58,16 @@ class RDPipelineColorBlendState; class RDFramebufferPass; class RDPipelineSpecializationConstant; -class RenderingDevice : public Object { +class RenderingDevice : public RenderingDeviceCommons { GDCLASS(RenderingDevice, Object) + + _THREAD_SAFE_CLASS_ public: enum DeviceFamily { DEVICE_UNKNOWN, DEVICE_OPENGL, DEVICE_VULKAN, - DEVICE_DIRECTX - }; - - // This enum matches VkPhysicalDeviceType (except for `DEVICE_TYPE_MAX`). - // Unlike VkPhysicalDeviceType, DeviceType is exposed to the scripting API. - enum DeviceType { - DEVICE_TYPE_OTHER, - DEVICE_TYPE_INTEGRATED_GPU, - DEVICE_TYPE_DISCRETE_GPU, - DEVICE_TYPE_VIRTUAL_GPU, - DEVICE_TYPE_CPU, - DEVICE_TYPE_MAX, - }; - - enum DriverResource { - DRIVER_RESOURCE_VULKAN_DEVICE = 0, - DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE, - DRIVER_RESOURCE_VULKAN_INSTANCE, - DRIVER_RESOURCE_VULKAN_QUEUE, - DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX, - DRIVER_RESOURCE_VULKAN_IMAGE, - DRIVER_RESOURCE_VULKAN_IMAGE_VIEW, - DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT, - DRIVER_RESOURCE_VULKAN_SAMPLER, - DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET, - DRIVER_RESOURCE_VULKAN_BUFFER, - DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE, - DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE, - //next driver continue enum from 1000 to keep order - }; - - enum ShaderStage { - SHADER_STAGE_VERTEX, - SHADER_STAGE_FRAGMENT, - SHADER_STAGE_TESSELATION_CONTROL, - SHADER_STAGE_TESSELATION_EVALUATION, - SHADER_STAGE_COMPUTE, - SHADER_STAGE_MAX, - SHADER_STAGE_VERTEX_BIT = (1 << SHADER_STAGE_VERTEX), - SHADER_STAGE_FRAGMENT_BIT = (1 << SHADER_STAGE_FRAGMENT), - SHADER_STAGE_TESSELATION_CONTROL_BIT = (1 << SHADER_STAGE_TESSELATION_CONTROL), - SHADER_STAGE_TESSELATION_EVALUATION_BIT = (1 << SHADER_STAGE_TESSELATION_EVALUATION), - SHADER_STAGE_COMPUTE_BIT = (1 << SHADER_STAGE_COMPUTE), + DEVICE_DIRECTX, }; enum ShaderLanguage { @@ -121,8 +89,8 @@ public: struct Capabilities { // main device info DeviceFamily device_family = DEVICE_UNKNOWN; - uint32_t version_major = 1.0; - uint32_t version_minor = 0.0; + uint32_t version_major = 1; + uint32_t version_minor = 0; }; typedef String (*ShaderSPIRVGetCacheKeyFunction)(const RenderingDevice *p_render_device); @@ -138,6 +106,10 @@ private: static RenderingDevice *singleton; + Capabilities device_capabilities; + + RenderingDeviceDriver *driver = nullptr; // Owned by the context. + protected: static void _bind_methods(); @@ -146,257 +118,38 @@ protected: static void _bind_compatibility_methods(); #endif - Capabilities device_capabilities; - + /***************************/ + /**** ID INFRASTRUCTURE ****/ + /***************************/ public: //base numeric ID for all types enum { - INVALID_ID = -1, INVALID_FORMAT_ID = -1 }; - /*****************/ - /**** GENERIC ****/ - /*****************/ - - enum CompareOperator { - COMPARE_OP_NEVER, - COMPARE_OP_LESS, - COMPARE_OP_EQUAL, - COMPARE_OP_LESS_OR_EQUAL, - COMPARE_OP_GREATER, - COMPARE_OP_NOT_EQUAL, - COMPARE_OP_GREATER_OR_EQUAL, - COMPARE_OP_ALWAYS, - COMPARE_OP_MAX //not an actual operator, just the amount of operators :D + enum IDType { + ID_TYPE_FRAMEBUFFER_FORMAT, + ID_TYPE_VERTEX_FORMAT, + ID_TYPE_DRAW_LIST, + ID_TYPE_SPLIT_DRAW_LIST, + ID_TYPE_COMPUTE_LIST, + ID_TYPE_MAX, + ID_BASE_SHIFT = 58, // 5 bits for ID types. + ID_MASK = (ID_BASE_SHIFT - 1), }; - enum DataFormat { - DATA_FORMAT_R4G4_UNORM_PACK8, - DATA_FORMAT_R4G4B4A4_UNORM_PACK16, - DATA_FORMAT_B4G4R4A4_UNORM_PACK16, - DATA_FORMAT_R5G6B5_UNORM_PACK16, - DATA_FORMAT_B5G6R5_UNORM_PACK16, - DATA_FORMAT_R5G5B5A1_UNORM_PACK16, - DATA_FORMAT_B5G5R5A1_UNORM_PACK16, - DATA_FORMAT_A1R5G5B5_UNORM_PACK16, - DATA_FORMAT_R8_UNORM, - DATA_FORMAT_R8_SNORM, - DATA_FORMAT_R8_USCALED, - DATA_FORMAT_R8_SSCALED, - DATA_FORMAT_R8_UINT, - DATA_FORMAT_R8_SINT, - DATA_FORMAT_R8_SRGB, - DATA_FORMAT_R8G8_UNORM, - DATA_FORMAT_R8G8_SNORM, - DATA_FORMAT_R8G8_USCALED, - DATA_FORMAT_R8G8_SSCALED, - DATA_FORMAT_R8G8_UINT, - DATA_FORMAT_R8G8_SINT, - DATA_FORMAT_R8G8_SRGB, - DATA_FORMAT_R8G8B8_UNORM, - DATA_FORMAT_R8G8B8_SNORM, - DATA_FORMAT_R8G8B8_USCALED, - DATA_FORMAT_R8G8B8_SSCALED, - DATA_FORMAT_R8G8B8_UINT, - DATA_FORMAT_R8G8B8_SINT, - DATA_FORMAT_R8G8B8_SRGB, - DATA_FORMAT_B8G8R8_UNORM, - DATA_FORMAT_B8G8R8_SNORM, - DATA_FORMAT_B8G8R8_USCALED, - DATA_FORMAT_B8G8R8_SSCALED, - DATA_FORMAT_B8G8R8_UINT, - DATA_FORMAT_B8G8R8_SINT, - DATA_FORMAT_B8G8R8_SRGB, - DATA_FORMAT_R8G8B8A8_UNORM, - DATA_FORMAT_R8G8B8A8_SNORM, - DATA_FORMAT_R8G8B8A8_USCALED, - DATA_FORMAT_R8G8B8A8_SSCALED, - DATA_FORMAT_R8G8B8A8_UINT, - DATA_FORMAT_R8G8B8A8_SINT, - DATA_FORMAT_R8G8B8A8_SRGB, - DATA_FORMAT_B8G8R8A8_UNORM, - DATA_FORMAT_B8G8R8A8_SNORM, - DATA_FORMAT_B8G8R8A8_USCALED, - DATA_FORMAT_B8G8R8A8_SSCALED, - DATA_FORMAT_B8G8R8A8_UINT, - DATA_FORMAT_B8G8R8A8_SINT, - DATA_FORMAT_B8G8R8A8_SRGB, - DATA_FORMAT_A8B8G8R8_UNORM_PACK32, - DATA_FORMAT_A8B8G8R8_SNORM_PACK32, - DATA_FORMAT_A8B8G8R8_USCALED_PACK32, - DATA_FORMAT_A8B8G8R8_SSCALED_PACK32, - DATA_FORMAT_A8B8G8R8_UINT_PACK32, - DATA_FORMAT_A8B8G8R8_SINT_PACK32, - DATA_FORMAT_A8B8G8R8_SRGB_PACK32, - DATA_FORMAT_A2R10G10B10_UNORM_PACK32, - DATA_FORMAT_A2R10G10B10_SNORM_PACK32, - DATA_FORMAT_A2R10G10B10_USCALED_PACK32, - DATA_FORMAT_A2R10G10B10_SSCALED_PACK32, - DATA_FORMAT_A2R10G10B10_UINT_PACK32, - DATA_FORMAT_A2R10G10B10_SINT_PACK32, - DATA_FORMAT_A2B10G10R10_UNORM_PACK32, - DATA_FORMAT_A2B10G10R10_SNORM_PACK32, - DATA_FORMAT_A2B10G10R10_USCALED_PACK32, - DATA_FORMAT_A2B10G10R10_SSCALED_PACK32, - DATA_FORMAT_A2B10G10R10_UINT_PACK32, - DATA_FORMAT_A2B10G10R10_SINT_PACK32, - DATA_FORMAT_R16_UNORM, - DATA_FORMAT_R16_SNORM, - DATA_FORMAT_R16_USCALED, - DATA_FORMAT_R16_SSCALED, - DATA_FORMAT_R16_UINT, - DATA_FORMAT_R16_SINT, - DATA_FORMAT_R16_SFLOAT, - DATA_FORMAT_R16G16_UNORM, - DATA_FORMAT_R16G16_SNORM, - DATA_FORMAT_R16G16_USCALED, - DATA_FORMAT_R16G16_SSCALED, - DATA_FORMAT_R16G16_UINT, - DATA_FORMAT_R16G16_SINT, - DATA_FORMAT_R16G16_SFLOAT, - DATA_FORMAT_R16G16B16_UNORM, - DATA_FORMAT_R16G16B16_SNORM, - DATA_FORMAT_R16G16B16_USCALED, - DATA_FORMAT_R16G16B16_SSCALED, - DATA_FORMAT_R16G16B16_UINT, - DATA_FORMAT_R16G16B16_SINT, - DATA_FORMAT_R16G16B16_SFLOAT, - DATA_FORMAT_R16G16B16A16_UNORM, - DATA_FORMAT_R16G16B16A16_SNORM, - DATA_FORMAT_R16G16B16A16_USCALED, - DATA_FORMAT_R16G16B16A16_SSCALED, - DATA_FORMAT_R16G16B16A16_UINT, - DATA_FORMAT_R16G16B16A16_SINT, - DATA_FORMAT_R16G16B16A16_SFLOAT, - DATA_FORMAT_R32_UINT, - DATA_FORMAT_R32_SINT, - DATA_FORMAT_R32_SFLOAT, - DATA_FORMAT_R32G32_UINT, - DATA_FORMAT_R32G32_SINT, - DATA_FORMAT_R32G32_SFLOAT, - DATA_FORMAT_R32G32B32_UINT, - DATA_FORMAT_R32G32B32_SINT, - DATA_FORMAT_R32G32B32_SFLOAT, - DATA_FORMAT_R32G32B32A32_UINT, - DATA_FORMAT_R32G32B32A32_SINT, - DATA_FORMAT_R32G32B32A32_SFLOAT, - DATA_FORMAT_R64_UINT, - DATA_FORMAT_R64_SINT, - DATA_FORMAT_R64_SFLOAT, - DATA_FORMAT_R64G64_UINT, - DATA_FORMAT_R64G64_SINT, - DATA_FORMAT_R64G64_SFLOAT, - DATA_FORMAT_R64G64B64_UINT, - DATA_FORMAT_R64G64B64_SINT, - DATA_FORMAT_R64G64B64_SFLOAT, - DATA_FORMAT_R64G64B64A64_UINT, - DATA_FORMAT_R64G64B64A64_SINT, - DATA_FORMAT_R64G64B64A64_SFLOAT, - DATA_FORMAT_B10G11R11_UFLOAT_PACK32, - DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32, - DATA_FORMAT_D16_UNORM, - DATA_FORMAT_X8_D24_UNORM_PACK32, - DATA_FORMAT_D32_SFLOAT, - DATA_FORMAT_S8_UINT, - DATA_FORMAT_D16_UNORM_S8_UINT, - DATA_FORMAT_D24_UNORM_S8_UINT, - DATA_FORMAT_D32_SFLOAT_S8_UINT, - DATA_FORMAT_BC1_RGB_UNORM_BLOCK, - DATA_FORMAT_BC1_RGB_SRGB_BLOCK, - DATA_FORMAT_BC1_RGBA_UNORM_BLOCK, - DATA_FORMAT_BC1_RGBA_SRGB_BLOCK, - DATA_FORMAT_BC2_UNORM_BLOCK, - DATA_FORMAT_BC2_SRGB_BLOCK, - DATA_FORMAT_BC3_UNORM_BLOCK, - DATA_FORMAT_BC3_SRGB_BLOCK, - DATA_FORMAT_BC4_UNORM_BLOCK, - DATA_FORMAT_BC4_SNORM_BLOCK, - DATA_FORMAT_BC5_UNORM_BLOCK, - DATA_FORMAT_BC5_SNORM_BLOCK, - DATA_FORMAT_BC6H_UFLOAT_BLOCK, - DATA_FORMAT_BC6H_SFLOAT_BLOCK, - DATA_FORMAT_BC7_UNORM_BLOCK, - DATA_FORMAT_BC7_SRGB_BLOCK, - DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, - DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, - DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, - DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, - DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, - DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, - DATA_FORMAT_EAC_R11_UNORM_BLOCK, - DATA_FORMAT_EAC_R11_SNORM_BLOCK, - DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, - DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, - DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, - DATA_FORMAT_ASTC_4x4_SRGB_BLOCK, - DATA_FORMAT_ASTC_5x4_UNORM_BLOCK, - DATA_FORMAT_ASTC_5x4_SRGB_BLOCK, - DATA_FORMAT_ASTC_5x5_UNORM_BLOCK, - DATA_FORMAT_ASTC_5x5_SRGB_BLOCK, - DATA_FORMAT_ASTC_6x5_UNORM_BLOCK, - DATA_FORMAT_ASTC_6x5_SRGB_BLOCK, - DATA_FORMAT_ASTC_6x6_UNORM_BLOCK, - DATA_FORMAT_ASTC_6x6_SRGB_BLOCK, - DATA_FORMAT_ASTC_8x5_UNORM_BLOCK, - DATA_FORMAT_ASTC_8x5_SRGB_BLOCK, - DATA_FORMAT_ASTC_8x6_UNORM_BLOCK, - DATA_FORMAT_ASTC_8x6_SRGB_BLOCK, - DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, - DATA_FORMAT_ASTC_8x8_SRGB_BLOCK, - DATA_FORMAT_ASTC_10x5_UNORM_BLOCK, - DATA_FORMAT_ASTC_10x5_SRGB_BLOCK, - DATA_FORMAT_ASTC_10x6_UNORM_BLOCK, - DATA_FORMAT_ASTC_10x6_SRGB_BLOCK, - DATA_FORMAT_ASTC_10x8_UNORM_BLOCK, - DATA_FORMAT_ASTC_10x8_SRGB_BLOCK, - DATA_FORMAT_ASTC_10x10_UNORM_BLOCK, - DATA_FORMAT_ASTC_10x10_SRGB_BLOCK, - DATA_FORMAT_ASTC_12x10_UNORM_BLOCK, - DATA_FORMAT_ASTC_12x10_SRGB_BLOCK, - DATA_FORMAT_ASTC_12x12_UNORM_BLOCK, - DATA_FORMAT_ASTC_12x12_SRGB_BLOCK, - DATA_FORMAT_G8B8G8R8_422_UNORM, - DATA_FORMAT_B8G8R8G8_422_UNORM, - DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM, - DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM, - DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM, - DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM, - DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM, - DATA_FORMAT_R10X6_UNORM_PACK16, - DATA_FORMAT_R10X6G10X6_UNORM_2PACK16, - DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, - DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, - DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, - DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, - DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, - DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, - DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, - DATA_FORMAT_R12X4_UNORM_PACK16, - DATA_FORMAT_R12X4G12X4_UNORM_2PACK16, - DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, - DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, - DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, - DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, - DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, - DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, - DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, - DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, - DATA_FORMAT_G16B16G16R16_422_UNORM, - DATA_FORMAT_B16G16R16G16_422_UNORM, - DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM, - DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM, - DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM, - DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM, - DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM, - DATA_FORMAT_MAX - }; +private: + HashMap<RID, HashSet<RID>> dependency_map; // IDs to IDs that depend on it. + HashMap<RID, HashSet<RID>> reverse_dependency_map; // Same as above, but in reverse. + + void _add_dependency(RID p_id, RID p_depends_on); + void _free_dependencies(RID p_id); /*****************/ /**** BARRIER ****/ /*****************/ +public: enum BarrierMask { BARRIER_MASK_VERTEX = 1, BARRIER_MASK_FRAGMENT = 8, @@ -408,172 +161,198 @@ public: BARRIER_MASK_NO_BARRIER = 0x8000, }; - /*****************/ - /**** TEXTURE ****/ - /*****************/ - - enum TextureType { - TEXTURE_TYPE_1D, - TEXTURE_TYPE_2D, - TEXTURE_TYPE_3D, - TEXTURE_TYPE_CUBE, - TEXTURE_TYPE_1D_ARRAY, - TEXTURE_TYPE_2D_ARRAY, - TEXTURE_TYPE_CUBE_ARRAY, - TEXTURE_TYPE_MAX +private: + void _full_barrier(bool p_sync_with_draw); + + /***************************/ + /**** BUFFER MANAGEMENT ****/ + /***************************/ + + // These are temporary buffers on CPU memory that hold + // the information until the CPU fetches it and places it + // either on GPU buffers, or images (textures). It ensures + // updates are properly synchronized with whatever the + // GPU is doing. + // + // The logic here is as follows, only 3 of these + // blocks are created at the beginning (one per frame) + // they can each belong to a frame (assigned to current when + // used) and they can only be reused after the same frame is + // recycled. + // + // When CPU requires to allocate more than what is available, + // more of these buffers are created. If a limit is reached, + // then a fence will ensure will wait for blocks allocated + // in previous frames are processed. If that fails, then + // another fence will ensure everything pending for the current + // frame is processed (effectively stalling). + // + // See the comments in the code to understand better how it works. + + struct StagingBufferBlock { + RDD::BufferID driver_id; + uint64_t frame_used = 0; + uint32_t fill_amount = 0; }; - enum TextureSamples { - TEXTURE_SAMPLES_1, - TEXTURE_SAMPLES_2, - TEXTURE_SAMPLES_4, - TEXTURE_SAMPLES_8, - TEXTURE_SAMPLES_16, - TEXTURE_SAMPLES_32, - TEXTURE_SAMPLES_64, - TEXTURE_SAMPLES_MAX - }; + Vector<StagingBufferBlock> staging_buffer_blocks; + int staging_buffer_current = 0; + uint32_t staging_buffer_block_size = 0; + uint64_t staging_buffer_max_size = 0; + bool staging_buffer_used = false; - enum TextureUsageBits { - TEXTURE_USAGE_SAMPLING_BIT = (1 << 0), - TEXTURE_USAGE_COLOR_ATTACHMENT_BIT = (1 << 1), - TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = (1 << 2), - TEXTURE_USAGE_STORAGE_BIT = (1 << 3), - TEXTURE_USAGE_STORAGE_ATOMIC_BIT = (1 << 4), - TEXTURE_USAGE_CPU_READ_BIT = (1 << 5), - TEXTURE_USAGE_CAN_UPDATE_BIT = (1 << 6), - TEXTURE_USAGE_CAN_COPY_FROM_BIT = (1 << 7), - TEXTURE_USAGE_CAN_COPY_TO_BIT = (1 << 8), - TEXTURE_USAGE_INPUT_ATTACHMENT_BIT = (1 << 9), - TEXTURE_USAGE_VRS_ATTACHMENT_BIT = (1 << 10), - }; + Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true); + Error _insert_staging_block(); - enum TextureSwizzle { - TEXTURE_SWIZZLE_IDENTITY, - TEXTURE_SWIZZLE_ZERO, - TEXTURE_SWIZZLE_ONE, - TEXTURE_SWIZZLE_R, - TEXTURE_SWIZZLE_G, - TEXTURE_SWIZZLE_B, - TEXTURE_SWIZZLE_A, - TEXTURE_SWIZZLE_MAX + struct Buffer { + RDD::BufferID driver_id; + uint32_t size = 0; + BitField<RDD::BufferUsageBits> usage; }; - struct TextureFormat { - DataFormat format; - uint32_t width; - uint32_t height; - uint32_t depth; - uint32_t array_layers; - uint32_t mipmaps; - TextureType texture_type; - TextureSamples samples; - uint32_t usage_bits; - Vector<DataFormat> shareable_formats; - bool is_resolve_buffer = false; + Buffer *_get_buffer_from_owner(RID p_buffer, BitField<RDD::PipelineStageBits> &r_stages, BitField<RDD::BarrierAccessBits> &r_access, BitField<BarrierMask> p_post_barrier); + Error _buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer = false, uint32_t p_required_align = 32); - bool operator==(const TextureFormat &b) const { - if (format != b.format) { - return false; - } else if (width != b.width) { - return false; - } else if (height != b.height) { - return false; - } else if (depth != b.depth) { - return false; - } else if (array_layers != b.array_layers) { - return false; - } else if (mipmaps != b.mipmaps) { - return false; - } else if (texture_type != b.texture_type) { - return false; - } else if (samples != b.samples) { - return false; - } else if (usage_bits != b.usage_bits) { - return false; - } else if (shareable_formats != b.shareable_formats) { - return false; - } else { - return true; - } - } + RID_Owner<Buffer> uniform_buffer_owner; + RID_Owner<Buffer> storage_buffer_owner; + RID_Owner<Buffer> texture_buffer_owner; - TextureFormat() { - format = DATA_FORMAT_R8_UNORM; - width = 1; - height = 1; - depth = 1; - array_layers = 1; - mipmaps = 1; - texture_type = TEXTURE_TYPE_2D; - samples = TEXTURE_SAMPLES_1; - usage_bits = 0; - } +public: + Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); // This causes stall, only use to retrieve large buffers for saving. + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + // In modern APIs, the concept of textures may not exist; + // instead there is the image (the memory pretty much, + // the view (how the memory is interpreted) and the + // sampler (how it's sampled from the shader). + // + // Texture here includes the first two stages, but + // It's possible to create textures sharing the image + // but with different views. The main use case for this + // is textures that can be read as both SRGB/Linear, + // or slices of a texture (a mipmap, a layer, a 3D slice) + // for a framebuffer to render into it. + + struct Texture { + RDD::TextureID driver_id; + + TextureType type = TEXTURE_TYPE_MAX; + DataFormat format = DATA_FORMAT_MAX; + TextureSamples samples = TEXTURE_SAMPLES_MAX; + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 0; + uint32_t layers = 0; + uint32_t mipmaps = 0; + uint32_t usage_flags = 0; + uint32_t base_mipmap = 0; + uint32_t base_layer = 0; + + Vector<DataFormat> allowed_shared_formats; + + RDD::TextureLayout layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + + uint64_t used_in_frame = 0; + bool used_in_transfer = false; + bool used_in_raster = false; + bool used_in_compute = false; + + bool is_resolve_buffer = false; + + BitField<RDD::TextureAspectBits> read_aspect_flags; + BitField<RDD::TextureAspectBits> barrier_aspect_flags; + bool bound = false; // Bound to framebffer. + RID owner; }; + RID_Owner<Texture> texture_owner; + uint32_t texture_upload_region_size_px = 0; + + Vector<uint8_t> _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false); + Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue); + +public: struct TextureView { - DataFormat format_override; - TextureSwizzle swizzle_r; - TextureSwizzle swizzle_g; - TextureSwizzle swizzle_b; - TextureSwizzle swizzle_a; - - bool operator==(const TextureView &p_view) const { - if (format_override != p_view.format_override) { + DataFormat format_override = DATA_FORMAT_MAX; // // Means, use same as format. + TextureSwizzle swizzle_r = TEXTURE_SWIZZLE_R; + TextureSwizzle swizzle_g = TEXTURE_SWIZZLE_G; + TextureSwizzle swizzle_b = TEXTURE_SWIZZLE_B; + TextureSwizzle swizzle_a = TEXTURE_SWIZZLE_A; + + bool operator==(const TextureView &p_other) const { + if (format_override != p_other.format_override) { return false; - } else if (swizzle_r != p_view.swizzle_r) { + } else if (swizzle_r != p_other.swizzle_r) { return false; - } else if (swizzle_g != p_view.swizzle_g) { + } else if (swizzle_g != p_other.swizzle_g) { return false; - } else if (swizzle_b != p_view.swizzle_b) { + } else if (swizzle_b != p_other.swizzle_b) { return false; - } else if (swizzle_a != p_view.swizzle_a) { + } else if (swizzle_a != p_other.swizzle_a) { return false; } else { return true; } } - - TextureView() { - format_override = DATA_FORMAT_MAX; //means, use same as format - swizzle_r = TEXTURE_SWIZZLE_R; - swizzle_g = TEXTURE_SWIZZLE_G; - swizzle_b = TEXTURE_SWIZZLE_B; - swizzle_a = TEXTURE_SWIZZLE_A; - } }; - virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()) = 0; - virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture) = 0; - virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) = 0; - - enum TextureSliceType { - TEXTURE_SLICE_2D, - TEXTURE_SLICE_CUBEMAP, - TEXTURE_SLICE_3D, - TEXTURE_SLICE_2D_ARRAY, - }; + RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); + RID texture_create_shared(const TextureView &p_view, RID p_with_texture); + RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_usage, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); + RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0); + Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer); // CPU textures will return immediately, while GPU textures will most likely force a flush + + bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<TextureUsageBits> p_usage) const; + bool texture_is_shared(RID p_texture); + bool texture_is_valid(RID p_texture); + TextureFormat texture_get_format(RID p_texture); + Size2i texture_size(RID p_texture); +#ifndef DISABLE_DEPRECATED + uint64_t texture_get_native_handle(RID p_texture); +#endif - virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0) = 0; + Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer) = 0; // CPU textures will return immediately, while GPU textures will most likely force a flush + /************************/ + /**** DRAW LISTS (I) ****/ + /************************/ - virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const = 0; - virtual bool texture_is_shared(RID p_texture) = 0; - virtual bool texture_is_valid(RID p_texture) = 0; - virtual TextureFormat texture_get_format(RID p_texture) = 0; - virtual Size2i texture_size(RID p_texture) = 0; - virtual uint64_t texture_get_native_handle(RID p_texture) = 0; + enum InitialAction { + INITIAL_ACTION_CLEAR, // Start rendering and clear the whole framebuffer. + INITIAL_ACTION_CLEAR_REGION, // Start rendering and clear the framebuffer in the specified region. + INITIAL_ACTION_CLEAR_REGION_CONTINUE, // Continue rendering and clear the framebuffer in the specified region. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. + INITIAL_ACTION_KEEP, // Start rendering, but keep attached color texture contents. If the framebuffer was previously used to read in a shader, this will automatically insert a layout transition. + INITIAL_ACTION_DROP, // Start rendering, ignore what is there; write above it. In general, this is the fastest option when you will be writing every single pixel and you don't need a clear color. + INITIAL_ACTION_CONTINUE, // Continue rendering. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. + INITIAL_ACTION_MAX + }; - virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; + enum FinalAction { + FINAL_ACTION_READ, // Store the texture for reading and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). + FINAL_ACTION_DISCARD, // Discard the texture data and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). + FINAL_ACTION_CONTINUE, // Store the texture and continue for further processing. Similar to `FINAL_ACTION_READ`, but does not make the texture read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit. + FINAL_ACTION_MAX + }; /*********************/ /**** FRAMEBUFFER ****/ /*********************/ + // In modern APIs, generally, framebuffers work similar to how they + // do in OpenGL, with the exception that + // the "format" (RDD::RenderPassID) is not dynamic + // and must be more or less the same as the one + // used for the render pipelines. + struct AttachmentFormat { enum { UNUSED_ATTACHMENT = 0xFFFFFFFF }; DataFormat format; @@ -586,14 +365,7 @@ public: } }; - typedef int64_t FramebufferFormatID; - - // This ID is warranted to be unique for the same formats, does not need to be freed - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1) = 0; struct FramebufferPass { - enum { - ATTACHMENT_UNUSED = -1 - }; Vector<int32_t> color_attachments; Vector<int32_t> input_attachments; Vector<int32_t> resolve_attachments; @@ -602,193 +374,446 @@ public: int32_t vrs_attachment = ATTACHMENT_UNUSED; // density map for VRS, only used if supported }; - virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1) = 0; - virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0; - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0) = 0; + typedef int64_t FramebufferFormatID; - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; - virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; - virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0; - virtual bool framebuffer_is_valid(RID p_framebuffer) const = 0; - virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) = 0; +private: + struct FramebufferFormatKey { + Vector<AttachmentFormat> attachments; + Vector<FramebufferPass> passes; + uint32_t view_count = 1; + + bool operator<(const FramebufferFormatKey &p_key) const { + if (view_count != p_key.view_count) { + return view_count < p_key.view_count; + } - virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; + uint32_t pass_size = passes.size(); + uint32_t key_pass_size = p_key.passes.size(); + if (pass_size != key_pass_size) { + return pass_size < key_pass_size; + } + const FramebufferPass *pass_ptr = passes.ptr(); + const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); + + for (uint32_t i = 0; i < pass_size; i++) { + { // Compare color attachments. + uint32_t attachment_size = pass_ptr[i].color_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { // Compare input attachments. + uint32_t attachment_size = pass_ptr[i].input_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { // Compare resolve attachments. + uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { // Compare preserve attachments. + uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) { + return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment; + } + } - /*****************/ - /**** SAMPLER ****/ - /*****************/ + int as = attachments.size(); + int bs = p_key.attachments.size(); + if (as != bs) { + return as < bs; + } - enum SamplerFilter { - SAMPLER_FILTER_NEAREST, - SAMPLER_FILTER_LINEAR, - }; + const AttachmentFormat *af_a = attachments.ptr(); + const AttachmentFormat *af_b = p_key.attachments.ptr(); + for (int i = 0; i < as; i++) { + const AttachmentFormat &a = af_a[i]; + const AttachmentFormat &b = af_b[i]; + if (a.format != b.format) { + return a.format < b.format; + } + if (a.samples != b.samples) { + return a.samples < b.samples; + } + if (a.usage_flags != b.usage_flags) { + return a.usage_flags < b.usage_flags; + } + } - enum SamplerRepeatMode { - SAMPLER_REPEAT_MODE_REPEAT, - SAMPLER_REPEAT_MODE_MIRRORED_REPEAT, - SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE, - SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER, - SAMPLER_REPEAT_MODE_MIRROR_CLAMP_TO_EDGE, - SAMPLER_REPEAT_MODE_MAX + return false; // Equal. + } }; - enum SamplerBorderColor { - SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, - SAMPLER_BORDER_COLOR_INT_TRANSPARENT_BLACK, - SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - SAMPLER_BORDER_COLOR_INT_OPAQUE_BLACK, - SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_WHITE, - SAMPLER_BORDER_COLOR_INT_OPAQUE_WHITE, - SAMPLER_BORDER_COLOR_MAX + RDD::RenderPassID _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr); + + // This is a cache and it's never freed, it ensures + // IDs for a given format are always unique. + RBMap<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; + struct FramebufferFormat { + const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E; + RDD::RenderPassID render_pass; // Here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec). + Vector<TextureSamples> pass_samples; + uint32_t view_count = 1; // Number of views. }; - struct SamplerState { - SamplerFilter mag_filter; - SamplerFilter min_filter; - SamplerFilter mip_filter; - SamplerRepeatMode repeat_u; - SamplerRepeatMode repeat_v; - SamplerRepeatMode repeat_w; - float lod_bias; - bool use_anisotropy; - float anisotropy_max; - bool enable_compare; - CompareOperator compare_op; - float min_lod; - float max_lod; - SamplerBorderColor border_color; - bool unnormalized_uvw; - - SamplerState() { - mag_filter = SAMPLER_FILTER_NEAREST; - min_filter = SAMPLER_FILTER_NEAREST; - mip_filter = SAMPLER_FILTER_NEAREST; - repeat_u = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; - repeat_v = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; - repeat_w = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; - lod_bias = 0; - use_anisotropy = false; - anisotropy_max = 1.0; - enable_compare = false; - compare_op = COMPARE_OP_ALWAYS; - min_lod = 0; - max_lod = 1e20; //something very large should do - border_color = SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK; - unnormalized_uvw = false; - } + HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; + + struct Framebuffer { + FramebufferFormatID format_id; + struct VersionKey { + InitialAction initial_color_action; + FinalAction final_color_action; + InitialAction initial_depth_action; + FinalAction final_depth_action; + uint32_t view_count; + + bool operator<(const VersionKey &p_key) const { + if (initial_color_action == p_key.initial_color_action) { + if (final_color_action == p_key.final_color_action) { + if (initial_depth_action == p_key.initial_depth_action) { + if (final_depth_action == p_key.final_depth_action) { + return view_count < p_key.view_count; + } else { + return final_depth_action < p_key.final_depth_action; + } + } else { + return initial_depth_action < p_key.initial_depth_action; + } + } else { + return final_color_action < p_key.final_color_action; + } + } else { + return initial_color_action < p_key.initial_color_action; + } + } + }; + + uint32_t storage_mask = 0; + Vector<RID> texture_ids; + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; + + struct Version { + RDD::FramebufferID framebuffer; + RDD::RenderPassID render_pass; // This one is owned. + uint32_t subpass_count = 1; + }; + + RBMap<VersionKey, Version> framebuffers; + Size2 size; + uint32_t view_count; }; - virtual RID sampler_create(const SamplerState &p_state) = 0; - virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const = 0; + RID_Owner<Framebuffer> framebuffer_owner; + +public: + // This ID is warranted to be unique for the same formats, does not need to be freed + FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); + FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); + FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); + TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); + + RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); + bool framebuffer_is_valid(RID p_framebuffer) const; + void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata); + + FramebufferFormatID framebuffer_get_format(RID p_framebuffer); + + /*****************/ + /**** SAMPLER ****/ + /*****************/ +private: + RID_Owner<RDD::SamplerID> sampler_owner; + +public: + RID sampler_create(const SamplerState &p_state); + bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const; /**********************/ /**** VERTEX ARRAY ****/ /**********************/ - enum VertexFrequency { - VERTEX_FREQUENCY_VERTEX, - VERTEX_FREQUENCY_INSTANCE, + typedef int64_t VertexFormatID; + +private: + // Vertex buffers in Vulkan are similar to how + // they work in OpenGL, except that instead of + // an attribute index, there is a buffer binding + // index (for binding the buffers in real-time) + // and a location index (what is used in the shader). + // + // This mapping is done here internally, and it's not + // exposed. + + RID_Owner<Buffer> vertex_buffer_owner; + + struct VertexDescriptionKey { + Vector<VertexAttribute> vertex_formats; + + bool operator==(const VertexDescriptionKey &p_key) const { + int vdc = vertex_formats.size(); + int vdck = p_key.vertex_formats.size(); + + if (vdc != vdck) { + return false; + } else { + const VertexAttribute *a_ptr = vertex_formats.ptr(); + const VertexAttribute *b_ptr = p_key.vertex_formats.ptr(); + for (int i = 0; i < vdc; i++) { + const VertexAttribute &a = a_ptr[i]; + const VertexAttribute &b = b_ptr[i]; + + if (a.location != b.location) { + return false; + } + if (a.offset != b.offset) { + return false; + } + if (a.format != b.format) { + return false; + } + if (a.stride != b.stride) { + return false; + } + if (a.frequency != b.frequency) { + return false; + } + } + return true; // They are equal. + } + } + + uint32_t hash() const { + int vdc = vertex_formats.size(); + uint32_t h = hash_murmur3_one_32(vdc); + const VertexAttribute *ptr = vertex_formats.ptr(); + for (int i = 0; i < vdc; i++) { + const VertexAttribute &vd = ptr[i]; + h = hash_murmur3_one_32(vd.location, h); + h = hash_murmur3_one_32(vd.offset, h); + h = hash_murmur3_one_32(vd.format, h); + h = hash_murmur3_one_32(vd.stride, h); + h = hash_murmur3_one_32(vd.frequency, h); + } + return hash_fmix32(h); + } }; - struct VertexAttribute { - uint32_t location; //shader location - uint32_t offset; - DataFormat format; - uint32_t stride; - VertexFrequency frequency; - VertexAttribute() { - location = 0; - offset = 0; - stride = 0; - format = DATA_FORMAT_MAX; - frequency = VERTEX_FREQUENCY_VERTEX; + struct VertexDescriptionHash { + static _FORCE_INLINE_ uint32_t hash(const VertexDescriptionKey &p_key) { + return p_key.hash(); } }; - virtual RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false) = 0; - typedef int64_t VertexFormatID; + // This is a cache and it's never freed, it ensures that + // ID used for a specific format always remain the same. + HashMap<VertexDescriptionKey, VertexFormatID, VertexDescriptionHash> vertex_format_cache; - // This ID is warranted to be unique for the same formats, does not need to be freed - virtual VertexFormatID vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats) = 0; - virtual RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers, const Vector<uint64_t> &p_offsets = Vector<uint64_t>()) = 0; + struct VertexDescriptionCache { + Vector<VertexAttribute> vertex_formats; + RDD::VertexFormatID driver_id; + }; - enum IndexBufferFormat { - INDEX_BUFFER_FORMAT_UINT16, - INDEX_BUFFER_FORMAT_UINT32, + HashMap<VertexFormatID, VertexDescriptionCache> vertex_formats; + + struct VertexArray { + RID buffer; + VertexFormatID description; + int vertex_count = 0; + uint32_t max_instances_allowed = 0; + + Vector<RDD::BufferID> buffers; // Not owned, just referenced. + Vector<uint64_t> offsets; }; - virtual RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_restart_indices = false) = 0; - virtual RID index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) = 0; + RID_Owner<VertexArray> vertex_array_owner; + + struct IndexBuffer : public Buffer { + uint32_t max_index = 0; // Used for validation. + uint32_t index_count = 0; + IndexBufferFormat format = INDEX_BUFFER_FORMAT_UINT16; + bool supports_restart_indices = false; + }; + + RID_Owner<IndexBuffer> index_buffer_owner; + + struct IndexArray { + uint32_t max_index = 0; // Remember the maximum index here too, for validation. + RDD::BufferID driver_id; // Not owned, inherited from index buffer. + uint32_t offset = 0; + uint32_t indices = 0; + IndexBufferFormat format = INDEX_BUFFER_FORMAT_UINT16; + bool supports_restart_indices = false; + }; + + RID_Owner<IndexArray> index_array_owner; + +public: + RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false); + + // This ID is warranted to be unique for the same formats, does not need to be freed + VertexFormatID vertex_format_create(const Vector<VertexAttribute> &p_vertex_descriptions); + RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers, const Vector<uint64_t> &p_offsets = Vector<uint64_t>()); + + RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_restart_indices = false); + RID index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count); /****************/ /**** SHADER ****/ /****************/ - const Capabilities *get_device_capabilities() const { return &device_capabilities; }; + // Some APIs (e.g., Vulkan) specifies a really complex behavior for the application + // in order to tell when descriptor sets need to be re-bound (or not). + // "When binding a descriptor set (see Descriptor Set Binding) to set + // number N, if the previously bound descriptor sets for sets zero + // through N-1 were all bound using compatible pipeline layouts, + // then performing this binding does not disturb any of the lower numbered sets. + // If, additionally, the previous bound descriptor set for set N was + // bound using a pipeline layout compatible for set N, then the bindings + // in sets numbered greater than N are also not disturbed." + // As a result, we need to figure out quickly when something is no longer "compatible". + // in order to avoid costly rebinds. - enum Features { - SUPPORTS_MULTIVIEW, - SUPPORTS_FSR_HALF_FLOAT, - SUPPORTS_ATTACHMENT_VRS, - // If not supported, a fragment shader with only side effets (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver. - SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS, +private: + struct UniformSetFormat { + Vector<ShaderUniform> uniforms; + + _FORCE_INLINE_ bool operator<(const UniformSetFormat &p_other) const { + if (uniforms.size() != p_other.uniforms.size()) { + return uniforms.size() < p_other.uniforms.size(); + } + for (int i = 0; i < uniforms.size(); i++) { + if (uniforms[i] < p_other.uniforms[i]) { + return true; + } else if (p_other.uniforms[i] < uniforms[i]) { + return false; + } + } + return false; + } + }; + + // Always grows, never shrinks, ensuring unique IDs, but we assume + // the amount of formats will never be a problem, as the amount of shaders + // in a game is limited. + RBMap<UniformSetFormat, uint32_t> uniform_set_format_cache; + + // Shaders in Vulkan are just pretty much + // precompiled blocks of SPIR-V bytecode. They + // are most likely not really compiled to host + // assembly until a pipeline is created. + // + // When supplying the shaders, this implementation + // will use the reflection abilities of glslang to + // understand and cache everything required to + // create and use the descriptor sets (Vulkan's + // biggest pain). + // + // Additionally, hashes are created for every set + // to do quick validation and ensuring the user + // does not submit something invalid. + + struct Shader : public ShaderDescription { + String name; // Used for debug. + RDD::ShaderID driver_id; + uint32_t layout_hash = 0; + Vector<uint32_t> set_formats; }; - virtual bool has_feature(const Features p_feature) const = 0; - virtual Vector<uint8_t> shader_compile_spirv_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true); - virtual String shader_get_spirv_cache_key() const; + String _shader_uniform_debug(RID p_shader, int p_set = -1); + + RID_Owner<Shader> shader_owner; + +#ifndef DISABLE_DEPRECATED + BitField<BarrierMask> _convert_barrier_mask_81356(BitField<BarrierMask> p_old_barrier); + void _draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); + void _compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); + void _barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to); +#endif + +public: + ApiContextRD *get_context() const { return context; } + + const Capabilities *get_device_capabilities() const { return &device_capabilities; }; + + bool has_feature(const Features p_feature) const; + + Vector<uint8_t> shader_compile_spirv_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true); + String shader_get_spirv_cache_key() const; static void shader_set_compile_to_spirv_function(ShaderCompileToSPIRVFunction p_function); static void shader_set_spirv_cache_function(ShaderCacheFunction p_function); static void shader_set_get_cache_key_function(ShaderSPIRVGetCacheKeyFunction p_function); - struct ShaderStageSPIRVData { - ShaderStage shader_stage; - Vector<uint8_t> spir_v; + String shader_get_binary_cache_key() const; + Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); - ShaderStageSPIRVData() { - shader_stage = SHADER_STAGE_VERTEX; - } - }; - - virtual String shader_get_binary_cache_key() const = 0; - virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "") = 0; + RID shader_create_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); + RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()); + RID shader_create_placeholder(); - virtual RID shader_create_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); - virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()) = 0; - virtual RID shader_create_placeholder() = 0; - - virtual uint64_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0; + uint64_t shader_get_vertex_input_attribute_mask(RID p_shader); /******************/ /**** UNIFORMS ****/ /******************/ - enum UniformType { - UNIFORM_TYPE_SAMPLER, //for sampling only (sampler GLSL type) - UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, // for sampling only, but includes a texture, (samplerXX GLSL type), first a sampler then a texture - UNIFORM_TYPE_TEXTURE, //only texture, (textureXX GLSL type) - UNIFORM_TYPE_IMAGE, // storage image (imageXX GLSL type), for compute mostly - UNIFORM_TYPE_TEXTURE_BUFFER, // buffer texture (or TBO, textureBuffer type) - UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER, // buffer texture with a sampler(or TBO, samplerBuffer type) - UNIFORM_TYPE_IMAGE_BUFFER, //texel buffer, (imageBuffer type), for compute mostly - UNIFORM_TYPE_UNIFORM_BUFFER, //regular uniform buffer (or UBO). - UNIFORM_TYPE_STORAGE_BUFFER, //storage buffer ("buffer" qualifier) like UBO, but supports storage, for compute mostly - UNIFORM_TYPE_INPUT_ATTACHMENT, //used for sub-pass read/write, for mobile mostly - UNIFORM_TYPE_MAX - }; - enum StorageBufferUsage { STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1, }; - virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; - virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>(), BitField<StorageBufferUsage> p_usage = 0) = 0; - virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; + RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()); + RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>(), BitField<StorageBufferUsage> p_usage = 0); + RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()); struct Uniform { - UniformType uniform_type; - int binding; // Binding index as specified in shader. + UniformType uniform_type = UNIFORM_TYPE_IMAGE; + uint32_t binding = 0; // Binding index as specified in shader. private: // In most cases only one ID is provided per binding, so avoid allocating memory unnecessarily for performance. @@ -846,465 +871,432 @@ public: binding = p_binding; ids = p_ids; } - _FORCE_INLINE_ Uniform() { - uniform_type = UNIFORM_TYPE_IMAGE; - binding = 0; - } + _FORCE_INLINE_ Uniform() = default; }; - virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) = 0; - virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; - virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0; +private: + // This structure contains the descriptor set. They _need_ to be allocated + // for a shader (and will be erased when this shader is erased), but should + // work for other shaders as long as the hash matches. This covers using + // them in shader variants. + // + // Keep also in mind that you can share buffers between descriptor sets, so + // the above restriction is not too serious. + + struct UniformSet { + uint32_t format = 0; + RID shader_id; + uint32_t shader_set = 0; + RDD::UniformSetID driver_id; + struct AttachableTexture { + uint32_t bind = 0; + RID texture; + }; - virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0) = 0; // This causes stall, only use to retrieve large buffers for saving. + LocalVector<AttachableTexture> attachable_textures; // Used for validation. + Vector<Texture *> mutable_sampled_textures; // Used for layout change. + Vector<Texture *> mutable_storage_textures; // Used for layout change. + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; + }; - /******************************************/ - /**** PIPELINE SPECIALIZATION CONSTANT ****/ - /******************************************/ + RID_Owner<UniformSet> uniform_set_owner; - enum PipelineSpecializationConstantType { - PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL, - PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT, - PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT, +public: + RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); + bool uniform_set_is_valid(RID p_uniform_set); + void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); + + /*******************/ + /**** PIPELINES ****/ + /*******************/ + + // Render pipeline contains ALL the + // information required for drawing. + // This includes all the rasterizer state + // as well as shader used, framebuffer format, + // etc. + // While the pipeline is just a single object + // (VkPipeline) a lot of values are also saved + // here to do validation (vulkan does none by + // default) and warn the user if something + // was not supplied as intended. +private: + struct RenderPipeline { + // Cached values for validation. +#ifdef DEBUG_ENABLED + struct Validation { + FramebufferFormatID framebuffer_format; + uint32_t render_pass = 0; + uint32_t dynamic_state = 0; + VertexFormatID vertex_format; + bool uses_restart_indices = false; + uint32_t primitive_minimum = 0; + uint32_t primitive_divisor = 0; + } validation; +#endif + // Actual pipeline. + RID shader; + RDD::ShaderID shader_driver_id; + uint32_t shader_layout_hash = 0; + Vector<uint32_t> set_formats; + RDD::PipelineID driver_id; + uint32_t push_constant_size = 0; }; - struct PipelineSpecializationConstant { - PipelineSpecializationConstantType type; - uint32_t constant_id; - union { - uint32_t int_value; - float float_value; - bool bool_value; - }; - - PipelineSpecializationConstant() { - type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; - constant_id = 0; - int_value = 0; - } + RID_Owner<RenderPipeline> render_pipeline_owner; + + bool pipelines_cache_enabled = false; + size_t pipelines_cache_size = 0; + String pipelines_cache_file_path; + WorkerThreadPool::TaskID pipelines_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; + + Vector<uint8_t> _load_pipeline_cache(); + void _update_pipeline_cache(bool p_closing = false); + static void _save_pipeline_cache(void *p_data); + + struct ComputePipeline { + RID shader; + RDD::ShaderID shader_driver_id; + uint32_t shader_layout_hash = 0; + Vector<uint32_t> set_formats; + RDD::PipelineID driver_id; + uint32_t push_constant_size = 0; + uint32_t local_group_size[3] = { 0, 0, 0 }; }; + RID_Owner<ComputePipeline> compute_pipeline_owner; + +public: + RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); + bool render_pipeline_is_valid(RID p_pipeline); + + RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); + bool compute_pipeline_is_valid(RID p_pipeline); + + /****************/ + /**** SCREEN ****/ + /****************/ + + int screen_get_width(DisplayServer::WindowID p_screen = 0) const; + int screen_get_height(DisplayServer::WindowID p_screen = 0) const; + FramebufferFormatID screen_get_framebuffer_format() const; + /*************************/ - /**** RENDER PIPELINE ****/ + /**** DRAW LISTS (II) ****/ /*************************/ - enum RenderPrimitive { - RENDER_PRIMITIVE_POINTS, - RENDER_PRIMITIVE_LINES, - RENDER_PRIMITIVE_LINES_WITH_ADJACENCY, - RENDER_PRIMITIVE_LINESTRIPS, - RENDER_PRIMITIVE_LINESTRIPS_WITH_ADJACENCY, - RENDER_PRIMITIVE_TRIANGLES, - RENDER_PRIMITIVE_TRIANGLES_WITH_ADJACENCY, - RENDER_PRIMITIVE_TRIANGLE_STRIPS, - RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_AJACENCY, - RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX, - RENDER_PRIMITIVE_TESSELATION_PATCH, - RENDER_PRIMITIVE_MAX + typedef int64_t DrawListID; + +private: + // Draw list contains both the command buffer + // used for drawing as well as a LOT of + // information used for validation. This + // validation is cheap so most of it can + // also run in release builds. + + // When using split command lists, this is + // implemented internally using secondary command + // buffers. As they can be created in threads, + // each needs its own command pool. + + struct SplitDrawListAllocator { + RDD::CommandPoolID command_pool; + Vector<RDD::CommandBufferID> command_buffers; // One for each frame. }; - //disable optimization, tessellate control points + Vector<SplitDrawListAllocator> split_draw_list_allocators; - enum PolygonCullMode { - POLYGON_CULL_DISABLED, - POLYGON_CULL_FRONT, - POLYGON_CULL_BACK, - }; + struct DrawList { + RDD::CommandBufferID command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. + Rect2i viewport; + bool viewport_set = false; - enum PolygonFrontFace { - POLYGON_FRONT_FACE_CLOCKWISE, - POLYGON_FRONT_FACE_COUNTER_CLOCKWISE, - }; + struct SetState { + uint32_t pipeline_expected_format = 0; + uint32_t uniform_set_format = 0; + RDD::UniformSetID uniform_set_driver_id; + RID uniform_set; + bool bound = false; + }; - enum StencilOperation { - STENCIL_OP_KEEP, - STENCIL_OP_ZERO, - STENCIL_OP_REPLACE, - STENCIL_OP_INCREMENT_AND_CLAMP, - STENCIL_OP_DECREMENT_AND_CLAMP, - STENCIL_OP_INVERT, - STENCIL_OP_INCREMENT_AND_WRAP, - STENCIL_OP_DECREMENT_AND_WRAP, - STENCIL_OP_MAX //not an actual operator, just the amount of operators :D + struct State { + SetState sets[MAX_UNIFORM_SETS]; + uint32_t set_count = 0; + RID pipeline; + RID pipeline_shader; + RDD::ShaderID pipeline_shader_driver_id; + uint32_t pipeline_shader_layout_hash = 0; + RID vertex_array; + RID index_array; + } state; + +#ifdef DEBUG_ENABLED + struct Validation { + bool active = true; // Means command buffer was not closed, so you can keep adding things. + // Actual render pass values. + uint32_t dynamic_state = 0; + VertexFormatID vertex_format = INVALID_ID; + uint32_t vertex_array_size = 0; + uint32_t vertex_max_instances_allowed = 0xFFFFFFFF; + bool index_buffer_uses_restart_indices = false; + uint32_t index_array_size = 0; + uint32_t index_array_max_index = 0; + uint32_t index_array_offset = 0; + Vector<uint32_t> set_formats; + Vector<bool> set_bound; + Vector<RID> set_rids; + // Last pipeline set values. + bool pipeline_active = false; + uint32_t pipeline_dynamic_state = 0; + VertexFormatID pipeline_vertex_format = INVALID_ID; + RID pipeline_shader; + bool pipeline_uses_restart_indices = false; + uint32_t pipeline_primitive_divisor = 0; + uint32_t pipeline_primitive_minimum = 0; + uint32_t pipeline_push_constant_size = 0; + bool pipeline_push_constant_supplied = false; + } validation; +#else + struct Validation { + uint32_t vertex_array_size = 0; + uint32_t index_array_size = 0; + uint32_t index_array_offset; + } validation; +#endif }; - enum LogicOperation { - LOGIC_OP_CLEAR, - LOGIC_OP_AND, - LOGIC_OP_AND_REVERSE, - LOGIC_OP_COPY, - LOGIC_OP_AND_INVERTED, - LOGIC_OP_NO_OP, - LOGIC_OP_XOR, - LOGIC_OP_OR, - LOGIC_OP_NOR, - LOGIC_OP_EQUIVALENT, - LOGIC_OP_INVERT, - LOGIC_OP_OR_REVERSE, - LOGIC_OP_COPY_INVERTED, - LOGIC_OP_OR_INVERTED, - LOGIC_OP_NAND, - LOGIC_OP_SET, - LOGIC_OP_MAX //not an actual operator, just the amount of operators :D - }; + DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. + uint32_t draw_list_subpass_count = 0; + uint32_t draw_list_count = 0; + RDD::RenderPassID draw_list_render_pass; + RDD::FramebufferID draw_list_vkframebuffer; +#ifdef DEBUG_ENABLED + FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; +#endif + uint32_t draw_list_current_subpass = 0; - enum BlendFactor { - BLEND_FACTOR_ZERO, - BLEND_FACTOR_ONE, - BLEND_FACTOR_SRC_COLOR, - BLEND_FACTOR_ONE_MINUS_SRC_COLOR, - BLEND_FACTOR_DST_COLOR, - BLEND_FACTOR_ONE_MINUS_DST_COLOR, - BLEND_FACTOR_SRC_ALPHA, - BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - BLEND_FACTOR_DST_ALPHA, - BLEND_FACTOR_ONE_MINUS_DST_ALPHA, - BLEND_FACTOR_CONSTANT_COLOR, - BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, - BLEND_FACTOR_CONSTANT_ALPHA, - BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, - BLEND_FACTOR_SRC_ALPHA_SATURATE, - BLEND_FACTOR_SRC1_COLOR, - BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, - BLEND_FACTOR_SRC1_ALPHA, - BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, - BLEND_FACTOR_MAX - }; + bool draw_list_split = false; + Vector<RID> draw_list_bound_textures; + Vector<RID> draw_list_storage_textures; + bool draw_list_unbind_color_textures = false; + bool draw_list_unbind_depth_textures = false; - enum BlendOperation { - BLEND_OP_ADD, - BLEND_OP_SUBTRACT, - BLEND_OP_REVERSE_SUBTRACT, - BLEND_OP_MINIMUM, - BLEND_OP_MAXIMUM, //yes this one is an actual operator - BLEND_OP_MAX //not an actual operator, just the amount of operators :D - }; + void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); + Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count); + Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, RDD::CommandBufferID p_command_buffer, RDD::CommandBufferType p_cmd_buffer_mode, const Vector<RID> &p_storage_textures, bool p_constrained_to_region); + _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); + Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); + void _draw_list_free(Rect2i *r_last_viewport = nullptr); - struct PipelineRasterizationState { - bool enable_depth_clamp; - bool discard_primitives; - bool wireframe; - PolygonCullMode cull_mode; - PolygonFrontFace front_face; - bool depth_bias_enabled; - float depth_bias_constant_factor; - float depth_bias_clamp; - float depth_bias_slope_factor; - float line_width; - uint32_t patch_control_points; - PipelineRasterizationState() { - enable_depth_clamp = false; - discard_primitives = false; - wireframe = false; - cull_mode = POLYGON_CULL_DISABLED; - front_face = POLYGON_FRONT_FACE_CLOCKWISE; - depth_bias_enabled = false; - depth_bias_constant_factor = 0; - depth_bias_clamp = 0; - depth_bias_slope_factor = 0; - line_width = 1.0; - patch_control_points = 1; - } - }; +public: + DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()); + DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); + Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - struct PipelineMultisampleState { - TextureSamples sample_count; - bool enable_sample_shading; - float min_sample_shading; - Vector<uint32_t> sample_mask; - bool enable_alpha_to_coverage; - bool enable_alpha_to_one; - - PipelineMultisampleState() { - sample_count = TEXTURE_SAMPLES_1; - enable_sample_shading = false; - min_sample_shading = 0; - enable_alpha_to_coverage = false; - enable_alpha_to_one = false; - } - }; + void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); + void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); + void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index); + void draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array); + void draw_list_bind_index_array(DrawListID p_list, RID p_index_array); + void draw_list_set_line_width(DrawListID p_list, float p_width); + void draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size); - struct PipelineDepthStencilState { - bool enable_depth_test; - bool enable_depth_write; - CompareOperator depth_compare_operator; - bool enable_depth_range; - float depth_range_min; - float depth_range_max; - bool enable_stencil; - - struct StencilOperationState { - StencilOperation fail; - StencilOperation pass; - StencilOperation depth_fail; - CompareOperator compare; - uint32_t compare_mask; - uint32_t write_mask; - uint32_t reference; - - StencilOperationState() { - fail = STENCIL_OP_ZERO; - pass = STENCIL_OP_ZERO; - depth_fail = STENCIL_OP_ZERO; - compare = COMPARE_OP_ALWAYS; - compare_mask = 0; - write_mask = 0; - reference = 0; - } - }; + void draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances = 1, uint32_t p_procedural_vertices = 0); - StencilOperationState front_op; - StencilOperationState back_op; - - PipelineDepthStencilState() { - enable_depth_test = false; - enable_depth_write = false; - depth_compare_operator = COMPARE_OP_ALWAYS; - enable_depth_range = false; - depth_range_min = 0; - depth_range_max = 0; - enable_stencil = false; - } - }; + void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); + void draw_list_disable_scissor(DrawListID p_list); - struct PipelineColorBlendState { - bool enable_logic_op; - LogicOperation logic_op; - struct Attachment { - bool enable_blend; - BlendFactor src_color_blend_factor; - BlendFactor dst_color_blend_factor; - BlendOperation color_blend_op; - BlendFactor src_alpha_blend_factor; - BlendFactor dst_alpha_blend_factor; - BlendOperation alpha_blend_op; - bool write_r; - bool write_g; - bool write_b; - bool write_a; - Attachment() { - enable_blend = false; - src_color_blend_factor = BLEND_FACTOR_ZERO; - dst_color_blend_factor = BLEND_FACTOR_ZERO; - color_blend_op = BLEND_OP_ADD; - src_alpha_blend_factor = BLEND_FACTOR_ZERO; - dst_alpha_blend_factor = BLEND_FACTOR_ZERO; - alpha_blend_op = BLEND_OP_ADD; - write_r = true; - write_g = true; - write_b = true; - write_a = true; - } - }; + uint32_t draw_list_get_current_pass(); + DrawListID draw_list_switch_to_next_pass(); + Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); - static PipelineColorBlendState create_disabled(int p_attachments = 1) { - PipelineColorBlendState bs; - for (int i = 0; i < p_attachments; i++) { - bs.attachments.push_back(Attachment()); - } - return bs; - } + void draw_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - static PipelineColorBlendState create_blend(int p_attachments = 1) { - PipelineColorBlendState bs; - for (int i = 0; i < p_attachments; i++) { - Attachment ba; - ba.enable_blend = true; - ba.src_color_blend_factor = BLEND_FACTOR_SRC_ALPHA; - ba.dst_color_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - ba.src_alpha_blend_factor = BLEND_FACTOR_SRC_ALPHA; - ba.dst_alpha_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - - bs.attachments.push_back(ba); - } - return bs; - } + /***********************/ + /**** COMPUTE LISTS ****/ + /***********************/ - Vector<Attachment> attachments; //one per render target texture - Color blend_constant; + typedef int64_t ComputeListID; - PipelineColorBlendState() { - enable_logic_op = false; - logic_op = LOGIC_OP_CLEAR; - } - }; +private: + struct ComputeList { + RDD::CommandBufferID command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. + + struct SetState { + uint32_t pipeline_expected_format = 0; + uint32_t uniform_set_format = 0; + RDD::UniformSetID uniform_set_driver_id; + RID uniform_set; + bool bound = false; + }; - enum PipelineDynamicStateFlags { - DYNAMIC_STATE_LINE_WIDTH = (1 << 0), - DYNAMIC_STATE_DEPTH_BIAS = (1 << 1), - DYNAMIC_STATE_BLEND_CONSTANTS = (1 << 2), - DYNAMIC_STATE_DEPTH_BOUNDS = (1 << 3), - DYNAMIC_STATE_STENCIL_COMPARE_MASK = (1 << 4), - DYNAMIC_STATE_STENCIL_WRITE_MASK = (1 << 5), - DYNAMIC_STATE_STENCIL_REFERENCE = (1 << 6), + struct State { + HashSet<Texture *> textures_to_sampled_layout; + SetState sets[MAX_UNIFORM_SETS]; + uint32_t set_count = 0; + RID pipeline; + RID pipeline_shader; + RDD::ShaderID pipeline_shader_driver_id; + uint32_t pipeline_shader_layout_hash = 0; + uint32_t local_group_size[3] = { 0, 0, 0 }; + bool allow_draw_overlap; + } state; + +#ifdef DEBUG_ENABLED + struct Validation { + bool active = true; // Means command buffer was not closed, so you can keep adding things. + Vector<uint32_t> set_formats; + Vector<bool> set_bound; + Vector<RID> set_rids; + // Last pipeline set values. + bool pipeline_active = false; + RID pipeline_shader; + uint32_t invalid_set_from = 0; + uint32_t pipeline_push_constant_size = 0; + bool pipeline_push_constant_supplied = false; + } validation; +#endif }; - virtual bool render_pipeline_is_valid(RID p_pipeline) = 0; - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()) = 0; + ComputeList *compute_list = nullptr; - /**************************/ - /**** COMPUTE PIPELINE ****/ - /**************************/ + void _compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, BitField<RDD::PipelineStageBits> p_stages, BitField<RDD::BarrierAccessBits> p_access); - virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()) = 0; - virtual bool compute_pipeline_is_valid(RID p_pipeline) = 0; +public: + ComputeListID compute_list_begin(bool p_allow_draw_overlap = false); + void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline); + void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index); + void compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size); + void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups); + void compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads); + void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset); + void compute_list_add_barrier(ComputeListID p_list); - /****************/ - /**** SCREEN ****/ - /****************/ + void compute_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual int screen_get_width(DisplayServer::WindowID p_screen = 0) const = 0; - virtual int screen_get_height(DisplayServer::WindowID p_screen = 0) const = 0; - virtual FramebufferFormatID screen_get_framebuffer_format() const = 0; + void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS); + void full_barrier(); - /********************/ - /**** DRAW LISTS ****/ - /********************/ + /**************************/ + /**** FRAME MANAGEMENT ****/ + /**************************/ - enum InitialAction { - INITIAL_ACTION_CLEAR, // Start rendering and clear the whole framebuffer. - INITIAL_ACTION_CLEAR_REGION, // Start rendering and clear the framebuffer in the specified region. - INITIAL_ACTION_CLEAR_REGION_CONTINUE, // Continue rendering and clear the framebuffer in the specified region. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. - INITIAL_ACTION_KEEP, // Start rendering, but keep attached color texture contents. If the framebuffer was previously used to read in a shader, this will automatically insert a layout transition. - INITIAL_ACTION_DROP, // Start rendering, ignore what is there; write above it. In general, this is the fastest option when you will be writing every single pixel and you don't need a clear color. - INITIAL_ACTION_CONTINUE, // Continue rendering. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. - INITIAL_ACTION_MAX - }; + // This is the frame structure. There are normally + // 3 of these (used for triple buffering), or 2 + // (double buffering). They are cycled constantly. + // + // It contains two command buffers, one that is + // used internally for setting up (creating stuff) + // and another used mostly for drawing. + // + // They also contains a list of things that need + // to be disposed of when deleted, which can't + // happen immediately due to the asynchronous + // nature of the GPU. They will get deleted + // when the frame is cycled. - enum FinalAction { - FINAL_ACTION_READ, // Store the texture for reading and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). - FINAL_ACTION_DISCARD, // Discard the texture data and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). - FINAL_ACTION_CONTINUE, // Store the texture and continue for further processing. Similar to `FINAL_ACTION_READ`, but does not make the texture read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit. - FINAL_ACTION_MAX - }; +private: + struct Frame { + // List in usage order, from last to free to first to free. + List<Buffer> buffers_to_dispose_of; + List<Texture> textures_to_dispose_of; + List<Framebuffer> framebuffers_to_dispose_of; + List<RDD::SamplerID> samplers_to_dispose_of; + List<Shader> shaders_to_dispose_of; + List<UniformSet> uniform_sets_to_dispose_of; + List<RenderPipeline> render_pipelines_to_dispose_of; + List<ComputePipeline> compute_pipelines_to_dispose_of; + + RDD::CommandPoolID command_pool; + // Used for filling up newly created buffers with data provided on creation. + // Primarily intended to be accessed by worker threads. + // Ideally this cmd buffer should use an async transfer queue. + RDD::CommandBufferID setup_command_buffer; // Used at the beginning of every frame for set-up. + // The main cmd buffer for drawing and compute. + // Primarily intended to be used by the main thread to do most stuff. + RDD::CommandBufferID draw_command_buffer; // Used at the beginning of every frame for set-up. + + struct Timestamp { + String description; + uint64_t value = 0; + }; - typedef int64_t DrawListID; + RDD::QueryPoolID timestamp_pool; - virtual DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()) = 0; - virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()) = 0; - virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()) = 0; + TightLocalVector<String> timestamp_names; + TightLocalVector<uint64_t> timestamp_cpu_values; + uint32_t timestamp_count = 0; + TightLocalVector<String> timestamp_result_names; + TightLocalVector<uint64_t> timestamp_cpu_result_values; + TightLocalVector<uint64_t> timestamp_result_values; + uint32_t timestamp_result_count = 0; + uint64_t index = 0; + }; - virtual void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color) = 0; - virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) = 0; - virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) = 0; - virtual void draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) = 0; - virtual void draw_list_bind_index_array(DrawListID p_list, RID p_index_array) = 0; - virtual void draw_list_set_line_width(DrawListID p_list, float p_width) = 0; - virtual void draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size) = 0; + uint32_t max_timestamp_query_elements = 0; - virtual void draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances = 1, uint32_t p_procedural_vertices = 0) = 0; + TightLocalVector<Frame> frames; // Frames available, for main device they are cycled (usually 3), for local devices only 1. + int frame = 0; // Current frame. + int frame_count = 0; // Total amount of frames. + uint64_t frames_drawn = 0; + RID local_device; + bool local_device_processing = false; - virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0; - virtual void draw_list_disable_scissor(DrawListID p_list) = 0; + void _free_pending_resources(int p_frame); - virtual uint32_t draw_list_get_current_pass() = 0; - virtual DrawListID draw_list_switch_to_next_pass() = 0; - virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) = 0; + ApiContextRD *context = nullptr; - virtual void draw_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; + uint64_t texture_memory = 0; + uint64_t buffer_memory = 0; - /***********************/ - /**** COMPUTE LISTS ****/ - /***********************/ + void _free_internal(RID p_id); + void _flush(bool p_current_frame); - typedef int64_t ComputeListID; + bool screen_prepared = false; - virtual ComputeListID compute_list_begin(bool p_allow_draw_overlap = false) = 0; - virtual void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) = 0; - virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) = 0; - virtual void compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) = 0; - virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) = 0; - virtual void compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads) = 0; - virtual void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) = 0; - virtual void compute_list_add_barrier(ComputeListID p_list) = 0; + template <class T> + void _free_rids(T &p_owner, const char *p_type); - virtual void compute_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; + void _finalize_command_bufers(); + void _begin_frame(); - virtual void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS) = 0; - virtual void full_barrier() = 0; +#ifdef DEV_ENABLED + HashMap<RID, String> resource_names; +#endif - /***************/ - /**** FREE! ****/ - /***************/ +public: + void initialize(ApiContextRD *p_context, bool p_local_device = false); + void finalize(); - virtual void free(RID p_id) = 0; + void free(RID p_id); /****************/ /**** Timing ****/ /****************/ - virtual void capture_timestamp(const String &p_name) = 0; - virtual uint32_t get_captured_timestamps_count() const = 0; - virtual uint64_t get_captured_timestamps_frame() const = 0; - virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const = 0; - virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const = 0; - virtual String get_captured_timestamp_name(uint32_t p_index) const = 0; + void capture_timestamp(const String &p_name); + uint32_t get_captured_timestamps_count() const; + uint64_t get_captured_timestamps_frame() const; + uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const; + uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const; + String get_captured_timestamp_name(uint32_t p_index) const; /****************/ /**** LIMITS ****/ /****************/ - enum Limit { - LIMIT_MAX_BOUND_UNIFORM_SETS, - LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS, - LIMIT_MAX_TEXTURES_PER_UNIFORM_SET, - LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET, - LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET, - LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET, - LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET, - LIMIT_MAX_DRAW_INDEXED_INDEX, - LIMIT_MAX_FRAMEBUFFER_HEIGHT, - LIMIT_MAX_FRAMEBUFFER_WIDTH, - LIMIT_MAX_TEXTURE_ARRAY_LAYERS, - LIMIT_MAX_TEXTURE_SIZE_1D, - LIMIT_MAX_TEXTURE_SIZE_2D, - LIMIT_MAX_TEXTURE_SIZE_3D, - LIMIT_MAX_TEXTURE_SIZE_CUBE, - LIMIT_MAX_TEXTURES_PER_SHADER_STAGE, - LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE, - LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE, - LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE, - LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE, - LIMIT_MAX_PUSH_CONSTANT_SIZE, - LIMIT_MAX_UNIFORM_BUFFER_SIZE, - LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET, - LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES, - LIMIT_MAX_VERTEX_INPUT_BINDINGS, - LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE, - LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT, - LIMIT_MAX_COMPUTE_SHARED_MEMORY_SIZE, - LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X, - LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y, - LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z, - LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS, - LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X, - LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y, - LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z, - LIMIT_MAX_VIEWPORT_DIMENSIONS_X, - LIMIT_MAX_VIEWPORT_DIMENSIONS_Y, - LIMIT_SUBGROUP_SIZE, - LIMIT_SUBGROUP_MIN_SIZE, - LIMIT_SUBGROUP_MAX_SIZE, - LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc. - LIMIT_SUBGROUP_OPERATIONS, - LIMIT_VRS_TEXEL_WIDTH, - LIMIT_VRS_TEXEL_HEIGHT, - }; - - virtual uint64_t limit_get(Limit p_limit) const = 0; + uint64_t limit_get(Limit p_limit) const; //methods below not exposed, used by RenderingDeviceRD - virtual void prepare_screen_for_drawing() = 0; + void prepare_screen_for_drawing(); - virtual void swap_buffers() = 0; + void swap_buffers(); - virtual uint32_t get_frame_delay() const = 0; + uint32_t get_frame_delay() const; - virtual void submit() = 0; - virtual void sync() = 0; + void submit(); + void sync(); enum MemoryType { MEMORY_TEXTURES, @@ -1312,33 +1304,34 @@ public: MEMORY_TOTAL }; - virtual uint64_t get_memory_usage(MemoryType p_type) const = 0; + uint64_t get_memory_usage(MemoryType p_type) const; - virtual RenderingDevice *create_local_device() = 0; + RenderingDevice *create_local_device(); - virtual void set_resource_name(RID p_id, const String p_name) = 0; + void set_resource_name(RID p_id, const String &p_name); - virtual void draw_command_begin_label(String p_label_name, const Color p_color = Color(1, 1, 1, 1)) = 0; - virtual void draw_command_insert_label(String p_label_name, const Color p_color = Color(1, 1, 1, 1)) = 0; - virtual void draw_command_end_label() = 0; + void draw_command_begin_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); + void draw_command_insert_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); + void draw_command_end_label(); - virtual String get_device_vendor_name() const = 0; - virtual String get_device_name() const = 0; - virtual RenderingDevice::DeviceType get_device_type() const = 0; - virtual String get_device_api_version() const = 0; - virtual String get_device_pipeline_cache_uuid() const = 0; + String get_device_vendor_name() const; + String get_device_name() const; + DeviceType get_device_type() const; + String get_device_api_version() const; + String get_device_pipeline_cache_uuid() const; - virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0) = 0; + uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); static RenderingDevice *get_singleton(); - RenderingDevice(); -protected: - static const char *shader_stage_names[RenderingDevice::SHADER_STAGE_MAX]; + RenderingDevice(); + ~RenderingDevice(); - static const uint32_t MAX_UNIFORM_SETS = 16; +private: + /*****************/ + /**** BINDERS ****/ + /*****************/ - //binders to script API RID _texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data = Array()); RID _texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture); RID _texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); @@ -1348,7 +1341,9 @@ protected: FramebufferFormatID _framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count); RID _framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); RID _framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + RID _sampler_create(const Ref<RDSamplerState> &p_state); + VertexFormatID _vertex_format_create(const TypedArray<RDVertexAttribute> &p_vertex_formats); RID _vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const TypedArray<RID> &p_src_buffers, const Vector<int64_t> &p_offsets = Vector<int64_t>()); @@ -1358,7 +1353,7 @@ protected: RID _uniform_set_create(const TypedArray<RDUniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); - Error _buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error _buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants); RID _compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants); @@ -1368,46 +1363,6 @@ protected: void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); - - struct SpirvReflectionData { - BitField<ShaderStage> stages_mask; - uint64_t vertex_input_mask; - uint32_t fragment_output_mask; - bool is_compute; - uint32_t compute_local_size[3]; - uint32_t push_constant_size; - BitField<ShaderStage> push_constant_stages_mask; - - struct Uniform { - UniformType type; - uint32_t binding; - BitField<ShaderStage> stages_mask; - uint32_t length; // Size of arrays (in total elements), or ubos (in bytes * total elements). - bool writable; - }; - Vector<Vector<Uniform>> uniforms; - - struct SpecializationConstant { - PipelineSpecializationConstantType type; - uint32_t constant_id; - union { - uint32_t int_value; - float float_value; - bool bool_value; - }; - BitField<ShaderStage> stages_mask; - }; - Vector<SpecializationConstant> specialization_constants; - }; - - Error _reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, SpirvReflectionData &r_reflection_data); - -#ifndef DISABLE_DEPRECATED - BitField<BarrierMask> _convert_barrier_mask_81356(BitField<BarrierMask> p_old_barrier); - void _draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); - void _compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); - void _barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to); -#endif }; VARIANT_ENUM_CAST(RenderingDevice::DeviceType) diff --git a/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h index 737a874abc..5f21207579 100644 --- a/servers/rendering/rendering_device_binds.h +++ b/servers/rendering/rendering_device_binds.h @@ -289,7 +289,7 @@ public: if (bytecode[i].size()) { RD::ShaderStageSPIRVData stage; stage.shader_stage = RD::ShaderStage(i); - stage.spir_v = bytecode[i]; + stage.spirv = bytecode[i]; stages.push_back(stage); } } diff --git a/servers/rendering/rendering_device_commons.cpp b/servers/rendering/rendering_device_commons.cpp new file mode 100644 index 0000000000..c8b7980633 --- /dev/null +++ b/servers/rendering/rendering_device_commons.cpp @@ -0,0 +1,912 @@ +/**************************************************************************/ +/* rendering_device_commons.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "rendering_device_commons.h" + +/*****************/ +/**** GENERIC ****/ +/*****************/ + +const char *const RenderingDeviceCommons::FORMAT_NAMES[DATA_FORMAT_MAX] = { + "R4G4_Unorm_Pack8", + "R4G4B4A4_Unorm_Pack16", + "B4G4R4A4_Unorm_Pack16", + "R5G6B5_Unorm_Pack16", + "B5G6R5_Unorm_Pack16", + "R5G5B5A1_Unorm_Pack16", + "B5G5R5A1_Unorm_Pack16", + "A1R5G5B5_Unorm_Pack16", + "R8_Unorm", + "R8_Snorm", + "R8_Uscaled", + "R8_Sscaled", + "R8_Uint", + "R8_Sint", + "R8_Srgb", + "R8G8_Unorm", + "R8G8_Snorm", + "R8G8_Uscaled", + "R8G8_Sscaled", + "R8G8_Uint", + "R8G8_Sint", + "R8G8_Srgb", + "R8G8B8_Unorm", + "R8G8B8_Snorm", + "R8G8B8_Uscaled", + "R8G8B8_Sscaled", + "R8G8B8_Uint", + "R8G8B8_Sint", + "R8G8B8_Srgb", + "B8G8R8_Unorm", + "B8G8R8_Snorm", + "B8G8R8_Uscaled", + "B8G8R8_Sscaled", + "B8G8R8_Uint", + "B8G8R8_Sint", + "B8G8R8_Srgb", + "R8G8B8A8_Unorm", + "R8G8B8A8_Snorm", + "R8G8B8A8_Uscaled", + "R8G8B8A8_Sscaled", + "R8G8B8A8_Uint", + "R8G8B8A8_Sint", + "R8G8B8A8_Srgb", + "B8G8R8A8_Unorm", + "B8G8R8A8_Snorm", + "B8G8R8A8_Uscaled", + "B8G8R8A8_Sscaled", + "B8G8R8A8_Uint", + "B8G8R8A8_Sint", + "B8G8R8A8_Srgb", + "A8B8G8R8_Unorm_Pack32", + "A8B8G8R8_Snorm_Pack32", + "A8B8G8R8_Uscaled_Pack32", + "A8B8G8R8_Sscaled_Pack32", + "A8B8G8R8_Uint_Pack32", + "A8B8G8R8_Sint_Pack32", + "A8B8G8R8_Srgb_Pack32", + "A2R10G10B10_Unorm_Pack32", + "A2R10G10B10_Snorm_Pack32", + "A2R10G10B10_Uscaled_Pack32", + "A2R10G10B10_Sscaled_Pack32", + "A2R10G10B10_Uint_Pack32", + "A2R10G10B10_Sint_Pack32", + "A2B10G10R10_Unorm_Pack32", + "A2B10G10R10_Snorm_Pack32", + "A2B10G10R10_Uscaled_Pack32", + "A2B10G10R10_Sscaled_Pack32", + "A2B10G10R10_Uint_Pack32", + "A2B10G10R10_Sint_Pack32", + "R16_Unorm", + "R16_Snorm", + "R16_Uscaled", + "R16_Sscaled", + "R16_Uint", + "R16_Sint", + "R16_Sfloat", + "R16G16_Unorm", + "R16G16_Snorm", + "R16G16_Uscaled", + "R16G16_Sscaled", + "R16G16_Uint", + "R16G16_Sint", + "R16G16_Sfloat", + "R16G16B16_Unorm", + "R16G16B16_Snorm", + "R16G16B16_Uscaled", + "R16G16B16_Sscaled", + "R16G16B16_Uint", + "R16G16B16_Sint", + "R16G16B16_Sfloat", + "R16G16B16A16_Unorm", + "R16G16B16A16_Snorm", + "R16G16B16A16_Uscaled", + "R16G16B16A16_Sscaled", + "R16G16B16A16_Uint", + "R16G16B16A16_Sint", + "R16G16B16A16_Sfloat", + "R32_Uint", + "R32_Sint", + "R32_Sfloat", + "R32G32_Uint", + "R32G32_Sint", + "R32G32_Sfloat", + "R32G32B32_Uint", + "R32G32B32_Sint", + "R32G32B32_Sfloat", + "R32G32B32A32_Uint", + "R32G32B32A32_Sint", + "R32G32B32A32_Sfloat", + "R64_Uint", + "R64_Sint", + "R64_Sfloat", + "R64G64_Uint", + "R64G64_Sint", + "R64G64_Sfloat", + "R64G64B64_Uint", + "R64G64B64_Sint", + "R64G64B64_Sfloat", + "R64G64B64A64_Uint", + "R64G64B64A64_Sint", + "R64G64B64A64_Sfloat", + "B10G11R11_Ufloat_Pack32", + "E5B9G9R9_Ufloat_Pack32", + "D16_Unorm", + "X8_D24_Unorm_Pack32", + "D32_Sfloat", + "S8_Uint", + "D16_Unorm_S8_Uint", + "D24_Unorm_S8_Uint", + "D32_Sfloat_S8_Uint", + "Bc1_Rgb_Unorm_Block", + "Bc1_Rgb_Srgb_Block", + "Bc1_Rgba_Unorm_Block", + "Bc1_Rgba_Srgb_Block", + "Bc2_Unorm_Block", + "Bc2_Srgb_Block", + "Bc3_Unorm_Block", + "Bc3_Srgb_Block", + "Bc4_Unorm_Block", + "Bc4_Snorm_Block", + "Bc5_Unorm_Block", + "Bc5_Snorm_Block", + "Bc6H_Ufloat_Block", + "Bc6H_Sfloat_Block", + "Bc7_Unorm_Block", + "Bc7_Srgb_Block", + "Etc2_R8G8B8_Unorm_Block", + "Etc2_R8G8B8_Srgb_Block", + "Etc2_R8G8B8A1_Unorm_Block", + "Etc2_R8G8B8A1_Srgb_Block", + "Etc2_R8G8B8A8_Unorm_Block", + "Etc2_R8G8B8A8_Srgb_Block", + "Eac_R11_Unorm_Block", + "Eac_R11_Snorm_Block", + "Eac_R11G11_Unorm_Block", + "Eac_R11G11_Snorm_Block", + "Astc_4X4_Unorm_Block", + "Astc_4X4_Srgb_Block", + "Astc_5X4_Unorm_Block", + "Astc_5X4_Srgb_Block", + "Astc_5X5_Unorm_Block", + "Astc_5X5_Srgb_Block", + "Astc_6X5_Unorm_Block", + "Astc_6X5_Srgb_Block", + "Astc_6X6_Unorm_Block", + "Astc_6X6_Srgb_Block", + "Astc_8X5_Unorm_Block", + "Astc_8X5_Srgb_Block", + "Astc_8X6_Unorm_Block", + "Astc_8X6_Srgb_Block", + "Astc_8X8_Unorm_Block", + "Astc_8X8_Srgb_Block", + "Astc_10X5_Unorm_Block", + "Astc_10X5_Srgb_Block", + "Astc_10X6_Unorm_Block", + "Astc_10X6_Srgb_Block", + "Astc_10X8_Unorm_Block", + "Astc_10X8_Srgb_Block", + "Astc_10X10_Unorm_Block", + "Astc_10X10_Srgb_Block", + "Astc_12X10_Unorm_Block", + "Astc_12X10_Srgb_Block", + "Astc_12X12_Unorm_Block", + "Astc_12X12_Srgb_Block", + "G8B8G8R8_422_Unorm", + "B8G8R8G8_422_Unorm", + "G8_B8_R8_3Plane_420_Unorm", + "G8_B8R8_2Plane_420_Unorm", + "G8_B8_R8_3Plane_422_Unorm", + "G8_B8R8_2Plane_422_Unorm", + "G8_B8_R8_3Plane_444_Unorm", + "R10X6_Unorm_Pack16", + "R10X6G10X6_Unorm_2Pack16", + "R10X6G10X6B10X6A10X6_Unorm_4Pack16", + "G10X6B10X6G10X6R10X6_422_Unorm_4Pack16", + "B10X6G10X6R10X6G10X6_422_Unorm_4Pack16", + "G10X6_B10X6_R10X6_3Plane_420_Unorm_3Pack16", + "G10X6_B10X6R10X6_2Plane_420_Unorm_3Pack16", + "G10X6_B10X6_R10X6_3Plane_422_Unorm_3Pack16", + "G10X6_B10X6R10X6_2Plane_422_Unorm_3Pack16", + "G10X6_B10X6_R10X6_3Plane_444_Unorm_3Pack16", + "R12X4_Unorm_Pack16", + "R12X4G12X4_Unorm_2Pack16", + "R12X4G12X4B12X4A12X4_Unorm_4Pack16", + "G12X4B12X4G12X4R12X4_422_Unorm_4Pack16", + "B12X4G12X4R12X4G12X4_422_Unorm_4Pack16", + "G12X4_B12X4_R12X4_3Plane_420_Unorm_3Pack16", + "G12X4_B12X4R12X4_2Plane_420_Unorm_3Pack16", + "G12X4_B12X4_R12X4_3Plane_422_Unorm_3Pack16", + "G12X4_B12X4R12X4_2Plane_422_Unorm_3Pack16", + "G12X4_B12X4_R12X4_3Plane_444_Unorm_3Pack16", + "G16B16G16R16_422_Unorm", + "B16G16R16G16_422_Unorm", + "G16_B16_R16_3Plane_420_Unorm", + "G16_B16R16_2Plane_420_Unorm", + "G16_B16_R16_3Plane_422_Unorm", + "G16_B16R16_2Plane_422_Unorm", + "G16_B16_R16_3Plane_444_Unorm", +}; + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +const uint32_t RenderingDeviceCommons::TEXTURE_SAMPLES_COUNT[TEXTURE_SAMPLES_MAX] = { 1, 2, 4, 8, 16, 32, 64 }; + +uint32_t RenderingDeviceCommons::get_image_format_pixel_size(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_R4G4_UNORM_PACK8: + return 1; + case DATA_FORMAT_R4G4B4A4_UNORM_PACK16: + case DATA_FORMAT_B4G4R4A4_UNORM_PACK16: + case DATA_FORMAT_R5G6B5_UNORM_PACK16: + case DATA_FORMAT_B5G6R5_UNORM_PACK16: + case DATA_FORMAT_R5G5B5A1_UNORM_PACK16: + case DATA_FORMAT_B5G5R5A1_UNORM_PACK16: + case DATA_FORMAT_A1R5G5B5_UNORM_PACK16: + return 2; + case DATA_FORMAT_R8_UNORM: + case DATA_FORMAT_R8_SNORM: + case DATA_FORMAT_R8_USCALED: + case DATA_FORMAT_R8_SSCALED: + case DATA_FORMAT_R8_UINT: + case DATA_FORMAT_R8_SINT: + case DATA_FORMAT_R8_SRGB: + return 1; + case DATA_FORMAT_R8G8_UNORM: + case DATA_FORMAT_R8G8_SNORM: + case DATA_FORMAT_R8G8_USCALED: + case DATA_FORMAT_R8G8_SSCALED: + case DATA_FORMAT_R8G8_UINT: + case DATA_FORMAT_R8G8_SINT: + case DATA_FORMAT_R8G8_SRGB: + return 2; + case DATA_FORMAT_R8G8B8_UNORM: + case DATA_FORMAT_R8G8B8_SNORM: + case DATA_FORMAT_R8G8B8_USCALED: + case DATA_FORMAT_R8G8B8_SSCALED: + case DATA_FORMAT_R8G8B8_UINT: + case DATA_FORMAT_R8G8B8_SINT: + case DATA_FORMAT_R8G8B8_SRGB: + case DATA_FORMAT_B8G8R8_UNORM: + case DATA_FORMAT_B8G8R8_SNORM: + case DATA_FORMAT_B8G8R8_USCALED: + case DATA_FORMAT_B8G8R8_SSCALED: + case DATA_FORMAT_B8G8R8_UINT: + case DATA_FORMAT_B8G8R8_SINT: + case DATA_FORMAT_B8G8R8_SRGB: + return 3; + case DATA_FORMAT_R8G8B8A8_UNORM: + case DATA_FORMAT_R8G8B8A8_SNORM: + case DATA_FORMAT_R8G8B8A8_USCALED: + case DATA_FORMAT_R8G8B8A8_SSCALED: + case DATA_FORMAT_R8G8B8A8_UINT: + case DATA_FORMAT_R8G8B8A8_SINT: + case DATA_FORMAT_R8G8B8A8_SRGB: + case DATA_FORMAT_B8G8R8A8_UNORM: + case DATA_FORMAT_B8G8R8A8_SNORM: + case DATA_FORMAT_B8G8R8A8_USCALED: + case DATA_FORMAT_B8G8R8A8_SSCALED: + case DATA_FORMAT_B8G8R8A8_UINT: + case DATA_FORMAT_B8G8R8A8_SINT: + case DATA_FORMAT_B8G8R8A8_SRGB: + return 4; + case DATA_FORMAT_A8B8G8R8_UNORM_PACK32: + case DATA_FORMAT_A8B8G8R8_SNORM_PACK32: + case DATA_FORMAT_A8B8G8R8_USCALED_PACK32: + case DATA_FORMAT_A8B8G8R8_SSCALED_PACK32: + case DATA_FORMAT_A8B8G8R8_UINT_PACK32: + case DATA_FORMAT_A8B8G8R8_SINT_PACK32: + case DATA_FORMAT_A8B8G8R8_SRGB_PACK32: + case DATA_FORMAT_A2R10G10B10_UNORM_PACK32: + case DATA_FORMAT_A2R10G10B10_SNORM_PACK32: + case DATA_FORMAT_A2R10G10B10_USCALED_PACK32: + case DATA_FORMAT_A2R10G10B10_SSCALED_PACK32: + case DATA_FORMAT_A2R10G10B10_UINT_PACK32: + case DATA_FORMAT_A2R10G10B10_SINT_PACK32: + case DATA_FORMAT_A2B10G10R10_UNORM_PACK32: + case DATA_FORMAT_A2B10G10R10_SNORM_PACK32: + case DATA_FORMAT_A2B10G10R10_USCALED_PACK32: + case DATA_FORMAT_A2B10G10R10_SSCALED_PACK32: + case DATA_FORMAT_A2B10G10R10_UINT_PACK32: + case DATA_FORMAT_A2B10G10R10_SINT_PACK32: + return 4; + case DATA_FORMAT_R16_UNORM: + case DATA_FORMAT_R16_SNORM: + case DATA_FORMAT_R16_USCALED: + case DATA_FORMAT_R16_SSCALED: + case DATA_FORMAT_R16_UINT: + case DATA_FORMAT_R16_SINT: + case DATA_FORMAT_R16_SFLOAT: + return 2; + case DATA_FORMAT_R16G16_UNORM: + case DATA_FORMAT_R16G16_SNORM: + case DATA_FORMAT_R16G16_USCALED: + case DATA_FORMAT_R16G16_SSCALED: + case DATA_FORMAT_R16G16_UINT: + case DATA_FORMAT_R16G16_SINT: + case DATA_FORMAT_R16G16_SFLOAT: + return 4; + case DATA_FORMAT_R16G16B16_UNORM: + case DATA_FORMAT_R16G16B16_SNORM: + case DATA_FORMAT_R16G16B16_USCALED: + case DATA_FORMAT_R16G16B16_SSCALED: + case DATA_FORMAT_R16G16B16_UINT: + case DATA_FORMAT_R16G16B16_SINT: + case DATA_FORMAT_R16G16B16_SFLOAT: + return 6; + case DATA_FORMAT_R16G16B16A16_UNORM: + case DATA_FORMAT_R16G16B16A16_SNORM: + case DATA_FORMAT_R16G16B16A16_USCALED: + case DATA_FORMAT_R16G16B16A16_SSCALED: + case DATA_FORMAT_R16G16B16A16_UINT: + case DATA_FORMAT_R16G16B16A16_SINT: + case DATA_FORMAT_R16G16B16A16_SFLOAT: + return 8; + case DATA_FORMAT_R32_UINT: + case DATA_FORMAT_R32_SINT: + case DATA_FORMAT_R32_SFLOAT: + return 4; + case DATA_FORMAT_R32G32_UINT: + case DATA_FORMAT_R32G32_SINT: + case DATA_FORMAT_R32G32_SFLOAT: + return 8; + case DATA_FORMAT_R32G32B32_UINT: + case DATA_FORMAT_R32G32B32_SINT: + case DATA_FORMAT_R32G32B32_SFLOAT: + return 12; + case DATA_FORMAT_R32G32B32A32_UINT: + case DATA_FORMAT_R32G32B32A32_SINT: + case DATA_FORMAT_R32G32B32A32_SFLOAT: + return 16; + case DATA_FORMAT_R64_UINT: + case DATA_FORMAT_R64_SINT: + case DATA_FORMAT_R64_SFLOAT: + return 8; + case DATA_FORMAT_R64G64_UINT: + case DATA_FORMAT_R64G64_SINT: + case DATA_FORMAT_R64G64_SFLOAT: + return 16; + case DATA_FORMAT_R64G64B64_UINT: + case DATA_FORMAT_R64G64B64_SINT: + case DATA_FORMAT_R64G64B64_SFLOAT: + return 24; + case DATA_FORMAT_R64G64B64A64_UINT: + case DATA_FORMAT_R64G64B64A64_SINT: + case DATA_FORMAT_R64G64B64A64_SFLOAT: + return 32; + case DATA_FORMAT_B10G11R11_UFLOAT_PACK32: + case DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32: + return 4; + case DATA_FORMAT_D16_UNORM: + return 2; + case DATA_FORMAT_X8_D24_UNORM_PACK32: + return 4; + case DATA_FORMAT_D32_SFLOAT: + return 4; + case DATA_FORMAT_S8_UINT: + return 1; + case DATA_FORMAT_D16_UNORM_S8_UINT: + return 4; + case DATA_FORMAT_D24_UNORM_S8_UINT: + return 4; + case DATA_FORMAT_D32_SFLOAT_S8_UINT: + return 5; // ? + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: + return 1; + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + return 1; + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: + return 1; + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: + return 1; + case DATA_FORMAT_G8B8G8R8_422_UNORM: + case DATA_FORMAT_B8G8R8G8_422_UNORM: + return 4; + case DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + case DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM: + case DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM: + case DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM: + case DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM: + return 4; + case DATA_FORMAT_R10X6_UNORM_PACK16: + case DATA_FORMAT_R10X6G10X6_UNORM_2PACK16: + case DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: + case DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: + case DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: + case DATA_FORMAT_R12X4_UNORM_PACK16: + case DATA_FORMAT_R12X4G12X4_UNORM_2PACK16: + case DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: + case DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: + case DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: + return 2; + case DATA_FORMAT_G16B16G16R16_422_UNORM: + case DATA_FORMAT_B16G16R16G16_422_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM: + case DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM: + case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM: + return 8; + default: { + ERR_PRINT("Format not handled, bug"); + } + } + + return 1; +} + +// https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.pdf +void RenderingDeviceCommons::get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h) { + switch (p_format) { + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. + case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: { + r_w = 4; + r_h = 4; + } break; + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: // Unsupported + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: { + r_w = 4; + r_h = 4; + } break; + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: { + r_w = 8; + r_h = 8; + } break; + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: // Unsupported + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: + r_w = 4; + r_h = 4; + return; + default: { + r_w = 1; + r_h = 1; + } + } +} + +uint32_t RenderingDeviceCommons::get_compressed_image_format_block_byte_size(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + return 8; + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: + return 16; + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: + return 16; + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + return 8; + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: + return 16; + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: + return 16; + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: + return 16; + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + return 8; + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + return 8; + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + return 16; + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + return 8; + case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: + return 16; + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. + case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: + return 16; + default: { + } + } + return 1; +} + +uint32_t RenderingDeviceCommons::get_compressed_image_format_pixel_rshift(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: // These formats are half byte size, so rshift is 1. + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + return 1; + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: { + return 2; + } + default: { + } + } + + return 0; +} + +uint32_t RenderingDeviceCommons::get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw, uint32_t *r_blockh, uint32_t *r_depth) { + ERR_FAIL_COND_V(p_mipmaps == 0, 0); + uint32_t w = p_width; + uint32_t h = p_height; + uint32_t d = p_depth; + + uint32_t size = 0; + + uint32_t pixel_size = get_image_format_pixel_size(p_format); + uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(p_format); + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(p_format, blockw, blockh); + + for (uint32_t i = 0; i < p_mipmaps; i++) { + uint32_t bw = w % blockw != 0 ? w + (blockw - w % blockw) : w; + uint32_t bh = h % blockh != 0 ? h + (blockh - h % blockh) : h; + + uint32_t s = bw * bh; + + s *= pixel_size; + s >>= pixel_rshift; + size += s * d; + if (r_blockw) { + *r_blockw = bw; + } + if (r_blockh) { + *r_blockh = bh; + } + if (r_depth) { + *r_depth = d; + } + w = MAX(blockw, w >> 1); + h = MAX(blockh, h >> 1); + d = MAX(1u, d >> 1); + } + + return size; +} + +uint32_t RenderingDeviceCommons::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { + // Formats and block size don't really matter here since they can all go down to 1px (even if block is larger). + uint32_t w = p_width; + uint32_t h = p_height; + uint32_t d = p_depth; + + uint32_t mipmaps = 1; + + while (true) { + if (w == 1 && h == 1 && d == 1) { + break; + } + + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); + + mipmaps++; + } + + return mipmaps; +} + +bool RenderingDeviceCommons::format_has_stencil(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_S8_UINT: + case DATA_FORMAT_D16_UNORM_S8_UINT: + case DATA_FORMAT_D24_UNORM_S8_UINT: + case DATA_FORMAT_D32_SFLOAT_S8_UINT: { + return true; + } + default: { + } + } + return false; +} + +uint32_t RenderingDeviceCommons::format_get_plane_count(DataFormat p_format) { + uint32_t planes = 1; + switch (p_format) { + case DATA_FORMAT_D16_UNORM_S8_UINT: + case DATA_FORMAT_D24_UNORM_S8_UINT: + case DATA_FORMAT_D32_SFLOAT_S8_UINT: { + planes = 2; + break; + } + default: { + } + } + DEV_ASSERT(planes <= MAX_IMAGE_FORMAT_PLANES); + return planes; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +const Color RenderingDeviceCommons::SAMPLER_BORDER_COLOR_VALUE[SAMPLER_BORDER_COLOR_MAX] = { + Color(0, 0, 0, 0), + Color(0, 0, 0, 0), + Color(0, 0, 0, 1), + Color(0, 0, 0, 1), + Color(1, 1, 1, 1), + Color(1, 1, 1, 1), +}; + +/**********************/ +/**** VERTEX ARRAY ****/ +/**********************/ + +uint32_t RenderingDeviceCommons::get_format_vertex_size(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_R8_UNORM: + case DATA_FORMAT_R8_SNORM: + case DATA_FORMAT_R8_UINT: + case DATA_FORMAT_R8_SINT: + case DATA_FORMAT_R8G8_UNORM: + case DATA_FORMAT_R8G8_SNORM: + case DATA_FORMAT_R8G8_UINT: + case DATA_FORMAT_R8G8_SINT: + case DATA_FORMAT_R8G8B8_UNORM: + case DATA_FORMAT_R8G8B8_SNORM: + case DATA_FORMAT_R8G8B8_UINT: + case DATA_FORMAT_R8G8B8_SINT: + case DATA_FORMAT_B8G8R8_UNORM: + case DATA_FORMAT_B8G8R8_SNORM: + case DATA_FORMAT_B8G8R8_UINT: + case DATA_FORMAT_B8G8R8_SINT: + case DATA_FORMAT_R8G8B8A8_UNORM: + case DATA_FORMAT_R8G8B8A8_SNORM: + case DATA_FORMAT_R8G8B8A8_UINT: + case DATA_FORMAT_R8G8B8A8_SINT: + case DATA_FORMAT_B8G8R8A8_UNORM: + case DATA_FORMAT_B8G8R8A8_SNORM: + case DATA_FORMAT_B8G8R8A8_UINT: + case DATA_FORMAT_B8G8R8A8_SINT: + case DATA_FORMAT_A2B10G10R10_UNORM_PACK32: + return 4; + case DATA_FORMAT_R16_UNORM: + case DATA_FORMAT_R16_SNORM: + case DATA_FORMAT_R16_UINT: + case DATA_FORMAT_R16_SINT: + case DATA_FORMAT_R16_SFLOAT: + return 4; + case DATA_FORMAT_R16G16_UNORM: + case DATA_FORMAT_R16G16_SNORM: + case DATA_FORMAT_R16G16_UINT: + case DATA_FORMAT_R16G16_SINT: + case DATA_FORMAT_R16G16_SFLOAT: + return 4; + case DATA_FORMAT_R16G16B16_UNORM: + case DATA_FORMAT_R16G16B16_SNORM: + case DATA_FORMAT_R16G16B16_UINT: + case DATA_FORMAT_R16G16B16_SINT: + case DATA_FORMAT_R16G16B16_SFLOAT: + return 8; + case DATA_FORMAT_R16G16B16A16_UNORM: + case DATA_FORMAT_R16G16B16A16_SNORM: + case DATA_FORMAT_R16G16B16A16_UINT: + case DATA_FORMAT_R16G16B16A16_SINT: + case DATA_FORMAT_R16G16B16A16_SFLOAT: + return 8; + case DATA_FORMAT_R32_UINT: + case DATA_FORMAT_R32_SINT: + case DATA_FORMAT_R32_SFLOAT: + return 4; + case DATA_FORMAT_R32G32_UINT: + case DATA_FORMAT_R32G32_SINT: + case DATA_FORMAT_R32G32_SFLOAT: + return 8; + case DATA_FORMAT_R32G32B32_UINT: + case DATA_FORMAT_R32G32B32_SINT: + case DATA_FORMAT_R32G32B32_SFLOAT: + return 12; + case DATA_FORMAT_R32G32B32A32_UINT: + case DATA_FORMAT_R32G32B32A32_SINT: + case DATA_FORMAT_R32G32B32A32_SFLOAT: + return 16; + case DATA_FORMAT_R64_UINT: + case DATA_FORMAT_R64_SINT: + case DATA_FORMAT_R64_SFLOAT: + return 8; + case DATA_FORMAT_R64G64_UINT: + case DATA_FORMAT_R64G64_SINT: + case DATA_FORMAT_R64G64_SFLOAT: + return 16; + case DATA_FORMAT_R64G64B64_UINT: + case DATA_FORMAT_R64G64B64_SINT: + case DATA_FORMAT_R64G64B64_SFLOAT: + return 24; + case DATA_FORMAT_R64G64B64A64_UINT: + case DATA_FORMAT_R64G64B64A64_SINT: + case DATA_FORMAT_R64G64B64A64_SFLOAT: + return 32; + default: + return 0; + } +} + +/****************/ +/**** SHADER ****/ +/****************/ + +const char *RenderingDeviceCommons::SHADER_STAGE_NAMES[SHADER_STAGE_MAX] = { + "Vertex", + "Fragment", + "TesselationControl", + "TesselationEvaluation", + "Compute", +}; diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h new file mode 100644 index 0000000000..dabd0c0867 --- /dev/null +++ b/servers/rendering/rendering_device_commons.h @@ -0,0 +1,921 @@ +/**************************************************************************/ +/* rendering_device_commons.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef RENDERING_DEVICE_COMMONS_H +#define RENDERING_DEVICE_COMMONS_H + +#include "core/object/object.h" +#include "core/variant/type_info.h" + +#define STEPIFY(m_number, m_alignment) ((((m_number) + ((m_alignment)-1)) / (m_alignment)) * (m_alignment)) + +class RenderingDeviceCommons : public Object { + //////////////////////////////////////////// + // PUBLIC STUFF + // Exposed by RenderingDevice, and shared + // with RenderingDeviceDriver. + //////////////////////////////////////////// +public: + /*****************/ + /**** GENERIC ****/ + /*****************/ + + static const int INVALID_ID = -1; + + enum DataFormat { + DATA_FORMAT_R4G4_UNORM_PACK8, + DATA_FORMAT_R4G4B4A4_UNORM_PACK16, + DATA_FORMAT_B4G4R4A4_UNORM_PACK16, + DATA_FORMAT_R5G6B5_UNORM_PACK16, + DATA_FORMAT_B5G6R5_UNORM_PACK16, + DATA_FORMAT_R5G5B5A1_UNORM_PACK16, + DATA_FORMAT_B5G5R5A1_UNORM_PACK16, + DATA_FORMAT_A1R5G5B5_UNORM_PACK16, + DATA_FORMAT_R8_UNORM, + DATA_FORMAT_R8_SNORM, + DATA_FORMAT_R8_USCALED, + DATA_FORMAT_R8_SSCALED, + DATA_FORMAT_R8_UINT, + DATA_FORMAT_R8_SINT, + DATA_FORMAT_R8_SRGB, + DATA_FORMAT_R8G8_UNORM, + DATA_FORMAT_R8G8_SNORM, + DATA_FORMAT_R8G8_USCALED, + DATA_FORMAT_R8G8_SSCALED, + DATA_FORMAT_R8G8_UINT, + DATA_FORMAT_R8G8_SINT, + DATA_FORMAT_R8G8_SRGB, + DATA_FORMAT_R8G8B8_UNORM, + DATA_FORMAT_R8G8B8_SNORM, + DATA_FORMAT_R8G8B8_USCALED, + DATA_FORMAT_R8G8B8_SSCALED, + DATA_FORMAT_R8G8B8_UINT, + DATA_FORMAT_R8G8B8_SINT, + DATA_FORMAT_R8G8B8_SRGB, + DATA_FORMAT_B8G8R8_UNORM, + DATA_FORMAT_B8G8R8_SNORM, + DATA_FORMAT_B8G8R8_USCALED, + DATA_FORMAT_B8G8R8_SSCALED, + DATA_FORMAT_B8G8R8_UINT, + DATA_FORMAT_B8G8R8_SINT, + DATA_FORMAT_B8G8R8_SRGB, + DATA_FORMAT_R8G8B8A8_UNORM, + DATA_FORMAT_R8G8B8A8_SNORM, + DATA_FORMAT_R8G8B8A8_USCALED, + DATA_FORMAT_R8G8B8A8_SSCALED, + DATA_FORMAT_R8G8B8A8_UINT, + DATA_FORMAT_R8G8B8A8_SINT, + DATA_FORMAT_R8G8B8A8_SRGB, + DATA_FORMAT_B8G8R8A8_UNORM, + DATA_FORMAT_B8G8R8A8_SNORM, + DATA_FORMAT_B8G8R8A8_USCALED, + DATA_FORMAT_B8G8R8A8_SSCALED, + DATA_FORMAT_B8G8R8A8_UINT, + DATA_FORMAT_B8G8R8A8_SINT, + DATA_FORMAT_B8G8R8A8_SRGB, + DATA_FORMAT_A8B8G8R8_UNORM_PACK32, + DATA_FORMAT_A8B8G8R8_SNORM_PACK32, + DATA_FORMAT_A8B8G8R8_USCALED_PACK32, + DATA_FORMAT_A8B8G8R8_SSCALED_PACK32, + DATA_FORMAT_A8B8G8R8_UINT_PACK32, + DATA_FORMAT_A8B8G8R8_SINT_PACK32, + DATA_FORMAT_A8B8G8R8_SRGB_PACK32, + DATA_FORMAT_A2R10G10B10_UNORM_PACK32, + DATA_FORMAT_A2R10G10B10_SNORM_PACK32, + DATA_FORMAT_A2R10G10B10_USCALED_PACK32, + DATA_FORMAT_A2R10G10B10_SSCALED_PACK32, + DATA_FORMAT_A2R10G10B10_UINT_PACK32, + DATA_FORMAT_A2R10G10B10_SINT_PACK32, + DATA_FORMAT_A2B10G10R10_UNORM_PACK32, + DATA_FORMAT_A2B10G10R10_SNORM_PACK32, + DATA_FORMAT_A2B10G10R10_USCALED_PACK32, + DATA_FORMAT_A2B10G10R10_SSCALED_PACK32, + DATA_FORMAT_A2B10G10R10_UINT_PACK32, + DATA_FORMAT_A2B10G10R10_SINT_PACK32, + DATA_FORMAT_R16_UNORM, + DATA_FORMAT_R16_SNORM, + DATA_FORMAT_R16_USCALED, + DATA_FORMAT_R16_SSCALED, + DATA_FORMAT_R16_UINT, + DATA_FORMAT_R16_SINT, + DATA_FORMAT_R16_SFLOAT, + DATA_FORMAT_R16G16_UNORM, + DATA_FORMAT_R16G16_SNORM, + DATA_FORMAT_R16G16_USCALED, + DATA_FORMAT_R16G16_SSCALED, + DATA_FORMAT_R16G16_UINT, + DATA_FORMAT_R16G16_SINT, + DATA_FORMAT_R16G16_SFLOAT, + DATA_FORMAT_R16G16B16_UNORM, + DATA_FORMAT_R16G16B16_SNORM, + DATA_FORMAT_R16G16B16_USCALED, + DATA_FORMAT_R16G16B16_SSCALED, + DATA_FORMAT_R16G16B16_UINT, + DATA_FORMAT_R16G16B16_SINT, + DATA_FORMAT_R16G16B16_SFLOAT, + DATA_FORMAT_R16G16B16A16_UNORM, + DATA_FORMAT_R16G16B16A16_SNORM, + DATA_FORMAT_R16G16B16A16_USCALED, + DATA_FORMAT_R16G16B16A16_SSCALED, + DATA_FORMAT_R16G16B16A16_UINT, + DATA_FORMAT_R16G16B16A16_SINT, + DATA_FORMAT_R16G16B16A16_SFLOAT, + DATA_FORMAT_R32_UINT, + DATA_FORMAT_R32_SINT, + DATA_FORMAT_R32_SFLOAT, + DATA_FORMAT_R32G32_UINT, + DATA_FORMAT_R32G32_SINT, + DATA_FORMAT_R32G32_SFLOAT, + DATA_FORMAT_R32G32B32_UINT, + DATA_FORMAT_R32G32B32_SINT, + DATA_FORMAT_R32G32B32_SFLOAT, + DATA_FORMAT_R32G32B32A32_UINT, + DATA_FORMAT_R32G32B32A32_SINT, + DATA_FORMAT_R32G32B32A32_SFLOAT, + DATA_FORMAT_R64_UINT, + DATA_FORMAT_R64_SINT, + DATA_FORMAT_R64_SFLOAT, + DATA_FORMAT_R64G64_UINT, + DATA_FORMAT_R64G64_SINT, + DATA_FORMAT_R64G64_SFLOAT, + DATA_FORMAT_R64G64B64_UINT, + DATA_FORMAT_R64G64B64_SINT, + DATA_FORMAT_R64G64B64_SFLOAT, + DATA_FORMAT_R64G64B64A64_UINT, + DATA_FORMAT_R64G64B64A64_SINT, + DATA_FORMAT_R64G64B64A64_SFLOAT, + DATA_FORMAT_B10G11R11_UFLOAT_PACK32, + DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32, + DATA_FORMAT_D16_UNORM, + DATA_FORMAT_X8_D24_UNORM_PACK32, + DATA_FORMAT_D32_SFLOAT, + DATA_FORMAT_S8_UINT, + DATA_FORMAT_D16_UNORM_S8_UINT, + DATA_FORMAT_D24_UNORM_S8_UINT, + DATA_FORMAT_D32_SFLOAT_S8_UINT, + DATA_FORMAT_BC1_RGB_UNORM_BLOCK, + DATA_FORMAT_BC1_RGB_SRGB_BLOCK, + DATA_FORMAT_BC1_RGBA_UNORM_BLOCK, + DATA_FORMAT_BC1_RGBA_SRGB_BLOCK, + DATA_FORMAT_BC2_UNORM_BLOCK, + DATA_FORMAT_BC2_SRGB_BLOCK, + DATA_FORMAT_BC3_UNORM_BLOCK, + DATA_FORMAT_BC3_SRGB_BLOCK, + DATA_FORMAT_BC4_UNORM_BLOCK, + DATA_FORMAT_BC4_SNORM_BLOCK, + DATA_FORMAT_BC5_UNORM_BLOCK, + DATA_FORMAT_BC5_SNORM_BLOCK, + DATA_FORMAT_BC6H_UFLOAT_BLOCK, + DATA_FORMAT_BC6H_SFLOAT_BLOCK, + DATA_FORMAT_BC7_UNORM_BLOCK, + DATA_FORMAT_BC7_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + DATA_FORMAT_EAC_R11_UNORM_BLOCK, + DATA_FORMAT_EAC_R11_SNORM_BLOCK, + DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, + DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, + DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, + DATA_FORMAT_ASTC_4x4_SRGB_BLOCK, + DATA_FORMAT_ASTC_5x4_UNORM_BLOCK, + DATA_FORMAT_ASTC_5x4_SRGB_BLOCK, + DATA_FORMAT_ASTC_5x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_5x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_6x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_6x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_6x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_6x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x8_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x8_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x8_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x10_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x10_SRGB_BLOCK, + DATA_FORMAT_ASTC_12x10_UNORM_BLOCK, + DATA_FORMAT_ASTC_12x10_SRGB_BLOCK, + DATA_FORMAT_ASTC_12x12_UNORM_BLOCK, + DATA_FORMAT_ASTC_12x12_SRGB_BLOCK, + DATA_FORMAT_G8B8G8R8_422_UNORM, + DATA_FORMAT_B8G8R8G8_422_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + DATA_FORMAT_R10X6_UNORM_PACK16, + DATA_FORMAT_R10X6G10X6_UNORM_2PACK16, + DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + DATA_FORMAT_R12X4_UNORM_PACK16, + DATA_FORMAT_R12X4G12X4_UNORM_2PACK16, + DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + DATA_FORMAT_G16B16G16R16_422_UNORM, + DATA_FORMAT_B16G16R16G16_422_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + DATA_FORMAT_MAX, + }; + + enum CompareOperator { + COMPARE_OP_NEVER, + COMPARE_OP_LESS, + COMPARE_OP_EQUAL, + COMPARE_OP_LESS_OR_EQUAL, + COMPARE_OP_GREATER, + COMPARE_OP_NOT_EQUAL, + COMPARE_OP_GREATER_OR_EQUAL, + COMPARE_OP_ALWAYS, + COMPARE_OP_MAX + }; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + enum TextureType { + TEXTURE_TYPE_1D, + TEXTURE_TYPE_2D, + TEXTURE_TYPE_3D, + TEXTURE_TYPE_CUBE, + TEXTURE_TYPE_1D_ARRAY, + TEXTURE_TYPE_2D_ARRAY, + TEXTURE_TYPE_CUBE_ARRAY, + TEXTURE_TYPE_MAX, + }; + + enum TextureSamples { + TEXTURE_SAMPLES_1, + TEXTURE_SAMPLES_2, + TEXTURE_SAMPLES_4, + TEXTURE_SAMPLES_8, + TEXTURE_SAMPLES_16, + TEXTURE_SAMPLES_32, + TEXTURE_SAMPLES_64, + TEXTURE_SAMPLES_MAX, + }; + + enum TextureUsageBits { + TEXTURE_USAGE_SAMPLING_BIT = (1 << 0), + TEXTURE_USAGE_COLOR_ATTACHMENT_BIT = (1 << 1), + TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = (1 << 2), + TEXTURE_USAGE_STORAGE_BIT = (1 << 3), + TEXTURE_USAGE_STORAGE_ATOMIC_BIT = (1 << 4), + TEXTURE_USAGE_CPU_READ_BIT = (1 << 5), + TEXTURE_USAGE_CAN_UPDATE_BIT = (1 << 6), + TEXTURE_USAGE_CAN_COPY_FROM_BIT = (1 << 7), + TEXTURE_USAGE_CAN_COPY_TO_BIT = (1 << 8), + TEXTURE_USAGE_INPUT_ATTACHMENT_BIT = (1 << 9), + TEXTURE_USAGE_VRS_ATTACHMENT_BIT = (1 << 10), + }; + + struct TextureFormat { + DataFormat format = DATA_FORMAT_R8_UNORM; + uint32_t width = 1; + uint32_t height = 1; + uint32_t depth = 1; + uint32_t array_layers = 1; + uint32_t mipmaps = 1; + TextureType texture_type = TEXTURE_TYPE_2D; + TextureSamples samples = TEXTURE_SAMPLES_1; + uint32_t usage_bits = 0; + Vector<DataFormat> shareable_formats; + bool is_resolve_buffer = false; + + bool operator==(const TextureFormat &b) const { + if (format != b.format) { + return false; + } else if (width != b.width) { + return false; + } else if (height != b.height) { + return false; + } else if (depth != b.depth) { + return false; + } else if (array_layers != b.array_layers) { + return false; + } else if (mipmaps != b.mipmaps) { + return false; + } else if (texture_type != b.texture_type) { + return false; + } else if (samples != b.samples) { + return false; + } else if (usage_bits != b.usage_bits) { + return false; + } else if (shareable_formats != b.shareable_formats) { + return false; + } else { + return true; + } + } + }; + + enum TextureSwizzle { + TEXTURE_SWIZZLE_IDENTITY, + TEXTURE_SWIZZLE_ZERO, + TEXTURE_SWIZZLE_ONE, + TEXTURE_SWIZZLE_R, + TEXTURE_SWIZZLE_G, + TEXTURE_SWIZZLE_B, + TEXTURE_SWIZZLE_A, + TEXTURE_SWIZZLE_MAX + }; + + enum TextureSliceType { + TEXTURE_SLICE_2D, + TEXTURE_SLICE_CUBEMAP, + TEXTURE_SLICE_3D, + TEXTURE_SLICE_2D_ARRAY, + }; + + /*****************/ + /**** SAMPLER ****/ + /*****************/ + + enum SamplerFilter { + SAMPLER_FILTER_NEAREST, + SAMPLER_FILTER_LINEAR, + }; + + enum SamplerRepeatMode { + SAMPLER_REPEAT_MODE_REPEAT, + SAMPLER_REPEAT_MODE_MIRRORED_REPEAT, + SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE, + SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER, + SAMPLER_REPEAT_MODE_MIRROR_CLAMP_TO_EDGE, + SAMPLER_REPEAT_MODE_MAX + }; + + enum SamplerBorderColor { + SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + SAMPLER_BORDER_COLOR_INT_TRANSPARENT_BLACK, + SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + SAMPLER_BORDER_COLOR_INT_OPAQUE_BLACK, + SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + SAMPLER_BORDER_COLOR_INT_OPAQUE_WHITE, + SAMPLER_BORDER_COLOR_MAX + }; + + struct SamplerState { + SamplerFilter mag_filter = SAMPLER_FILTER_NEAREST; + SamplerFilter min_filter = SAMPLER_FILTER_NEAREST; + SamplerFilter mip_filter = SAMPLER_FILTER_NEAREST; + SamplerRepeatMode repeat_u = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + SamplerRepeatMode repeat_v = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + SamplerRepeatMode repeat_w = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + float lod_bias = 0.0f; + bool use_anisotropy = false; + float anisotropy_max = 1.0f; + bool enable_compare = false; + CompareOperator compare_op = COMPARE_OP_ALWAYS; + float min_lod = 0.0f; + float max_lod = 1e20; // Something very large should do. + SamplerBorderColor border_color = SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + bool unnormalized_uvw = false; + }; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ + + enum IndexBufferFormat { + INDEX_BUFFER_FORMAT_UINT16, + INDEX_BUFFER_FORMAT_UINT32, + }; + + enum VertexFrequency { + VERTEX_FREQUENCY_VERTEX, + VERTEX_FREQUENCY_INSTANCE, + }; + + struct VertexAttribute { + uint32_t location = 0; // Shader location. + uint32_t offset = 0; + DataFormat format = DATA_FORMAT_MAX; + uint32_t stride = 0; + VertexFrequency frequency = VERTEX_FREQUENCY_VERTEX; + }; + + /*********************/ + /**** FRAMEBUFFER ****/ + /*********************/ + + static const int32_t ATTACHMENT_UNUSED = -1; + + /****************/ + /**** SHADER ****/ + /****************/ + + enum ShaderStage { + SHADER_STAGE_VERTEX, + SHADER_STAGE_FRAGMENT, + SHADER_STAGE_TESSELATION_CONTROL, + SHADER_STAGE_TESSELATION_EVALUATION, + SHADER_STAGE_COMPUTE, + SHADER_STAGE_MAX, + SHADER_STAGE_VERTEX_BIT = (1 << SHADER_STAGE_VERTEX), + SHADER_STAGE_FRAGMENT_BIT = (1 << SHADER_STAGE_FRAGMENT), + SHADER_STAGE_TESSELATION_CONTROL_BIT = (1 << SHADER_STAGE_TESSELATION_CONTROL), + SHADER_STAGE_TESSELATION_EVALUATION_BIT = (1 << SHADER_STAGE_TESSELATION_EVALUATION), + SHADER_STAGE_COMPUTE_BIT = (1 << SHADER_STAGE_COMPUTE), + }; + + struct ShaderStageSPIRVData { + ShaderStage shader_stage = SHADER_STAGE_MAX; + Vector<uint8_t> spirv; + }; + + /*********************/ + /**** UNIFORM SET ****/ + /*********************/ + + static const uint32_t MAX_UNIFORM_SETS = 16; + + enum UniformType { + UNIFORM_TYPE_SAMPLER, // For sampling only (sampler GLSL type). + UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, // For sampling only, but includes a texture, (samplerXX GLSL type), first a sampler then a texture. + UNIFORM_TYPE_TEXTURE, // Only texture, (textureXX GLSL type). + UNIFORM_TYPE_IMAGE, // Storage image (imageXX GLSL type), for compute mostly. + UNIFORM_TYPE_TEXTURE_BUFFER, // Buffer texture (or TBO, textureBuffer type). + UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER, // Buffer texture with a sampler(or TBO, samplerBuffer type). + UNIFORM_TYPE_IMAGE_BUFFER, // Texel buffer, (imageBuffer type), for compute mostly. + UNIFORM_TYPE_UNIFORM_BUFFER, // Regular uniform buffer (or UBO). + UNIFORM_TYPE_STORAGE_BUFFER, // Storage buffer ("buffer" qualifier) like UBO, but supports storage, for compute mostly. + UNIFORM_TYPE_INPUT_ATTACHMENT, // Used for sub-pass read/write, for mobile mostly. + UNIFORM_TYPE_MAX + }; + + /******************/ + /**** PIPELINE ****/ + /******************/ + + enum PipelineSpecializationConstantType { + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL, + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT, + PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT, + }; + + struct PipelineSpecializationConstant { + PipelineSpecializationConstantType type = {}; + uint32_t constant_id = 0xffffffff; + union { + uint32_t int_value = 0; + float float_value; + bool bool_value; + }; + }; + + /*******************/ + /**** RENDERING ****/ + /*******************/ + + // ----- PIPELINE ----- + + enum RenderPrimitive { + RENDER_PRIMITIVE_POINTS, + RENDER_PRIMITIVE_LINES, + RENDER_PRIMITIVE_LINES_WITH_ADJACENCY, + RENDER_PRIMITIVE_LINESTRIPS, + RENDER_PRIMITIVE_LINESTRIPS_WITH_ADJACENCY, + RENDER_PRIMITIVE_TRIANGLES, + RENDER_PRIMITIVE_TRIANGLES_WITH_ADJACENCY, + RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_AJACENCY, + RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX, + RENDER_PRIMITIVE_TESSELATION_PATCH, + RENDER_PRIMITIVE_MAX + }; + + enum PolygonCullMode { + POLYGON_CULL_DISABLED, + POLYGON_CULL_FRONT, + POLYGON_CULL_BACK, + POLYGON_CULL_MAX + }; + + enum PolygonFrontFace { + POLYGON_FRONT_FACE_CLOCKWISE, + POLYGON_FRONT_FACE_COUNTER_CLOCKWISE, + }; + + enum StencilOperation { + STENCIL_OP_KEEP, + STENCIL_OP_ZERO, + STENCIL_OP_REPLACE, + STENCIL_OP_INCREMENT_AND_CLAMP, + STENCIL_OP_DECREMENT_AND_CLAMP, + STENCIL_OP_INVERT, + STENCIL_OP_INCREMENT_AND_WRAP, + STENCIL_OP_DECREMENT_AND_WRAP, + STENCIL_OP_MAX + }; + + enum LogicOperation { + LOGIC_OP_CLEAR, + LOGIC_OP_AND, + LOGIC_OP_AND_REVERSE, + LOGIC_OP_COPY, + LOGIC_OP_AND_INVERTED, + LOGIC_OP_NO_OP, + LOGIC_OP_XOR, + LOGIC_OP_OR, + LOGIC_OP_NOR, + LOGIC_OP_EQUIVALENT, + LOGIC_OP_INVERT, + LOGIC_OP_OR_REVERSE, + LOGIC_OP_COPY_INVERTED, + LOGIC_OP_OR_INVERTED, + LOGIC_OP_NAND, + LOGIC_OP_SET, + LOGIC_OP_MAX + }; + + enum BlendFactor { + BLEND_FACTOR_ZERO, + BLEND_FACTOR_ONE, + BLEND_FACTOR_SRC_COLOR, + BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + BLEND_FACTOR_DST_COLOR, + BLEND_FACTOR_ONE_MINUS_DST_COLOR, + BLEND_FACTOR_SRC_ALPHA, + BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + BLEND_FACTOR_DST_ALPHA, + BLEND_FACTOR_ONE_MINUS_DST_ALPHA, + BLEND_FACTOR_CONSTANT_COLOR, + BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + BLEND_FACTOR_CONSTANT_ALPHA, + BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + BLEND_FACTOR_SRC_ALPHA_SATURATE, + BLEND_FACTOR_SRC1_COLOR, + BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, + BLEND_FACTOR_SRC1_ALPHA, + BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, + BLEND_FACTOR_MAX + }; + + enum BlendOperation { + BLEND_OP_ADD, + BLEND_OP_SUBTRACT, + BLEND_OP_REVERSE_SUBTRACT, + BLEND_OP_MINIMUM, + BLEND_OP_MAXIMUM, // Yes, this one is an actual operator. + BLEND_OP_MAX + }; + + struct PipelineRasterizationState { + bool enable_depth_clamp = false; + bool discard_primitives = false; + bool wireframe = false; + PolygonCullMode cull_mode = POLYGON_CULL_DISABLED; + PolygonFrontFace front_face = POLYGON_FRONT_FACE_CLOCKWISE; + bool depth_bias_enabled = false; + float depth_bias_constant_factor = 0.0f; + float depth_bias_clamp = 0.0f; + float depth_bias_slope_factor = 0.0f; + float line_width = 1.0f; + uint32_t patch_control_points = 1; + }; + + struct PipelineMultisampleState { + TextureSamples sample_count = TEXTURE_SAMPLES_1; + bool enable_sample_shading = false; + float min_sample_shading = 0.0f; + Vector<uint32_t> sample_mask; + bool enable_alpha_to_coverage = false; + bool enable_alpha_to_one = false; + }; + + struct PipelineDepthStencilState { + bool enable_depth_test = false; + bool enable_depth_write = false; + CompareOperator depth_compare_operator = COMPARE_OP_ALWAYS; + bool enable_depth_range = false; + float depth_range_min = 0; + float depth_range_max = 0; + bool enable_stencil = false; + + struct StencilOperationState { + StencilOperation fail = STENCIL_OP_ZERO; + StencilOperation pass = STENCIL_OP_ZERO; + StencilOperation depth_fail = STENCIL_OP_ZERO; + CompareOperator compare = COMPARE_OP_ALWAYS; + uint32_t compare_mask = 0; + uint32_t write_mask = 0; + uint32_t reference = 0; + }; + + StencilOperationState front_op; + StencilOperationState back_op; + }; + + struct PipelineColorBlendState { + bool enable_logic_op = false; + LogicOperation logic_op = LOGIC_OP_CLEAR; + + struct Attachment { + bool enable_blend = false; + BlendFactor src_color_blend_factor = BLEND_FACTOR_ZERO; + BlendFactor dst_color_blend_factor = BLEND_FACTOR_ZERO; + BlendOperation color_blend_op = BLEND_OP_ADD; + BlendFactor src_alpha_blend_factor = BLEND_FACTOR_ZERO; + BlendFactor dst_alpha_blend_factor = BLEND_FACTOR_ZERO; + BlendOperation alpha_blend_op = BLEND_OP_ADD; + bool write_r = true; + bool write_g = true; + bool write_b = true; + bool write_a = true; + }; + + static PipelineColorBlendState create_disabled(int p_attachments = 1) { + PipelineColorBlendState bs; + for (int i = 0; i < p_attachments; i++) { + bs.attachments.push_back(Attachment()); + } + return bs; + } + + static PipelineColorBlendState create_blend(int p_attachments = 1) { + PipelineColorBlendState bs; + for (int i = 0; i < p_attachments; i++) { + Attachment ba; + ba.enable_blend = true; + ba.src_color_blend_factor = BLEND_FACTOR_SRC_ALPHA; + ba.dst_color_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + ba.src_alpha_blend_factor = BLEND_FACTOR_SRC_ALPHA; + ba.dst_alpha_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + bs.attachments.push_back(ba); + } + return bs; + } + + Vector<Attachment> attachments; // One per render target texture. + Color blend_constant; + }; + + enum PipelineDynamicStateFlags { + DYNAMIC_STATE_LINE_WIDTH = (1 << 0), + DYNAMIC_STATE_DEPTH_BIAS = (1 << 1), + DYNAMIC_STATE_BLEND_CONSTANTS = (1 << 2), + DYNAMIC_STATE_DEPTH_BOUNDS = (1 << 3), + DYNAMIC_STATE_STENCIL_COMPARE_MASK = (1 << 4), + DYNAMIC_STATE_STENCIL_WRITE_MASK = (1 << 5), + DYNAMIC_STATE_STENCIL_REFERENCE = (1 << 6), + }; + + /**************/ + /**** MISC ****/ + /**************/ + + // This enum matches VkPhysicalDeviceType (except for `DEVICE_TYPE_MAX`). + // Unlike VkPhysicalDeviceType, DeviceType is exposed to the scripting API. + enum DeviceType { + DEVICE_TYPE_OTHER, + DEVICE_TYPE_INTEGRATED_GPU, + DEVICE_TYPE_DISCRETE_GPU, + DEVICE_TYPE_VIRTUAL_GPU, + DEVICE_TYPE_CPU, + DEVICE_TYPE_MAX + }; + + // Defined in an API-agnostic way. + // Some may not make sense for the underlying API; in that case, 0 is returned. + enum DriverResource { + DRIVER_RESOURCE_LOGICAL_DEVICE, + DRIVER_RESOURCE_PHYSICAL_DEVICE, + DRIVER_RESOURCE_TOPMOST_OBJECT, + DRIVER_RESOURCE_COMMAND_QUEUE, + DRIVER_RESOURCE_QUEUE_FAMILY, + DRIVER_RESOURCE_TEXTURE, + DRIVER_RESOURCE_TEXTURE_VIEW, + DRIVER_RESOURCE_TEXTURE_DATA_FORMAT, + DRIVER_RESOURCE_SAMPLER, + DRIVER_RESOURCE_UNIFORM_SET, + DRIVER_RESOURCE_BUFFER, + DRIVER_RESOURCE_COMPUTE_PIPELINE, + DRIVER_RESOURCE_RENDER_PIPELINE, +#ifndef DISABLE_DEPRECATED + DRIVER_RESOURCE_VULKAN_DEVICE = DRIVER_RESOURCE_LOGICAL_DEVICE, + DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE = DRIVER_RESOURCE_PHYSICAL_DEVICE, + DRIVER_RESOURCE_VULKAN_INSTANCE = DRIVER_RESOURCE_TOPMOST_OBJECT, + DRIVER_RESOURCE_VULKAN_QUEUE = DRIVER_RESOURCE_COMMAND_QUEUE, + DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX = DRIVER_RESOURCE_QUEUE_FAMILY, + DRIVER_RESOURCE_VULKAN_IMAGE = DRIVER_RESOURCE_TEXTURE, + DRIVER_RESOURCE_VULKAN_IMAGE_VIEW = DRIVER_RESOURCE_TEXTURE_VIEW, + DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT = DRIVER_RESOURCE_TEXTURE_DATA_FORMAT, + DRIVER_RESOURCE_VULKAN_SAMPLER = DRIVER_RESOURCE_SAMPLER, + DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET = DRIVER_RESOURCE_UNIFORM_SET, + DRIVER_RESOURCE_VULKAN_BUFFER = DRIVER_RESOURCE_BUFFER, + DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE = DRIVER_RESOURCE_COMPUTE_PIPELINE, + DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE = DRIVER_RESOURCE_RENDER_PIPELINE, +#endif + }; + + enum Limit { + LIMIT_MAX_BOUND_UNIFORM_SETS, + LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS, + LIMIT_MAX_TEXTURES_PER_UNIFORM_SET, + LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET, + LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET, + LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET, + LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET, + LIMIT_MAX_DRAW_INDEXED_INDEX, + LIMIT_MAX_FRAMEBUFFER_HEIGHT, + LIMIT_MAX_FRAMEBUFFER_WIDTH, + LIMIT_MAX_TEXTURE_ARRAY_LAYERS, + LIMIT_MAX_TEXTURE_SIZE_1D, + LIMIT_MAX_TEXTURE_SIZE_2D, + LIMIT_MAX_TEXTURE_SIZE_3D, + LIMIT_MAX_TEXTURE_SIZE_CUBE, + LIMIT_MAX_TEXTURES_PER_SHADER_STAGE, + LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE, + LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE, + LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE, + LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE, + LIMIT_MAX_PUSH_CONSTANT_SIZE, + LIMIT_MAX_UNIFORM_BUFFER_SIZE, + LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET, + LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES, + LIMIT_MAX_VERTEX_INPUT_BINDINGS, + LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE, + LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT, + LIMIT_MAX_COMPUTE_SHARED_MEMORY_SIZE, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z, + LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z, + LIMIT_MAX_VIEWPORT_DIMENSIONS_X, + LIMIT_MAX_VIEWPORT_DIMENSIONS_Y, + LIMIT_SUBGROUP_SIZE, + LIMIT_SUBGROUP_MIN_SIZE, + LIMIT_SUBGROUP_MAX_SIZE, + LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc. + LIMIT_SUBGROUP_OPERATIONS, + LIMIT_VRS_TEXEL_WIDTH, + LIMIT_VRS_TEXEL_HEIGHT, + }; + + enum Features { + SUPPORTS_MULTIVIEW, + SUPPORTS_FSR_HALF_FLOAT, + SUPPORTS_ATTACHMENT_VRS, + // If not supported, a fragment shader with only side effets (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver. + SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS, + }; + + //////////////////////////////////////////// + // PROTECTED STUFF + // Not exposed by RenderingDevice, but shared + // with RenderingDeviceDriver for convenience. + //////////////////////////////////////////// +protected: + /*****************/ + /**** GENERIC ****/ + /*****************/ + + static const char *const FORMAT_NAMES[DATA_FORMAT_MAX]; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + static const uint32_t MAX_IMAGE_FORMAT_PLANES = 2; + + static const uint32_t TEXTURE_SAMPLES_COUNT[TEXTURE_SAMPLES_MAX]; + + static uint32_t get_image_format_pixel_size(DataFormat p_format); + static void get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h); + uint32_t get_compressed_image_format_block_byte_size(DataFormat p_format); + static uint32_t get_compressed_image_format_pixel_rshift(DataFormat p_format); + static uint32_t get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw = nullptr, uint32_t *r_blockh = nullptr, uint32_t *r_depth = nullptr); + static uint32_t get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth); + static bool format_has_stencil(DataFormat p_format); + static uint32_t format_get_plane_count(DataFormat p_format); + + /*****************/ + /**** SAMPLER ****/ + /*****************/ + + static const Color SAMPLER_BORDER_COLOR_VALUE[SAMPLER_BORDER_COLOR_MAX]; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ + + static uint32_t get_format_vertex_size(DataFormat p_format); + + /****************/ + /**** SHADER ****/ + /****************/ + + static const char *SHADER_STAGE_NAMES[SHADER_STAGE_MAX]; + + struct ShaderUniform { + UniformType type = UniformType::UNIFORM_TYPE_MAX; + bool writable = false; + uint32_t binding = 0; + BitField<ShaderStage> stages; + uint32_t length = 0; // Size of arrays (in total elements), or ubos (in bytes * total elements). + + bool operator!=(const ShaderUniform &p_other) const { + return binding != p_other.binding || type != p_other.type || writable != p_other.writable || stages != p_other.stages || length != p_other.length; + } + + bool operator<(const ShaderUniform &p_other) const { + if (binding != p_other.binding) { + return binding < p_other.binding; + } + if (type != p_other.type) { + return type < p_other.type; + } + if (writable != p_other.writable) { + return writable < p_other.writable; + } + if (stages != p_other.stages) { + return stages < p_other.stages; + } + if (length != p_other.length) { + return length < p_other.length; + } + return false; + } + }; + + struct ShaderSpecializationConstant : public PipelineSpecializationConstant { + BitField<ShaderStage> stages; + }; + + struct ShaderDescription { + uint64_t vertex_input_mask = 0; + uint32_t fragment_output_mask = 0; + bool is_compute = false; + uint32_t compute_local_size[3] = {}; + uint32_t push_constant_size = 0; + + Vector<Vector<ShaderUniform>> uniform_sets; + Vector<ShaderSpecializationConstant> specialization_constants; + }; + + struct ShaderReflection : public ShaderDescription { + BitField<ShaderStage> stages; + BitField<ShaderStage> push_constant_stages; + }; +}; + +#endif // RENDERING_DEVICE_COMMONS_H diff --git a/servers/rendering/rendering_device_driver.cpp b/servers/rendering/rendering_device_driver.cpp new file mode 100644 index 0000000000..9e05a6a133 --- /dev/null +++ b/servers/rendering/rendering_device_driver.cpp @@ -0,0 +1,380 @@ +/**************************************************************************/ +/* rendering_device_driver.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "rendering_device_driver.h" + +#include "thirdparty/spirv-reflect/spirv_reflect.h" + +/****************/ +/**** SHADER ****/ +/****************/ + +Error RenderingDeviceDriver::_reflect_spirv(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection) { + r_reflection = {}; + + for (uint32_t i = 0; i < p_spirv.size(); i++) { + ShaderStage stage = p_spirv[i].shader_stage; + ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage); + + if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) { + r_reflection.is_compute = true; + ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, FAILED, + "Compute shaders can only receive one stage, dedicated to compute."); + } + ERR_FAIL_COND_V_MSG(r_reflection.stages.has_flag(stage_flag), FAILED, + "Stage " + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + " submitted more than once."); + + { + SpvReflectShaderModule module; + const uint8_t *spirv = p_spirv[i].spirv.ptr(); + SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spirv.size(), spirv, &module); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed parsing shader."); + + if (r_reflection.is_compute) { + r_reflection.compute_local_size[0] = module.entry_points->local_size.x; + r_reflection.compute_local_size[1] = module.entry_points->local_size.y; + r_reflection.compute_local_size[2] = module.entry_points->local_size.z; + } + uint32_t binding_count = 0; + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings."); + + if (binding_count > 0) { + // Parse bindings. + + Vector<SpvReflectDescriptorBinding *> bindings; + bindings.resize(binding_count); + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); + + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings."); + + for (uint32_t j = 0; j < binding_count; j++) { + const SpvReflectDescriptorBinding &binding = *bindings[j]; + + ShaderUniform uniform; + + bool need_array_dimensions = false; + bool need_block_size = false; + bool may_be_writable = false; + + switch (binding.descriptor_type) { + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { + uniform.type = UNIFORM_TYPE_SAMPLER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + uniform.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + uniform.type = UNIFORM_TYPE_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + uniform.type = UNIFORM_TYPE_IMAGE; + need_array_dimensions = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { + uniform.type = UNIFORM_TYPE_TEXTURE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + uniform.type = UNIFORM_TYPE_IMAGE_BUFFER; + need_array_dimensions = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + uniform.type = UNIFORM_TYPE_UNIFORM_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + uniform.type = UNIFORM_TYPE_STORAGE_BUFFER; + need_block_size = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic uniform buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic storage buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + uniform.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { + ERR_PRINT("Acceleration structure not supported."); + continue; + } break; + } + + if (need_array_dimensions) { + if (binding.array.dims_count == 0) { + uniform.length = 1; + } else { + for (uint32_t k = 0; k < binding.array.dims_count; k++) { + if (k == 0) { + uniform.length = binding.array.dims[0]; + } else { + uniform.length *= binding.array.dims[k]; + } + } + } + + } else if (need_block_size) { + uniform.length = binding.block.size; + } else { + uniform.length = 0; + } + + if (may_be_writable) { + uniform.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); + } else { + uniform.writable = false; + } + + uniform.binding = binding.binding; + uint32_t set = binding.set; + + ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, FAILED, + "On shader stage '" + String(SHADER_STAGE_NAMES[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); + + if (set < (uint32_t)r_reflection.uniform_sets.size()) { + // Check if this already exists. + bool exists = false; + for (int k = 0; k < r_reflection.uniform_sets[set].size(); k++) { + if (r_reflection.uniform_sets[set][k].binding == uniform.binding) { + // Already exists, verify that it's the same type. + ERR_FAIL_COND_V_MSG(r_reflection.uniform_sets[set][k].type != uniform.type, FAILED, + "On shader stage '" + String(SHADER_STAGE_NAMES[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(uniform.binding) + " with different uniform type."); + + // Also, verify that it's the same size. + ERR_FAIL_COND_V_MSG(r_reflection.uniform_sets[set][k].length != uniform.length, FAILED, + "On shader stage '" + String(SHADER_STAGE_NAMES[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(uniform.binding) + " with different uniform size."); + + // Also, verify that it has the same writability. + ERR_FAIL_COND_V_MSG(r_reflection.uniform_sets[set][k].writable != uniform.writable, FAILED, + "On shader stage '" + String(SHADER_STAGE_NAMES[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(uniform.binding) + " with different writability."); + + // Just append stage mask and return. + r_reflection.uniform_sets.write[set].write[k].stages.set_flag(stage_flag); + exists = true; + break; + } + } + + if (exists) { + continue; // Merged. + } + } + + uniform.stages.set_flag(stage_flag); + + if (set >= (uint32_t)r_reflection.uniform_sets.size()) { + r_reflection.uniform_sets.resize(set + 1); + } + + r_reflection.uniform_sets.write[set].push_back(uniform); + } + } + + { + // Specialization constants. + + uint32_t sc_count = 0; + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants."); + + if (sc_count) { + Vector<SpvReflectSpecializationConstant *> spec_constants; + spec_constants.resize(sc_count); + + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants."); + + for (uint32_t j = 0; j < sc_count; j++) { + int32_t existing = -1; + ShaderSpecializationConstant sconst; + SpvReflectSpecializationConstant *spc = spec_constants[j]; + + sconst.constant_id = spc->constant_id; + sconst.int_value = 0; // Clear previous value JIC. + switch (spc->constant_type) { + case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sconst.bool_value = spc->default_value.int_bool_value != 0; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sconst.int_value = spc->default_value.int_bool_value; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sconst.float_value = spc->default_value.float_value; + } break; + } + sconst.stages.set_flag(stage_flag); + + for (int k = 0; k < r_reflection.specialization_constants.size(); k++) { + if (r_reflection.specialization_constants[k].constant_id == sconst.constant_id) { + ERR_FAIL_COND_V_MSG(r_reflection.specialization_constants[k].type != sconst.type, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ."); + ERR_FAIL_COND_V_MSG(r_reflection.specialization_constants[k].int_value != sconst.int_value, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ."); + existing = k; + break; + } + } + + if (existing > 0) { + r_reflection.specialization_constants.write[existing].stages.set_flag(stage_flag); + } else { + r_reflection.specialization_constants.push_back(sconst); + } + } + } + } + + if (stage == SHADER_STAGE_VERTEX) { + uint32_t iv_count = 0; + result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed enumerating input variables."); + + if (iv_count) { + Vector<SpvReflectInterfaceVariable *> input_vars; + input_vars.resize(iv_count); + + result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); + + for (uint32_t j = 0; j < iv_count; j++) { + if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. + r_reflection.vertex_input_mask |= (((uint64_t)1) << input_vars[j]->location); + } + } + } + } + + if (stage == SHADER_STAGE_FRAGMENT) { + uint32_t ov_count = 0; + result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed enumerating output variables."); + + if (ov_count) { + Vector<SpvReflectInterfaceVariable *> output_vars; + output_vars.resize(ov_count); + + result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed obtaining output variables."); + + for (uint32_t j = 0; j < ov_count; j++) { + const SpvReflectInterfaceVariable *refvar = output_vars[j]; + if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { + r_reflection.fragment_output_mask |= 1 << refvar->location; + } + } + } + } + + uint32_t pc_count = 0; + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed enumerating push constants."); + + if (pc_count) { + ERR_FAIL_COND_V_MSG(pc_count > 1, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + + Vector<SpvReflectBlockVariable *> pconstants; + pconstants.resize(pc_count); + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "' failed obtaining push constants."); +#if 0 + if (pconstants[0] == nullptr) { + Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); + f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); + } +#endif + + ERR_FAIL_COND_V_MSG(r_reflection.push_constant_size && r_reflection.push_constant_size != pconstants[0]->size, FAILED, + "Reflection of SPIR-V shader stage '" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + + r_reflection.push_constant_size = pconstants[0]->size; + r_reflection.push_constant_stages.set_flag(stage_flag); + + //print_line("Stage: " + String(SHADER_STAGE_NAMES[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + } + + // Destroy the reflection data when no longer required. + spvReflectDestroyShaderModule(&module); + } + + r_reflection.stages.set_flag(stage_flag); + } + + return OK; +} + +/**************/ +/**** MISC ****/ +/**************/ + +uint64_t RenderingDeviceDriver::api_trait_get(ApiTrait p_trait) { + // Sensible canonical defaults. + switch (p_trait) { + case API_TRAIT_HONORS_PIPELINE_BARRIERS: + return 1; + case API_TRAIT_SHADER_CHANGE_INVALIDATION: + return SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS; + case API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT: + return 1; + case API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP: + return 1; + case API_TRAIT_SECONDARY_VIEWPORT_SCISSOR: + return 1; + default: + ERR_FAIL_V(0); + } +} + +/******************/ + +RenderingDeviceDriver::~RenderingDeviceDriver() {} diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h new file mode 100644 index 0000000000..bb71a29bbc --- /dev/null +++ b/servers/rendering/rendering_device_driver.h @@ -0,0 +1,687 @@ +/**************************************************************************/ +/* rendering_device_driver.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef RENDERING_DEVICE_DRIVER_H +#define RENDERING_DEVICE_DRIVER_H + +// *********************************************************************************** +// RenderingDeviceDriver - Design principles +// ----------------------------------------- +// - Very little validation is done, and normally only in dev or debug builds. +// - Error reporting is generally simple: returning an id of 0 or a false boolean. +// - Certain enums/constants/structs follow Vulkan values/layout. That makes things easier for RDDVulkan (it asserts compatibility). +// - We allocate as little as possible in functions expected to be quick (a counterexample is loading/saving shaders) and use alloca() whenever suitable. +// - We try to back opaque ids with the native ones or memory addresses. +// - When using bookkeeping structures because the actual API id of a resource is not enough, we use a PagedAllocator. +// - Every struct has default initializers. +// - Using VectorView to take array-like arguments. Vector<uint8_t> is an exception (an indiom for "BLOB"). +// - If a driver needs some higher-level information (the kind of info RenderingDevice keeps), it shall store a copy of what it needs. +// There's no backwards communication from the driver to query data from RenderingDevice. +// *********************************************************************************** + +#include "core/object/object.h" +#include "core/variant/type_info.h" +#include "servers/display_server.h" +#include "servers/rendering/rendering_device_commons.h" + +#include <algorithm> + +class ApiContextRD; + +// This may one day be used in Godot for interoperability between C arrays, Vector and LocalVector. +// (See https://github.com/godotengine/godot-proposals/issues/5144.) +template <class T> +class VectorView { + const T *_ptr = nullptr; + const uint32_t _size = 0; + +public: + const T &operator[](uint32_t p_index) { + DEV_ASSERT(p_index < _size); + return _ptr[p_index]; + } + + _ALWAYS_INLINE_ const T *ptr() const { return _ptr; } + _ALWAYS_INLINE_ uint32_t size() const { return _size; } + + VectorView() = default; + VectorView(const T &p_ptr) : + // With this one you can pass a single element very conveniently! + _ptr(&p_ptr), + _size(1) {} + VectorView(const T *p_ptr, uint32_t p_size) : + _ptr(p_ptr), _size(p_size) {} + VectorView(const Vector<T> &p_lv) : + _ptr(p_lv.ptr()), _size(p_lv.size()) {} + VectorView(const LocalVector<T> &p_lv) : + _ptr(p_lv.ptr()), _size(p_lv.size()) {} +}; + +// These utilities help drivers avoid allocations. +#define ALLOCA(m_size) ((m_size != 0) ? alloca(m_size) : nullptr) +#define ALLOCA_ARRAY(m_type, m_count) ((m_type *)ALLOCA(sizeof(m_type) * (m_count))) +#define ALLOCA_SINGLE(m_type) ALLOCA_ARRAY(m_type, 1) + +// This helps forwarding certain arrays to the API with confidence. +#define ARRAYS_COMPATIBLE(m_type_a, m_type_b) (sizeof(m_type_a) == sizeof(m_type_b) && alignof(m_type_a) == alignof(m_type_b)) +// This is used when you also need to ensure structured types are compatible field-by-field. +// TODO: The fieldwise check is unimplemented, but still this one is useful, as a strong annotation about the needs. +#define ARRAYS_COMPATIBLE_FIELDWISE(m_type_a, m_type_b) ARRAYS_COMPATIBLE(m_type_a, m_type_b) +// Another utility, to make it easy to compare members of different enums, which is not fine with some compilers. +#define ENUM_MEMBERS_EQUAL(m_a, m_b) ((int64_t)m_a == (int64_t)m_b) + +// This helps using a single paged allocator for many resource types. +template <class... RESOURCE_TYPES> +struct VersatileResourceTemplate { + static constexpr size_t RESOURCE_SIZES[] = { sizeof(RESOURCE_TYPES)... }; + static constexpr size_t MAX_RESOURCE_SIZE = std::max_element(RESOURCE_SIZES, RESOURCE_SIZES + sizeof...(RESOURCE_TYPES))[0]; + uint8_t data[MAX_RESOURCE_SIZE]; + + template <class T> + static T *allocate(PagedAllocator<VersatileResourceTemplate> &p_allocator) { + T *obj = (T *)p_allocator.alloc(); + *obj = T(); + return obj; + } + + template <class T> + static void free(PagedAllocator<VersatileResourceTemplate> &p_allocator, T *p_object) { + p_object->~T(); + p_allocator.free((VersatileResourceTemplate *)p_object); + } +}; + +class RenderingDeviceDriver : public RenderingDeviceCommons { +public: + struct ID { + size_t id = 0; + _ALWAYS_INLINE_ ID() = default; + _ALWAYS_INLINE_ ID(size_t p_id) : + id(p_id) {} + }; + +#define DEFINE_ID(m_name) \ + struct m_name##ID : public ID { \ + _ALWAYS_INLINE_ operator bool() const { return id != 0; } \ + _ALWAYS_INLINE_ m_name##ID &operator=(m_name##ID p_other) { \ + id = p_other.id; \ + return *this; \ + } \ + _ALWAYS_INLINE_ bool operator<(const m_name##ID &p_other) const { return id < p_other.id; } \ + _ALWAYS_INLINE_ m_name##ID(const m_name##ID &p_other) : ID(p_other.id) {} \ + _ALWAYS_INLINE_ explicit m_name##ID(uint64_t p_int) : ID(p_int) {} \ + _ALWAYS_INLINE_ explicit m_name##ID(void *p_ptr) : ID((size_t)p_ptr) {} \ + _ALWAYS_INLINE_ m_name##ID() = default; \ + }; \ + /* Ensure type-punnable to pointer. Makes some things easier.*/ \ + static_assert(sizeof(m_name##ID) == sizeof(void *)); + + // Id types declared before anything else to prevent cyclic dependencies between the different concerns. + DEFINE_ID(Buffer); + DEFINE_ID(Texture); + DEFINE_ID(Sampler); + DEFINE_ID(VertexFormat); + DEFINE_ID(CommandPool); + DEFINE_ID(CommandBuffer); + DEFINE_ID(Framebuffer); + DEFINE_ID(Shader); + DEFINE_ID(UniformSet); + DEFINE_ID(Pipeline); + DEFINE_ID(RenderPass); + DEFINE_ID(QueryPool); + + /****************/ + /**** MEMORY ****/ + /****************/ + + enum MemoryAllocationType { + MEMORY_ALLOCATION_TYPE_CPU, // For images, CPU allocation also means linear, GPU is tiling optimal. + MEMORY_ALLOCATION_TYPE_GPU, + }; + + /*****************/ + /**** BUFFERS ****/ + /*****************/ + + enum BufferUsageBits { + BUFFER_USAGE_TRANSFER_FROM_BIT = (1 << 0), + BUFFER_USAGE_TRANSFER_TO_BIT = (1 << 1), + BUFFER_USAGE_TEXEL_BIT = (1 << 2), + BUFFER_USAGE_UNIFORM_BIT = (1 << 4), + BUFFER_USAGE_STORAGE_BIT = (1 << 5), + BUFFER_USAGE_INDEX_BIT = (1 << 6), + BUFFER_USAGE_VERTEX_BIT = (1 << 7), + BUFFER_USAGE_INDIRECT_BIT = (1 << 8), + }; + + virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) = 0; + // Only for a buffer with BUFFER_USAGE_TEXEL_BIT. + virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) = 0; + virtual void buffer_free(BufferID p_buffer) = 0; + virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) = 0; + virtual uint8_t *buffer_map(BufferID p_buffer) = 0; + virtual void buffer_unmap(BufferID p_buffer) = 0; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + struct TextureView { + DataFormat format = DATA_FORMAT_MAX; + TextureSwizzle swizzle_r = TEXTURE_SWIZZLE_R; + TextureSwizzle swizzle_g = TEXTURE_SWIZZLE_G; + TextureSwizzle swizzle_b = TEXTURE_SWIZZLE_B; + TextureSwizzle swizzle_a = TEXTURE_SWIZZLE_A; + }; + + enum TextureLayout { + TEXTURE_LAYOUT_UNDEFINED, + TEXTURE_LAYOUT_GENERAL, + TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + TEXTURE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, + TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, + TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, + TEXTURE_LAYOUT_PREINITIALIZED, + TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL = 1000164003, + }; + + enum TextureAspect { + TEXTURE_ASPECT_COLOR = 0, + TEXTURE_ASPECT_DEPTH = 1, + TEXTURE_ASPECT_STENCIL = 2, + TEXTURE_ASPECT_MAX + }; + + enum TextureAspectBits { + TEXTURE_ASPECT_COLOR_BIT = (1 << TEXTURE_ASPECT_COLOR), + TEXTURE_ASPECT_DEPTH_BIT = (1 << TEXTURE_ASPECT_DEPTH), + TEXTURE_ASPECT_STENCIL_BIT = (1 << TEXTURE_ASPECT_STENCIL), + }; + + struct TextureSubresource { + TextureAspect aspect = TEXTURE_ASPECT_COLOR; + uint32_t layer = 0; + uint32_t mipmap = 0; + }; + + struct TextureSubresourceLayers { + BitField<TextureAspectBits> aspect; + uint32_t mipmap = 0; + uint32_t base_layer = 0; + uint32_t layer_count = 0; + }; + + struct TextureSubresourceRange { + BitField<TextureAspectBits> aspect; + uint32_t base_mipmap = 0; + uint32_t mipmap_count = 0; + uint32_t base_layer = 0; + uint32_t layer_count = 0; + }; + + struct TextureCopyableLayout { + uint64_t offset = 0; + uint64_t size = 0; + uint64_t row_pitch = 0; + uint64_t depth_pitch = 0; + uint64_t layer_pitch = 0; + }; + + virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) = 0; + virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) = 0; + // texture_create_shared_*() can only use original, non-view textures as original. RenderingDevice is responsible for ensuring that. + virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) = 0; + virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) = 0; + virtual void texture_free(TextureID p_texture) = 0; + virtual uint64_t texture_get_allocation_size(TextureID p_texture) = 0; + virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) = 0; + virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) = 0; + virtual void texture_unmap(TextureID p_texture) = 0; + virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) = 0; + + /*****************/ + /**** SAMPLER ****/ + /*****************/ + + virtual SamplerID sampler_create(const SamplerState &p_state) = 0; + virtual void sampler_free(SamplerID p_sampler) = 0; + virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) = 0; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ + + virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) = 0; + virtual void vertex_format_free(VertexFormatID p_vertex_format) = 0; + + /******************/ + /**** BARRIERS ****/ + /******************/ + + enum PipelineStageBits { + PIPELINE_STAGE_TOP_OF_PIPE_BIT = (1 << 0), + PIPELINE_STAGE_DRAW_INDIRECT_BIT = (1 << 1), + PIPELINE_STAGE_VERTEX_INPUT_BIT = (1 << 2), + PIPELINE_STAGE_VERTEX_SHADER_BIT = (1 << 3), + PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = (1 << 4), + PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = (1 << 5), + PIPELINE_STAGE_GEOMETRY_SHADER_BIT = (1 << 6), + PIPELINE_STAGE_FRAGMENT_SHADER_BIT = (1 << 7), + PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = (1 << 8), + PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = (1 << 9), + PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = (1 << 10), + PIPELINE_STAGE_COMPUTE_SHADER_BIT = (1 << 11), + PIPELINE_STAGE_TRANSFER_BIT = (1 << 12), + PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = (1 << 13), + PIPELINE_STAGE_ALL_GRAPHICS_BIT = (1 << 15), + PIPELINE_STAGE_ALL_COMMANDS_BIT = (1 << 16), + }; + + enum BarrierAccessBits { + BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT = (1 << 0), + BARRIER_ACCESS_INDEX_READ_BIT = (1 << 1), + BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = (1 << 2), + BARRIER_ACCESS_UNIFORM_READ_BIT = (1 << 3), + BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT = (1 << 4), + BARRIER_ACCESS_SHADER_READ_BIT = (1 << 5), + BARRIER_ACCESS_SHADER_WRITE_BIT = (1 << 6), + BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT = (1 << 7), + BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = (1 << 8), + BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = (1 << 9), + BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = (1 << 10), + BARRIER_ACCESS_TRANSFER_READ_BIT = (1 << 11), + BARRIER_ACCESS_TRANSFER_WRITE_BIT = (1 << 12), + BARRIER_ACCESS_HOST_READ_BIT = (1 << 13), + BARRIER_ACCESS_HOST_WRITE_BIT = (1 << 14), + BARRIER_ACCESS_MEMORY_READ_BIT = (1 << 15), + BARRIER_ACCESS_MEMORY_WRITE_BIT = (1 << 16), + BARRIER_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT = (1 << 23), + }; + + struct MemoryBarrier { + BitField<BarrierAccessBits> src_access; + BitField<BarrierAccessBits> dst_access; + }; + + struct BufferBarrier { + BufferID buffer; + BitField<BarrierAccessBits> src_access; + BitField<BarrierAccessBits> dst_access; + uint64_t offset = 0; + uint64_t size = 0; + }; + + struct TextureBarrier { + TextureID texture; + BitField<BarrierAccessBits> src_access; + BitField<BarrierAccessBits> dst_access; + TextureLayout prev_layout = TEXTURE_LAYOUT_UNDEFINED; + TextureLayout next_layout = TEXTURE_LAYOUT_UNDEFINED; + TextureSubresourceRange subresources; + }; + + virtual void command_pipeline_barrier( + CommandBufferID p_cmd_buffer, + BitField<PipelineStageBits> p_src_stages, + BitField<PipelineStageBits> p_dst_stages, + VectorView<MemoryBarrier> p_memory_barriers, + VectorView<BufferBarrier> p_buffer_barriers, + VectorView<TextureBarrier> p_texture_barriers) = 0; + + /*************************/ + /**** COMMAND BUFFERS ****/ + /*************************/ + + // ----- POOL ----- + + enum CommandBufferType { + COMMAND_BUFFER_TYPE_PRIMARY, + COMMAND_BUFFER_TYPE_SECONDARY, + }; + + virtual CommandPoolID command_pool_create(CommandBufferType p_cmd_buffer_type) = 0; + virtual void command_pool_free(CommandPoolID p_cmd_pool) = 0; + + // ----- BUFFER ----- + + virtual CommandBufferID command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) = 0; + virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) = 0; + virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) = 0; + virtual void command_buffer_end(CommandBufferID p_cmd_buffer) = 0; + virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) = 0; + + /*********************/ + /**** FRAMEBUFFER ****/ + /*********************/ + + virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) = 0; + virtual void framebuffer_free(FramebufferID p_framebuffer) = 0; + + /****************/ + /**** SHADER ****/ + /****************/ + + virtual String shader_get_binary_cache_key() = 0; + virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) = 0; + virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) = 0; + // Only meaningful if API_TRAIT_SHADER_CHANGE_INVALIDATION is SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH. + virtual uint32_t shader_get_layout_hash(ShaderID p_shader) { return 0; } + virtual void shader_free(ShaderID p_shader) = 0; + +protected: + // An optional service to implementations. + Error _reflect_spirv(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection); + +public: + /*********************/ + /**** UNIFORM SET ****/ + /*********************/ + + struct BoundUniform { + UniformType type = UNIFORM_TYPE_MAX; + uint32_t binding = 0xffffffff; // Binding index as specified in shader. + LocalVector<ID> ids; + }; + + virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) = 0; + virtual void uniform_set_free(UniformSetID p_uniform_set) = 0; + + // ----- COMMANDS ----- + + virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) = 0; + + /******************/ + /**** TRANSFER ****/ + /******************/ + + struct BufferCopyRegion { + uint64_t src_offset = 0; + uint64_t dst_offset = 0; + uint64_t size = 0; + }; + + struct TextureCopyRegion { + TextureSubresourceLayers src_subresources; + Vector3i src_offset; + TextureSubresourceLayers dst_subresources; + Vector3i dst_offset; + Vector3i size; + }; + + struct BufferTextureCopyRegion { + uint64_t buffer_offset = 0; + TextureSubresourceLayers texture_subresources; + Vector3i texture_offset; + Vector3i texture_region_size; + }; + + virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) = 0; + virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) = 0; + + virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) = 0; + virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) = 0; + virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) = 0; + + virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) = 0; + virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) = 0; + + /******************/ + /**** PIPELINE ****/ + /******************/ + + virtual void pipeline_free(PipelineID p_pipeline) = 0; + + // ----- BINDING ----- + + virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_first_index, VectorView<uint32_t> p_data) = 0; + + // ----- CACHE ----- + + virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) = 0; + virtual void pipeline_cache_free() = 0; + virtual size_t pipeline_cache_query_size() = 0; + virtual Vector<uint8_t> pipeline_cache_serialize() = 0; + + /*******************/ + /**** RENDERING ****/ + /*******************/ + + // ----- SUBPASS ----- + + enum AttachmentLoadOp { + ATTACHMENT_LOAD_OP_LOAD = 0, + ATTACHMENT_LOAD_OP_CLEAR = 1, + ATTACHMENT_LOAD_OP_DONT_CARE = 2, + }; + + enum AttachmentStoreOp { + ATTACHMENT_STORE_OP_STORE = 0, + ATTACHMENT_STORE_OP_DONT_CARE = 1, + }; + + struct Attachment { + DataFormat format = DATA_FORMAT_MAX; + TextureSamples samples = TEXTURE_SAMPLES_MAX; + AttachmentLoadOp load_op = ATTACHMENT_LOAD_OP_DONT_CARE; + AttachmentStoreOp store_op = ATTACHMENT_STORE_OP_DONT_CARE; + AttachmentLoadOp stencil_load_op = ATTACHMENT_LOAD_OP_DONT_CARE; + AttachmentStoreOp stencil_store_op = ATTACHMENT_STORE_OP_DONT_CARE; + TextureLayout initial_layout = TEXTURE_LAYOUT_UNDEFINED; + TextureLayout final_layout = TEXTURE_LAYOUT_UNDEFINED; + }; + + struct AttachmentReference { + static const uint32_t UNUSED = 0xffffffff; + uint32_t attachment = UNUSED; + TextureLayout layout = TEXTURE_LAYOUT_UNDEFINED; + BitField<TextureAspectBits> aspect; + }; + + struct Subpass { + LocalVector<AttachmentReference> input_references; + LocalVector<AttachmentReference> color_references; + AttachmentReference depth_stencil_reference; + LocalVector<AttachmentReference> resolve_references; + LocalVector<uint32_t> preserve_attachments; + AttachmentReference vrs_reference; + }; + + struct SubpassDependency { + uint32_t src_subpass = 0xffffffff; + uint32_t dst_subpass = 0xffffffff; + BitField<PipelineStageBits> src_stages; + BitField<PipelineStageBits> dst_stages; + BitField<BarrierAccessBits> src_access; + BitField<BarrierAccessBits> dst_access; + }; + + virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) = 0; + virtual void render_pass_free(RenderPassID p_render_pass) = 0; + + // ----- COMMANDS ----- + + union RenderPassClearValue { + Color color = {}; + struct { + float depth; + uint32_t stencil; + }; + }; + + struct AttachmentClear { + BitField<TextureAspectBits> aspect; + uint32_t color_attachment = 0xffffffff; + RenderPassClearValue value; + }; + + virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) = 0; + virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) = 0; + virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) = 0; + virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) = 0; + virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) = 0; + virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) = 0; + + // Binding. + virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) = 0; + virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) = 0; + + // Drawing. + virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) = 0; + virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) = 0; + virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) = 0; + virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) = 0; + virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) = 0; + virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) = 0; + + // Buffer binding. + virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) = 0; + virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) = 0; + + // Dynamic state. + virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) = 0; + virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) = 0; + + // ----- PIPELINE ----- + + virtual PipelineID render_pipeline_create( + ShaderID p_shader, + VertexFormatID p_vertex_format, + RenderPrimitive p_render_primitive, + PipelineRasterizationState p_rasterization_state, + PipelineMultisampleState p_multisample_state, + PipelineDepthStencilState p_depth_stencil_state, + PipelineColorBlendState p_blend_state, + VectorView<int32_t> p_color_attachments, + BitField<PipelineDynamicStateFlags> p_dynamic_state, + RenderPassID p_render_pass, + uint32_t p_render_subpass, + VectorView<PipelineSpecializationConstant> p_specialization_constants) = 0; + + /*****************/ + /**** COMPUTE ****/ + /*****************/ + + // ----- COMMANDS ----- + + // Binding. + virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) = 0; + virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) = 0; + + // Dispatching. + virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) = 0; + virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) = 0; + + // ----- PIPELINE ----- + + virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) = 0; + + /*****************/ + /**** QUERIES ****/ + /*****************/ + + // ----- TIMESTAMP ----- + + // Basic. + virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) = 0; + virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) = 0; + virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) = 0; + virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) = 0; + + // Commands. + virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) = 0; + virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) = 0; + + /****************/ + /**** SCREEN ****/ + /****************/ + + virtual DataFormat screen_get_format() = 0; + + /********************/ + /**** SUBMISSION ****/ + /********************/ + + virtual void begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) = 0; + virtual void end_segment() = 0; + + /**************/ + /**** MISC ****/ + /**************/ + + enum ObjectType { + OBJECT_TYPE_TEXTURE, + OBJECT_TYPE_SAMPLER, + OBJECT_TYPE_BUFFER, + OBJECT_TYPE_SHADER, + OBJECT_TYPE_UNIFORM_SET, + OBJECT_TYPE_PIPELINE, + }; + + struct MultiviewCapabilities { + bool is_supported = false; + bool geometry_shader_is_supported = false; + bool tessellation_shader_is_supported = false; + uint32_t max_view_count = 0; + uint32_t max_instance_count = 0; + }; + + enum ApiTrait { + API_TRAIT_HONORS_PIPELINE_BARRIERS, + API_TRAIT_SHADER_CHANGE_INVALIDATION, + API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT, + API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP, + API_TRAIT_SECONDARY_VIEWPORT_SCISSOR, + }; + enum ShaderChangeInvalidation { + SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS, + // What Vulkan does. + SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE, + // What D3D12 does. + SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH, + }; + + virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) = 0; + virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) = 0; + virtual uint64_t get_total_memory_used() = 0; + virtual uint64_t limit_get(Limit p_limit) = 0; + virtual uint64_t api_trait_get(ApiTrait p_trait); + virtual bool has_feature(Features p_feature) = 0; + virtual const MultiviewCapabilities &get_multiview_capabilities() = 0; + + /******************/ + + virtual ~RenderingDeviceDriver(); +}; + +using RDD = RenderingDeviceDriver; + +#endif // RENDERING_DEVICE_DRIVER_H diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 65a6da8ac3..77fe91e4c9 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -379,6 +379,7 @@ void RenderingServerDefault::sync() { } void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred()."); if (create_thread) { command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step); } else { diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 3d75ced3e3..dac1275957 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -476,6 +476,8 @@ public: FUNC2(voxel_gi_set_interior, RID, bool) FUNC2(voxel_gi_set_use_two_bounces, RID, bool) + FUNC0(sdfgi_reset) + /* PARTICLES */ #undef ServerName @@ -938,6 +940,8 @@ public: FUNC1(canvas_set_shadow_texture_size, int) + FUNC1R(Rect2, _debug_canvas_item_get_rect, RID) + /* GLOBAL SHADER UNIFORMS */ #undef server_name diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 71f821205e..6875400d1e 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -432,13 +432,13 @@ static String _get_global_shader_uniform_from_type_and_index(const String &p_buf return "(" + p_buffer + "[" + p_index + "].xyzw)"; } case ShaderLanguage::TYPE_MAT2: { - return "mat2(" + p_buffer + "[" + p_index + "].xy," + p_buffer + "[" + p_index + "+1].xy)"; + return "mat2(" + p_buffer + "[" + p_index + "].xy," + p_buffer + "[" + p_index + "+1u].xy)"; } case ShaderLanguage::TYPE_MAT3: { - return "mat3(" + p_buffer + "[" + p_index + "].xyz," + p_buffer + "[" + p_index + "+1].xyz," + p_buffer + "[" + p_index + "+2].xyz)"; + return "mat3(" + p_buffer + "[" + p_index + "].xyz," + p_buffer + "[" + p_index + "+1u].xyz," + p_buffer + "[" + p_index + "+2u].xyz)"; } case ShaderLanguage::TYPE_MAT4: { - return "mat4(" + p_buffer + "[" + p_index + "].xyzw," + p_buffer + "[" + p_index + "+1].xyzw," + p_buffer + "[" + p_index + "+2].xyzw," + p_buffer + "[" + p_index + "+3].xyzw)"; + return "mat4(" + p_buffer + "[" + p_index + "].xyzw," + p_buffer + "[" + p_index + "+1u].xyzw," + p_buffer + "[" + p_index + "+2u].xyzw," + p_buffer + "[" + p_index + "+3u].xyzw)"; } default: { ERR_FAIL_V("void"); @@ -543,7 +543,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene uniform_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced for (int k = 0; k < uniform_names.size(); k++) { - StringName uniform_name = uniform_names[k]; + const StringName &uniform_name = uniform_names[k]; const SL::ShaderNode::Uniform &uniform = pnode->uniforms[uniform_name]; String ucode; @@ -670,7 +670,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene varying_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced for (int k = 0; k < varying_names.size(); k++) { - StringName varying_name = varying_names[k]; + const StringName &varying_name = varying_names[k]; const SL::ShaderNode::Varying &varying = pnode->varyings[varying_name]; if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) { @@ -1191,6 +1191,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene // we will add logic to automatically switch between // sampler2D and sampler2D array and vec2 UV and vec3 UV. bool multiview_uv_needed = false; + bool is_normal_roughness_texture = false; for (int i = 1; i < onode->arguments.size(); i++) { if (i > 1) { @@ -1259,7 +1260,6 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene // Need to map from texture to sampler in order to sample when using Vulkan GLSL. String sampler_name; bool is_depth_texture = false; - bool is_normal_roughness_texture = false; if (actions.custom_samplers.has(texture_uniform)) { sampler_name = actions.custom_samplers[texture_uniform]; @@ -1340,6 +1340,9 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (is_screen_texture && !texture_func_returns_data && actions.apply_luminance_multiplier) { code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))"; } + if (is_normal_roughness_texture) { + code = "normal_roughness_compatibility(" + code + ")"; + } } break; case SL::OP_INDEX: { code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 41f2fbb03f..a2da26eb65 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -4639,6 +4639,10 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi return false; } + if (shader->varyings.has(var->name)) { + return _validate_varying_assign(shader->varyings[var->name], r_message); + } + if (!(p_function_info.built_ins.has(var->name) && p_function_info.built_ins[var->name].constant)) { return true; } diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index a6b4646f43..3e19e8f01a 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -361,7 +361,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL); - shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["AMOUNT_RATIO"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["AMOUNT_RATIO"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["start"].main_function = true; shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; @@ -394,7 +394,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_NORMAL"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_DEPTH"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["ATTRACTOR_FORCE"] = constt(ShaderLanguage::TYPE_VEC3); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["AMOUNT_RATIO"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["AMOUNT_RATIO"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["process"].main_function = true; { diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index 6332dd578e..cf37fbfb4a 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -151,6 +151,9 @@ public: virtual void render_target_set_as_unused(RID p_render_target) = 0; virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) = 0; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const = 0; + virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) = 0; + virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const = 0; + virtual void render_target_do_msaa_resolve(RID p_render_target) = 0; virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) = 0; virtual bool render_target_is_using_hdr(RID p_render_target) const = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 137cbfa861..00015b74a1 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -103,7 +103,7 @@ PackedInt64Array RenderingServer::_instances_cull_convex_bind(const TypedArray<P } Vector<Plane> planes; for (int i = 0; i < p_convex.size(); ++i) { - Variant v = p_convex[i]; + const Variant &v = p_convex[i]; ERR_FAIL_COND_V(v.get_type() != Variant::PLANE, PackedInt64Array()); planes.push_back(v); } @@ -323,16 +323,18 @@ RID RenderingServer::get_white_texture() { } void _get_axis_angle(const Vector3 &p_normal, const Vector4 &p_tangent, float &r_angle, Vector3 &r_axis) { - Vector3 tangent = Vector3(p_tangent.x, p_tangent.y, p_tangent.z); + Vector3 normal = p_normal.normalized(); + Vector3 tangent = Vector3(p_tangent.x, p_tangent.y, p_tangent.z).normalized(); float d = p_tangent.w; - Vector3 binormal = p_normal.cross(tangent); + Vector3 binormal = normal.cross(tangent).normalized(); + real_t angle; - r_angle = Math::acos((tangent.x + binormal.y + p_normal.z - 1.0) / 2.0); - float denom = 2.0 * Math::sin(r_angle); - r_axis.x = (p_normal.y - binormal.z) / denom; - r_axis.y = (tangent.z - p_normal.x) / denom; - r_axis.z = (binormal.x - tangent.y) / denom; - r_axis.normalize(); + Basis tbn = Basis(); + tbn.rows[0] = tangent; + tbn.rows[1] = binormal; + tbn.rows[2] = normal; + tbn.get_axis_angle(r_axis, angle); + r_angle = float(angle); if (d < 0.0) { r_angle = CLAMP((1.0 - r_angle / Math_PI) * 0.5, 0.0, 0.49999); @@ -346,13 +348,11 @@ void _get_axis_angle(const Vector3 &p_normal, const Vector4 &p_tangent, float &r void _get_tbn_from_axis_angle(const Vector3 &p_axis, float p_angle, Vector3 &r_normal, Vector4 &r_tangent) { float binormal_sign = p_angle > 0.5 ? 1.0 : -1.0; float angle = Math::abs(p_angle * 2.0 - 1.0) * Math_PI; - float c = cos(angle); - float s = sin(angle); - Vector3 omc_axis = (1.0 - c) * p_axis; - Vector3 s_axis = s * p_axis; - Vector3 tan = omc_axis.x * p_axis + Vector3(c, -s_axis.z, s_axis.y); + + Basis tbn = Basis(p_axis, angle); + Vector3 tan = tbn.rows[0]; r_tangent = Vector4(tan.x, tan.y, tan.z, binormal_sign); - r_normal = omc_axis.z * p_axis + Vector3(-s_axis.y, s_axis.x, c); + r_normal = tbn.rows[2]; } Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint32_t *p_offsets, uint32_t p_vertex_stride, uint32_t p_normal_stride, uint32_t p_attrib_stride, uint32_t p_skin_stride, Vector<uint8_t> &r_vertex_array, Vector<uint8_t> &r_attrib_array, Vector<uint8_t> &r_skin_array, int p_vertex_array_len, Vector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb, Vector4 &r_uv_scale) { @@ -400,7 +400,14 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint max_val = max_val.abs().max(min_val.abs()); max_val2 = max_val2.abs().max(min_val2.abs()); - r_uv_scale = Vector4(max_val.x, max_val.y, max_val2.x, max_val2.y) * Vector4(2.0, 2.0, 2.0, 2.0); + if (min_val.x >= 0.0 && min_val2.x >= 0.0 && max_val.x <= 1.0 && max_val2.x <= 1.0 && + min_val.y >= 0.0 && min_val2.y >= 0.0 && max_val.y <= 1.0 && max_val2.y <= 1.0) { + // When all channels are in the 0-1 range, we will compress to 16-bit without scaling to + // preserve the bits as best as possible. + r_uv_scale = Vector4(0.0, 0.0, 0.0, 0.0); + } else { + r_uv_scale = Vector4(max_val.x, max_val.y, max_val2.x, max_val2.y) * Vector4(2.0, 2.0, 2.0, 2.0); + } } for (int ai = 0; ai < RS::ARRAY_MAX; ai++) { @@ -441,25 +448,22 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint const Vector3 *src = array.ptr(); - // Setting vertices means regenerating the AABB. - AABB aabb; + r_aabb = AABB(); if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) { // First we need to generate the AABB for the entire surface. for (int i = 0; i < p_vertex_array_len; i++) { if (i == 0) { - aabb = AABB(src[i], SMALL_VEC3); + r_aabb = AABB(src[i], SMALL_VEC3); } else { - aabb.expand_to(src[i]); + r_aabb.expand_to(src[i]); } } - bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT); - - if (!using_normals_tangents) { + if (!(p_format & RS::ARRAY_FORMAT_NORMAL)) { // Early out if we are only setting vertex positions. for (int i = 0; i < p_vertex_array_len; i++) { - Vector3 pos = (src[i] - aabb.position) / aabb.size; + Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; uint16_t vector[4] = { (uint16_t)CLAMP(pos.x * 65535, 0, 65535), (uint16_t)CLAMP(pos.y * 65535, 0, 65535), @@ -474,13 +478,14 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // Validate normal and tangent arrays. ERR_FAIL_COND_V(p_arrays[RS::ARRAY_NORMAL].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER); - Variant::Type tangent_type = p_arrays[RS::ARRAY_TANGENT].get_type(); - ERR_FAIL_COND_V(tangent_type != Variant::PACKED_FLOAT32_ARRAY && tangent_type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER); Vector<Vector3> normal_array = p_arrays[RS::ARRAY_NORMAL]; ERR_FAIL_COND_V(normal_array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER); const Vector3 *normal_src = normal_array.ptr(); + Variant::Type tangent_type = p_arrays[RS::ARRAY_TANGENT].get_type(); + ERR_FAIL_COND_V(tangent_type != Variant::PACKED_FLOAT32_ARRAY && tangent_type != Variant::PACKED_FLOAT64_ARRAY && tangent_type != Variant::NIL, ERR_INVALID_PARAMETER); + // We need a different version if using double precision tangents. if (tangent_type == Variant::PACKED_FLOAT32_ARRAY) { Vector<float> tangent_array = p_arrays[RS::ARRAY_TANGENT]; @@ -507,7 +512,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // Store vertex position + angle. { - Vector3 pos = (src[i] - aabb.position) / aabb.size; + Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; uint16_t vector[4] = { (uint16_t)CLAMP(pos.x * 65535, 0, 65535), (uint16_t)CLAMP(pos.y * 65535, 0, 65535), @@ -518,7 +523,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4); } } - } else { // PACKED_FLOAT64_ARRAY + } else if (tangent_type == Variant::PACKED_FLOAT64_ARRAY) { Vector<double> tangent_array = p_arrays[RS::ARRAY_TANGENT]; ERR_FAIL_COND_V(tangent_array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); const double *tangent_src = tangent_array.ptr(); @@ -543,7 +548,41 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // Store vertex position + angle. { - Vector3 pos = (src[i] - aabb.position) / aabb.size; + Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; + uint16_t vector[4] = { + (uint16_t)CLAMP(pos.x * 65535, 0, 65535), + (uint16_t)CLAMP(pos.y * 65535, 0, 65535), + (uint16_t)CLAMP(pos.z * 65535, 0, 65535), + (uint16_t)CLAMP(angle * 65535, 0, 65535) + }; + + memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4); + } + } + } else { // No tangent array. + // Set data for vertex, normal, and tangent. + for (int i = 0; i < p_vertex_array_len; i++) { + float angle; + Vector3 axis; + // Generate an arbitrary vector that is tangential to normal. + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized()); + Vector4 tangent = Vector4(tan.x, tan.y, tan.z, 1.0); + _get_axis_angle(normal_src[i], tangent, angle, axis); + + // Store axis. + { + Vector2 res = axis.octahedron_encode(); + uint16_t vector[2] = { + (uint16_t)CLAMP(res.x * 65535, 0, 65535), + (uint16_t)CLAMP(res.y * 65535, 0, 65535), + }; + + memcpy(&vw[p_offsets[RS::ARRAY_NORMAL] + i * p_normal_stride], vector, 4); + } + + // Store vertex position + angle. + { + Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; uint16_t vector[4] = { (uint16_t)CLAMP(pos.x * 65535, 0, 65535), (uint16_t)CLAMP(pos.y * 65535, 0, 65535), @@ -562,14 +601,12 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(float) * 3); if (i == 0) { - aabb = AABB(src[i], SMALL_VEC3); + r_aabb = AABB(src[i], SMALL_VEC3); } else { - aabb.expand_to(src[i]); + r_aabb.expand_to(src[i]); } } } - - r_aabb = aabb; } } break; @@ -598,7 +635,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // If using compression we store tangent while storing vertices. if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) { Variant::Type type = p_arrays[ai].get_type(); - ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY && type != Variant::NIL, ERR_INVALID_PARAMETER); + if (type == Variant::PACKED_FLOAT32_ARRAY) { Vector<float> array = p_arrays[ai]; ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); @@ -620,7 +658,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4); } - } else { // PACKED_FLOAT64_ARRAY + } else if (type == Variant::PACKED_FLOAT64_ARRAY) { Vector<double> array = p_arrays[ai]; ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); const double *src_ptr = array.ptr(); @@ -641,6 +679,30 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4); } + } else { // No tangent array. + ERR_FAIL_COND_V(p_arrays[RS::ARRAY_NORMAL].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER); + + Vector<Vector3> normal_array = p_arrays[RS::ARRAY_NORMAL]; + ERR_FAIL_COND_V(normal_array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER); + const Vector3 *normal_src = normal_array.ptr(); + // Set data for tangent. + for (int i = 0; i < p_vertex_array_len; i++) { + // Generate an arbitrary vector that is tangential to normal. + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized()); + Vector2 res = tan.octahedron_tangent_encode(1.0); + uint16_t vector[2] = { + (uint16_t)CLAMP(res.x * 65535, 0, 65535), + (uint16_t)CLAMP(res.y * 65535, 0, 65535), + }; + + if (vector[0] == 0 && vector[1] == 65535) { + // (1, 1) and (0, 1) decode to the same value, but (0, 1) messes with our compression detection. + // So we sanitize here. + vector[0] = 65535; + } + + memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4); + } } } } break; @@ -673,8 +735,11 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int i = 0; i < p_vertex_array_len; i++) { Vector2 vec = src[i]; - // Normalize into 0-1 from possible range -uv_scale - uv_scale. - vec = vec / (Vector2(r_uv_scale.x, r_uv_scale.y)) + Vector2(0.5, 0.5); + if (!r_uv_scale.is_zero_approx()) { + // Normalize into 0-1 from possible range -uv_scale - uv_scale. + vec = vec / (Vector2(r_uv_scale.x, r_uv_scale.y)) + Vector2(0.5, 0.5); + } + uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) }; memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4); } @@ -698,8 +763,10 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int i = 0; i < p_vertex_array_len; i++) { Vector2 vec = src[i]; - // Normalize into 0-1 from possible range -uv_scale - uv_scale. - vec = vec / (Vector2(r_uv_scale.z, r_uv_scale.w)) + Vector2(0.5, 0.5); + if (!r_uv_scale.is_zero_approx()) { + // Normalize into 0-1 from possible range -uv_scale - uv_scale. + vec = vec / (Vector2(r_uv_scale.z, r_uv_scale.w)) + Vector2(0.5, 0.5); + } uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) }; memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4); } @@ -1126,10 +1193,15 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa array_len = v3.size(); } break; default: { - ERR_FAIL_V(ERR_INVALID_DATA); + ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Vertex array must be a PackedVector2Array or PackedVector3Array."); } break; } ERR_FAIL_COND_V(array_len == 0, ERR_INVALID_DATA); + } else if (i == RS::ARRAY_NORMAL) { + if (p_arrays[RS::ARRAY_TANGENT].get_type() == Variant::NIL) { + // We must use tangents if using normals. + format |= (1ULL << RS::ARRAY_TANGENT); + } } else if (i == RS::ARRAY_BONES) { switch (p_arrays[i].get_type()) { case Variant::PACKED_INT32_ARRAY: { @@ -1142,7 +1214,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa } } break; default: { - ERR_FAIL_V(ERR_INVALID_DATA); + ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bones array must be a PackedInt32Array."); } break; } } else if (i == RS::ARRAY_INDEX) { @@ -1194,11 +1266,10 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa format |= RS::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY; } - if (format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && ((format & RS::ARRAY_FORMAT_NORMAL) || (format & RS::ARRAY_FORMAT_TANGENT))) { + if ((format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && ((format & RS::ARRAY_FORMAT_NORMAL) || (format & RS::ARRAY_FORMAT_TANGENT))) { // If using normals or tangents, then we need all three. ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_VERTEX), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals or tangents without vertex array."); ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_NORMAL), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using tangents without normal array."); - ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_TANGENT), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals without tangent array."); } int vertex_array_size = (vertex_element_size + normal_element_size) * array_len; @@ -1314,7 +1385,7 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p mesh_add_surface(p_mesh, sd); } -Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb) const { +Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb, const Vector4 &p_uv_scale) const { uint32_t offsets[RS::ARRAY_MAX]; uint32_t vertex_elem_size; @@ -1359,10 +1430,8 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t Vector3 *w = arr_3d.ptrw(); if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) { - bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT); - // We only have vertices to read, so just read them and skip everything else. - if (!using_normals_tangents) { + if (!(p_format & RS::ARRAY_FORMAT_NORMAL)) { for (int j = 0; j < p_vertex_len; j++) { const uint16_t *v = reinterpret_cast<const uint16_t *>(&r[j * vertex_elem_size + offsets[i]]); Vector3 vec = Vector3(float(v[0]) / 65535.0, float(v[1]) / 65535.0, float(v[2]) / 65535.0); @@ -1399,7 +1468,7 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t tangentsw[j * 4 + 3] = tan.w; } ret[RS::ARRAY_NORMAL] = normals; - ret[RS::ARRAY_FORMAT_TANGENT] = tangents; + ret[RS::ARRAY_TANGENT] = tangents; } else { for (int j = 0; j < p_vertex_len; j++) { @@ -1472,7 +1541,12 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int j = 0; j < p_vertex_len; j++) { const uint16_t *v = reinterpret_cast<const uint16_t *>(&ar[j * attrib_elem_size + offsets[i]]); - w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + Vector2 vec = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + if (!p_uv_scale.is_zero_approx()) { + vec = (vec - Vector2(0.5, 0.5)) * Vector2(p_uv_scale.x, p_uv_scale.y); + } + + w[j] = vec; } } else { for (int j = 0; j < p_vertex_len; j++) { @@ -1492,7 +1566,11 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int j = 0; j < p_vertex_len; j++) { const uint16_t *v = reinterpret_cast<const uint16_t *>(&ar[j * attrib_elem_size + offsets[i]]); - w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + Vector2 vec = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + if (!p_uv_scale.is_zero_approx()) { + vec = (vec - Vector2(0.5, 0.5)) * Vector2(p_uv_scale.z, p_uv_scale.w); + } + w[j] = vec; } } else { for (int j = 0; j < p_vertex_len; j++) { @@ -1689,7 +1767,7 @@ TypedArray<Array> RenderingServer::mesh_surface_get_blend_shape_arrays(RID p_mes for (uint32_t i = 0; i < blend_shape_count; i++) { Vector<uint8_t> bs_data = blend_shape_data.slice(i * divisor, (i + 1) * divisor); Vector<uint8_t> unused; - blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0, sd.aabb)); + blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0, sd.aabb, sd.uv_scale)); } return blend_shape_array; @@ -1711,7 +1789,7 @@ Array RenderingServer::mesh_create_arrays_from_surface_data(const SurfaceData &p uint64_t format = p_data.format; - return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len, p_data.aabb); + return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len, p_data.aabb, p_data.uv_scale); } #if 0 Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const { @@ -1724,6 +1802,14 @@ Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_su } #endif +Rect2 RenderingServer::debug_canvas_item_get_rect(RID p_item) { +#ifdef TOOLS_ENABLED + return _debug_canvas_item_get_rect(p_item); +#else + return Rect2(); +#endif +} + int RenderingServer::global_shader_uniform_type_get_shader_datatype(GlobalShaderParameterType p_type) { switch (p_type) { case RS::GLOBAL_VAR_TYPE_BOOL: @@ -2036,55 +2122,81 @@ Vector<uint8_t> _convert_surface_version_1_to_surface_version_2(uint64_t p_forma return new_vertex_data; } +#ifdef TOOLS_ENABLED +void RenderingServer::set_surface_upgrade_callback(SurfaceUpgradeCallback p_callback) { + surface_upgrade_callback = p_callback; +} + +void RenderingServer::set_warn_on_surface_upgrade(bool p_warn) { + warn_on_surface_upgrade = p_warn; +} +#endif + #ifndef DISABLE_DEPRECATED -void RenderingServer::_fix_surface_compatibility(SurfaceData &p_surface) { +void RenderingServer::fix_surface_compatibility(SurfaceData &p_surface, const String &p_path) { uint64_t surface_version = p_surface.format & (ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT); ERR_FAIL_COND_MSG(surface_version > ARRAY_FLAG_FORMAT_CURRENT_VERSION, "Cannot convert surface with version provided (" + itos((surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + ") to current version (" + itos((RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + ")"); +#ifdef TOOLS_ENABLED + // Editor callback to ask user about re-saving all meshes. + if (surface_upgrade_callback && warn_on_surface_upgrade) { + surface_upgrade_callback(); + } + + if (warn_on_surface_upgrade) { + WARN_PRINT_ONCE_ED("At least one surface uses an old surface format and needs to be upgraded. The upgrade happens automatically at load time every time until the mesh is saved again or re-imported. Once saved (or re-imported), this mesh will be incompatible with earlier versions of Godot."); + + if (!p_path.is_empty()) { + WARN_PRINT("A surface of " + p_path + " uses an old surface format and needs to be upgraded."); + } + } +#endif + if (surface_version == ARRAY_FLAG_FORMAT_VERSION_1) { // The only difference for now is that Version 1 uses interleaved vertex positions while version 2 does not. // I.e. PNTPNTPNT -> PPPNTNTNT. - WARN_PRINT_ED("Upgrading mesh from older surface format. Once saved again (or re-imported), this mesh will be incompatible with earlier versions of Godot."); - - int vertex_size = 0; - int normal_size = 0; - int tangent_size = 0; - if (p_surface.format & ARRAY_FORMAT_VERTEX) { - if (p_surface.format & ARRAY_FLAG_USE_2D_VERTICES) { - vertex_size = sizeof(float) * 2; - } else { - vertex_size = sizeof(float) * 3; + + if (p_surface.vertex_data.size() > 0 && p_surface.vertex_count > 0) { + int vertex_size = 0; + int normal_size = 0; + int tangent_size = 0; + if (p_surface.format & ARRAY_FORMAT_VERTEX) { + if (p_surface.format & ARRAY_FLAG_USE_2D_VERTICES) { + vertex_size = sizeof(float) * 2; + } else { + vertex_size = sizeof(float) * 3; + } } - } - if (p_surface.format & ARRAY_FORMAT_NORMAL) { - normal_size += sizeof(uint16_t) * 2; - } - if (p_surface.format & ARRAY_FORMAT_TANGENT) { - tangent_size = sizeof(uint16_t) * 2; - } - int stride = p_surface.vertex_data.size() / p_surface.vertex_count; - int position_stride = vertex_size; - int normal_tangent_stride = normal_size + tangent_size; + if (p_surface.format & ARRAY_FORMAT_NORMAL) { + normal_size += sizeof(uint16_t) * 2; + } + if (p_surface.format & ARRAY_FORMAT_TANGENT) { + tangent_size = sizeof(uint16_t) * 2; + } + int stride = p_surface.vertex_data.size() / p_surface.vertex_count; + int position_stride = vertex_size; + int normal_tangent_stride = normal_size + tangent_size; - p_surface.vertex_data = _convert_surface_version_1_to_surface_version_2(p_surface.format, p_surface.vertex_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride); + p_surface.vertex_data = _convert_surface_version_1_to_surface_version_2(p_surface.format, p_surface.vertex_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride); - if (p_surface.blend_shape_data.size() > 0) { - // The size of one blend shape. - int divisor = (vertex_size + normal_size + tangent_size) * p_surface.vertex_count; - ERR_FAIL_COND((p_surface.blend_shape_data.size() % divisor) != 0); + if (p_surface.blend_shape_data.size() > 0) { + // The size of one blend shape. + int divisor = (vertex_size + normal_size + tangent_size) * p_surface.vertex_count; + ERR_FAIL_COND((p_surface.blend_shape_data.size() % divisor) != 0); - uint32_t blend_shape_count = p_surface.blend_shape_data.size() / divisor; + uint32_t blend_shape_count = p_surface.blend_shape_data.size() / divisor; - Vector<uint8_t> new_blend_shape_data; - for (uint32_t i = 0; i < blend_shape_count; i++) { - Vector<uint8_t> bs_data = p_surface.blend_shape_data.slice(i * divisor, (i + 1) * divisor); - Vector<uint8_t> blend_shape = _convert_surface_version_1_to_surface_version_2(p_surface.format, bs_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride); - new_blend_shape_data.append_array(blend_shape); - } + Vector<uint8_t> new_blend_shape_data; + for (uint32_t i = 0; i < blend_shape_count; i++) { + Vector<uint8_t> bs_data = p_surface.blend_shape_data.slice(i * divisor, (i + 1) * divisor); + Vector<uint8_t> blend_shape = _convert_surface_version_1_to_surface_version_2(p_surface.format, bs_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride); + new_blend_shape_data.append_array(blend_shape); + } - ERR_FAIL_COND(p_surface.blend_shape_data.size() != new_blend_shape_data.size()); + ERR_FAIL_COND(p_surface.blend_shape_data.size() != new_blend_shape_data.size()); - p_surface.blend_shape_data = new_blend_shape_data; + p_surface.blend_shape_data = new_blend_shape_data; + } } } p_surface.format &= ~(ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT); @@ -2744,6 +2856,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_RENDER_INFO_TYPE_VISIBLE); BIND_ENUM_CONSTANT(VIEWPORT_RENDER_INFO_TYPE_SHADOW); + BIND_ENUM_CONSTANT(VIEWPORT_RENDER_INFO_TYPE_CANVAS); BIND_ENUM_CONSTANT(VIEWPORT_RENDER_INFO_TYPE_MAX); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_DISABLED); @@ -3084,6 +3197,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_set_visibility_notifier", "item", "enable", "area", "enter_callable", "exit_callable"), &RenderingServer::canvas_item_set_visibility_notifier); ClassDB::bind_method(D_METHOD("canvas_item_set_canvas_group_mode", "item", "mode", "clear_margin", "fit_empty", "fit_margin", "blur_mipmaps"), &RenderingServer::canvas_item_set_canvas_group_mode, DEFVAL(5.0), DEFVAL(false), DEFVAL(0.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("debug_canvas_item_get_rect", "item"), &RenderingServer::debug_canvas_item_get_rect); + BIND_ENUM_CONSTANT(NINE_PATCH_STRETCH); BIND_ENUM_CONSTANT(NINE_PATCH_TILE); BIND_ENUM_CONSTANT(NINE_PATCH_TILE_FIT); @@ -3345,7 +3460,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"), 2); GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); - GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/2d/shadow_atlas/size", PROPERTY_HINT_RANGE, "128,16384"), 2048); // Number of commands that can be drawn per frame. GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); @@ -3356,15 +3471,15 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug", false); GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug.release", true); - GLOBAL_DEF_RST("rendering/reflections/sky_reflections/roughness_layers", 8); // Assumes a 256x256 cubemap + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/roughness_layers", PROPERTY_HINT_RANGE, "1,32,1"), 8); // Assumes a 256x256 cubemap GLOBAL_DEF_RST("rendering/reflections/sky_reflections/texture_array_reflections", true); GLOBAL_DEF("rendering/reflections/sky_reflections/texture_array_reflections.mobile", false); - GLOBAL_DEF_RST("rendering/reflections/sky_reflections/ggx_samples", 32); - GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples.mobile", 16); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/ggx_samples", PROPERTY_HINT_RANGE, "0,256,1"), 32); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/ggx_samples.mobile", PROPERTY_HINT_RANGE, "0,128,1"), 16); GLOBAL_DEF("rendering/reflections/sky_reflections/fast_filter_high_quality", false); - GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size", 256); - GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size.mobile", 128); - GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_count", 64); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/reflection_atlas/reflection_size", PROPERTY_HINT_RANGE, "0,4096,1"), 256); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/reflection_atlas/reflection_size.mobile", PROPERTY_HINT_RANGE, "0,2048,1"), 128); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/reflection_atlas/reflection_count", PROPERTY_HINT_RANGE, "0,256,1"), 64); GLOBAL_DEF("rendering/global_illumination/gi/use_half_resolution", false); @@ -3375,8 +3490,8 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley", false); GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley.mobile", true); - GLOBAL_DEF("rendering/driver/depth_prepass/enable", true); - GLOBAL_DEF("rendering/driver/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno,Apple"); + GLOBAL_DEF_RST("rendering/driver/depth_prepass/enable", true); + GLOBAL_DEF_RST("rendering/driver/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno,Apple"); GLOBAL_DEF_RST("rendering/textures/default_filters/use_nearest_mipmap_filter", false); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/textures/default_filters/anisotropic_filtering_level", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Faster),4× (Fast),8× (Average),16× (Slow)")), 2); @@ -3411,7 +3526,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)"), DECAL_FILTER_LINEAR_MIPMAPS); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/textures/light_projectors/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)"), LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS); - GLOBAL_DEF_RST("rendering/occlusion_culling/occlusion_rays_per_thread", 512); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/occlusion_rays_per_thread", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), 512); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/glow/upscale_mode", PROPERTY_HINT_ENUM, "Linear (Fast),Bicubic (Slow)"), 1); GLOBAL_DEF("rendering/environment/glow/upscale_mode.mobile", 0); @@ -3422,7 +3537,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.05); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.01); - GLOBAL_DEF("rendering/limits/global_shader_variables/buffer_size", 65536); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/limits/global_shader_variables/buffer_size", PROPERTY_HINT_RANGE, "1,1048576,1"), 65536); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index ba8cc3ba80..016801fa6e 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -52,7 +52,7 @@ class RenderingServer : public Object { int mm_policy = 0; bool render_loop_enabled = true; - Array _get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb) const; + Array _get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb, const Vector4 &p_uv_scale) const; const Vector2 SMALL_VEC2 = Vector2(CMP_EPSILON, CMP_EPSILON); const Vector3 SMALL_VEC3 = Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON); @@ -298,6 +298,8 @@ public: ARRAY_FLAG_FORMAT_VERSION_MASK = 0xFF, // 8 bits version }; + static_assert(sizeof(ArrayFormat) == 8, "ArrayFormat should be 64 bits long."); + enum PrimitiveType { PRIMITIVE_POINTS, PRIMITIVE_LINES, @@ -326,6 +328,10 @@ public: Vector<LOD> lods; Vector<AABB> bone_aabbs; + // Transforms used in runtime bone AABBs compute. + // Since bone AABBs is saved in Mesh space, but bones is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + Vector<uint8_t> blend_shape_data; Vector4 uv_scale; @@ -631,6 +637,8 @@ public: virtual void voxel_gi_set_quality(VoxelGIQuality) = 0; + virtual void sdfgi_reset() = 0; + /* LIGHTMAP */ virtual RID lightmap_create() = 0; @@ -957,6 +965,7 @@ public: enum ViewportRenderInfoType { VIEWPORT_RENDER_INFO_TYPE_VISIBLE, VIEWPORT_RENDER_INFO_TYPE_SHADOW, + VIEWPORT_RENDER_INFO_TYPE_CANVAS, VIEWPORT_RENDER_INFO_TYPE_MAX }; @@ -1487,6 +1496,9 @@ public: virtual void canvas_set_shadow_texture_size(int p_size) = 0; + Rect2 debug_canvas_item_get_rect(RID p_item); + virtual Rect2 _debug_canvas_item_get_rect(RID p_item) = 0; + /* GLOBAL SHADER UNIFORMS */ enum GlobalShaderParameterType { @@ -1630,8 +1642,14 @@ public: RenderingServer(); virtual ~RenderingServer(); +#ifdef TOOLS_ENABLED + typedef void (*SurfaceUpgradeCallback)(); + void set_surface_upgrade_callback(SurfaceUpgradeCallback p_callback); + void set_warn_on_surface_upgrade(bool p_warn); +#endif + #ifndef DISABLE_DEPRECATED - static void _fix_surface_compatibility(SurfaceData &p_surface); + void fix_surface_compatibility(SurfaceData &p_surface, const String &p_path = ""); #endif private: @@ -1647,6 +1665,10 @@ private: TypedArray<Dictionary> _instance_geometry_get_shader_parameter_list(RID p_instance) const; TypedArray<Image> _bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size); void _particles_set_trail_bind_poses(RID p_particles, const TypedArray<Transform3D> &p_bind_poses); +#ifdef TOOLS_ENABLED + SurfaceUpgradeCallback surface_upgrade_callback = nullptr; + bool warn_on_surface_upgrade = true; +#endif }; // Make variant understand the enums. diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index d6844bba6a..ec622cdb1c 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -236,6 +236,9 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_set_custom_punctuation, "shaped", "punct"); GDVIRTUAL_BIND(_shaped_text_get_custom_punctuation, "shaped"); + GDVIRTUAL_BIND(_shaped_text_set_custom_ellipsis, "shaped", "char"); + GDVIRTUAL_BIND(_shaped_text_get_custom_ellipsis, "shaped"); + GDVIRTUAL_BIND(_shaped_text_set_orientation, "shaped", "orientation"); GDVIRTUAL_BIND(_shaped_text_get_orientation, "shaped"); @@ -1058,6 +1061,16 @@ String TextServerExtension::shaped_text_get_custom_punctuation(const RID &p_shap return ret; } +void TextServerExtension::shaped_text_set_custom_ellipsis(const RID &p_shaped, int64_t p_char) { + GDVIRTUAL_CALL(_shaped_text_set_custom_ellipsis, p_shaped, p_char); +} + +int64_t TextServerExtension::shaped_text_get_custom_ellipsis(const RID &p_shaped) const { + int64_t ret = 0; + GDVIRTUAL_CALL(_shaped_text_get_custom_ellipsis, p_shaped, ret); + return ret; +} + void TextServerExtension::shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) { GDVIRTUAL_CALL(_shaped_text_set_preserve_invalid, p_shaped, p_enabled); } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 7605ed480d..d0d7ee31ad 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -391,6 +391,11 @@ public: GDVIRTUAL2(_shaped_text_set_custom_punctuation, RID, String); GDVIRTUAL1RC(String, _shaped_text_get_custom_punctuation, RID); + virtual void shaped_text_set_custom_ellipsis(const RID &p_shaped, int64_t p_char) override; + virtual int64_t shaped_text_get_custom_ellipsis(const RID &p_shaped) const override; + GDVIRTUAL2(_shaped_text_set_custom_ellipsis, RID, int64_t); + GDVIRTUAL1RC(int64_t, _shaped_text_get_custom_ellipsis, RID); + virtual void shaped_text_set_orientation(const RID &p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override; virtual Orientation shaped_text_get_orientation(const RID &p_shaped) const override; GDVIRTUAL2(_shaped_text_set_orientation, RID, Orientation); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 302d14bd8d..b282c517d3 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -390,6 +390,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_set_custom_punctuation", "shaped", "punct"), &TextServer::shaped_text_set_custom_punctuation); ClassDB::bind_method(D_METHOD("shaped_text_get_custom_punctuation", "shaped"), &TextServer::shaped_text_get_custom_punctuation); + ClassDB::bind_method(D_METHOD("shaped_text_set_custom_ellipsis", "shaped", "char"), &TextServer::shaped_text_set_custom_ellipsis); + ClassDB::bind_method(D_METHOD("shaped_text_get_custom_ellipsis", "shaped"), &TextServer::shaped_text_get_custom_ellipsis); + ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL)); ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation); diff --git a/servers/text_server.h b/servers/text_server.h index fdc0e9fc4d..70d2129b51 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -427,6 +427,9 @@ public: virtual void shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) = 0; virtual String shaped_text_get_custom_punctuation(const RID &p_shaped) const = 0; + virtual void shaped_text_set_custom_ellipsis(const RID &p_shaped, int64_t p_char) = 0; + virtual int64_t shaped_text_get_custom_ellipsis(const RID &p_shaped) const = 0; + virtual void shaped_text_set_orientation(const RID &p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0; virtual Orientation shaped_text_get_orientation(const RID &p_shaped) const = 0; diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index 7e2c512554..dae342a037 100644 --- a/servers/xr_server.cpp +++ b/servers/xr_server.cpp @@ -198,9 +198,7 @@ void XRServer::remove_interface(const Ref<XRInterface> &p_interface) { }; ERR_FAIL_COND_MSG(idx == -1, "Interface not found."); - - print_verbose("XR: Removed interface" + p_interface->get_name()); - + print_verbose("XR: Removed interface \"" + p_interface->get_name() + "\""); emit_signal(SNAME("interface_removed"), p_interface->get_name()); interfaces.remove_at(idx); }; |
