[BGM Shader] Enable batching

This commit is contained in:
Luke Hubmayer-Werner 2024-07-14 23:34:23 +09:30
parent 3a66a26ff8
commit 87739d57f4
6 changed files with 74 additions and 57 deletions

View File

@ -550,6 +550,7 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
p_end = note_event.p_start
else:
channel_loop_p_returns.append(-1)
channel_loop_p_lengths.append(0)
if p_end > longest_channel_p_end:
longest_channel_p_end = p_end

View File

@ -198,7 +198,7 @@ func samples_to_texture():
var loop_begin: int = sample.loop_begin
var loop_length: int = sample.loop_end - loop_begin
var nonlooping: bool = loop_length <= 0
print('Processing sample, nonlooping=%s'%nonlooping)
# print('Processing sample, nonlooping=%s'%nonlooping)
for i in FILTER_PAD: # Prepend frames of silence
payload_buffer.put_16(0)

View File

@ -153,12 +153,12 @@ float get_instrument_sample(float instrument_index, float note, float t) {
const int NUM_CHANNELS = 8;
const int MAX_CHANNEL_NOTE_EVENTS = 2048;
const int NUM_CHANNEL_NOTE_PROBES = 11; // log2(MAX_CHANNEL_NOTE_EVENTS)
uniform sampler2D midi_events : hint_normal;
uniform vec2 midi_events_size = vec2(2048.0, 16.0);
vec4 get_midi_texel(float x, float y) {
return texture(midi_events, vec2(x, y)/midi_events_size).xyzw;
// uniform sampler2D midi_events : hint_normal;
uniform vec2 midi_events_size = vec2(2048.0, 32.0);
vec4 get_midi_texel(sampler2D tex, float x, float y) {
return texture(tex, vec2(x, y)/midi_events_size).xyzw;
}
vec4 render_song(int smp) {
vec4 render_song(sampler2D tex, int smp) {
// Each output texel rendered is a stereo S16LE frame representing 1/32000 of a second
// 2048 is an established safe texture dimension so may as well go 2048 wide
@ -172,17 +172,17 @@ vec4 render_song(int smp) {
int smp_start;
for (int i = 0; i < NUM_CHANNEL_NOTE_PROBES; i++) {
float step_size = exp2(float(NUM_CHANNEL_NOTE_PROBES - i - 1));
smp_start = int(unpack_int32(get_midi_texel(event_idx + step_size, row)));
smp_start = int(unpack_int32(get_midi_texel(tex, event_idx + step_size, row)));
event_idx += (smp >= smp_start) ? step_size : 0.0;
}
smp_start = int(unpack_int32(get_midi_texel(event_idx, row)));
int smp_end = int(unpack_int32(get_midi_texel(event_idx, row+1.0)));
vec4 note_event_supplement = get_midi_texel(event_idx, row+2.0); // left as [0.0, 1.0]
smp_start = int(unpack_int32(get_midi_texel(tex, event_idx, row)));
int smp_end = int(unpack_int32(get_midi_texel(tex, event_idx, row+1.0)));
vec4 note_event_supplement = get_midi_texel(tex, event_idx, row+2.0); // left as [0.0, 1.0]
float instrument_idx = trunc(note_event_supplement.x * 255.0);
float pitch_idx = note_event_supplement.y * 255.0;
float velocity = note_event_supplement.z;
float pan = note_event_supplement.w;
vec4 adsr = get_midi_texel(event_idx, row+3.0); // left as [0.0, 1.0]
vec4 adsr = get_midi_texel(tex, event_idx, row+3.0); // left as [0.0, 1.0]
// ====================At some point I'll look back into packing floats====================
// TBD = note_event_supplement.zw; - tremolo/vibrato/noise/pan_lfo/pitchbend/echo remain
// ====================At some point I'll look back into packing floats====================
@ -214,5 +214,5 @@ void fragment() {
// uv = (trunc(uv*UV_QUANTIZE)+0.5)/UV_QUANTIZE;
// COLOR.xyzw = test_writeback(TEXTURE, uv);
ivec2 xy = ivec2(trunc(uv*TEX_SIZE));
COLOR.xyzw = render_song(xy.x + (xy.y*INT_TEX_SIZE));
COLOR.xyzw = render_song(TEXTURE, xy.x + (xy.y*INT_TEX_SIZE));
}

View File

@ -5,11 +5,12 @@ 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 OUTPUT_HEIGHT := 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 current_textures: Array # of ImageTextures - Needed to prevent GC before draw
var waiting_for_viewport: Array
var done_first_draw: bool
@ -19,7 +20,7 @@ func _ready() -> void:
self.result_queue = []
self.waiting_for_viewport = []
self.done_first_draw = false
self.current_tex = ImageTexture.new()
self.current_textures = []
func push_image(img: Image, uv_rows: int = 4096, desc: String = '') -> void:
self.render_queue.append([img, uv_rows, desc])
@ -51,29 +52,47 @@ func _draw() -> void:
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
self.waiting_for_viewport = []
var rows_drawn := 0
while self.render_queue:
var draw_rows: int = self.render_queue[0][1]
rows_drawn += draw_rows
if rows_drawn > OUTPUT_HEIGHT:
break
# Draw the next ImageTexture
var image_and_uv_rows_and_desc = self.render_queue.pop_front()
var i := len(self.waiting_for_viewport)
if len(self.current_textures) < i+1:
self.current_textures.append(ImageTexture.new())
var tex: ImageTexture = self.current_textures[i]
tex.create_from_image(image_and_uv_rows_and_desc[0], 0)
# self.material.set_shader_param('midi_events', tex)
self.material.set_shader_param('midi_events_size', tex.get_size())
var y_top: int = OUTPUT_HEIGHT - rows_drawn
var y_bot: int = y_top + draw_rows
var uv_inv_v: float = 1 - (draw_rows / float(OUTPUT_WIDTH))
var uvs := PoolVector2Array([Vector2(0, uv_inv_v), Vector2(1, uv_inv_v), Vector2(1, 1), Vector2(0, 1)])
var points := PoolVector2Array([Vector2(0, y_top), Vector2(OUTPUT_WIDTH, y_top), Vector2(OUTPUT_WIDTH, y_bot), Vector2(0, y_bot)])
draw_primitive(points, QUAD_COLOR, uvs, tex)
self.waiting_for_viewport.append([draw_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])
var retrieved_rows := 0
for rows_and_desc in self.waiting_for_viewport:
var entry_rows: int = rows_and_desc[0]
var entry_desc: String = rows_and_desc[1]
var bytes_start := retrieved_rows * OUTPUT_WIDTH * OUTPUT_BYTES_PER_TEXEL
var bytes_end := (retrieved_rows + entry_rows) * OUTPUT_WIDTH * OUTPUT_BYTES_PER_TEXEL
var entry_bytes := result_bytes.subarray(bytes_start, bytes_end-1)
self.result_queue.append([entry_desc, entry_bytes])
retrieved_rows += entry_rows
# result_bytes.resize(result_byte_count)
self.waiting_for_viewport = []
# # Debugging: compare a sequence of all the possible 16bit integers

View File

@ -199,27 +199,28 @@ func render_all_bgm(bgms_to_render: int = 64) -> void:
const save_prerendered_audio := false
func _get_prerendered_audio():
audio_renderer.get_result()
var result = audio_renderer.result_queue.pop_back()
var desc = result[0]
var rendered_audio := AudioStreamSample.new()
rendered_audio.data = result[1]
rendered_audio.stereo = true
rendered_audio.mix_rate = 32000
rendered_audio.format = AudioStreamSample.FORMAT_16_BITS
if prerendered_bgm_start_and_end_loops[desc][0] >= 0:
rendered_audio.loop_begin = int(round(prerendered_bgm_start_and_end_loops[desc][0]))
rendered_audio.loop_end = int(round(prerendered_bgm_start_and_end_loops[desc][1]))
rendered_audio.loop_mode = AudioStreamSample.LOOP_FORWARD
self.prerendered_bgms[desc] = rendered_audio
if save_prerendered_audio:
var error = rendered_audio.save_to_wav('output/rendered_%s.wav'%desc)
print('@%dms - Saved render of %s (error code %s)' % [get_ms(), desc, globals.ERROR_CODE_STRINGS[error]])
else:
print('@%dms - Rendered %s without saving' % [get_ms(), desc])
if self.queued_bgm_playback == desc:
self.audio_player.stream = rendered_audio
self.audio_player.play()
self.queued_bgm_playback = ''
while audio_renderer.result_queue:
var result = audio_renderer.result_queue.pop_front()
var desc = result[0]
var rendered_audio := AudioStreamSample.new()
rendered_audio.data = result[1]
rendered_audio.stereo = true
rendered_audio.mix_rate = 32000
rendered_audio.format = AudioStreamSample.FORMAT_16_BITS
if prerendered_bgm_start_and_end_loops[desc][0] >= 0:
rendered_audio.loop_begin = int(round(prerendered_bgm_start_and_end_loops[desc][0]))
rendered_audio.loop_end = int(round(prerendered_bgm_start_and_end_loops[desc][1]))
rendered_audio.loop_mode = AudioStreamSample.LOOP_FORWARD
self.prerendered_bgms[desc] = rendered_audio
if save_prerendered_audio:
var error = rendered_audio.save_to_wav('output/rendered_%s.wav'%desc)
print('@%dms - Saved render of %s (error code %s)' % [get_ms(), desc, globals.ERROR_CODE_STRINGS[error]])
else:
print('@%dms - Rendered %s without saving' % [get_ms(), desc])
if self.queued_bgm_playback == desc:
self.audio_player.stream = rendered_audio
self.audio_player.play()
self.queued_bgm_playback = ''
func get_shader_test_pattern() -> PoolByteArray:

View File

@ -1,12 +1,9 @@
[gd_scene load_steps=6 format=2]
[gd_scene load_steps=5 format=2]
[ext_resource path="res://test/audio_system.gd" type="Script" id=1]
[ext_resource path="res://test/audio_renderer.gd" type="Script" id=3]
[ext_resource path="res://shaders/audio_renderer.gdshader" type="Shader" id=4]
[sub_resource type="Curve" id=3]
_data = [ Vector2( 0, 0 ), 0.0, 2.46705, 0, 1, Vector2( 0.335329, 0.827273 ), 2.46705, -1.6562, 1, 1, Vector2( 0.631737, 0.336364 ), -1.6562, 1.80207, 1, 1, Vector2( 1, 1 ), 1.80207, 0.0, 1, 0 ]
[sub_resource type="ShaderMaterial" id=2]
shader = ExtResource( 4 )
shader_param/instrument_samples_size = Vector2( 2048, 128 )
@ -16,7 +13,6 @@ shader_param/midi_events_size = Vector2( 2048, 16 )
[node name="audio_system" type="Node2D"]
script = ExtResource( 1 )
curve = SubResource( 3 )
[node name="viewport_audio_renderer" type="Viewport" parent="."]
size = Vector2( 4096, 4096 )