Rendering triangle using RenderingDevice API

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By EpEp

So I’ve managed to get a compute pipeline working, but now I’m trying to render a triangle using a render pipeline. I’ve wrote the following code by referencing the source code and as far as I can see it should work. I very limited experience with GPU related things so I might very well be missing something completely.

extends Node3D
@onready var rd := RenderingServer.get_rendering_device()

func _ready():
	var shader_file: RDShaderFile = load("res://RenderTest.glsl")
	var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
	var shader := rd.shader_create_from_spirv(shader_spirv)
	
	# Create format
	var vertex_attrs = [RDVertexAttribute.new()]
	vertex_attrs[0].format = RenderingDevice.DATA_FORMAT_R8G8B8_SNORM
	vertex_attrs[0].location = 0
	var vertex_format = rd.vertex_format_create(vertex_attrs)
	
	
	# Create Vertex buffers
	var points := PackedFloat32Array([
		0, 0, 0,
		0, 1, 0,
		1, 1, 0,
	])
	var points_bytes := points.to_byte_array()
	var vertex_buffers := [rd.vertex_buffer_create(points_bytes.size(), points_bytes)]
	# Create vertex array
	var vertex_array := rd.vertex_array_create(3, vertex_format, vertex_buffers)
	
	
	# Blend
	var blend = RDPipelineColorBlendState.new()
	blend.attachments.push_back(RDPipelineColorBlendStateAttachment.new())
	
	# Create empty framebuffer size of viewport
	var rect_size = get_viewport().get_visible_rect().size
	var screen_size = Vector2i(rect_size.x, rect_size.y)
	var framebuffer = rd.framebuffer_create_empty(screen_size)
	
	
	# Define pipe
	var pipeline := rd.render_pipeline_create(
		shader,
		rd.screen_get_framebuffer_format(),
		vertex_format,
		RenderingDevice.RENDER_PRIMITIVE_TRIANGLES,
		RDPipelineRasterizationState.new(),
		RDPipelineMultisampleState.new(),
		RDPipelineDepthStencilState.new(),
		blend,
		0
	)
	
	# Create render pipeline
	if rd.render_pipeline_is_valid(pipeline):
		var draw_list := rd.draw_list_begin(framebuffer, RenderingDevice.INITIAL_ACTION_DROP, RenderingDevice.FINAL_ACTION_DISCARD, RenderingDevice.INITIAL_ACTION_DROP, RenderingDevice.FINAL_ACTION_DISCARD)
		# Bind pipe
		rd.draw_list_bind_render_pipeline(draw_list, pipeline)
		# Bind vertex array
		rd.draw_list_bind_vertex_array(draw_list, vertex_array)
		# Render
		rd.draw_list_draw(draw_list, false, 1)
		rd.draw_list_end()
	else:
		print("Invalid pipe")

And this is the shader file, I have limitex experience with shaders but as far as I understand this should be sufficient to render a mesh. Everything executes without errors, but no triangle is displayed…

#[vertex]
#version 450 core

layout(location = 0) in vec3 vertex_attrib;

void main()
{
    gl_Position = vec4(vertex_attrib, 1.0);
}


#[fragment]
#version 450 core

layout (location = 0) out vec4 frag_color;

void main()
{
    //Default color
    frag_color = vec4(1,1,1,1);
}

Note that the function “vertex_array_create” is not currently (commit cf194847) bound, as pre this gitHub issue, so I added the following line of code to rendering_device.cpp, thus making it accessible, and recompiled. As far as I can see this works fine.

ClassDB::bind_method(D_METHOD("vertex_array_create", "vertex_count", "vertex_format", "buffers"), &RenderingDevice::_vertex_array_create)

Note that the function “vertexarraycreate” is not currently (commit cf194847) bound, as pre this gitHub issue, so I added the following line of code to rendering_device.cpp, thus making it accessible, and recompiled. As far as I can see this works fine.

The method is bound as of 4.0.stable: https://github.com/godotengine/godot/blob/92b7a9603aa2395be6bf361067096538ba393c45/servers/rendering/rendering_device.cpp#L729

Calinou | 2023-04-06 17:08

:bust_in_silhouette: Reply From: joemarshall

In case anyone wants this to work, there’s a few fiddly bugs in the code above.

Firstly, the vertex data format should be RenderingDevice.DATA_FORMAT_R32G32B32_SFLOAT

Secondly, it needs a stride in there (which is 3 * 32 bit floats = 4*3 = 12)
vertex_attrs[0].stride=4*3

My (working) code looks like the below (possibly give or take some class variables which aren’t defined in the code below)


	rd= RenderingServer.create_local_rendering_device()
	var tex_format:=RDTextureFormat.new()
	var tex_view:=RDTextureView.new()
	tex_format.texture_type=RenderingDevice.TEXTURE_TYPE_2D
	tex_format.height=1024
	tex_format.width=1024
	tex_format.format=RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT
	tex_format.usage_bits=(RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT) 
	framebuf_texture=rd.texture_create(tex_format,tex_view)

	var shader_file = load("res://ripple_calc_float.glsl")
	var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
	var shader := rd.shader_create_from_spirv(shader_spirv,"RenderTestJoe")
	print(shader.is_valid())
	var points := PackedFloat32Array([
		0.0, 0.0, 0.0,
		0.0, 1.0 ,0.0,
		1.0,1.0,0.0,
		1.0,0.0,0.0
	])
	var points_bytes := points.to_byte_array()
	
	var indices:= PackedByteArray()
	indices.resize(12)
	var pos:=0
	for x in [0,2,1,0,2,3]:
		indices.encode_u16(pos,x)
		pos+=2
	var index_buffer=rd.index_buffer_create(6,RenderingDevice.INDEX_BUFFER_FORMAT_UINT16,indices)
	index_array=rd.index_array_create(index_buffer,0,6)
	
	var vertex_buffers := [
		rd.vertex_buffer_create(points_bytes.size(), points_bytes),
	]
	
	var vertex_attrs = [ RDVertexAttribute.new()]
	vertex_attrs[0].format = RenderingDevice.DATA_FORMAT_R32G32B32_SFLOAT
	vertex_attrs[0].location = 0
	vertex_attrs[0].stride=4*3
	var vertex_format = rd.vertex_format_create(vertex_attrs)
	vertex_array = rd.vertex_array_create(4, vertex_format, vertex_buffers)
		
	var blend = RDPipelineColorBlendState.new()
	var blend_attachment =RDPipelineColorBlendStateAttachment.new()
	blend_attachment.write_r=true
	blend_attachment.write_g=true
	blend_attachment.write_b=true
	blend_attachment.enable_blend=false
	blend.attachments.push_back(blend_attachment)	

	var attachments=[]
	var af:=RDAttachmentFormat.new()
	af.set_format(tex_format.format)
	af.set_samples(RenderingDevice.TEXTURE_SAMPLES_1)
	af.usage_flags = RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT
	attachments.push_back(af)	
	var framebuf_format=rd.framebuffer_format_create(attachments)
		
	# second param means to check that this framebuffer has correct format
	framebuffer = rd.framebuffer_create([framebuf_texture],framebuf_format)
	print(rd.framebuffer_is_valid(framebuffer))
	pipeline = rd.render_pipeline_create(
		shader,
		framebuf_format,
		vertex_format,
		RenderingDevice.RENDER_PRIMITIVE_TRIANGLES,
		RDPipelineRasterizationState.new(),
		RDPipelineMultisampleState.new(),
		RDPipelineDepthStencilState.new(),
		blend
	)

	print(rd.render_pipeline_is_valid(pipeline))
	clear_color_values= PackedColorArray([Color(1,1,1,1)])

	var draw_list := rd.draw_list_begin(framebuffer, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ,clear_color_values)
	rd.draw_list_bind_render_pipeline(draw_list, pipeline)
	rd.draw_list_bind_vertex_array(draw_list, vertex_array)
	rd.draw_list_bind_index_array(draw_list,index_array)
	rd.draw_list_draw(draw_list, true, 2)
	rd.draw_list_end()
	var td=rd.texture_get_data(framebuf_texture,0)