Browse Source

Merge pull request #98670 from DarioSamo/rd-transient-targets

Automatically resolve initial and final action for draw lists.
Thaddeus Crews 9 months ago
parent
commit
ed01f5f2aa
32 changed files with 798 additions and 472 deletions
  1. 7 0
      doc/classes/RDTextureFormat.xml
  2. 1 0
      doc/classes/RenderSceneBuffersRD.xml
  3. 121 25
      doc/classes/RenderingDevice.xml
  4. 19 0
      misc/extension_api_validation/4.3-stable.expected
  5. 3 4
      modules/lightmapper_rd/lightmapper_rd.cpp
  6. 1 1
      servers/rendering/renderer_rd/cluster_builder_rd.cpp
  7. 6 6
      servers/rendering/renderer_rd/effects/bokeh_dof.cpp
  8. 12 12
      servers/rendering/renderer_rd/effects/copy_effects.cpp
  9. 3 3
      servers/rendering/renderer_rd/effects/debug_effects.cpp
  10. 1 0
      servers/rendering/renderer_rd/effects/fsr2.cpp
  11. 1 1
      servers/rendering/renderer_rd/effects/luminance.cpp
  12. 1 1
      servers/rendering/renderer_rd/effects/tone_mapper.cpp
  13. 1 1
      servers/rendering/renderer_rd/effects/vrs.cpp
  14. 1 1
      servers/rendering/renderer_rd/environment/gi.cpp
  15. 5 5
      servers/rendering/renderer_rd/environment/sky.cpp
  16. 19 19
      servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
  17. 3 3
      servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
  18. 11 11
      servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
  19. 2 2
      servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
  20. 5 4
      servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
  21. 1 1
      servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
  22. 6 0
      servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc
  23. 5 4
      servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
  24. 3 1
      servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
  25. 3 3
      servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
  26. 22 2
      servers/rendering/rendering_device.compat.inc
  27. 192 214
      servers/rendering/rendering_device.cpp
  28. 65 73
      servers/rendering/rendering_device.h
  29. 5 0
      servers/rendering/rendering_device_binds.h
  30. 5 0
      servers/rendering/rendering_device_commons.h
  31. 196 66
      servers/rendering/rendering_device_graph.cpp
  32. 72 9
      servers/rendering/rendering_device_graph.h

+ 7 - 0
doc/classes/RDTextureFormat.xml

@@ -37,6 +37,13 @@
 		<member name="height" type="int" setter="set_height" getter="get_height" default="1">
 			The texture's height (in pixels).
 		</member>
+		<member name="is_discardable" type="bool" setter="set_is_discardable" getter="get_is_discardable" default="false">
+			If a texture is discardable, its contents do not need to be preserved between frames. This flag is only relevant when the texture is used as target in a draw list.
+			This information is used by [RenderingDevice] to figure out if a texture's contents can be discarded, eliminating unnecessary writes to memory and boosting performance.
+		</member>
+		<member name="is_resolve_buffer" type="bool" setter="set_is_resolve_buffer" getter="get_is_resolve_buffer" default="false">
+			The texture will be used as the destination of a resolve operation.
+		</member>
 		<member name="mipmaps" type="int" setter="set_mipmaps" getter="get_mipmaps" default="1">
 			The number of mipmaps available in the texture.
 		</member>

+ 1 - 0
doc/classes/RenderSceneBuffersRD.xml

@@ -30,6 +30,7 @@
 			<param index="6" name="layers" type="int" />
 			<param index="7" name="mipmaps" type="int" />
 			<param index="8" name="unique" type="bool" />
+			<param index="9" name="discardable" type="bool" />
 			<description>
 				Create a new texture with the given definition and cache this under the given name. Will return the existing texture if it already exists.
 			</description>

+ 121 - 25
doc/classes/RenderingDevice.xml

@@ -210,15 +210,12 @@
 		<method name="draw_list_begin">
 			<return type="int" />
 			<param index="0" name="framebuffer" type="RID" />
-			<param index="1" name="initial_color_action" type="int" enum="RenderingDevice.InitialAction" />
-			<param index="2" name="final_color_action" type="int" enum="RenderingDevice.FinalAction" />
-			<param index="3" name="initial_depth_action" type="int" enum="RenderingDevice.InitialAction" />
-			<param index="4" name="final_depth_action" type="int" enum="RenderingDevice.FinalAction" />
-			<param index="5" name="clear_color_values" type="PackedColorArray" default="PackedColorArray()" />
-			<param index="6" name="clear_depth" type="float" default="1.0" />
-			<param index="7" name="clear_stencil" type="int" default="0" />
-			<param index="8" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
-			<param index="9" name="breadcrumb" type="int" default="0" />
+			<param index="1" name="draw_flags" type="int" enum="RenderingDevice.DrawFlags" is_bitfield="true" default="0" />
+			<param index="2" name="clear_color_values" type="PackedColorArray" default="PackedColorArray()" />
+			<param index="3" name="clear_depth_value" type="float" default="1.0" />
+			<param index="4" name="clear_stencil_value" type="int" default="0" />
+			<param index="5" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
+			<param index="6" name="breadcrumb" type="int" default="0" />
 			<description>
 				Starts a list of raster drawing commands created with the [code]draw_*[/code] methods. The returned value should be passed to other [code]draw_list_*[/code] functions.
 				Multiple draw lists cannot be created at the same time; you must finish the previous draw list first using [method draw_list_end].
@@ -226,7 +223,7 @@
 				[codeblock]
 				var rd = RenderingDevice.new()
 				var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)])
-				var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS)
+				var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.CLEAR_COLOR_ALL, clear_colors, true, 1.0f, true, 0, Rect2(), RenderingDevice.OPAQUE_PASS)
 
 				# Draw opaque.
 				rd.draw_list_bind_render_pipeline(draw_list, raster_pipeline)
@@ -241,10 +238,11 @@
 
 				rd.draw_list_end()
 				[/codeblock]
+				The [param draw_flags] indicates if the texture attachments of the framebuffer should be cleared or ignored. Only one of the two flags can be used for each individual attachment. Ignoring an attachment means that any contents that existed before the draw list will be completely discarded, reducing the memory bandwidth used by the render pass but producing garbage results if the pixels aren't replaced. The default behavior allows the engine to figure out the right operation to use if the texture is discardable, which can result in increased performance. See [RDTextureFormat] or [method texture_set_discardable].
 				The [param breadcrumb] parameter can be an arbitrary 32-bit integer that is useful to diagnose GPU crashes. If Godot is built in dev or debug mode; when the GPU crashes Godot will dump all shaders that were being executed at the time of the crash and the breadcrumb is useful to diagnose what passes did those shaders belong to.
 				It does not affect rendering behavior and can be set to 0. It is recommended to use [enum BreadcrumbMarker] enumerations for consistency but it's not required. It is also possible to use bitwise operations to add extra data. e.g.
 				[codeblock]
-				rd.draw_list_begin(fb[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS | 5)
+				rd.draw_list_begin(fb[i], RenderingDevice.CLEAR_COLOR_ALL, clear_colors, true, 1.0f, true, 0, Rect2(), RenderingDevice.OPAQUE_PASS | 5)
 				[/codeblock]
 			</description>
 		</method>
@@ -947,6 +945,13 @@
 				[b]Note:[/b] This function returns a [code]uint64_t[/code] which internally maps to a [code]GLuint[/code] (OpenGL) or [code]VkImage[/code] (Vulkan).
 			</description>
 		</method>
+		<method name="texture_is_discardable">
+			<return type="bool" />
+			<param index="0" name="texture" type="RID" />
+			<description>
+				Returns [code]true[/code] if the [param texture] is discardable, [code]false[/code] otherwise. See [RDTextureFormat] or [method texture_set_discardable].
+			</description>
+		</method>
 		<method name="texture_is_format_supported_for_usage" qualifiers="const">
 			<return type="bool" />
 			<param index="0" name="format" type="int" enum="RenderingDevice.DataFormat" />
@@ -984,6 +989,16 @@
 				[b]Note:[/b] [param to_texture] texture must [b]not[/b] be multisampled and must also be 2D (or a slice of a 3D/cubemap texture).
 			</description>
 		</method>
+		<method name="texture_set_discardable">
+			<return type="void" />
+			<param index="0" name="texture" type="RID" />
+			<param index="1" name="discardable" type="bool" />
+			<description>
+				Updates the discardable property of [param texture].
+				If a texture is discardable, its contents do not need to be preserved between frames. This flag is only relevant when the texture is used as target in a draw list.
+				This information is used by [RenderingDevice] to figure out if a texture's contents can be discarded, eliminating unnecessary writes to memory and boosting performance.
+			</description>
+		</method>
 		<method name="texture_update">
 			<return type="int" enum="Error" />
 			<param index="0" name="texture" type="RID" />
@@ -2279,40 +2294,40 @@
 		</constant>
 		<constant name="DYNAMIC_STATE_STENCIL_REFERENCE" value="64" enum="PipelineDynamicStateFlags" is_bitfield="true">
 		</constant>
-		<constant name="INITIAL_ACTION_LOAD" value="0" enum="InitialAction">
+		<constant name="INITIAL_ACTION_LOAD" value="0" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 			Load the previous contents of the framebuffer.
 		</constant>
-		<constant name="INITIAL_ACTION_CLEAR" value="1" enum="InitialAction">
+		<constant name="INITIAL_ACTION_CLEAR" value="1" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 			Clear the whole framebuffer or its specified region.
 		</constant>
-		<constant name="INITIAL_ACTION_DISCARD" value="2" enum="InitialAction">
+		<constant name="INITIAL_ACTION_DISCARD" value="2" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 			Ignore the previous contents of the framebuffer. This is the fastest option if you'll overwrite all of the pixels and don't need to read any of them.
 		</constant>
-		<constant name="INITIAL_ACTION_MAX" value="3" enum="InitialAction">
+		<constant name="INITIAL_ACTION_MAX" value="3" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 			Represents the size of the [enum InitialAction] enum.
 		</constant>
-		<constant name="INITIAL_ACTION_CLEAR_REGION" value="1" enum="InitialAction" deprecated="Use [constant INITIAL_ACTION_CLEAR] instead.">
+		<constant name="INITIAL_ACTION_CLEAR_REGION" value="1" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="INITIAL_ACTION_CLEAR_REGION_CONTINUE" value="1" enum="InitialAction" deprecated="Use [constant INITIAL_ACTION_LOAD] instead.">
+		<constant name="INITIAL_ACTION_CLEAR_REGION_CONTINUE" value="1" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="INITIAL_ACTION_KEEP" value="0" enum="InitialAction" deprecated="Use [constant INITIAL_ACTION_LOAD] instead.">
+		<constant name="INITIAL_ACTION_KEEP" value="0" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="INITIAL_ACTION_DROP" value="2" enum="InitialAction" deprecated="Use [constant INITIAL_ACTION_DISCARD] instead.">
+		<constant name="INITIAL_ACTION_DROP" value="2" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="INITIAL_ACTION_CONTINUE" value="0" enum="InitialAction" deprecated="Use [constant INITIAL_ACTION_LOAD] instead.">
+		<constant name="INITIAL_ACTION_CONTINUE" value="0" enum="InitialAction" deprecated="Initial actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="FINAL_ACTION_STORE" value="0" enum="FinalAction">
+		<constant name="FINAL_ACTION_STORE" value="0" enum="FinalAction" deprecated="Final actions are solved automatically by RenderingDevice.">
 			Store the result of the draw list in the framebuffer. This is generally what you want to do.
 		</constant>
-		<constant name="FINAL_ACTION_DISCARD" value="1" enum="FinalAction">
+		<constant name="FINAL_ACTION_DISCARD" value="1" enum="FinalAction" deprecated="Final actions are solved automatically by RenderingDevice.">
 			Discard the contents of the framebuffer. This is the fastest option if you don't need to use the results of the draw list.
 		</constant>
-		<constant name="FINAL_ACTION_MAX" value="2" enum="FinalAction">
+		<constant name="FINAL_ACTION_MAX" value="2" enum="FinalAction" deprecated="Final actions are solved automatically by RenderingDevice.">
 			Represents the size of the [enum FinalAction] enum.
 		</constant>
-		<constant name="FINAL_ACTION_READ" value="0" enum="FinalAction" deprecated="Use [constant FINAL_ACTION_STORE] instead.">
+		<constant name="FINAL_ACTION_READ" value="0" enum="FinalAction" deprecated="Final actions are solved automatically by RenderingDevice.">
 		</constant>
-		<constant name="FINAL_ACTION_CONTINUE" value="0" enum="FinalAction" deprecated="Use [constant FINAL_ACTION_STORE] instead.">
+		<constant name="FINAL_ACTION_CONTINUE" value="0" enum="FinalAction" deprecated="Final actions are solved automatically by RenderingDevice.">
 		</constant>
 		<constant name="SHADER_STAGE_VERTEX" value="0" enum="ShaderStage">
 			Vertex shader stage. This can be used to manipulate vertices from a shader (but not create new vertices).
@@ -2514,5 +2529,86 @@
 		</constant>
 		<constant name="DEBUG_PASS" value="786432" enum="BreadcrumbMarker">
 		</constant>
+		<constant name="DRAW_DEFAULT_ALL" value="0" enum="DrawFlags" is_bitfield="true">
+			Do not clear or ignore any attachments.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_0" value="1" enum="DrawFlags" is_bitfield="true">
+			Clear the first color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_1" value="2" enum="DrawFlags" is_bitfield="true">
+			Clear the second color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_2" value="4" enum="DrawFlags" is_bitfield="true">
+			Clear the third color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_3" value="8" enum="DrawFlags" is_bitfield="true">
+			Clear the fourth color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_4" value="16" enum="DrawFlags" is_bitfield="true">
+			Clear the fifth color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_5" value="32" enum="DrawFlags" is_bitfield="true">
+			Clear the sixth color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_6" value="64" enum="DrawFlags" is_bitfield="true">
+			Clear the seventh color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_7" value="128" enum="DrawFlags" is_bitfield="true">
+			Clear the eighth color attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_MASK" value="255" enum="DrawFlags" is_bitfield="true">
+			Mask for clearing all color attachments.
+		</constant>
+		<constant name="DRAW_CLEAR_COLOR_ALL" value="255" enum="DrawFlags" is_bitfield="true">
+			Clear all color attachments.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_0" value="256" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the first color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_1" value="512" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the second color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_2" value="1024" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the third color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_3" value="2048" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the fourth color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_4" value="4096" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the fifth color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_5" value="8192" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the sixth color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_6" value="16384" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the seventh color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_7" value="32768" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the eighth color attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_MASK" value="65280" enum="DrawFlags" is_bitfield="true">
+			Mask for ignoring all the previous contents of the color attachments.
+		</constant>
+		<constant name="DRAW_IGNORE_COLOR_ALL" value="65280" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of all color attachments.
+		</constant>
+		<constant name="DRAW_CLEAR_DEPTH" value="65536" enum="DrawFlags" is_bitfield="true">
+			Clear the depth attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_DEPTH" value="131072" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the depth attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_STENCIL" value="262144" enum="DrawFlags" is_bitfield="true">
+			Clear the stencil attachment.
+		</constant>
+		<constant name="DRAW_IGNORE_STENCIL" value="524288" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of the stencil attachment.
+		</constant>
+		<constant name="DRAW_CLEAR_ALL" value="327935" enum="DrawFlags" is_bitfield="true">
+			Clear all attachments.
+		</constant>
+		<constant name="DRAW_IGNORE_ALL" value="720640" enum="DrawFlags" is_bitfield="true">
+			Ignore the previous contents of all attachments.
+		</constant>
 	</constants>
 </class>

+ 19 - 0
misc/extension_api_validation/4.3-stable.expected

@@ -159,3 +159,22 @@ Validate extension JSON: API was removed: builtin_classes/Vector4i/constants/AXI
 Validate extension JSON: API was removed: builtin_classes/Vector4i/constants/AXIS_Z
 
 These constants have been replaced with corresponding enum constants.
+
+
+GH-98670
+--------
+Validate extension JSON: Error: Field 'classes/RenderSceneBuffersRD/methods/create_texture/arguments': size changed value in new API, from 9 to 10.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments': size changed value in new API, from 10 to 7.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments': size changed value in new API, from 9 to 7.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/1': type changed value in new API, from "enum::RenderingDevice.InitialAction" to "bitfield::RenderingDevice.DrawFlags".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/2': type changed value in new API, from "enum::RenderingDevice.FinalAction" to "PackedColorArray".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/3': type changed value in new API, from "enum::RenderingDevice.InitialAction" to "float".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/4': type changed value in new API, from "enum::RenderingDevice.FinalAction" to "int".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/5': default_value changed value in new API, from "PackedColorArray()" to "Rect2(0, 0, 0, 0)".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/5': type changed value in new API, from "PackedColorArray" to "Rect2".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': default_value changed value in new API, from "1.0" to "0".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': meta changed value in new API, from "float" to "uint32".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': type changed value in new API, from "float" to "int".
+
+Draw lists no longer require the initial and final action for color and depth attachments to be specified.
+Draw lists can now specify if a particular color, depth, or stencil attachment should be cleared.

+ 3 - 4
modules/lightmapper_rd/lightmapper_rd.cpp

@@ -722,7 +722,7 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int
 		raster_push_constant.uv_offset[0] = -0.5f / float(atlas_size.x);
 		raster_push_constant.uv_offset[1] = -0.5f / float(atlas_size.y);
 
-		RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 1.0, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS);
+		RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::DRAW_CLEAR_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS);
 		//draw opaque
 		rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline);
 		rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
