[BGM Shader] Enable batching
This commit is contained in:
parent
3a66a26ff8
commit
87739d57f4
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 )
|
||||
|
|
Loading…
Reference in New Issue