Jelajahi Sumber

Merge pull request #49885 from reduz/implement-render-pass-support

Implement Framebuffer Subpass support
Rémi Verschelde 4 tahun lalu
induk
melakukan
e4430771db

+ 35 - 0
doc/classes/RDFramebufferPass.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RDFramebufferPass" inherits="RefCounted" version="4.0">
+	<brief_description>
+		Framebuffer pass attachment description.
+	</brief_description>
+	<description>
+		This class contains the list of attachment descriptions for a framebuffer pass. Each points with an index to a previously supplied list of texture attachments.
+	Multipass framebuffers can optimize some configurations in mobile, on desktop they provide little to no advantage.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<members>
+		<member name="color_attachments" type="PackedInt32Array" setter="set_color_attachments" getter="get_color_attachments" default="PackedInt32Array()">
+			Color attachments in order starting from 0. If this attachment is not used by the shader, pass ATTACHMENT_UNUSED to skip.
+		</member>
+		<member name="depth_attachment" type="int" setter="set_depth_attachment" getter="get_depth_attachment" default="-1">
+			Depth attachment. ATTACHMENT_UNUSED should be used if no depth buffer is required for this pass.
+		</member>
+		<member name="input_attachments" type="PackedInt32Array" setter="set_input_attachments" getter="get_input_attachments" default="PackedInt32Array()">
+			Used for multipass framebuffers (more than one render pass). Converts an attachment to an input. Make sure to also supply it properly in the [RDUniform] for the uniform set.
+		</member>
+		<member name="preserve_attachments" type="PackedInt32Array" setter="set_preserve_attachments" getter="get_preserve_attachments" default="PackedInt32Array()">
+			Attachments to preserve in this pass (otherwise they are erased).
+		</member>
+		<member name="resolve_attachments" type="PackedInt32Array" setter="set_resolve_attachments" getter="get_resolve_attachments" default="PackedInt32Array()">
+			If the color attachments are multisampled, non-multisampled resolve attachments can be provided.
+		</member>
+	</members>
+	<constants>
+		<constant name="ATTACHMENT_UNUSED" value="-1">
+		</constant>
+	</constants>
+</class>

+ 49 - 1
doc/classes/RenderingDevice.xml

@@ -341,13 +341,29 @@
 			<description>
 			</description>
 		</method>
+		<method name="draw_list_switch_to_next_pass">
+			<return type="int">
+			</return>
+			<description>
+			</description>
+		</method>
+		<method name="draw_list_switch_to_next_pass_split">
+			<return type="PackedInt64Array">
+			</return>
+			<argument index="0" name="splits" type="int">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="framebuffer_create">
 			<return type="RID">
 			</return>
-			<argument index="0" name="textures" type="Array">
+			<argument index="0" name="textures" type="RID[]">
 			</argument>
 			<argument index="1" name="validate_with_format" type="int" default="-1">
 			</argument>
+			<argument index="2" name="view_count" type="int" default="1">
+			</argument>
 			<description>
 			</description>
 		</method>
@@ -363,11 +379,27 @@
 			<description>
 			</description>
 		</method>
+		<method name="framebuffer_create_multipass">
+			<return type="RID">
+			</return>
+			<argument index="0" name="textures" type="RID[]">
+			</argument>
+			<argument index="1" name="passes" type="RDFramebufferPass[]">
+			</argument>
+			<argument index="2" name="validate_with_format" type="int" default="-1">
+			</argument>
+			<argument index="3" name="view_count" type="int" default="1">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="framebuffer_format_create">
 			<return type="int">
 			</return>
 			<argument index="0" name="attachments" type="RDAttachmentFormat[]">
 			</argument>
+			<argument index="1" name="view_count" type="int" default="1">
+			</argument>
 			<description>
 			</description>
 		</method>
@@ -379,11 +411,25 @@
 			<description>
 			</description>
 		</method>
+		<method name="framebuffer_format_create_multipass">
+			<return type="int">
+			</return>
+			<argument index="0" name="attachments" type="RDAttachmentFormat[]">
+			</argument>
+			<argument index="1" name="passes" type="RDFramebufferPass[]">
+			</argument>
+			<argument index="2" name="view_count" type="int" default="1">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="framebuffer_format_get_texture_samples">
 			<return type="int" enum="RenderingDevice.TextureSamples">
 			</return>
 			<argument index="0" name="format" type="int">
 			</argument>