@@ -1419,6 +1419,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		tf.texture_type = RD::TEXTURE_TYPE_2D;
 		tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 		tf.format = RD::DATA_FORMAT_D32_SFLOAT;
+		tf.is_discardable = true;
 
 		raster_depth_buffer = rd->texture_create(tf, RD::TextureView());
 	}
@@ -2049,8 +2050,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		uint32_t seam_offset = 0;
 		uint32_t triangle_offset = 0;
 
-		Vector<Color> clear_colors;
-		clear_colors.push_back(Color(0, 0, 0, 1));
 		for (int i = 0; i < atlas_slices; i++) {
 			int subslices = (p_bake_sh ? 4 : 1);
 
@@ -2064,7 +2063,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 				seams_push_constant.debug = debug;
 
 				// Store the current subslice in the breadcrumb.
-				RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 1.0, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice);
+				RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::DRAW_CLEAR_DEPTH, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice);
 
 				rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
 				rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1);

+ 1 - 1
servers/rendering/renderer_rd/cluster_builder_rd.cpp

@@ -452,7 +452,7 @@ void ClusterBuilderRD::bake_cluster() {
 
 		// Render elements.
 		{
-			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			ClusterBuilderSharedDataRD::ClusterRender::PushConstant push_constant = {};
 
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shared->cluster_render.shader_pipelines[use_msaa ? ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_MSAA : ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_NORMAL]);

+ 6 - 6
servers/rendering/renderer_rd/effects/bokeh_dof.cpp

@@ -356,7 +356,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 			ERR_FAIL_COND(shader.is_null());
 
 			RID framebuffer = p_buffers.base_weight_fb;
-			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0);
 
@@ -388,7 +388,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 			RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
 
 			// Pass 1
-			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
@@ -412,7 +412,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 			RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture;
 			RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1;
 
-			draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+			draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0);
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1);
@@ -430,7 +430,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 
 				framebuffer = p_buffers.base_fb;
 
-				draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+				draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 				RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0);
 				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1);
@@ -463,7 +463,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 
 			RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
 
-			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
@@ -481,7 +481,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
 
 				framebuffer = p_buffers.base_fb;
 
-				draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+				draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 				RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0);
 				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1);

+ 12 - 12
servers/rendering/renderer_rd/effects/copy_effects.cpp

@@ -591,7 +591,7 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe
 	RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, p_rect);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, p_rect);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 	if (p_secondary.is_valid()) {
@@ -658,7 +658,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
 	ERR_FAIL_COND(shader.is_null());
 
 	// Just copy it back (we use our blur raster shader here)..
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
@@ -731,7 +731,7 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu
 	RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 
@@ -833,7 +833,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
 	ERR_FAIL_COND(shader.is_null());
 
 	//HORIZONTAL
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 	if (p_auto_exposure.is_valid() && p_first_pass) {
@@ -853,7 +853,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
 	ERR_FAIL_COND(shader.is_null());
 
 	//VERTICAL
-	draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0);
 
@@ -923,7 +923,7 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture
 	RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
@@ -988,7 +988,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con
 	RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, p_region);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, p_region);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
@@ -1025,7 +1025,7 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
 	RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0f, 0, screen_rect);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, screen_rect);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 	RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
@@ -1090,7 +1090,7 @@ void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_fra
 	RID shader = cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
 
@@ -1169,7 +1169,7 @@ void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebu
 	RID shader = filter.raster_shader.version_get_shader(filter.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1);
@@ -1247,7 +1247,7 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f
 	RID shader = roughness.raster_shader.version_get_shader(roughness.shader_version, 0);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 
@@ -1267,7 +1267,7 @@ void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_b
 
 	RD::get_singleton()->draw_command_begin_label("Merge specular");
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>());
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 
 	int mode;
 	if (p_reflection.is_valid()) {

+ 3 - 3
servers/rendering/renderer_rd/effects/debug_effects.cpp

@@ -282,7 +282,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
 		// And draw our frustum.
 		RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb);
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, rect);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, rect);
 
 		RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
 		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
@@ -326,7 +326,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
 			rect.size.x *= atlas_rect_norm.size.x;
 			rect.size.y *= atlas_rect_norm.size.y;
 
-			draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, rect);
+			draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, rect);
 
 			pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
@@ -351,7 +351,7 @@ void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_f
 	RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_velocity }));
 	RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_depth }));
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass()));
 
 	Projection correction;

+ 1 - 0
servers/rendering/renderer_rd/effects/fsr2.cpp

@@ -235,6 +235,7 @@ static FfxErrorCode create_resource_rd(FfxFsr2Interface *p_backend_interface, co
 	texture_format.height = res_desc.height;
 	texture_format.depth = res_desc.depth;
 	texture_format.mipmaps = res_desc.mipCount;
+	texture_format.is_discardable = true;
 
 	RID texture = rd->texture_create(texture_format, RD::TextureView(), initial_data);
 	ERR_FAIL_COND_V(texture.is_null(), FFX_ERROR_BACKEND_API_ERROR);

+ 1 - 1
servers/rendering/renderer_rd/effects/luminance.cpp

@@ -184,7 +184,7 @@ void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_
 
 			RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] }));
 
-			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+			RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
 			RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
 			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
 			if (final) {

+ 1 - 1
servers/rendering/renderer_rd/effects/tone_mapper.cpp

@@ -166,7 +166,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
 	RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1);

+ 1 - 1
servers/rendering/renderer_rd/effects/vrs.cpp

@@ -94,7 +94,7 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
 	RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode);
 	ERR_FAIL_COND(shader.is_null());
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>());
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(VRSPushConstant));

+ 1 - 1
servers/rendering/renderer_rd/environment/gi.cpp

@@ -1721,7 +1721,7 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con
 
 	SDFGIShader::ProbeDebugMode mode = p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : SDFGIShader::PROBE_DEBUG_PROBES;
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer);
 	RD::get_singleton()->draw_command_begin_label("Debug SDFGI");
 
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));

+ 5 - 5
servers/rendering/renderer_rd/environment/sky.cpp

@@ -1315,7 +1315,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
 				Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
 				RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers);
 
-				cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+				cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i]);
 				_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier);
 				RD::get_singleton()->draw_list_end();
 			}
@@ -1336,7 +1336,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
 				Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
 				RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd, p_render_buffers);
 
-				cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+				cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i]);
 				_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier);
 				RD::get_singleton()->draw_list_end();
 			}
@@ -1353,7 +1353,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
 			Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
 			RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd, p_render_buffers);
 
-			cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i));
+			cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i));
 			_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier);
 			RD::get_singleton()->draw_list_end();
 		}
@@ -1477,7 +1477,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
 		Vector<Color> clear_colors;
 		clear_colors.push_back(Color(0.0, 0.0, 0.0));
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 0.0);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors);
 		_render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier);
 		RD::get_singleton()->draw_list_end();
 	}
@@ -1496,7 +1496,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
 		Vector<Color> clear_colors;
 		clear_colors.push_back(Color(0.0, 0.0, 0.0));
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 0.0);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors);
 		_render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier);
 		RD::get_singleton()->draw_list_end();
 	}

+ 19 - 19
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -643,11 +643,11 @@ void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_lis
 	}
 }
 
-void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::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) {
+void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField<RD::DrawFlags> p_draw_flags, const Vector<Color> &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region) {
 	RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer);
 	p_params->framebuffer_format = fb_format;
 
-	RD::DrawListID draw_list = RD::get_singleton()->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);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_draw_flags, p_clear_color_values, p_clear_depth_value, p_clear_stencil_value, p_region);
 	_render_list(draw_list, fb_format, p_params, 0, p_params->element_count);
 	RD::get_singleton()->draw_list_end();
 }
@@ -1476,7 +1476,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
 		if (p_render_data->directional_shadows.size()) {
 			//open the pass for directional shadows
 			light_storage->update_directional_shadow_atlas();
-			RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0);
+			RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::DRAW_CLEAR_DEPTH, Vector<Color>(), 0.0f);
 			RD::get_singleton()->draw_list_end();
 		}
 	}
@@ -2010,7 +2010,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 		}
 		if (needs_pre_resolve) {
 			//pre clear the depth framebuffer, as AMD (and maybe others?) use compute for it, and barrier other compute shaders.
-			RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pass_clear, 0.0);
+			RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::DRAW_CLEAR_ALL, depth_pass_clear, 0.0f);
 			RD::get_singleton()->draw_list_end();
 			//start compute processes here, so they run at the same time as depth pre-pass
 			_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi);
@@ -2022,7 +2022,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 
 		bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth;
 		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, 0, base_specialization);
-		_render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear);
+		_render_list_with_draw_list(&render_list_params, depth_framebuffer, RD::DrawFlags(needs_pre_resolve ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_ALL), depth_pass_clear, 0.0f);
 
 		RD::get_singleton()->draw_command_end_label();
 
