Some plumbing for upcoming fake compute shaders
This commit is contained in:
parent
b07ed09513
commit
80cbfa7ab8
|
@ -120,6 +120,11 @@ var EVENT_MAP: Dictionary
|
|||
var NOTE_DURATIONS: PoolByteArray # This might need to be made untyped if a future addition uses PoolIntArray instead
|
||||
var REFERENCE_NOTE: int
|
||||
|
||||
const LOGGING_LEVEL_INFO: bool = false
|
||||
func print_info(s: String) -> void:
|
||||
if LOGGING_LEVEL_INFO:
|
||||
print(s)
|
||||
|
||||
# These would be static methods if NOTE_DURATIONS and EVENT_MAP could be static
|
||||
func translate_instruction(buffer: StreamPeer) -> Array:
|
||||
var instruction := buffer.get_u8()
|
||||
|
@ -189,7 +194,7 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
var break_pos := loop_break_positions[loop_level]
|
||||
var jump_pos := loop_break_targets[loop_level]
|
||||
if jump_pos - pos != 1: # This happens once on track 68:06 FFV SNES BGM
|
||||
print('LOOP_END at 0x%06X would break instead from 0x%06X to 0x%06X (%d past end)' % [pos, break_pos, jump_pos, jump_pos-pos])
|
||||
print_debug('LOOP_END at 0x%06X would break instead from 0x%06X to 0x%06X (%d past end)' % [pos, break_pos, jump_pos, jump_pos-pos])
|
||||
buffer.seek(jump_pos)
|
||||
var loop_events := events.slice(rom_address_to_event_index[loop_positions[loop_level]], -1)
|
||||
var partial_loop_events := events.slice(rom_address_to_event_index[loop_positions[loop_level]], rom_address_to_event_index[break_pos]-1)
|
||||
|
@ -211,7 +216,7 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
loop_level -= 1
|
||||
else:
|
||||
# Infinite loop, convert it to a GOTO and return
|
||||
print('ended with infinite loop')
|
||||
print_info('ended with infinite loop')
|
||||
events.append([EventType.GOTO, rom_address_to_event_index[loop_break_targets[loop_level]]])
|
||||
loop_level -= 1
|
||||
break
|
||||
|
@ -225,10 +230,10 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
loop_break_positions[loop_level] = pos
|
||||
loop_break_targets[loop_level] = target_pos
|
||||
if on_loop != loop_counts[loop_level]:
|
||||
print('LOOP_BREAK found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X on loop %d of %d' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos, on_loop, loop_counts[loop_level]])
|
||||
print_info('LOOP_BREAK found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X on loop %d of %d' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos, on_loop, loop_counts[loop_level]])
|
||||
loop_counts[loop_level] = on_loop
|
||||
else:
|
||||
print('LOOP_BREAK found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X on last loop' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos])
|
||||
print_info('LOOP_BREAK found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X on last loop' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos])
|
||||
if target_pos in rom_address_to_event_index:
|
||||
print_debug('LOOP_BREAK is a past address, this is either malformed or an infinite loop')
|
||||
break
|
||||
|
@ -242,7 +247,7 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
target_pos += 0x010000
|
||||
var event_num = rom_address_to_event_index.get(target_pos, -1)
|
||||
if event_num >= 0:
|
||||
print('Infinite GOTO found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X (event number %d)' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos, event_num])
|
||||
print_info('Infinite GOTO found in track %s at 0x%06X:0x%06X:0x%06X: 0x%06X to 0x%06X (event number %d)' % [bgm_id, bgm_start_pos, track_start_pos, bgm_end_pos, pos, target_pos, event_num])
|
||||
events.append([EventType.GOTO, rom_address_to_event_index[target_pos]])
|
||||
break
|
||||
else:
|
||||
|
@ -253,10 +258,10 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
event_idx += 1
|
||||
# DEBUG: Report if track doesn't end all the contained loops
|
||||
if loop_level >= 0:
|
||||
print('track %s ended on loop_level %d (0x%06X)' % [bgm_id, loop_level, loop_positions[loop_level]])
|
||||
print_info('track %s ended on loop_level %d (0x%06X)' % [bgm_id, loop_level, loop_positions[loop_level]])
|
||||
# DEBUG: Report total note duration and repeat note duration
|
||||
if events:
|
||||
print('%d events' % len(events))
|
||||
print_info('%d events' % len(events))
|
||||
var total_note_dur := 0
|
||||
var total_notes := 0
|
||||
var total_rests := 0
|
||||
|
@ -272,7 +277,7 @@ func unroll_track(buffer: StreamPeerBuffer, bgm_start_pos: int, track_start_pos:
|
|||
for e in events.slice(events[-1][1], -1):
|
||||
if e[0] == EventType.NOTE:
|
||||
repeat_note_dur += e[2]
|
||||
print('track %s has duration %d and repeat duration %d (intro %d) - %d notes %d rests' % [bgm_id, total_note_dur, repeat_note_dur, total_note_dur-repeat_note_dur, total_notes, total_rests])
|
||||
print_info('track %s has duration %d and repeat duration %d (intro %d) - %d notes %d rests' % [bgm_id, total_note_dur, repeat_note_dur, total_note_dur-repeat_note_dur, total_notes, total_rests])
|
||||
else:
|
||||
print('track %s has duration %d - %d notes %d rests' % [bgm_id, total_note_dur, total_notes, total_rests])
|
||||
print_info('track %s has duration %d - %d notes %d rests' % [bgm_id, total_note_dur, total_notes, total_rests])
|
||||
return events
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
shader_type canvas_item;
|
||||
render_mode blend_premul_alpha;
|
||||
const float TEX_SIZE = 4096.0;
|
||||
const float UV_QUANTIZE = TEX_SIZE;
|
||||
// I feel like these magic numbers are a bit more intuitive in hex
|
||||
const float x00FF = float(0x00FF); // 255.0
|
||||
const float x0100 = float(0x0100); // 256.0
|
||||
const float x7FFF = float(0x7FFF); // 32767.0
|
||||
const float x8000 = float(0x8000); // 32768.0
|
||||
const float xFF00 = float(0xFF00); // 65280.0
|
||||
const float xFFFF = float(0xFFFF); // 65535.0
|
||||
const float x10000 = float(0x10000); // 65536.0
|
||||
|
||||
const vec2 INT16_DOT_BE = vec2(xFF00, x00FF);
|
||||
const vec2 INT16_DOT_LE = vec2(x00FF, xFF00);
|
||||
uniform sampler2D tex : hint_normal;
|
||||
|
||||
float unpack_int16(vec2 int16) {
|
||||
// Convert packed 2byte integer, sampled as two [0.0, 1.0] range floats,
|
||||
// to the original int value [-32768, 32767] or [0, 65535] but in float32
|
||||
float unsigned = dot(int16, INT16_DOT_LE);
|
||||
return unsigned - (unsigned < x7FFF ? 0.0 : x10000);
|
||||
}
|
||||
|
||||
float rescale_int16(float int16) {
|
||||
// Rescale from [-32768, 32767] to [-1.0, 1.0)
|
||||
return int16 / x8000;
|
||||
}
|
||||
|
||||
vec2 pack_float_to_int16(float value) {
|
||||
// Convert a float in range [-1.0, 1.0) to a signed 2byte integer [-32768, 32767] packed into two [0.0, 1.0] floats
|
||||
float scaled = value * x8000;
|
||||
float unsigned = scaled + (scaled < 0.0 ? x10000 : 0.0);
|
||||
float unsigned_div_256 = unsigned / x0100;
|
||||
float MSB = trunc(unsigned_div_256) / x00FF;
|
||||
float LSB = fract(unsigned_div_256) * x0100 / x00FF;
|
||||
return vec2(LSB, MSB);
|
||||
}
|
||||
|
||||
vec4 test_writeback(vec2 uv) {
|
||||
// Test importing and exporting the samples,
|
||||
// and exporting a value derived from the UV
|
||||
vec4 output;
|
||||
float sample_1 = rescale_int16(unpack_int16(texture(tex, uv).xw));
|
||||
float sample_2 = rescale_int16(dot(trunc(uv*TEX_SIZE), vec2(1.0, TEX_SIZE)));
|
||||
output.xy = pack_float_to_int16(sample_1);
|
||||
output.zw = pack_float_to_int16(sample_2);
|
||||
return output;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// GLES2
|
||||
vec2 uv = vec2(UV.x, 1.0-UV.y);
|
||||
uv = (trunc(uv*UV_QUANTIZE)+0.5)/UV_QUANTIZE;
|
||||
|
||||
COLOR.xyzw = test_writeback(uv);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
extends Control
|
||||
|
||||
var viewport: Viewport
|
||||
var render_queue: Array # of PoolByteArrays
|
||||
var result_queue: Array # of PoolByteArrays
|
||||
var current_image: Image
|
||||
var current_tex: ImageTexture # Needed to prevent GC before draw
|
||||
var waiting_for_viewport: bool
|
||||
var done_first_draw: bool
|
||||
|
||||
func _ready() -> void:
|
||||
self.viewport = get_parent()
|
||||
self.render_queue = []
|
||||
self.result_queue = []
|
||||
self.waiting_for_viewport = false
|
||||
self.done_first_draw = false
|
||||
self.current_image = Image.new()
|
||||
self.current_tex = ImageTexture.new()
|
||||
|
||||
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 data: PoolByteArray = self.render_queue.pop_front()
|
||||
print(data.subarray(0, 15))
|
||||
self.current_image.create_from_data(4096, 4096, false, Image.FORMAT_LA8, data)
|
||||
self.current_tex.create_from_image(self.current_image, Texture.FLAG_FILTER)
|
||||
self.material.set_shader_param('tex', self.current_tex)
|
||||
draw_texture(self.current_tex, Vector2.ZERO)
|
||||
self.waiting_for_viewport = true # Grab the result next draw
|
||||
|
||||
func get_result() -> void:
|
||||
var result_texture := self.viewport.get_texture()
|
||||
var result_image := result_texture.get_data()
|
||||
var result_bytes := result_image.get_data()
|
||||
|
||||
# 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)
|
||||
|
||||
self.result_queue.append(result_bytes)
|
||||
self.waiting_for_viewport = false
|
||||
|
||||
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])
|
|
@ -1,11 +1,31 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://test/audio_system.gd" type="Script" id=1]
|
||||
[ext_resource path="res://theme/menu_theme.tres" type="Theme" id=2]
|
||||
[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="ShaderMaterial" id=2]
|
||||
shader = ExtResource( 4 )
|
||||
|
||||
[node name="audio_system" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="viewport_audio_renderer" type="Viewport" parent="."]
|
||||
size = Vector2( 4096, 4096 )
|
||||
size_override_stretch = true
|
||||
transparent_bg = true
|
||||
handle_input_locally = false
|
||||
hdr = false
|
||||
render_target_update_mode = 3
|
||||
|
||||
[node name="audio_renderer" type="Control" parent="viewport_audio_renderer"]
|
||||
unique_name_in_owner = true
|
||||
material = SubResource( 2 )
|
||||
margin_right = 40.0
|
||||
margin_bottom = 40.0
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="inst_buttons" type="ReferenceRect" parent="."]
|
||||
margin_right = 348.0
|
||||
margin_bottom = 118.0
|
||||
|
@ -42,4 +62,5 @@ margin_top = 192.0
|
|||
margin_right = 315.0
|
||||
margin_bottom = 216.0
|
||||
theme = ExtResource( 2 )
|
||||
pressed = true
|
||||
text = "HACK: Loop Extension"
|
||||
|
|
Loading…
Reference in New Issue