+			<argument index="1" name="render_pass" type="int" default="0">
+			</argument>
 			<description>
 			</description>
 		</method>
@@ -524,6 +570,8 @@
 			</argument>
 			<argument index="8" name="dynamic_state_flags" type="int" default="0">
 			</argument>
+			<argument index="9" name="for_render_pass" type="int" default="0">
+			</argument>
 			<description>
 			</description>
 		</method>

File diff ditekan karena terlalu besar
+ 422 - 217
drivers/vulkan/rendering_device_vulkan.cpp


+ 100 - 9
drivers/vulkan/rendering_device_vulkan.h

@@ -234,12 +234,87 @@ class RenderingDeviceVulkan : public RenderingDevice {
 
 	struct FramebufferFormatKey {
 		Vector<AttachmentFormat> attachments;
+		Vector<FramebufferPass> passes;
 		uint32_t view_count = 1;
 		bool operator<(const FramebufferFormatKey &p_key) const {
 			if (view_count != p_key.view_count) {
 				return view_count < p_key.view_count;
 			}
 
+			uint32_t pass_size = passes.size();
+			uint32_t key_pass_size = p_key.passes.size();
+			if (pass_size != key_pass_size) {
+				return pass_size < key_pass_size;
+			}
+			const FramebufferPass *pass_ptr = passes.ptr();
+			const FramebufferPass *key_pass_ptr = p_key.passes.ptr();
+
+			for (uint32_t i = 0; i < pass_size; i++) {
+				{ //compare color attachments
+					uint32_t attachment_size = pass_ptr[i].color_attachments.size();
+					uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size();
+					if (attachment_size != key_attachment_size) {
+						return attachment_size < key_attachment_size;
+					}
+					const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr();
+					const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr();
+
+					for (uint32_t j = 0; j < attachment_size; j++) {
+						if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+							return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+						}
+					}
+				}
+				{ //compare input attachments
+					uint32_t attachment_size = pass_ptr[i].input_attachments.size();
+					uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size();
+					if (attachment_size != key_attachment_size) {
+						return attachment_size < key_attachment_size;
+					}
+					const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr();
+					const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr();
+
+					for (uint32_t j = 0; j < attachment_size; j++) {
+						if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+							return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+						}
+					}
+				}
+				{ //compare resolve attachments
+					uint32_t attachment_size = pass_ptr[i].resolve_attachments.size();
+					uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size();
+					if (attachment_size != key_attachment_size) {
+						return attachment_size < key_attachment_size;
+					}
+					const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr();
+					const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr();
+
+					for (uint32_t j = 0; j < attachment_size; j++) {
+						if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+							return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+						}
+					}
+				}
+				{ //compare preserve attachments
+					uint32_t attachment_size = pass_ptr[i].preserve_attachments.size();
+					uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size();
+					if (attachment_size != key_attachment_size) {
+						return attachment_size < key_attachment_size;
+					}
+					const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr();
+					const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr();
+
+					for (uint32_t j = 0; j < attachment_size; j++) {
+						if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+							return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+						}
+					}
+				}
+				if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) {
+					return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment;
+				}
+			}
+
 			int as = attachments.size();
 			int bs = p_key.attachments.size();
 			if (as != bs) {
@@ -266,16 +341,14 @@ class RenderingDeviceVulkan : public RenderingDevice {
 		}
 	};
 
-	VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr, uint32_t p_view_count = 1);
-
+	VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr);
 	// This is a cache and it's never freed, it ensures
 	// IDs for a given format are always unique.
 	Map<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache;
 	struct FramebufferFormat {
 		const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E;
 		VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec)
-		int color_attachments = 0; //used for pipeline validation
-		TextureSamples samples;
+		Vector<TextureSamples> pass_samples;
 		uint32_t view_count = 1; // number of views
 	};
 
@@ -289,6 +362,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 			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) {
@@ -316,6 +390,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 		struct Version {
 			VkFramebuffer framebuffer = VK_NULL_HANDLE;
 			VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned
+			uint32_t subpass_count = 1;
 		};
 
 		Map<VersionKey, Version> framebuffers;
@@ -536,7 +611,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 		};
 
 		uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation
-		int fragment_outputs = 0;
+		uint32_t fragment_output_mask = 0;
 
 		struct PushConstant {
 			uint32_t push_constant_size = 0;
@@ -680,6 +755,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 #ifdef DEBUG_ENABLED
 		struct Validation {
 			FramebufferFormatID framebuffer_format = 0;
+			uint32_t render_pass = 0;
 			uint32_t dynamic_state = 0;
 			VertexFormatID vertex_format = 0;
 			bool uses_restart_indices = false;
@@ -735,6 +811,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	struct DrawList {
 		VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer.
 		Rect2i viewport;
+		bool viewport_set = false;
 
 		struct SetState {
 			uint32_t pipeline_expected_format = 0;
@@ -758,7 +835,6 @@ class RenderingDeviceVulkan : public RenderingDevice {
 #ifdef DEBUG_ENABLED
 		struct Validation {
 			bool active = true; // Means command buffer was not closed, so you can keep adding things.
-			FramebufferFormatID framebuffer_format = INVALID_ID;
 			// Actual render pass values.
 			uint32_t dynamic_state = 0;
 			VertexFormatID vertex_format = INVALID_ID;
@@ -794,7 +870,15 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	};
 
 	DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split.
+	uint32_t draw_list_subpass_count = 0;
 	uint32_t draw_list_count = 0;
+	VkRenderPass draw_list_render_pass;
+	VkFramebuffer draw_list_vkframebuffer;
+#ifdef DEBUG_ENABLED
+	FramebufferFormatID draw_list_framebuffer_format = INVALID_ID;
+#endif
+	uint32_t draw_list_current_subpass = 0;
+
 	bool draw_list_split = false;
 	Vector<RID> draw_list_bound_textures;
 	Vector<RID> draw_list_storage_textures;
@@ -802,10 +886,12 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	bool draw_list_unbind_depth_textures = false;
 
 	void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i 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, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass);
+	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, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count);
 	Error _draw_list_render_pass_begin(Framebuffer *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 viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures);
 	_FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id);
 	Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, uint32_t p_post_barrier);
+	Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass);
+	void _draw_list_free(Rect2i *r_last_viewport = nullptr);
 
 	/**********************/
 	/**** COMPUTE LIST ****/
@@ -951,10 +1037,12 @@ public:
 	/*********************/
 
 	virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1);
+	virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1);
 	virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1);
-	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format);
+	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0);
 
 	virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1);
+	virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1);
 	virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID);
 
 	virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer);
@@ -1005,7 +1093,7 @@ public:
 	/**** RENDER PIPELINE ****/
 	/*************************/
 
-	virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0);
+	virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0);
 	virtual bool render_pipeline_is_valid(RID p_pipeline);
 
 	/**************************/
@@ -1044,6 +1132,9 @@ public:
 	virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect);
 	virtual void draw_list_disable_scissor(DrawListID p_list);
 
+	virtual DrawListID draw_list_switch_to_next_pass();
+	virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids);
+
 	virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL);
 
 	/***********************/

+ 1 - 0
servers/register_server_types.cpp

@@ -194,6 +194,7 @@ void register_server_types() {
 	ClassDB::register_class<RDTextureFormat>();
 	ClassDB::register_class<RDTextureView>();
 	ClassDB::register_class<RDAttachmentFormat>();
+	ClassDB::register_class<RDFramebufferPass>();
 	ClassDB::register_class<RDSamplerState>();
 	ClassDB::register_class<RDVertexAttribute>();
 	ClassDB::register_class<RDUniform>();

+ 4 - 3
servers/rendering/renderer_rd/pipeline_cache_rd.cpp

@@ -31,20 +31,21 @@
 #include "pipeline_cache_rd.h"
 #include "core/os/memory.h"
 
-RID PipelineCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe) {
+RID PipelineCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass) {
 	RD::PipelineMultisampleState multisample_state_version = multisample_state;
-	multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id);
+	multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id, p_render_pass);
 
 	RD::PipelineRasterizationState raster_state_version = rasterization_state;
 	raster_state_version.wireframe = p_wireframe;
 