@@ -2084,7 +2084,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 
 		{
 			Vector<Color> c;
-			{
+			if (!load_color) {
 				Color cc = clear_color.srgb_to_linear();
 				if (using_separate_specular || rb_data.is_valid()) {
 					// Effects that rely on separate specular, like subsurface scattering, must clear the alpha to zero.
@@ -2101,7 +2101,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 			uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags;
 			RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_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, opaque_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, 0, base_specialization);
-			_render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0);
+			_render_list_with_draw_list(&render_list_params, opaque_framebuffer, RD::DrawFlags(load_color ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_COLOR_ALL) | (depth_pre_pass ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_DEPTH), c, 0.0f);
 		}
 
 		RD::get_singleton()->draw_command_end_label();
@@ -2109,7 +2109,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 		if (using_motion_pass) {
 			Vector<Color> motion_vector_clear_colors;
 			motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0));
-			RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, motion_vector_clear_colors);
+			RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::DRAW_CLEAR_ALL, motion_vector_clear_colors);
 			RD::get_singleton()->draw_list_end();
 		}
 
@@ -2121,7 +2121,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 			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, 0, base_specialization);
-			_render_list_with_draw_list(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+			_render_list_with_draw_list(&render_list_params, color_framebuffer);
 
 			RD::get_singleton()->draw_command_end_label();
 		}
@@ -2148,7 +2148,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 		Projection dc;
 		dc.set_depth_correction(true);
 		Projection cm = (dc * p_render_data->scene_data->cam_projection) * Projection(p_render_data->scene_data->cam_transform.affine_inverse());
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer);
 		RD::get_singleton()->draw_command_begin_label("Debug VoxelGIs");
 		for (int i = 0; i < (int)p_render_data->voxel_gi_instances->size(); i++) {
 			gi.debug_voxel_gi((*p_render_data->voxel_gi_instances)[i], draw_list, color_only_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION, 1.0);
@@ -2171,7 +2171,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 		RENDER_TIMESTAMP("Render Sky");
 
 		RD::get_singleton()->draw_command_begin_label("Draw Sky");
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer);
 
 		sky.draw_sky(draw_list, rb, p_render_data->environment, color_only_framebuffer, time, sky_energy_multiplier);
 
@@ -2236,7 +2236,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 		RENDER_TIMESTAMP("Clear Separate Specular (Canvas Background Mode)");
 		Vector<Color> blank_clear_color;
 		blank_clear_color.push_back(Color(0.0, 0.0, 0.0));
-		RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, blank_clear_color);
+		RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::DRAW_CLEAR_ALL, blank_clear_color);
 		RD::get_singleton()->draw_list_end();
 	}
 
@@ -2300,7 +2300,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
 
 		RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_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_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, 0, base_specialization);
-		_render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+		_render_list_with_draw_list(&render_list_params, alpha_framebuffer);
 	}
 
 	RD::get_singleton()->draw_command_end_label();
@@ -2699,7 +2699,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page
 		shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
 
 		shadow_pass.framebuffer = p_framebuffer;
-		shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD);
+		shadow_pass.clear_depth = p_begin || p_clear_region;
 		shadow_pass.rect = p_rect;
 
 		scene_state.shadow_passes.push_back(shadow_pass);
@@ -2723,7 +2723,7 @@ void RenderForwardClustered::_render_shadow_end() {
 
 	for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) {
 		RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
-		_render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0, 0, shadow_pass.rect);
+		_render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, shadow_pass.clear_depth ? RD::DRAW_CLEAR_DEPTH : RD::DRAW_DEFAULT_ALL, Vector<Color>(), 0.0f, 0, shadow_pass.rect);
 	}
 
 	RD::get_singleton()->draw_command_end_label();
@@ -2770,7 +2770,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con
 	{
 		//regular forward for now
 		RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, 0, true, false, rp_uniform_set);
-		_render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE);
+		_render_list_with_draw_list(&render_list_params, p_fb);
 	}
 	RD::get_singleton()->draw_command_end_label();
 }
@@ -2824,7 +2824,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform
 			Color(0, 0, 0, 0)
 		};
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region);
 		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count);
 		RD::get_singleton()->draw_list_end();
 	}
@@ -2874,7 +2874,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance
 			Color(0, 0, 0, 0),
 			Color(0, 0, 0, 0)
 		};
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region);
 
 		const int uv_offset_count = 9;
 		static const Vector2 uv_offsets[uv_offset_count] = {
@@ -2983,7 +2983,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu
 		}
 
 		RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false);
-		_render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, Rect2());
+		_render_list_with_draw_list(&render_list_params, E->value);
 	}
 
 	RD::get_singleton()->draw_command_end_label();

+ 3 - 3
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h

@@ -350,7 +350,6 @@ private:
 		struct ShadowPass {
 			uint32_t element_from;
 			uint32_t element_count;
-			bool flip_cull;
 			PassMode pass_mode;
 
 			RID rp_uniform_set;
@@ -358,8 +357,9 @@ private:
 			float screen_mesh_lod_threshold;
 
 			RID framebuffer;
-			RD::InitialAction initial_depth_action;
 			Rect2i rect;
+			bool clear_depth;
+			bool flip_cull;
 		};
 
 		LocalVector<ShadowPass> shadow_passes;
@@ -385,7 +385,7 @@ private:
 	template <PassMode p_pass_mode, uint32_t p_color_pass_flags = 0>
 	_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
 	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
-	void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+	void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField<RD::DrawFlags> p_draw_flags = RD::DRAW_DEFAULT_ALL, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth_value = 0.0, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2());
 
 	void _update_instance_data_buffer(RenderListType p_render_list);
 	void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);

+ 11 - 11
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

@@ -689,7 +689,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
 		if (p_render_data->directional_shadows.size()) {
 			//open the pass for directional shadows
 			light_storage->update_directional_shadow_atlas();
-			RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0);
+			RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::DRAW_CLEAR_DEPTH, Vector<Color>(), 0.0f);
 			RD::get_singleton()->draw_list_end();
 		}
 	}
@@ -1051,7 +1051,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 
 		// Set clear colors.
 		Vector<Color> c;
-		{
+		if (!load_color) {
 			Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier;
 			if (rb_data.is_valid()) {
 				cc.a = 0; // For transparent viewport backgrounds.
@@ -1068,7 +1068,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 			}
 		}
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0, Rect2(), breadcrumb);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::DRAW_CLEAR_DEPTH : (RD::DRAW_CLEAR_COLOR_0 | RD::DRAW_CLEAR_DEPTH), c, 0.0f, 0, Rect2(), breadcrumb);
 		RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
 
 		if (copy_canvas) {
@@ -1165,7 +1165,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 				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_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>(), 0, 0, Rect2(), breadcrumb);
+				draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, Rect2(), breadcrumb);
 				_render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count);
 				RD::get_singleton()->draw_list_end();
 
@@ -1445,7 +1445,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
 		shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
 
 		shadow_pass.framebuffer = p_framebuffer;
-		shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD);
+		shadow_pass.clear_depth = p_begin || p_clear_region;
 		shadow_pass.rect = p_rect;
 
 		scene_state.shadow_passes.push_back(shadow_pass);
@@ -1469,7 +1469,7 @@ void RenderForwardMobile::_render_shadow_end() {
 
 	for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) {
 		RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, scene_shader.default_specialization, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
-		_render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0, 0, shadow_pass.rect);
+		_render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, shadow_pass.clear_depth ? RD::DRAW_CLEAR_DEPTH : RD::DRAW_DEFAULT_ALL, Vector<Color>(), 0.0f, 0, shadow_pass.rect);
 	}
 
 	RD::get_singleton()->draw_command_end_label();
@@ -1521,7 +1521,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
 			Color(0, 0, 0, 0),
 			Color(0, 0, 0, 0)
 		};
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region);
 		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count);
 		RD::get_singleton()->draw_list_end();
 	}
@@ -1567,7 +1567,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *>
 			Color(0, 0, 0, 0)
 		};
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region);
 
 		const int uv_offset_count = 9;
 		static const Vector2 uv_offsets[uv_offset_count] = {
@@ -1642,7 +1642,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
 	{
 		//regular forward for now
 		RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, scene_shader.default_specialization);
-		_render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE);
+		_render_list_with_draw_list(&render_list_params, p_fb);
 	}
 	RD::get_singleton()->draw_command_end_label();
 }
@@ -2094,11 +2094,11 @@ void RenderForwardMobile::_render_list(RenderingDevice::DrawListID p_draw_list,
 	}
 }
 
-void RenderForwardMobile::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::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) {
+void RenderForwardMobile::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField<RD::DrawFlags> p_draw_flags, const Vector<Color> &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region) {
 	RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer);
 	p_params->framebuffer_format = fb_format;
 
-	RD::DrawListID draw_list = RD::get_singleton()->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);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_draw_flags, p_clear_color_values, p_clear_depth_value, p_clear_stencil_value, p_region);
 	_render_list(draw_list, fb_format, p_params, 0, p_params->element_count);
 	RD::get_singleton()->draw_list_end();
 }

+ 2 - 2
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h

@@ -257,8 +257,8 @@ private:
 			float screen_mesh_lod_threshold;
 
 			RID framebuffer;
-			RD::InitialAction initial_depth_action;
 			Rect2i rect;
+			bool clear_depth;
 		};
 
 		LocalVector<ShadowPass> shadow_passes;
@@ -332,7 +332,7 @@ private:
 	template <PassMode p_pass_mode>
 	_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
 	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
-	void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+	void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField<RD::DrawFlags> p_clear_colors = RD::DRAW_DEFAULT_ALL, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth_value = 0.0, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2());
 
 	RenderList render_list[RENDER_LIST_MAX];
 

+ 5 - 4
servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp

@@ -975,6 +975,7 @@ void RendererCanvasRenderRD::_update_shadow_atlas() {
 			tf.height = state.max_lights_per_render * 2;
 			tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 			tf.format = RD::DATA_FORMAT_D32_SFLOAT;
+			tf.is_discardable = true;
 			//chunks to write
 			state.shadow_depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
 			fb_textures.push_back(state.shadow_depth_texture);
@@ -1026,7 +1027,7 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index,
 
 	for (int i = 0; i < 4; i++) {
 		Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect);
 
 		ShadowRenderPushConstant push_constant;
 		for (int y = 0; y < 4; y++) {
@@ -1097,7 +1098,7 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh
 	cc.push_back(Color(1, 1, 1, 1));
 
 	Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2);
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect);
 
 	Projection projection;
 	projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance);
@@ -1167,7 +1168,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan
 	Vector<Color> cc;
 	cc.push_back(Color(0, 0, 0, 0));
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::DRAW_CLEAR_ALL, cc);
 
 	Projection projection;
 
@@ -2174,7 +2175,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
 
 	RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
 
-	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::DRAW_CLEAR_COLOR_ALL : RD::DRAW_DEFAULT_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS);
 
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET);

+ 1 - 1
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -646,7 +646,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		if (use_intermediate_fb) {
 			// If we use FSR to upscale we need to write our result into an intermediate buffer.
 			// Note that this is cached so we only create the texture the first time.
-			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT);
+			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
 			dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
 		} else {
 			// If we do a bilinear upscale we just render into our render target and our shader will upscale automatically.

+ 6 - 0
servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc

@@ -54,6 +54,10 @@ RID RenderSceneBuffersRD::_get_velocity_layer_compat_80214(const uint32_t p_laye
 	return _get_velocity_layer(p_layer, msaa_3d != RS::VIEWPORT_MSAA_DISABLED);
 }
 
+RID RenderSceneBuffersRD::_create_texture_bind_compat_98670(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique) {
+	return create_texture(p_context, p_texture_name, p_data_format, p_usage_bits, p_texture_samples, p_size, p_layers, p_mipmaps, p_unique, false);
+}
+
 void RenderSceneBuffersRD::_bind_compatibility_methods() {
 	ClassDB::bind_compatibility_method(D_METHOD("get_color_texture"), &RenderSceneBuffersRD::_get_color_texture_compat_80214);
 	ClassDB::bind_compatibility_method(D_METHOD("get_color_layer", "layer"), &RenderSceneBuffersRD::_get_color_layer_compat_80214);
@@ -61,6 +65,8 @@ void RenderSceneBuffersRD::_bind_compatibility_methods() {
 	ClassDB::bind_compatibility_method(D_METHOD("get_depth_layer", "layer"), &RenderSceneBuffersRD::_get_depth_layer_compat_80214);
 	ClassDB::bind_compatibility_method(D_METHOD("get_velocity_texture"), &RenderSceneBuffersRD::_get_velocity_texture_compat_80214);
 	ClassDB::bind_compatibility_method(D_METHOD("get_velocity_layer", "layer"), &RenderSceneBuffersRD::_get_velocity_layer_compat_80214);
+
+	ClassDB::bind_compatibility_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::_create_texture_bind_compat_98670);
 }
 
 #endif // DISABLE_DEPRECATED

+ 5 - 4
servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp

@@ -48,7 +48,7 @@ RenderSceneBuffersRD::~RenderSceneBuffersRD() {
 
 void RenderSceneBuffersRD::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("has_texture", "context", "name"), &RenderSceneBuffersRD::has_texture);
-	ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::create_texture);
+	ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique", "discardable"), &RenderSceneBuffersRD::create_texture);
 	ClassDB::bind_method(D_METHOD("create_texture_from_format", "context", "name", "format", "view", "unique"), &RenderSceneBuffersRD::_create_texture_from_format);
 	ClassDB::bind_method(D_METHOD("create_texture_view", "context", "name", "view_name", "view"), &RenderSceneBuffersRD::_create_texture_view);
 	ClassDB::bind_method(D_METHOD("get_texture", "context", "name"), &RenderSceneBuffersRD::get_texture);
@@ -179,8 +179,8 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co
 		texture_samples = RD::TEXTURE_SAMPLES_1;
 	} else {
 		texture_samples = msaa_to_samples(msaa_3d);
-		create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, base_data_format, get_color_usage_bits(false, true, can_be_storage), texture_samples);
-		create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, get_depth_format(false, true, can_be_storage), get_depth_usage_bits(false, true, can_be_storage), texture_samples);
+		create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, base_data_format, get_color_usage_bits(false, true, can_be_storage), texture_samples, Size2i(), 0, 1, true, true);
+		create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, get_depth_format(false, true, can_be_storage), get_depth_usage_bits(false, true, can_be_storage), texture_samples, Size2i(), 0, 1, true, true);
 	}
 
 	// VRS (note, our vrs object will only be set if VRS is supported)
@@ -242,7 +242,7 @@ bool RenderSceneBuffersRD::has_texture(const StringName &p_context, const String
 	return named_textures.has(key);
 }
 
-RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique) {
+RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique, bool p_discardable) {
 	// Keep some useful data, we use default values when these are 0.
 	Size2i size = p_size == Size2i(0, 0) ? internal_size : p_size;
 	uint32_t layers = p_layers == 0 ? view_count : p_layers;
@@ -262,6 +262,7 @@ RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const Stri
 	tf.mipmaps = mipmaps;
 	tf.usage_bits = p_usage_bits;
 	tf.samples = p_texture_samples;
+	tf.is_discardable = p_discardable;
 
 	return create_texture_from_format(p_context, p_texture_name, tf, RD::TextureView(), p_unique);
 }

+ 3 - 1
servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h

@@ -195,7 +195,7 @@ public:
 	// Named Textures
 
 	bool has_texture(const StringName &p_context, const StringName &p_texture_name) const;
-	RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true);
+	RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true, bool p_discardable = false);
 	RID create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view = RD::TextureView(), bool p_unique = true);
 	RID create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, RD::TextureView p_view = RD::TextureView());
 	RID get_texture(const StringName &p_context, const StringName &p_texture_name) const;
@@ -423,6 +423,8 @@ private:
 	RID _get_velocity_texture_compat_80214();
 	RID _get_velocity_layer_compat_80214(const uint32_t p_layer);
 
+	RID _create_texture_bind_compat_98670(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique);
+
 	static void _bind_compatibility_methods();
 
 #endif // DISABLE_DEPRECATED

+ 3 - 3
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -2894,7 +2894,7 @@ void TextureStorage::update_decal_atlas() {
 				Vector<Color> cc;
 				cc.push_back(clear_color);
 
-				RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, cc);
+				RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::DRAW_CLEAR_ALL, cc);
 
 				for (const KeyValue<RID, DecalAtlas::Texture> &E : decal_atlas.textures) {
 					DecalAtlas::Texture *t = decal_atlas.textures.getptr(E.key);
@@ -3575,7 +3575,7 @@ void TextureStorage::render_target_do_msaa_resolve(RID p_render_target) {
 	if (!rt->msaa_needs_resolve) {
 		return;
 	}
-	RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
+	RD::get_singleton()->draw_list_begin(rt->get_framebuffer());
 	RD::get_singleton()->draw_list_end();
 	rt->msaa_needs_resolve = false;
 }
@@ -3692,7 +3692,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
 	}
 	Vector<Color> clear_colors;
 	clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color);
-	RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors);
+	RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::DRAW_CLEAR_ALL, clear_colors);
 	RD::get_singleton()->draw_list_end();
 	rt->clear_requested = false;
 	rt->msaa_needs_resolve = false;

+ 22 - 2
servers/rendering/rendering_device.compat.inc

@@ -86,11 +86,29 @@ RenderingDevice::FinalAction RenderingDevice::_convert_final_action_84976(FinalA
 }
 
 RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_84976(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) {
-	return draw_list_begin(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE);
+	return _draw_list_begin_bind_compat_98670(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE);
 }
 
 RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_90993(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) {
-	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, RDD::BreadcrumbMarker::NONE);
+	return _draw_list_begin_bind_compat_98670(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, RDD::BreadcrumbMarker::NONE);
+}
+
+RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_98670(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, uint32_t p_breadcrumb) {
+	// Deduce flags from initial and final actions.
+	BitField<DrawFlags> draw_flags = RD::DRAW_DEFAULT_ALL;
+	if (p_initial_color_action == RD::INITIAL_ACTION_CLEAR) {
+		draw_flags.set_flag(DrawFlags(RD::DRAW_CLEAR_COLOR_ALL));
+	} else if (p_initial_color_action == RD::INITIAL_ACTION_DISCARD) {
+		draw_flags.set_flag(DrawFlags(RD::DRAW_IGNORE_COLOR_ALL));
+	}
+
+	if (p_initial_depth_action == RD::INITIAL_ACTION_CLEAR) {
+		draw_flags.set_flag(DrawFlags(RD::DRAW_CLEAR_DEPTH | RD::DRAW_CLEAR_STENCIL));
+	} else if (p_initial_depth_action == RD::INITIAL_ACTION_DISCARD) {
+		draw_flags.set_flag(DrawFlags(RD::DRAW_IGNORE_DEPTH | RD::DRAW_IGNORE_STENCIL));
+	}
+
+	return draw_list_begin(p_framebuffer, draw_flags, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_breadcrumb);
 }
 
 RenderingDevice::ComputeListID RenderingDevice::_compute_list_begin_bind_compat_84976(bool p_allow_draw_overlap) {
@@ -146,6 +164,8 @@ void RenderingDevice::_bind_compatibility_methods() {
 	ClassDB::bind_compatibility_method(D_METHOD("screen_get_framebuffer_format"), &RenderingDevice::_screen_get_framebuffer_format_bind_compat_87340);
 
 	ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::_draw_list_begin_bind_compat_90993, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()));
+
+	ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::_draw_list_begin_bind_compat_98670, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0));
 }
 
 #endif

+ 192 - 214
servers/rendering/rendering_device.cpp

@@ -846,6 +846,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
 	texture.base_mipmap = 0;
 	texture.base_layer = 0;
 	texture.is_resolve_buffer = format.is_resolve_buffer;
+	texture.is_discardable = format.is_discardable;
 	texture.usage_flags = format.usage_bits & ~forced_usage_bits;
 	texture.samples = format.samples;
 	texture.allowed_shared_formats = format.shareable_formats;
@@ -949,6 +950,7 @@ RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with
 		tracker->texture_size = Size2i(texture.width, texture.height);
 		tracker->texture_subresources = texture.barrier_range();
 		tracker->texture_usage = alias_format.usage_bits;
+		tracker->is_discardable = texture.is_discardable;
 		tracker->reference_count = 1;
 		texture.shared_fallback->texture_tracker = tracker;
 		texture.shared_fallback->revision = 0;
@@ -1129,6 +1131,7 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view,
 		tracker->texture_size = Size2i(texture.width, texture.height);
 		tracker->texture_subresources = slice_range;
 		tracker->texture_usage = slice_format.usage_bits;
+		tracker->is_discardable = slice_format.is_discardable;
 		tracker->reference_count = 1;
 		texture.shared_fallback->texture_tracker = tracker;
 		texture.shared_fallback->revision = 0;
@@ -1889,6 +1892,7 @@ RD::TextureFormat RenderingDevice::texture_get_format(RID p_texture) {
 	tf.usage_bits = tex->usage_flags;
 	tf.shareable_formats = tex->allowed_shared_formats;
 	tf.is_resolve_buffer = tex->is_resolve_buffer;
+	tf.is_discardable = tex->is_discardable;
 
 	return tf;
 }
@@ -2029,6 +2033,32 @@ Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_
 	return OK;
 }
 
+void RenderingDevice::texture_set_discardable(RID p_texture, bool p_discardable) {
+	ERR_RENDER_THREAD_GUARD();
+
+	Texture *texture = texture_owner.get_or_null(p_texture);
+	ERR_FAIL_NULL(texture);
+
+	texture->is_discardable = p_discardable;
+
+	if (texture->draw_tracker != nullptr) {
+		texture->draw_tracker->is_discardable = p_discardable;
+	}
+
+	if (texture->shared_fallback != nullptr && texture->shared_fallback->texture_tracker != nullptr) {
+		texture->shared_fallback->texture_tracker->is_discardable = p_discardable;
+	}
+}
+
+bool RenderingDevice::texture_is_discardable(RID p_texture) {
+	ERR_RENDER_THREAD_GUARD_V(false);
+
+	Texture *texture = texture_owner.get_or_null(p_texture);
+	ERR_FAIL_NULL_V(texture, false);
+
+	return texture->is_discardable;
+}
+
 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) {
 	ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
 
@@ -2082,31 +2112,7 @@ bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format,
 /**** FRAMEBUFFER ****/
 /*********************/
 
-static RDD::AttachmentLoadOp initial_action_to_load_op(RenderingDevice::InitialAction p_action) {
-	switch (p_action) {
-		case RenderingDevice::INITIAL_ACTION_LOAD:
-			return RDD::ATTACHMENT_LOAD_OP_LOAD;
-		case RenderingDevice::INITIAL_ACTION_CLEAR:
-			return RDD::ATTACHMENT_LOAD_OP_CLEAR;
-		case RenderingDevice::INITIAL_ACTION_DISCARD:
-			return RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
-		default:
-			ERR_FAIL_V_MSG(RDD::ATTACHMENT_LOAD_OP_DONT_CARE, "Invalid initial action value (" + itos(p_action) + ")");
-	}
-}
-
-static RDD::AttachmentStoreOp final_action_to_store_op(RenderingDevice::FinalAction p_action) {
-	switch (p_action) {
-		case RenderingDevice::FINAL_ACTION_STORE:
-			return RDD::ATTACHMENT_STORE_OP_STORE;
-		case RenderingDevice::FINAL_ACTION_DISCARD:
-			return RDD::ATTACHMENT_STORE_OP_DONT_CARE;
-		default:
-			ERR_FAIL_V_MSG(RDD::ATTACHMENT_STORE_OP_DONT_CARE, "Invalid final action value (" + itos(p_action) + ")");
-	}
-}
-
-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) {
+RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, 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
@@ -2116,7 +2122,7 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFo
 	attachment_last_pass.resize(p_attachments.size());
 
 	if (p_view_count > 1) {
-		const RDD::MultiviewCapabilities &capabilities = driver->get_multiview_capabilities();
+		const RDD::MultiviewCapabilities &capabilities = p_driver->get_multiview_capabilities();
 
 		// This only works with multiview!
 		ERR_FAIL_COND_V_MSG(!capabilities.is_supported, RDD::RenderPassID(), "Multiview not supported");
@@ -2157,17 +2163,17 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFo
 			description.final_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 		} else {
 			if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
-				description.load_op = initial_action_to_load_op(p_initial_action);
-				description.store_op = final_action_to_store_op(p_final_action);
+				description.load_op = p_load_ops[i];
+				description.store_op = p_store_ops[i];
 				description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
 				description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE;
 				description.initial_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 				description.final_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 			} else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
-				description.load_op = initial_action_to_load_op(p_initial_depth_action);
-				description.store_op = final_action_to_store_op(p_final_depth_action);
-				description.stencil_load_op = initial_action_to_load_op(p_initial_depth_action);
-				description.stencil_store_op = final_action_to_store_op(p_final_depth_action);
+				description.load_op = p_load_ops[i];
+				description.store_op = p_store_ops[i];
+				description.stencil_load_op = p_load_ops[i];
+				description.stencil_store_op = p_store_ops[i];
 				description.initial_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 				description.final_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 			} else {
@@ -2329,12 +2335,23 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFo
 		}
 	}
 
-	RDD::RenderPassID render_pass = driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count);
+	RDD::RenderPassID render_pass = p_driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count);
 	ERR_FAIL_COND_V(!render_pass, RDD::RenderPassID());
 
 	return render_pass;
 }
 
+RDD::RenderPassID RenderingDevice::_render_pass_create_from_graph(RenderingDeviceDriver *p_driver, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, void *p_user_data) {
+	DEV_ASSERT(p_driver != nullptr);
+	DEV_ASSERT(p_user_data != nullptr);
+
+	// The graph delegates the creation of the render pass to the user according to the load and store ops that were determined as necessary after
+	// resolving the dependencies between commands. This function creates a render pass for the framebuffer accordingly.
+	Framebuffer *framebuffer = (Framebuffer *)(p_user_data);
+	const FramebufferFormatKey &key = framebuffer->rendering_device->framebuffer_formats[framebuffer->format_id].E->key();
+	return _render_pass_create(p_driver, key.attachments, key.passes, p_load_ops, p_store_ops, framebuffer->view_count);
+}
+
 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++) {
@@ -2349,6 +2366,7 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(
 	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_
 
@@ -2364,14 +2382,21 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_
 	}
 
 	Vector<TextureSamples> samples;
-	RDD::RenderPassID render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, p_view_count, &samples); // Actions don't matter for this use case.
+	LocalVector<RDD::AttachmentLoadOp> load_ops;
+	LocalVector<RDD::AttachmentStoreOp> store_ops;
+	for (int64_t i = 0; i < p_attachments.size(); i++) {
+		load_ops.push_back(RDD::ATTACHMENT_LOAD_OP_CLEAR);
+		store_ops.push_back(RDD::ATTACHMENT_STORE_OP_STORE);
+	}
 
