extends Control const INPUT_TEX_WIDTH := 2048 const INPUT_FORMAT := Image.FORMAT_RGBA8 # Image.FORMAT_LA8 const INPUT_BYTES_PER_TEXEL := 4 # 2 const OUTPUT_BYTES_PER_TEXEL := 4 const OUTPUT_WIDTH := 4096 const QUAD_COLOR := PoolColorArray([Color.white, Color.white, Color.white, Color.white]) var viewport: Viewport var render_queue: Array # of Images var result_queue: Array # of [String, PoolByteArray] var current_tex: ImageTexture # Needed to prevent GC before draw var waiting_for_viewport: Array var done_first_draw: bool func _ready() -> void: self.viewport = get_parent() self.render_queue = [] self.result_queue = [] self.waiting_for_viewport = [] self.done_first_draw = false self.current_tex = ImageTexture.new() func push_image(img: Image, uv_rows: int = 4096, desc: String = '') -> void: self.render_queue.append([img, uv_rows, desc]) func push_bytes(data: PoolByteArray, uv_rows: int = 4096, desc: String = '') -> void: # print(data.subarray(0, 15)) var rows = int(pow(2, ceil(log((len(data)/INPUT_BYTES_PER_TEXEL) / INPUT_TEX_WIDTH)/log(2)))) var target_length = rows * INPUT_BYTES_PER_TEXEL * INPUT_FORMAT while len(data) < target_length: # This is inefficient, but this function should be called with pre-padded data anyway data.append(0) var image := Image.new() image.create_from_data(INPUT_TEX_WIDTH, rows, false, INPUT_FORMAT, data) self.render_queue.append([image, uv_rows, desc]) func _process(_delta) -> void: update() func _draw() -> void: # Seems like the first one always fails if not self.done_first_draw: self.done_first_draw = true return if self.waiting_for_viewport: # Another node later in the draw sequence can call this within the same frame, # otherwise, this picks it up the following frame get_result() if not self.render_queue: return # Draw the next ImageTexture var image_and_uv_rows_and_desc = self.render_queue.pop_front() self.current_tex.create_from_image(image_and_uv_rows_and_desc[0], 0) self.material.set_shader_param('midi_events', self.current_tex) self.material.set_shader_param('midi_events_size', self.current_tex.get_size()) var uv_rows: int = image_and_uv_rows_and_desc[1] var uv_rows_inv: int = 4096 - uv_rows var uv_v: float = uv_rows / float(OUTPUT_WIDTH) var points := PoolVector2Array([Vector2(0, uv_rows_inv), Vector2(OUTPUT_WIDTH, uv_rows_inv), Vector2(OUTPUT_WIDTH, OUTPUT_WIDTH), Vector2(0, OUTPUT_WIDTH)]) var uvs := PoolVector2Array([Vector2(0, 1-uv_v), Vector2(1, 1-uv_v), Vector2(1, 1), Vector2(0, 1)]) draw_primitive(points, QUAD_COLOR, uvs, self.current_tex) self.waiting_for_viewport = [uv_rows, image_and_uv_rows_and_desc[2]] # Grab the result next draw func get_result() -> void: var result_rows: int = waiting_for_viewport[0] var result_desc: String = waiting_for_viewport[1] var result_texture := self.viewport.get_texture() var result_image := result_texture.get_data() var result_bytes := result_image.get_data() var result_byte_count := result_rows * OUTPUT_WIDTH * OUTPUT_BYTES_PER_TEXEL result_bytes.resize(result_byte_count) self.result_queue.append([result_desc, result_bytes]) self.waiting_for_viewport = [] # # Debugging: compare a sequence of all the possible 16bit integers # print_debug('result_image format is %d and has size'%result_image.get_format(), result_image.get_size(), result_bytes.subarray(0, 11)) # test_readback(result_bytes) func test_readback(result_bytes: PoolByteArray): # Debugging: compare a sequence of all the possible 16bit integers var buff := StreamPeerBuffer.new() buff.set_data_array(result_bytes) var tex_readback = 0 var uv_readback = 0 for i in 0x1000: tex_readback = buff.get_u16() uv_readback = buff.get_u16() if tex_readback != i: print('tex readback %d (0x%04x) was instead %d (0x%04x)'%[i, i, tex_readback, tex_readback]) if uv_readback != i: print('uv readback %d (0x%04x) was instead %d (0x%04x)'%[i, i, uv_readback, uv_readback])