-	RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags);
+	RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags, p_render_pass);
 	ERR_FAIL_COND_V(pipeline.is_null(), RID());
 	versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1));
 	versions[version_count].framebuffer_id = p_framebuffer_format_id;
 	versions[version_count].vertex_id = p_vertex_format_id;
 	versions[version_count].wireframe = p_wireframe;
 	versions[version_count].pipeline = pipeline;
+	versions[version_count].render_pass = p_render_pass;
 	version_count++;
 	return pipeline;
 }

+ 5 - 4
servers/rendering/renderer_rd/pipeline_cache_rd.h

@@ -50,6 +50,7 @@ class PipelineCacheRD {
 	struct Version {
 		RD::VertexFormatID vertex_id;
 		RD::FramebufferFormatID framebuffer_id;
+		uint32_t render_pass;
 		bool wireframe;
 		RID pipeline;
 	};
@@ -57,7 +58,7 @@ class PipelineCacheRD {
 	Version *versions;
 	uint32_t version_count;
 
-	RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe);
+	RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass);
 
 	void _clear();
 
@@ -65,7 +66,7 @@ public:
 	void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0);
 	void update_shader(RID p_shader);
 
-	_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false) {
+	_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false, uint32_t p_render_pass = 0) {
 #ifdef DEBUG_ENABLED
 		ERR_FAIL_COND_V_MSG(shader.is_null(), RID(),
 				"Attempted to use an unused shader variant (shader is null),");
@@ -74,13 +75,13 @@ public:
 		spin_lock.lock();
 		RID result;
 		for (uint32_t i = 0; i < version_count; i++) {
-			if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe) {
+			if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe && versions[i].render_pass == p_render_pass) {
 				result = versions[i].pipeline;
 				spin_lock.unlock();
 				return result;
 			}
 		}
-		result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe);
+		result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe, p_render_pass);
 		spin_lock.unlock();
 		return result;
 	}

+ 62 - 10
servers/rendering/rendering_device.cpp

@@ -98,7 +98,7 @@ RID RenderingDevice::_texture_create_shared_from_slice(const Ref<RDTextureView>
 	return texture_create_shared_from_slice(p_view->base, p_with_texture, p_layer, p_mipmap, p_slice_type);
 }
 
-RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments) {
+RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count) {
 	Vector<AttachmentFormat> attachments;
 	attachments.resize(p_attachments.size());
 
@@ -107,12 +107,43 @@ RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create
 		ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID);
 		attachments.write[i] = af->base;
 	}
-	return framebuffer_format_create(attachments);
+	return framebuffer_format_create(attachments, p_view_count);
 }
 
-RID RenderingDevice::_framebuffer_create(const Array &p_textures, FramebufferFormatID p_format_check) {
+RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count) {
+	Vector<AttachmentFormat> attachments;
+	attachments.resize(p_attachments.size());
+
+	for (int i = 0; i < p_attachments.size(); i++) {
+		Ref<RDAttachmentFormat> af = p_attachments[i];
+		ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID);
+		attachments.write[i] = af->base;
+	}
+
+	Vector<FramebufferPass> passes;
+	for (int i = 0; i < p_passes.size(); i++) {
+		Ref<RDFramebufferPass> pass = p_passes[i];
+		ERR_CONTINUE(pass.is_null());
+		passes.push_back(pass->base);
+	}
+
+	return framebuffer_format_create_multipass(attachments, passes, p_view_count);
+}
+
+RID RenderingDevice::_framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check, uint32_t p_view_count) {
 	Vector<RID> textures = Variant(p_textures);
-	return framebuffer_create(textures, p_format_check);
+	return framebuffer_create(textures, p_format_check, p_view_count);
+}
+
+RID RenderingDevice::_framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) {
+	Vector<RID> textures = Variant(p_textures);
+	Vector<FramebufferPass> passes;
+	for (int i = 0; i < p_passes.size(); i++) {
+		Ref<RDFramebufferPass> pass = p_passes[i];
+		ERR_CONTINUE(pass.is_null());
+		passes.push_back(pass->base);
+	}
+	return framebuffer_create_multipass(textures, passes, p_format_check, p_view_count);
 }
 
 RID RenderingDevice::_sampler_create(const Ref<RDSamplerState> &p_state) {
@@ -190,7 +221,7 @@ Error RenderingDevice::_buffer_update(RID p_buffer, uint32_t p_offset, uint32_t
 	return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier);
 }
 
-RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags) {
+RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) {
 	PipelineRasterizationState rasterization_state;
 	if (p_rasterization_state.is_valid()) {
 		rasterization_state = p_rasterization_state->base;
@@ -221,7 +252,7 @@ RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p
 		}
 	}
 
-	return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags);
+	return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass);
 }
 
 Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) {
@@ -242,6 +273,22 @@ Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint3
 	return split_ids;
 }
 
+Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p_splits) {
+	Vector<DrawListID> splits;
+	splits.resize(p_splits);
+
+	Error err = draw_list_switch_to_next_pass_split(p_splits, splits.ptrw());
+	ERR_FAIL_COND_V(err != OK, Vector<int64_t>());
+
+	Vector<int64_t> split_ids;
+	split_ids.resize(splits.size());
+	for (int i = 0; i < splits.size(); i++) {
+		split_ids.write[i] = splits[i];
+	}
+
+	return split_ids;
+}
+
 void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) {
 	ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size);
 	draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size);
@@ -269,10 +316,12 @@ void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::texture_clear, DEFVAL(BARRIER_MASK_ALL));
 	ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::texture_resolve_multisample, DEFVAL(BARRIER_MASK_ALL));
 
-	ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments"), &RenderingDevice::_framebuffer_format_create);
+	ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments", "view_count"), &RenderingDevice::_framebuffer_format_create, DEFVAL(1));
+	ClassDB::bind_method(D_METHOD("framebuffer_format_create_multipass", "attachments", "passes", "view_count"), &RenderingDevice::_framebuffer_format_create_multipass, DEFVAL(1));
 	ClassDB::bind_method(D_METHOD("framebuffer_format_create_empty", "samples"), &RenderingDevice::framebuffer_format_create_empty, DEFVAL(TEXTURE_SAMPLES_1));
-	ClassDB::bind_method(D_METHOD("framebuffer_format_get_texture_samples", "format"), &RenderingDevice::framebuffer_format_get_texture_samples);
-	ClassDB::bind_method(D_METHOD("framebuffer_create", "textures", "validate_with_format"), &RenderingDevice::_framebuffer_create, DEFVAL(INVALID_FORMAT_ID));
+	ClassDB::bind_method(D_METHOD("framebuffer_format_get_texture_samples", "format", "render_pass"), &RenderingDevice::framebuffer_format_get_texture_samples, DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("framebuffer_create", "textures", "validate_with_format", "view_count"), &RenderingDevice::_framebuffer_create, DEFVAL(INVALID_FORMAT_ID), DEFVAL(1));
+	ClassDB::bind_method(D_METHOD("framebuffer_create_multipass", "textures", "passes", "validate_with_format", "view_count"), &RenderingDevice::_framebuffer_create_multipass, DEFVAL(INVALID_FORMAT_ID), DEFVAL(1));
 	ClassDB::bind_method(D_METHOD("framebuffer_create_empty", "size", "samples", "validate_with_format"), &RenderingDevice::framebuffer_create_empty, DEFVAL(TEXTURE_SAMPLES_1), DEFVAL(INVALID_FORMAT_ID));
 	ClassDB::bind_method(D_METHOD("framebuffer_get_format", "framebuffer"), &RenderingDevice::framebuffer_get_format);
 
@@ -299,7 +348,7 @@ void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::buffer_clear, DEFVAL(BARRIER_MASK_ALL));
 	ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer"), &RenderingDevice::buffer_get_data);
 
-	ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags"), &RenderingDevice::_render_pipeline_create, DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("render_pipeline_is_valid", "render_pipeline"), &RenderingDevice::render_pipeline_is_valid);
 
 	ClassDB::bind_method(D_METHOD("compute_pipeline_create", "shader"), &RenderingDevice::compute_pipeline_create);
@@ -325,6 +374,9 @@ void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("draw_list_enable_scissor", "draw_list", "rect"), &RenderingDevice::draw_list_enable_scissor, DEFVAL(Rect2i()));
 	ClassDB::bind_method(D_METHOD("draw_list_disable_scissor", "draw_list"), &RenderingDevice::draw_list_disable_scissor);
 