+	RDD::RenderPassID render_pass = _render_pass_create(driver, p_attachments, p_passes, load_ops, store_ops, 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));
 
+	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;
@@ -2438,15 +2463,24 @@ RID RenderingDevice::framebuffer_create_empty(const Size2i &p_size, TextureSampl
 	_THREAD_SAFE_METHOD_
 
 	Framebuffer framebuffer;
+	framebuffer.rendering_device = this;
 	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;
 
+	RDG::FramebufferCache *framebuffer_cache = RDG::framebuffer_cache_create();
+	framebuffer_cache->width = p_size.width;
+	framebuffer_cache->height = p_size.height;
+	framebuffer.framebuffer_cache = framebuffer_cache;
+
 	RID id = framebuffer_owner.make_rid(framebuffer);
 #ifdef DEV_ENABLED
 	set_resource_name(id, "RID:" + itos(id.get_id()));
 #endif
+
+	framebuffer_cache->render_pass_creation_user_data = framebuffer_owner.get_or_null(id);
+
 	return id;
 }
 
@@ -2487,6 +2521,8 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
 	_THREAD_SAFE_METHOD_
 
 	Vector<AttachmentFormat> attachments;
+	LocalVector<RDD::TextureID> textures;
+	LocalVector<RDG::ResourceTracker *> trackers;
 	attachments.resize(p_texture_attachments.size());
 	Size2i size;
 	bool size_set = false;
@@ -2495,6 +2531,7 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
 		Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]);
 		if (!texture) {
 			af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT;
+			trackers.push_back(nullptr);
 		} else {
 			ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer");
 
@@ -2516,6 +2553,11 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
 			af.format = texture->format;
 			af.samples = texture->samples;
 			af.usage_flags = texture->usage_flags;
+
+			_texture_make_mutable(texture, p_texture_attachments[i]);
+
+			textures.push_back(texture->driver_id);
+			trackers.push_back(texture->draw_tracker);
 		}
 		attachments.write[i] = af;
 	}
@@ -2531,11 +2573,19 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
 			"The format used to check this framebuffer differs from the intended framebuffer format.");
 
 	Framebuffer framebuffer;
+	framebuffer.rendering_device = this;
 	framebuffer.format_id = format_id;
 	framebuffer.texture_ids = p_texture_attachments;
 	framebuffer.size = size;
 	framebuffer.view_count = p_view_count;
 
+	RDG::FramebufferCache *framebuffer_cache = RDG::framebuffer_cache_create();
+	framebuffer_cache->width = size.width;
+	framebuffer_cache->height = size.height;
+	framebuffer_cache->textures = textures;
+	framebuffer_cache->trackers = trackers;
+	framebuffer.framebuffer_cache = framebuffer_cache;
+
 	RID id = framebuffer_owner.make_rid(framebuffer);
 #ifdef DEV_ENABLED
 	set_resource_name(id, "RID:" + itos(id.get_id()));
@@ -2547,6 +2597,8 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
 		}
 	}
 
+	framebuffer_cache->render_pass_creation_user_data = framebuffer_owner.get_or_null(id);
+
 	return id;
 }
 
@@ -3833,7 +3885,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS
 	clear_value.color = p_clear_color;
 
 	RDD::RenderPassID render_pass = driver->swap_chain_get_render_pass(sc_it->value);
-	draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS);
+	draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, RDG::ATTACHMENT_OPERATION_CLEAR, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS);
 
 	draw_graph.add_draw_list_set_viewport(viewport);
 	draw_graph.add_draw_list_set_scissor(viewport);
@@ -3841,150 +3893,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS
 	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, uint32_t p_breadcrumb) {
-	thread_local LocalVector<RDD::RenderPassClearValue> clear_values;
-	thread_local LocalVector<RDG::ResourceTracker *> resource_trackers;
-	thread_local LocalVector<RDG::ResourceUsage> resource_usages;
-	bool uses_color = false;
-	bool uses_depth = false;
-	clear_values.clear();
-	clear_values.resize(p_framebuffer->texture_ids.size());
-	resource_trackers.clear();
-	resource_usages.clear();
-	int clear_values_count = 0;
-	{
-		int color_index = 0;
-		for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) {
-			RDD::RenderPassClearValue clear_value;
-
-			RID texture_rid = p_framebuffer->texture_ids[i];
-			Texture *texture = texture_owner.get_or_null(texture_rid);
-			if (!texture) {
-				color_index++;
-				continue;
-			}
-
-			// Indicate the texture will get modified for the shared texture fallback.
-			_texture_update_shared_fallback(texture_rid, texture, true);
-
-			if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
-				if (color_index < p_clear_colors.size()) {
-					ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug.
-					clear_value.color = p_clear_colors[color_index];
-					color_index++;
-				}
-
-				resource_trackers.push_back(texture->draw_tracker);
-				resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE);
-				uses_color = true;
-			} else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
-				clear_value.depth = p_clear_depth;
-				clear_value.stencil = p_clear_stencil;
-				resource_trackers.push_back(texture->draw_tracker);
-				resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE);
-				uses_depth = true;
-			}
-
-			clear_values[clear_values_count++] = clear_value;
-		}
-	}
-
-	draw_graph.add_draw_list_begin(p_render_pass, p_framebuffer_driver_id, Rect2i(p_viewport_offset, p_viewport_size), clear_values, uses_color, uses_depth, p_breadcrumb);
-	draw_graph.add_draw_list_usages(resource_trackers, resource_usages);
-
-	// Mark textures as bound.
-	draw_list_bound_textures.clear();
-
-	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;
-}
-
-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]);
-
-		if (!texture) {
-			texture_index++;
-			continue;
-		}
-
-		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);
-	}
-
-	Rect2i rect = Rect2i(p_viewport_offset, p_viewport_size);
-	draw_graph.add_draw_list_clear_attachments(clear_attachments, rect);
-}
-
-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, uint32_t p_breadcrumb) {
+RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, BitField<DrawFlags> p_draw_flags, const Vector<Color> &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region, uint32_t p_breadcrumb) {
 	ERR_RENDER_THREAD_GUARD_V(INVALID_ID);
 
 	ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
@@ -4008,43 +3917,88 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
 		viewport_size = regioni.size;
 	}
 
-	if (p_initial_color_action == INITIAL_ACTION_CLEAR) { // 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++;
-				}
+	thread_local LocalVector<RDG::AttachmentOperation> operations;
+	thread_local LocalVector<RDD::RenderPassClearValue> clear_values;
+	thread_local LocalVector<RDG::ResourceTracker *> resource_trackers;
+	thread_local LocalVector<RDG::ResourceUsage> resource_usages;
+	bool uses_color = false;
+	bool uses_depth = false;
+	operations.resize(framebuffer->texture_ids.size());
+	clear_values.resize(framebuffer->texture_ids.size());
+	resource_trackers.clear();
+	resource_usages.clear();
+
+	uint32_t color_index = 0;
+	for (int i = 0; i < framebuffer->texture_ids.size(); i++) {
+		RID texture_rid = framebuffer->texture_ids[i];
+		Texture *texture = texture_owner.get_or_null(texture_rid);
+		if (texture == nullptr) {
+			operations[i] = RDG::ATTACHMENT_OPERATION_DEFAULT;
+			clear_values[i] = RDD::RenderPassClearValue();
+			continue;
+		}
+
+		// Indicate the texture will get modified for the shared texture fallback.
+		_texture_update_shared_fallback(texture_rid, texture, true);
+
+		RDG::AttachmentOperation operation = RDG::ATTACHMENT_OPERATION_DEFAULT;
+		RDD::RenderPassClearValue clear_value;
+		if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+			if (p_draw_flags.has_flag(DrawFlags(DRAW_CLEAR_COLOR_0 << color_index))) {
+				ERR_FAIL_COND_V_MSG(color_index >= p_clear_color_values.size(), INVALID_ID, vformat("Color texture (%d) was specified to be cleared but no color value was provided.", color_index));
+				operation = RDG::ATTACHMENT_OPERATION_CLEAR;
+				clear_value.color = p_clear_color_values[color_index];
+			} else if (p_draw_flags.has_flag(DrawFlags(DRAW_IGNORE_COLOR_0 << color_index))) {
+				operation = RDG::ATTACHMENT_OPERATION_IGNORE;
+			}
+
+			resource_trackers.push_back(texture->draw_tracker);
+			resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE);
+			uses_color = true;
+			color_index++;
+		} else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+			if (p_draw_flags.has_flag(DRAW_CLEAR_DEPTH) || p_draw_flags.has_flag(DRAW_CLEAR_STENCIL)) {
+				operation = RDG::ATTACHMENT_OPERATION_CLEAR;
+				clear_value.depth = p_clear_depth_value;
+				clear_value.stencil = p_clear_stencil_value;
+			} else if (p_draw_flags.has_flag(DRAW_IGNORE_DEPTH) || p_draw_flags.has_flag(DRAW_IGNORE_STENCIL)) {
+				operation = RDG::ATTACHMENT_OPERATION_IGNORE;
 			}
+
+			resource_trackers.push_back(texture->draw_tracker);
+			resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE);
+			uses_depth = true;
 		}
-		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) + ").");
+
+		operations[i] = operation;
+		clear_values[i] = clear_value;
 	}
 
-	RDD::FramebufferID fb_driver_id;
-	RDD::RenderPassID render_pass;
+	draw_graph.add_draw_list_begin(framebuffer->framebuffer_cache, Rect2i(viewport_offset, viewport_size), operations, clear_values, uses_color, uses_depth, p_breadcrumb);
+	draw_graph.add_draw_list_usages(resource_trackers, resource_usages);
 
-	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);
+	// Mark textures as bound.
+	draw_list_bound_textures.clear();
 
-	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, p_breadcrumb);
+	for (int i = 0; i < framebuffer->texture_ids.size(); i++) {
+		Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]);
+		if (texture == nullptr) {
+			continue;
+		}
 
-	if (err != OK) {
-		return INVALID_ID;
+		texture->bound = true;
+		draw_list_bound_textures.push_back(framebuffer->texture_ids[i]);
 	}
 
-	draw_list_render_pass = render_pass;
-	draw_list_vkframebuffer = fb_driver_id;
-
 	_draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0);
 #ifdef DEBUG_ENABLED
 	draw_list_framebuffer_format = framebuffer->format_id;
 #endif
 	draw_list_current_subpass = 0;
 
+	const FramebufferFormatKey &key = framebuffer_formats[framebuffer->format_id].E->key();
+	draw_list_subpass_count = key.passes.size();
+
 	Rect2i viewport_rect(viewport_offset, viewport_size);
 	draw_graph.add_draw_list_set_viewport(viewport_rect);
 	draw_graph.add_draw_list_set_scissor(viewport_rect);
@@ -5443,6 +5397,7 @@ bool RenderingDevice::_texture_make_mutable(Texture *p_texture, RID p_texture_id
 			p_texture->draw_tracker->texture_size = Size2i(p_texture->width, p_texture->height);
 			p_texture->draw_tracker->texture_subresources = p_texture->barrier_range();
 			p_texture->draw_tracker->texture_usage = p_texture->usage_flags;
+			p_texture->draw_tracker->is_discardable = p_texture->is_discardable;
 			p_texture->draw_tracker->reference_count = 1;
 
 			if (p_texture_id.is_valid()) {
@@ -5853,13 +5808,7 @@ void RenderingDevice::_free_pending_resources(int p_frame) {
 	// 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);
-		}
-
+		draw_graph.framebuffer_cache_free(driver, framebuffer->framebuffer_cache);
 		frames[p_frame].framebuffers_to_dispose_of.pop_front();
 	}
 
@@ -6211,7 +6160,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
 	driver->command_buffer_begin(frames[0].command_buffer);
 
 	// Create draw graph and start it initialized as well.
-	draw_graph.initialize(driver, device, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME);
+	draw_graph.initialize(driver, device, &_render_pass_create_from_graph, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME);
 	draw_graph.begin();
 
 	for (uint32_t i = 0; i < frames.size(); i++) {
@@ -6709,6 +6658,9 @@ void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("texture_is_shared", "texture"), &RenderingDevice::texture_is_shared);
 	ClassDB::bind_method(D_METHOD("texture_is_valid", "texture"), &RenderingDevice::texture_is_valid);
 
+	ClassDB::bind_method(D_METHOD("texture_set_discardable", "texture", "discardable"), &RenderingDevice::texture_set_discardable);
+	ClassDB::bind_method(D_METHOD("texture_is_discardable", "texture"), &RenderingDevice::texture_is_discardable);
+
 	ClassDB::bind_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer"), &RenderingDevice::texture_copy);
 	ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count"), &RenderingDevice::texture_clear);
 	ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture"), &RenderingDevice::texture_resolve_multisample);
@@ -6770,7 +6722,7 @@ void RenderingDevice::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color()));
 
-	ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "draw_flags", "clear_color_values", "clear_depth_value", "clear_stencil_value", "region", "breadcrumb"), &RenderingDevice::draw_list_begin, DEFVAL(DRAW_DEFAULT_ALL), DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0));
 #ifndef DISABLE_DEPRECATED
 	ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>()));
 #endif
@@ -7294,22 +7246,20 @@ void RenderingDevice::_bind_methods() {
 	BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_WRITE_MASK);
 	BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_REFERENCE);
 
+#ifndef DISABLE_DEPRECATED
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_LOAD);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_DISCARD);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_MAX);
-#ifndef DISABLE_DEPRECATED
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION_CONTINUE);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_KEEP);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_DROP);
 	BIND_ENUM_CONSTANT(INITIAL_ACTION_CONTINUE);
-#endif
 
 	BIND_ENUM_CONSTANT(FINAL_ACTION_STORE);
 	BIND_ENUM_CONSTANT(FINAL_ACTION_DISCARD);
 	BIND_ENUM_CONSTANT(FINAL_ACTION_MAX);
-#ifndef DISABLE_DEPRECATED
 	BIND_ENUM_CONSTANT(FINAL_ACTION_READ);
 	BIND_ENUM_CONSTANT(FINAL_ACTION_CONTINUE);
 #endif
@@ -7391,6 +7341,34 @@ void RenderingDevice::_bind_methods() {
 	BIND_ENUM_CONSTANT(BLIT_PASS);
 	BIND_ENUM_CONSTANT(UI_PASS);
 	BIND_ENUM_CONSTANT(DEBUG_PASS);
+
+	BIND_BITFIELD_FLAG(DRAW_DEFAULT_ALL);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_0);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_1);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_2);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_3);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_4);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_5);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_6);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_7);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_MASK);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_ALL);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_0);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_1);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_2);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_3);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_4);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_5);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_6);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_7);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_MASK);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_ALL);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_DEPTH);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_DEPTH);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_STENCIL);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_STENCIL);
+	BIND_BITFIELD_FLAG(DRAW_CLEAR_ALL);
+	BIND_BITFIELD_FLAG(DRAW_IGNORE_ALL);
 }
 
 void RenderingDevice::make_current() {

+ 65 - 73
servers/rendering/rendering_device.h

@@ -251,6 +251,7 @@ public:
 		Vector<DataFormat> allowed_shared_formats;
 
 		bool is_resolve_buffer = false;
+		bool is_discardable = false;
 		bool has_initial_data = false;
 
 		BitField<RDD::TextureAspectBits> read_aspect_flags;
@@ -287,6 +288,7 @@ public:
 			tf.usage_bits = usage_flags;
 			tf.shareable_formats = allowed_shared_formats;
 			tf.is_resolve_buffer = is_resolve_buffer;
+			tf.is_discardable = is_discardable;
 			return tf;
 		}
 	};
@@ -349,33 +351,8 @@ public:
 	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);
 	Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture);
 
-	/************************/
-	/**** DRAW LISTS (I) ****/
-	/************************/
-
-	enum InitialAction {
-		INITIAL_ACTION_LOAD,
-		INITIAL_ACTION_CLEAR,
-		INITIAL_ACTION_DISCARD,
-		INITIAL_ACTION_MAX,
-#ifndef DISABLE_DEPRECATED
-		INITIAL_ACTION_CLEAR_REGION = INITIAL_ACTION_CLEAR,
-		INITIAL_ACTION_CLEAR_REGION_CONTINUE = INITIAL_ACTION_CLEAR,
-		INITIAL_ACTION_KEEP = INITIAL_ACTION_LOAD,
-		INITIAL_ACTION_DROP = INITIAL_ACTION_DISCARD,
-		INITIAL_ACTION_CONTINUE = INITIAL_ACTION_LOAD,
-#endif
-	};
-
-	enum FinalAction {
-		FINAL_ACTION_STORE,
-		FINAL_ACTION_DISCARD,
-		FINAL_ACTION_MAX,
-#ifndef DISABLE_DEPRECATED
-		FINAL_ACTION_READ = FINAL_ACTION_STORE,
-		FINAL_ACTION_CONTINUE = FINAL_ACTION_STORE,
-#endif
-	};
+	void texture_set_discardable(RID p_texture, bool p_discardable);
+	bool texture_is_discardable(RID p_texture);
 
 	/*********************/
 	/**** FRAMEBUFFER ****/
@@ -523,7 +500,8 @@ private:
 		}
 	};
 
-	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);
+	static RDD::RenderPassID _render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr);
+	static RDD::RenderPassID _render_pass_create_from_graph(RenderingDeviceDriver *p_driver, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, void *p_user_data);
 
 	// This is a cache and it's never freed, it ensures
 	// IDs for a given format are always unique.
@@ -538,47 +516,13 @@ private:
 	HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats;
 
 	struct Framebuffer {
+		RenderingDevice *rendering_device = nullptr;
 		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;
+		RDG::FramebufferCache *framebuffer_cache = nullptr;
 		Size2 size;
 		uint32_t view_count;
 	};
@@ -826,6 +770,26 @@ public:
 		BARRIER_MASK_NO_BARRIER = 0x8000,
 	};
 
+	enum InitialAction {
+		INITIAL_ACTION_LOAD,
+		INITIAL_ACTION_CLEAR,
+		INITIAL_ACTION_DISCARD,
+		INITIAL_ACTION_MAX,
+		INITIAL_ACTION_CLEAR_REGION = INITIAL_ACTION_CLEAR,
+		INITIAL_ACTION_CLEAR_REGION_CONTINUE = INITIAL_ACTION_CLEAR,
+		INITIAL_ACTION_KEEP = INITIAL_ACTION_LOAD,
+		INITIAL_ACTION_DROP = INITIAL_ACTION_DISCARD,
+		INITIAL_ACTION_CONTINUE = INITIAL_ACTION_LOAD,
+	};
+
+	enum FinalAction {
+		FINAL_ACTION_STORE,
+		FINAL_ACTION_DISCARD,
+		FINAL_ACTION_MAX,
+		FINAL_ACTION_READ = FINAL_ACTION_STORE,
+		FINAL_ACTION_CONTINUE = FINAL_ACTION_STORE,
+	};
+
 	void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS);
 	void full_barrier();
 	void draw_command_insert_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1));
@@ -854,7 +818,9 @@ private:
 
 	FramebufferFormatID _screen_get_framebuffer_format_bind_compat_87340() const;
 
-	DrawListID _draw_list_begin_bind_compat_90993(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());
+	DrawListID _draw_list_begin_bind_compat_90993(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);
+
+	DrawListID _draw_list_begin_bind_compat_98670(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, uint32_t p_breadcrumb);
 #endif
 
 public:
@@ -1158,8 +1124,6 @@ private:
 
 	DrawList *draw_list = nullptr;
 	uint32_t draw_list_subpass_count = 0;
-	RDD::RenderPassID draw_list_render_pass;
-	RDD::FramebufferID draw_list_vkframebuffer;
 #ifdef DEBUG_ENABLED
 	FramebufferFormatID draw_list_framebuffer_format = INVALID_ID;
 #endif
@@ -1167,16 +1131,43 @@ private:
 
 	Vector<RID> draw_list_bound_textures;
 
-	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, uint32_t p_breadcrumb);
 	_FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id);
 	Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass);
 	void _draw_list_free(Rect2i *r_last_viewport = nullptr);
 
 public:
+	enum DrawFlags {
+		DRAW_DEFAULT_ALL = 0,
+		DRAW_CLEAR_COLOR_0 = (1 << 0),
+		DRAW_CLEAR_COLOR_1 = (1 << 1),
+		DRAW_CLEAR_COLOR_2 = (1 << 2),
+		DRAW_CLEAR_COLOR_3 = (1 << 3),
+		DRAW_CLEAR_COLOR_4 = (1 << 4),
+		DRAW_CLEAR_COLOR_5 = (1 << 5),
+		DRAW_CLEAR_COLOR_6 = (1 << 6),
+		DRAW_CLEAR_COLOR_7 = (1 << 7),
+		DRAW_CLEAR_COLOR_MASK = 0xFF,
+		DRAW_CLEAR_COLOR_ALL = DRAW_CLEAR_COLOR_MASK,
+		DRAW_IGNORE_COLOR_0 = (1 << 8),
+		DRAW_IGNORE_COLOR_1 = (1 << 9),
+		DRAW_IGNORE_COLOR_2 = (1 << 10),
+		DRAW_IGNORE_COLOR_3 = (1 << 11),
+		DRAW_IGNORE_COLOR_4 = (1 << 12),
+		DRAW_IGNORE_COLOR_5 = (1 << 13),
+		DRAW_IGNORE_COLOR_6 = (1 << 14),
+		DRAW_IGNORE_COLOR_7 = (1 << 15),
+		DRAW_IGNORE_COLOR_MASK = 0xFF00,
+		DRAW_IGNORE_COLOR_ALL = DRAW_IGNORE_COLOR_MASK,
+		DRAW_CLEAR_DEPTH = (1 << 16),
+		DRAW_IGNORE_DEPTH = (1 << 17),
+		DRAW_CLEAR_STENCIL = (1 << 18),
+		DRAW_IGNORE_STENCIL = (1 << 19),
+		DRAW_CLEAR_ALL = DRAW_CLEAR_COLOR_ALL | DRAW_CLEAR_DEPTH | DRAW_CLEAR_STENCIL,
+		DRAW_IGNORE_ALL = DRAW_IGNORE_COLOR_ALL | DRAW_IGNORE_DEPTH | DRAW_IGNORE_STENCIL
+	};
+
 	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(), uint32_t p_breadcrumb = 0);
+	DrawListID draw_list_begin(RID p_framebuffer, BitField<DrawFlags> p_draw_flags = DRAW_DEFAULT_ALL, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth_value = 1.0f, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2(), uint32_t p_breadcrumb = 0);
 
 	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);
@@ -1569,15 +1560,16 @@ VARIANT_ENUM_CAST(RenderingDevice::BlendFactor)
 VARIANT_ENUM_CAST(RenderingDevice::BlendOperation)
 VARIANT_BITFIELD_CAST(RenderingDevice::PipelineDynamicStateFlags)
 VARIANT_ENUM_CAST(RenderingDevice::PipelineSpecializationConstantType)
-VARIANT_ENUM_CAST(RenderingDevice::InitialAction)
-VARIANT_ENUM_CAST(RenderingDevice::FinalAction)
 VARIANT_ENUM_CAST(RenderingDevice::Limit)
 VARIANT_ENUM_CAST(RenderingDevice::MemoryType)
 VARIANT_ENUM_CAST(RenderingDevice::Features)
 VARIANT_ENUM_CAST(RenderingDevice::BreadcrumbMarker)
+VARIANT_BITFIELD_CAST(RenderingDevice::DrawFlags);
 
 #ifndef DISABLE_DEPRECATED
 VARIANT_BITFIELD_CAST(RenderingDevice::BarrierMask);
+VARIANT_ENUM_CAST(RenderingDevice::InitialAction)
+VARIANT_ENUM_CAST(RenderingDevice::FinalAction)
 #endif
 
 typedef RenderingDevice RD;

+ 5 - 0
servers/rendering/rendering_device_binds.h

@@ -69,6 +69,8 @@ public:
 	RD_SETGET(RD::TextureType, texture_type)
 	RD_SETGET(RD::TextureSamples, samples)
 	RD_SETGET(BitField<RenderingDevice::TextureUsageBits>, usage_bits)
+	RD_SETGET(bool, is_resolve_buffer)
+	RD_SETGET(bool, is_discardable)
 
 	void add_shareable_format(RD::DataFormat p_format) { base.shareable_formats.push_back(p_format); }
 	void remove_shareable_format(RD::DataFormat p_format) { base.shareable_formats.erase(p_format); }
@@ -84,6 +86,9 @@ protected:
 		RD_BIND(Variant::INT, RDTextureFormat, texture_type);
 		RD_BIND(Variant::INT, RDTextureFormat, samples);
 		RD_BIND(Variant::INT, RDTextureFormat, usage_bits);
+		RD_BIND(Variant::BOOL, RDTextureFormat, is_resolve_buffer);
+		RD_BIND(Variant::BOOL, RDTextureFormat, is_discardable);
+
 		ClassDB::bind_method(D_METHOD("add_shareable_format", "format"), &RDTextureFormat::add_shareable_format);
 		ClassDB::bind_method(D_METHOD("remove_shareable_format", "format"), &RDTextureFormat::remove_shareable_format);
 	}

+ 5 - 0
servers/rendering/rendering_device_commons.h

@@ -373,6 +373,7 @@ public:
 		uint32_t usage_bits = 0;
 		Vector<DataFormat> shareable_formats;
 		bool is_resolve_buffer = false;
+		bool is_discardable = false;
 
 		bool operator==(const TextureFormat &b) const {
 			if (format != b.format) {
@@ -395,6 +396,10 @@ public:
 				return false;
 			} else if (shareable_formats != b.shareable_formats) {
 				return false;
+			} else if (is_resolve_buffer != b.is_resolve_buffer) {
+				return false;
+			} else if (is_discardable != b.is_discardable) {
+				return false;
 			} else {
 				return true;
 			}

+ 196 - 66
servers/rendering/rendering_device_graph.cpp

@@ -248,6 +248,40 @@ RenderingDeviceGraph::ComputeListInstruction *RenderingDeviceGraph::_allocate_co
 	return reinterpret_cast<ComputeListInstruction *>(&compute_instruction_list.data[compute_list_data_offset]);
 }
 
+void RenderingDeviceGraph::_check_discardable_attachment_dependency(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) {
+	if (!p_resource_tracker->is_discardable) {
+		return;
+	}
+
+	// Check if the command is a a draw list that clears the attachment completely. If it is, we don't need to modify the previous draw list.
+	uint32_t command_offset = command_data_offsets[p_command_index];
+	RecordedDrawListCommand *draw_list_command = reinterpret_cast<RecordedDrawListCommand *>(&command_data[command_offset]);
+	if (draw_list_command->type == RecordedCommand::TYPE_DRAW_LIST) {
+		ResourceTracker **trackers = draw_list_command->trackers();
+		for (uint32_t i = 0; i < draw_list_command->trackers_count; i++) {
+			if (trackers[i] == p_resource_tracker && draw_list_command->load_ops()[i] == RDD::ATTACHMENT_LOAD_OP_CLEAR) {
+				return;
+			}
+		}
+	}
+
+	// Check if the previous command is a draw list.
+	uint32_t previous_command_offset = command_data_offsets[p_previous_command_index];
+	RecordedDrawListCommand *previous_draw_list_command = reinterpret_cast<RecordedDrawListCommand *>(&command_data[previous_command_offset]);
+	if (previous_draw_list_command->type != RecordedCommand::TYPE_DRAW_LIST) {
+		return;
+	}
+
+	// Search for the tracker inside the draw list command and modify the store operation accordingly.
+	ResourceTracker **trackers = previous_draw_list_command->trackers();
+	for (uint32_t i = 0; i < previous_draw_list_command->trackers_count; i++) {
+		if (trackers[i] == p_resource_tracker) {
+			previous_draw_list_command->store_ops()[i] = RDD::ATTACHMENT_STORE_OP_STORE;
+			return;
+		}
+	}
+}
+
 void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command) {
 	// Assign the next stages derived from the stages the command requires first.
 	r_command->next_stages = r_command->self_stages;
@@ -502,6 +536,8 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
 						if (write_list_node.command_index == p_command_index) {
 							ERR_FAIL_COND_MSG(!resource_has_parent, "Command can't have itself as a dependency.");
 						} else if (!write_list_node.partial_coverage || _check_command_intersection(resource_tracker, write_list_node.command_index, p_command_index)) {
+							_check_discardable_attachment_dependency(search_tracker, write_list_node.command_index, p_command_index);
+
 							// Command is dependent on this command. Add this command to the adjacency list of the write command.
 							_add_adjacent_command(write_list_node.command_index, p_command_index, r_command);
 
@@ -528,6 +564,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
 				if (search_tracker->write_command_or_list_index == p_command_index) {
 					ERR_FAIL_MSG("Command can't have itself as a dependency.");
 				} else {
+					_check_discardable_attachment_dependency(search_tracker, search_tracker->write_command_or_list_index, p_command_index);
 					_add_adjacent_command(search_tracker->write_command_or_list_index, p_command_index, r_command);
 				}
 			}
@@ -698,6 +735,38 @@ void RenderingDeviceGraph::_run_compute_list_command(RDD::CommandBufferID p_comm
 	}
 }
 
+void RenderingDeviceGraph::_get_draw_list_render_pass_and_framebuffer(const RecordedDrawListCommand *p_draw_list_command, RDD::RenderPassID &r_render_pass, RDD::FramebufferID &r_framebuffer) {
+	DEV_ASSERT(p_draw_list_command->trackers_count <= 21 && "Max number of attachments that can be encoded into the key.");
+
+	// Build a unique key from the load and store ops for each attachment.
+	const RDD::AttachmentLoadOp *load_ops = p_draw_list_command->load_ops();
+	const RDD::AttachmentStoreOp *store_ops = p_draw_list_command->store_ops();
+	uint64_t key = 0;
+	for (uint32_t i = 0; i < p_draw_list_command->trackers_count; i++) {
+		key |= uint64_t(load_ops[i]) << (i * 3);
+		key |= uint64_t(store_ops[i]) << (i * 3 + 2);
+	}
+
+	// Check the storage map if the render pass and the framebuffer needs to be created.
+	FramebufferCache *framebuffer_cache = p_draw_list_command->framebuffer_cache;
+	HashMap<uint64_t, FramebufferStorage>::Iterator it = framebuffer_cache->storage_map.find(key);
+	if (it == framebuffer_cache->storage_map.end()) {
+		FramebufferStorage storage;
+		VectorView<RDD::AttachmentLoadOp> load_ops_view(load_ops, p_draw_list_command->trackers_count);
+		VectorView<RDD::AttachmentStoreOp> store_ops_view(store_ops, p_draw_list_command->trackers_count);
+		storage.render_pass = render_pass_creation_function(driver, load_ops_view, store_ops_view, framebuffer_cache->render_pass_creation_user_data);
+		ERR_FAIL_COND(!storage.render_pass);
+
+		storage.framebuffer = driver->framebuffer_create(storage.render_pass, framebuffer_cache->textures, framebuffer_cache->width, framebuffer_cache->height);
+		ERR_FAIL_COND(!storage.framebuffer);
+
+		it = framebuffer_cache->storage_map.insert(key, storage);
+	}
+
+	r_render_pass = it->value.render_pass;
+	r_framebuffer = it->value.framebuffer;
+}
+
 void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) {
 	uint32_t instruction_data_cursor = 0;
 	while (instruction_data_cursor < p_instruction_data_size) {
@@ -805,6 +874,37 @@ void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command
 	}
 }
 
+void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) {
+	DEV_ASSERT(p_attachment_operations.size() == p_attachment_clear_values.size());
+
+	draw_instruction_list.clear();
+	draw_instruction_list.index++;
+	draw_instruction_list.framebuffer_cache = p_framebuffer_cache;
+	draw_instruction_list.render_pass = p_render_pass;
+	draw_instruction_list.framebuffer = p_framebuffer;
+	draw_instruction_list.region = p_region;
+	draw_instruction_list.attachment_operations.resize(p_attachment_operations.size());
+	draw_instruction_list.attachment_clear_values.resize(p_attachment_clear_values.size());
+
+	for (uint32_t i = 0; i < p_attachment_operations.size(); i++) {
+		draw_instruction_list.attachment_operations[i] = p_attachment_operations[i];
+		draw_instruction_list.attachment_clear_values[i] = p_attachment_clear_values[i];
+	}
+
+	if (p_uses_color) {
+		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+	}
+
+	if (p_uses_depth) {
+		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
+		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
+	}
+
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+	draw_instruction_list.breadcrumb = p_breadcrumb;
+#endif
+}
+
 void RenderingDeviceGraph::_run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary) {
 	driver->command_buffer_begin_secondary(p_secondary->command_buffer, p_secondary->render_pass, 0, p_secondary->framebuffer);
 	_run_draw_list_command(p_secondary->command_buffer, p_secondary->instruction_data.ptr(), p_secondary->instruction_data.size());
@@ -825,7 +925,7 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC
 	for (uint32_t i = 0; i < p_sorted_commands_count; i++) {
 		const uint32_t command_index = p_sorted_commands[i].index;
 		const uint32_t command_data_offset = command_data_offsets[command_index];
-		const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]);
+		const RecordedCommand *command = reinterpret_cast<const RecordedCommand *>(&command_data[command_data_offset]);
 		_run_label_command_change(r_command_buffer, command->label_index, p_level, false, true, &p_sorted_commands[i], p_sorted_commands_count - i, r_current_label_index, r_current_label_level);
 
 		switch (command->type) {
@@ -883,9 +983,20 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC
 #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 				driver->command_insert_breadcrumb(r_command_buffer, draw_list_command->breadcrumb);
 #endif
-				driver->command_begin_render_pass(r_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values);
-				_run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size);
-				driver->command_end_render_pass(r_command_buffer);
+				RDD::RenderPassID render_pass;
+				RDD::FramebufferID framebuffer;
+				if (draw_list_command->framebuffer_cache != nullptr) {
+					_get_draw_list_render_pass_and_framebuffer(draw_list_command, render_pass, framebuffer);
+				} else {
+					render_pass = draw_list_command->render_pass;
+					framebuffer = draw_list_command->framebuffer;
+				}
+
+				if (framebuffer && render_pass) {
+					driver->command_begin_render_pass(r_command_buffer, render_pass, framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values);
+					_run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size);
+					driver->command_end_render_pass(r_command_buffer);
+				}
 			} break;
 			case RecordedCommand::TYPE_TEXTURE_CLEAR: {
 				const RecordedTextureClearCommand *texture_clear_command = reinterpret_cast<const RecordedTextureClearCommand *>(command);
@@ -1338,9 +1449,14 @@ void RenderingDeviceGraph::_print_compute_list(const uint8_t *p_instruction_data
 	}
 }
 
-void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) {
+void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Device p_device, RenderPassCreationFunction p_render_pass_creation_function, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) {
+	DEV_ASSERT(p_driver != nullptr);
+	DEV_ASSERT(p_render_pass_creation_function != nullptr);
+	DEV_ASSERT(p_frame_count > 0);
+
 	driver = p_driver;
 	device = p_device;
+	render_pass_creation_function = p_render_pass_creation_function;
 	frames.resize(p_frame_count);
 
 	for (uint32_t i = 0; i < p_frame_count; i++) {
@@ -1566,28 +1682,12 @@ void RenderingDeviceGraph::add_compute_list_end() {
 	_add_command_to_graph(compute_instruction_list.command_trackers.ptr(), compute_instruction_list.command_tracker_usages.ptr(), compute_instruction_list.command_trackers.size(), command_index, command);
 }
 
-void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) {
-	draw_instruction_list.clear();
-	draw_instruction_list.index++;
-	draw_instruction_list.render_pass = p_render_pass;
-	draw_instruction_list.framebuffer = p_framebuffer;
-	draw_instruction_list.region = p_region;
-#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
-	draw_instruction_list.breadcrumb = p_breadcrumb;
-#endif
-	draw_instruction_list.clear_values.resize(p_clear_values.size());
-	for (uint32_t i = 0; i < p_clear_values.size(); i++) {
-		draw_instruction_list.clear_values[i] = p_clear_values[i];
-	}
-
-	if (p_uses_color) {
-		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
-	}
+void RenderingDeviceGraph::add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) {
+	_add_draw_list_begin(p_framebuffer_cache, RDD::RenderPassID(), RDD::FramebufferID(), p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb);
+}
 
-	if (p_uses_depth) {
-		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
-		draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
-	}
+void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) {
+	_add_draw_list_begin(nullptr, p_render_pass, p_framebuffer, p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb);
 }
 
 void RenderingDeviceGraph::add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset) {
@@ -1768,51 +1868,62 @@ void RenderingDeviceGraph::add_draw_list_usages(VectorView<ResourceTracker *> p_
 }
 
 void RenderingDeviceGraph::add_draw_list_end() {
-	// Arbitrary size threshold to evaluate if it'd be best to record the draw list on the background as a secondary buffer.
-	const uint32_t instruction_data_threshold_for_secondary = 16384;
-	RDD::CommandBufferType command_buffer_type;
-	uint32_t &secondary_buffers_used = frames[frame].secondary_command_buffers_used;
-	if (draw_instruction_list.data.size() > instruction_data_threshold_for_secondary && secondary_buffers_used < frames[frame].secondary_command_buffers.size()) {
-		// Copy the current instruction list data into another array that will be used by the secondary command buffer worker.
-		SecondaryCommandBuffer &secondary = frames[frame].secondary_command_buffers[secondary_buffers_used];
-		secondary.render_pass = draw_instruction_list.render_pass;
-		secondary.framebuffer = draw_instruction_list.framebuffer;
-		secondary.instruction_data.resize(draw_instruction_list.data.size());
-		memcpy(secondary.instruction_data.ptr(), draw_instruction_list.data.ptr(), draw_instruction_list.data.size());
-
-		// Run a background task for recording the secondary command buffer.
-		secondary.task = WorkerThreadPool::get_singleton()->add_template_task(this, &RenderingDeviceGraph::_run_secondary_command_buffer_task, &secondary, true);
-
-		// Clear the instruction list and add a single command for executing the secondary command buffer instead.
-		draw_instruction_list.data.clear();
-		add_draw_list_execute_commands(secondary.command_buffer);
-		secondary_buffers_used++;
-
-		command_buffer_type = RDD::COMMAND_BUFFER_TYPE_SECONDARY;
-	} else {
-		command_buffer_type = RDD::COMMAND_BUFFER_TYPE_PRIMARY;
-	}
-
+	FramebufferCache *framebuffer_cache = draw_instruction_list.framebuffer_cache;
 	int32_t command_index;
-	uint32_t clear_values_size = sizeof(RDD::RenderPassClearValue) * draw_instruction_list.clear_values.size();
+	uint32_t clear_values_size = sizeof(RDD::RenderPassClearValue) * draw_instruction_list.attachment_clear_values.size();
+	uint32_t trackers_count = framebuffer_cache != nullptr ? framebuffer_cache->trackers.size() : 0;
+	uint32_t trackers_and_ops_size = (sizeof(ResourceTracker *) + sizeof(RDD::AttachmentLoadOp) + sizeof(RDD::AttachmentStoreOp)) * trackers_count;
 	uint32_t instruction_data_size = draw_instruction_list.data.size();
-	uint32_t command_size = sizeof(RecordedDrawListCommand) + clear_values_size + instruction_data_size;
+	uint32_t command_size = sizeof(RecordedDrawListCommand) + clear_values_size + trackers_and_ops_size + instruction_data_size;
 	RecordedDrawListCommand *command = static_cast<RecordedDrawListCommand *>(_allocate_command(command_size, command_index));
 	command->type = RecordedCommand::TYPE_DRAW_LIST;
 	command->self_stages = draw_instruction_list.stages;
-	command->instruction_data_size = instruction_data_size;
+	command->framebuffer_cache = framebuffer_cache;
 	command->render_pass = draw_instruction_list.render_pass;
 	command->framebuffer = draw_instruction_list.framebuffer;
-	command->command_buffer_type = command_buffer_type;
+	command->instruction_data_size = instruction_data_size;
+	command->command_buffer_type = RDD::COMMAND_BUFFER_TYPE_PRIMARY;
 	command->region = draw_instruction_list.region;
 #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 	command->breadcrumb = draw_instruction_list.breadcrumb;
 #endif
-	command->clear_values_count = draw_instruction_list.clear_values.size();
+	command->clear_values_count = draw_instruction_list.attachment_clear_values.size();
+	command->trackers_count = trackers_count;
+
+	// Initialize the load and store operations to their default behaviors. The store behavior will be modified if a command depends on the result of this render pass.
+	uint32_t attachment_op_count = draw_instruction_list.attachment_operations.size();
+	ResourceTracker **trackers = command->trackers();
+	RDD::AttachmentLoadOp *load_ops = command->load_ops();
+	RDD::AttachmentStoreOp *store_ops = command->store_ops();
+	for (uint32_t i = 0; i < command->trackers_count; i++) {
+		ResourceTracker *resource_tracker = framebuffer_cache->trackers[i];
+		if (resource_tracker != nullptr) {
+			if (i < command->clear_values_count && i < attachment_op_count && draw_instruction_list.attachment_operations[i] == ATTACHMENT_OPERATION_CLEAR) {
+				load_ops[i] = RDD::ATTACHMENT_LOAD_OP_CLEAR;
+			} else if (i < attachment_op_count && draw_instruction_list.attachment_operations[i] == ATTACHMENT_OPERATION_IGNORE) {
+				load_ops[i] = RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
+			} else if (resource_tracker->is_discardable) {
+				bool resource_has_parent = resource_tracker->parent != nullptr;
+				ResourceTracker *search_tracker = resource_has_parent ? resource_tracker->parent : resource_tracker;
+				search_tracker->reset_if_outdated(tracking_frame);
+				bool resource_was_modified_this_frame = search_tracker->write_command_or_list_index >= 0;
+				load_ops[i] = resource_was_modified_this_frame ? RDD::ATTACHMENT_LOAD_OP_LOAD : RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
+			} else {
+				load_ops[i] = RDD::ATTACHMENT_LOAD_OP_LOAD;
+			}
+
+			store_ops[i] = resource_tracker->is_discardable ? RDD::ATTACHMENT_STORE_OP_DONT_CARE : RDD::ATTACHMENT_STORE_OP_STORE;
+		} else {
+			load_ops[i] = RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
+			store_ops[i] = RDD::ATTACHMENT_STORE_OP_DONT_CARE;
+		}
+
+		trackers[i] = resource_tracker;
+	}
 
 	RDD::RenderPassClearValue *clear_values = command->clear_values();
 	for (uint32_t i = 0; i < command->clear_values_count; i++) {
-		clear_values[i] = draw_instruction_list.clear_values[i];
+		clear_values[i] = draw_instruction_list.attachment_clear_values[i];
 	}
 
 	memcpy(command->instruction_data(), draw_instruction_list.data.ptr(), instruction_data_size);
@@ -2182,20 +2293,20 @@ RenderingDeviceGraph::ResourceTracker *RenderingDeviceGraph::resource_tracker_cr
 	return memnew(ResourceTracker);
 }
 
-void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *tracker) {
-	if (tracker == nullptr) {
+void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *p_tracker) {
+	if (p_tracker == nullptr) {
 		return;
 	}
 
-	if (tracker->in_parent_dirty_list) {
+	if (p_tracker->in_parent_dirty_list) {
 		// Delete the tracker from the parent's dirty linked list.
-		if (tracker->parent->dirty_shared_list == tracker) {
-			tracker->parent->dirty_shared_list = tracker->next_shared;
+		if (p_tracker->parent->dirty_shared_list == p_tracker) {
+			p_tracker->parent->dirty_shared_list = p_tracker->next_shared;
 		} else {
-			ResourceTracker *node = tracker->parent->dirty_shared_list;
+			ResourceTracker *node = p_tracker->parent->dirty_shared_list;
 			while (node != nullptr) {
-				if (node->next_shared == tracker) {
-					node->next_shared = tracker->next_shared;
+				if (node->next_shared == p_tracker) {
+					node->next_shared = p_tracker->next_shared;
 					node = nullptr;
 				} else {
 					node = node->next_shared;
@@ -2204,9 +2315,28 @@ void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *tracker) {
 		}
 	}
 
-	memdelete(tracker);
+	memdelete(p_tracker);
 
 #if PRINT_RESOURCE_TRACKER_TOTAL
 	print_line("Resource trackers:", --resource_tracker_total);
 #endif
 }
+
+RenderingDeviceGraph::FramebufferCache *RenderingDeviceGraph::framebuffer_cache_create() {
+	return memnew(FramebufferCache);
+}
+
+void RenderingDeviceGraph::framebuffer_cache_free(RDD *p_driver, FramebufferCache *p_cache) {
+	DEV_ASSERT(p_driver != nullptr);
+
+	if (p_cache == nullptr) {
+		return;
+	}
+
+	for (KeyValue<uint64_t, FramebufferStorage> &E : p_cache->storage_map) {
+		p_driver->framebuffer_free(E.value.framebuffer);
+		p_driver->render_pass_free(E.value.render_pass);
+	}
+
+	memdelete(p_cache);
+}

+ 72 - 9
servers/rendering/rendering_device_graph.h

@@ -176,6 +176,7 @@ public:
 		Rect2i texture_slice_or_dirty_rect;
 		bool in_parent_dirty_list = false;
 		bool write_command_list_enabled = false;
+		bool is_discardable = false;
 
 		_FORCE_INLINE_ void reset_if_outdated(int64_t new_command_frame) {
 			if (new_command_frame != command_frame) {
@@ -193,6 +194,22 @@ public:
 		}
 	};
 
+	typedef RDD::RenderPassID (*RenderPassCreationFunction)(RenderingDeviceDriver *p_driver, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, void *p_user_data);
+
+	struct FramebufferStorage {
+		RDD::FramebufferID framebuffer;
+		RDD::RenderPassID render_pass;
+	};
+
+	struct FramebufferCache {
+		uint32_t width = 0;
+		uint32_t height = 0;
+		LocalVector<RDD::TextureID> textures;
+		LocalVector<ResourceTracker *> trackers;
+		HashMap<uint64_t, FramebufferStorage> storage_map;
+		void *render_pass_creation_user_data = nullptr;
+	};
+
 	struct CommandBufferPool {
 		// Provided by RenderingDevice.
 		RDD::CommandPoolID pool;
@@ -207,6 +224,15 @@ public:
 		bool draw_list_found = false;
 	};
 
+	enum AttachmentOperation {
+		// Loads or ignores if the attachment is discardable.
+		ATTACHMENT_OPERATION_DEFAULT,
+		// Clear the attachment to a value.
+		ATTACHMENT_OPERATION_CLEAR,
+		// Ignore any contents from the attachment.
+		ATTACHMENT_OPERATION_IGNORE,
+	};
+
 private:
 	struct InstructionList {
 		LocalVector<uint8_t> data;
@@ -230,13 +256,16 @@ private:
 	};
 
 	struct DrawInstructionList : InstructionList {
+		FramebufferCache *framebuffer_cache = nullptr;
 		RDD::RenderPassID render_pass;
 		RDD::FramebufferID framebuffer;
 		Rect2i region;
+		LocalVector<AttachmentOperation> attachment_operations;
+		LocalVector<RDD::RenderPassClearValue> attachment_clear_values;
+
 #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 		uint32_t breadcrumb;
 #endif
-		LocalVector<RDD::RenderPassClearValue> clear_values;
 	};
 
 	struct RecordedCommandSort {
@@ -320,15 +349,18 @@ private:
 	};
 
 	struct RecordedDrawListCommand : RecordedCommand {
-		uint32_t instruction_data_size = 0;
-		RDD::RenderPassID render_pass;
+		FramebufferCache *framebuffer_cache = nullptr;
 		RDD::FramebufferID framebuffer;
+		RDD::RenderPassID render_pass;
+		uint32_t instruction_data_size = 0;
 		RDD::CommandBufferType command_buffer_type;
 		Rect2i region;
+		uint32_t clear_values_count = 0;
+		uint32_t trackers_count = 0;
+
 #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 		uint32_t breadcrumb = 0;
 #endif
-		uint32_t clear_values_count = 0;
 
 		_FORCE_INLINE_ RDD::RenderPassClearValue *clear_values() {
 			return reinterpret_cast<RDD::RenderPassClearValue *>(&this[1]);
@@ -338,12 +370,36 @@ private:
 			return reinterpret_cast<const RDD::RenderPassClearValue *>(&this[1]);
 		}
 
+		_FORCE_INLINE_ ResourceTracker **trackers() {
+			return reinterpret_cast<ResourceTracker **>(&clear_values()[clear_values_count]);
+		}
+
+		_FORCE_INLINE_ ResourceTracker *const *trackers() const {
+			return reinterpret_cast<ResourceTracker *const *>(&clear_values()[clear_values_count]);
+		}
+
+		_FORCE_INLINE_ RDD::AttachmentLoadOp *load_ops() {
+			return reinterpret_cast<RDD::AttachmentLoadOp *>(&trackers()[trackers_count]);
+		}
+
+		_FORCE_INLINE_ const RDD::AttachmentLoadOp *load_ops() const {
+			return reinterpret_cast<const RDD::AttachmentLoadOp *>(&trackers()[trackers_count]);
+		}
+
+		_FORCE_INLINE_ RDD::AttachmentStoreOp *store_ops() {
+			return reinterpret_cast<RDD::AttachmentStoreOp *>(&load_ops()[trackers_count]);
+		}
+
+		_FORCE_INLINE_ const RDD::AttachmentStoreOp *store_ops() const {
+			return reinterpret_cast<const RDD::AttachmentStoreOp *>(&load_ops()[trackers_count]);
+		}
+
 		_FORCE_INLINE_ uint8_t *instruction_data() {
-			return reinterpret_cast<uint8_t *>(&clear_values()[clear_values_count]);
+			return reinterpret_cast<uint8_t *>(&store_ops()[trackers_count]);
 		}
 
 		_FORCE_INLINE_ const uint8_t *instruction_data() const {
-			return reinterpret_cast<const uint8_t *>(&clear_values()[clear_values_count]);
+			return reinterpret_cast<const uint8_t *>(&store_ops()[trackers_count]);
 		}
 	};
 
@@ -616,6 +672,7 @@ private:
 
 	RDD *driver = nullptr;
 	RenderingContextDriver::Device device;
+	RenderPassCreationFunction render_pass_creation_function = nullptr;
 	int64_t tracking_frame = 0;
 	LocalVector<uint8_t> command_data;
 	LocalVector<uint32_t> command_data_offsets;
@@ -660,13 +717,16 @@ private:
 	RecordedCommand *_allocate_command(uint32_t p_command_size, int32_t &r_command_index);
 	DrawListInstruction *_allocate_draw_list_instruction(uint32_t p_instruction_size);
 	ComputeListInstruction *_allocate_compute_list_instruction(uint32_t p_instruction_size);
+	void _check_discardable_attachment_dependency(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index);
 	void _add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command);
 	void _add_texture_barrier_to_command(RDD::TextureID p_texture_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, ResourceUsage p_prev_usage, ResourceUsage p_next_usage, RDD::TextureSubresourceRange p_subresources, LocalVector<RDD::TextureBarrier> &r_barrier_vector, int32_t &r_barrier_index, int32_t &r_barrier_count);
 #if USE_BUFFER_BARRIERS
 	void _add_buffer_barrier_to_command(RDD::BufferID p_buffer_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, int32_t &r_barrier_index, int32_t &r_barrier_count);
 #endif
 	void _run_compute_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size);
+	void _get_draw_list_render_pass_and_framebuffer(const RecordedDrawListCommand *p_draw_list_command, RDD::RenderPassID &r_render_pass, RDD::FramebufferID &r_framebuffer);
 	void _run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size);
+	void _add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb);
 	void _run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary);
 	void _wait_for_secondary_command_buffer_tasks();
 	void _run_render_commands(int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool, int32_t &r_current_label_index, int32_t &r_current_label_level);
@@ -680,7 +740,7 @@ private:
 public:
 	RenderingDeviceGraph();
 	~RenderingDeviceGraph();
-	void initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame);
+	void initialize(RDD *p_driver, RenderingContextDriver::Device p_device, RenderPassCreationFunction p_render_pass_creation_function, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame);
 	void finalize();
 	void begin();
 	void add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size);
@@ -697,7 +757,8 @@ public:
 	void add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage);
 	void add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages);
 	void add_compute_list_end();
-	void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0);
+	void add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0);
+	void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0);
 	void add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset);
 	void add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField<RDD::PipelineStageBits> p_pipeline_stage_bits);
 	void add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index);
@@ -729,7 +790,9 @@ public:
 	void end_label();
 	void end(bool p_reorder_commands, bool p_full_barriers, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool);
 	static ResourceTracker *resource_tracker_create();
-	static void resource_tracker_free(ResourceTracker *tracker);
+	static void resource_tracker_free(ResourceTracker *p_tracker);
+	static FramebufferCache *framebuffer_cache_create();
+	static void framebuffer_cache_free(RDD *p_driver, FramebufferCache *p_cache);
 };
 
 using RDG = RenderingDeviceGraph;