+	ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass"), &RenderingDevice::draw_list_switch_to_next_pass);
+	ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass_split", "splits"), &RenderingDevice::_draw_list_switch_to_next_pass_split);
+
 	ClassDB::bind_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::draw_list_end, DEFVAL(BARRIER_MASK_ALL));
 
 	ClassDB::bind_method(D_METHOD("compute_list_begin", "allow_draw_overlap"), &RenderingDevice::compute_list_begin, DEFVAL(false));

+ 25 - 5
servers/rendering/rendering_device.h

@@ -47,6 +47,7 @@ class RDPipelineRasterizationState;
 class RDPipelineMultisampleState;
 class RDPipelineDepthStencilState;
 class RDPipelineColorBlendState;
+class RDFramebufferPass;
 
 class RenderingDevice : public Object {
 	GDCLASS(RenderingDevice, Object)
@@ -517,10 +518,23 @@ public:
 
 	// This ID is warranted to be unique for the same formats, does not need to be freed
 	virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1) = 0;
+	struct FramebufferPass {
+		enum {
+			ATTACHMENT_UNUSED = -1
+		};
+		Vector<int32_t> color_attachments;
+		Vector<int32_t> input_attachments;
+		Vector<int32_t> resolve_attachments;
+		Vector<int32_t> preserve_attachments;
+		int32_t depth_attachment = ATTACHMENT_UNUSED;
+	};
+
+	virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1) = 0;
 	virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0;
-	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0;
+	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0) = 0;
 
 	virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0;
+	virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0;
 	virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0;
 
 	virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0;
@@ -962,7 +976,7 @@ public:
 	};
 
 	virtual bool render_pipeline_is_valid(RID p_pipeline) = 0;
-	virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0) = 0;
+	virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0) = 0;
 
 	/**************************/
 	/**** COMPUTE PIPELINE ****/
@@ -1018,6 +1032,9 @@ public:
 	virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0;
 	virtual void draw_list_disable_scissor(DrawListID p_list) = 0;
 
+	virtual DrawListID draw_list_switch_to_next_pass() = 0;
+	virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) = 0;
+
 	virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0;
 
 	/***********************/
@@ -1134,8 +1151,10 @@ protected:
 	RID _texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture);
 	RID _texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type = TEXTURE_SLICE_2D);
 
-	FramebufferFormatID _framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments);
-	RID _framebuffer_create(const Array &p_textures, FramebufferFormatID p_format_check = INVALID_ID);
+	FramebufferFormatID _framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count);
+	FramebufferFormatID _framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count);
+	RID _framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1);
+	RID _framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1);
 	RID _sampler_create(const Ref<RDSamplerState> &p_state);
 	VertexFormatID _vertex_format_create(const TypedArray<RDVertexAttribute> &p_vertex_formats);
 	RID _vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const TypedArray<RID> &p_src_buffers);
@@ -1146,11 +1165,12 @@ protected:
 
 	Error _buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL);
 
-	RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags = 0);
+	RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0);
 
 	Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>());
 	void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
 	void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
+	Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits);
 };
 
 VARIANT_ENUM_CAST(RenderingDevice::ShaderStage)

+ 28 - 0
servers/rendering/rendering_device_binds.h

@@ -128,6 +128,34 @@ protected:
 	}
 };
 
+class RDFramebufferPass : public RefCounted {
+	GDCLASS(RDFramebufferPass, RefCounted)
+	friend class RenderingDevice;
+
+	RD::FramebufferPass base;
+
+public:
+	RD_SETGET(PackedInt32Array, color_attachments)
+	RD_SETGET(PackedInt32Array, input_attachments)
+	RD_SETGET(PackedInt32Array, resolve_attachments)
+	RD_SETGET(PackedInt32Array, preserve_attachments)
+	RD_SETGET(int32_t, depth_attachment)
+protected:
+	enum {
+		ATTACHMENT_UNUSED = -1
+	};
+
+	static void _bind_methods() {
+		RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, color_attachments);
+		RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, input_attachments);
+		RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, resolve_attachments);
+		RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, preserve_attachments);
+		RD_BIND(Variant::INT, RDFramebufferPass, depth_attachment);
+
+		BIND_CONSTANT(ATTACHMENT_UNUSED);
+	}
+};
+
 class RDSamplerState : public RefCounted {
 	GDCLASS(RDSamplerState, RefCounted)
 	friend class RenderingDevice;

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini