diff --git a/shard.yml b/shard.yml index c9c8b74..90f3692 100644 --- a/shard.yml +++ b/shard.yml @@ -16,5 +16,7 @@ dependencies: version: 0.1.3 sdl: git: git@github.com:ysbaddaden/sdl.cr.git + lib_gl: + github: nulldotpro/LibGL license: MIT diff --git a/src/crab/display.cr b/src/crab/display.cr index 8b58f92..80c871d 100644 --- a/src/crab/display.cr +++ b/src/crab/display.cr @@ -1,31 +1,22 @@ +require "lib_gl" + class Display WIDTH = 240 HEIGHT = 160 SCALE = 4 - - PIXELFORMAT_RGB24 = (1 << 28) | (7 << 24) | (1 << 20) | (0 << 16) | (24 << 8) | (3 << 0) - PIXELFORMAT_BGR555 = (1 << 28) | (5 << 24) | (5 << 20) | (3 << 16) | (15 << 8) | (2 << 0) - TEXTUREACCESS_STREAMING = 1 - - def initialize - @window = SDL::Window.new("crab", WIDTH * SCALE, HEIGHT * SCALE) - @renderer = SDL::Renderer.new @window - @renderer.logical_size = {WIDTH, HEIGHT} - @texture = LibSDL.create_texture @renderer, PIXELFORMAT_BGR555, TEXTUREACCESS_STREAMING, WIDTH, HEIGHT - end - - def window_title : String - "crab - #{@fps} fps" - end - @fps = 30 @seconds : Int32 = Time.utc.second + def initialize + @window = SDL::Window.new(window_title, WIDTH * SCALE, HEIGHT * SCALE, flags: SDL::Window::Flags::OPENGL) + setup_gl + end + def draw(framebuffer : Bytes) : Nil - LibSDL.update_texture @texture, nil, framebuffer, WIDTH * 2 - @renderer.clear - @renderer.copy @texture - @renderer.present + LibGL.tex_image_2d(LibGL::TEXTURE_2D, 0, LibGL::RGB5, 240, 160, 0, LibGL::RGBA, LibGL::UNSIGNED_SHORT_1_5_5_5_REV, framebuffer) + LibGL.draw_arrays(LibGL::TRIANGLE_STRIP, 0, 4) + LibSDL.gl_swap_window @window + @fps += 1 if Time.utc.second != @seconds @window.title = window_title @@ -33,4 +24,65 @@ class Display @seconds = Time.utc.second end end + + private def window_title : String + "crab - #{@fps} fps" + end + + private def compile_shader(source : String, type : UInt32) : UInt32 + source_ptr = source.to_unsafe + shader = LibGL.create_shader(type) + LibGL.shader_source(shader, 1, pointerof(source_ptr), nil) + LibGL.compile_shader shader + shader_compiled = 0 + LibGL.get_shader_iv(shader, LibGL::COMPILE_STATUS, pointerof(shader_compiled)) + if shader_compiled != LibGL::TRUE + log_length = 0 + LibGL.get_shader_iv(shader, LibGL::INFO_LOG_LENGTH, pointerof(log_length)) + s = " " * log_length + if log_length > 0 + LibGL.get_shader_info_log(shader, log_length, pointerof(log_length), s) + end + puts source + abort "Error compiling shader: #{s}" + end + shader + end + + protected def self.callback(source : UInt32, type : UInt32, id : UInt32, severity : UInt32, length : Int32, message : Pointer(UInt8), userParam : Pointer(Void)) : Nil + puts String.new message + end + + private def setup_gl : Nil + glcontext = LibSDL.gl_create_context @window + shader_program = LibGL.create_program + + LibSDL.gl_set_attribute LibSDL::GLattr::SDL_GL_CONTEXT_MAJOR_VERSION, 4 + LibSDL.gl_set_attribute LibSDL::GLattr::SDL_GL_CONTEXT_MINOR_VERSION, 5 + LibSDL.gl_set_swap_interval 1 + LibGL.enable(LibGL::DEBUG_OUTPUT) + LibGL.enable(LibGL::DEBUG_OUTPUT_SYNCHRONOUS) + LibGL.debug_message_callback(->Display.callback, nil) + + vtx_shader_id = compile_shader(File.read("src/crab/shaders/gba_colors.vert"), LibGL::VERTEX_SHADER) + frag_shader_id = compile_shader(File.read("src/crab/shaders/gba_colors.frag"), LibGL::FRAGMENT_SHADER) + + frame_buffer = 0_u32 + LibGL.gen_textures(1, pointerof(frame_buffer)) + LibGL.active_texture(LibGL::TEXTURE0) + LibGL.bind_texture(LibGL::TEXTURE_2D, frame_buffer) + LibGL.attach_shader shader_program, vtx_shader_id + LibGL.attach_shader shader_program, frag_shader_id + LibGL.link_program shader_program + LibGL.validate_program shader_program + a = [LibGL::BLUE, LibGL::GREEN, LibGL::RED, LibGL::ONE] # flip the rgba to bgra where a is always 1 + a_ptr = pointerof(a).as(Int32*) + LibGL.tex_parameter_iv(LibGL::TEXTURE_2D, LibGL::TEXTURE_SWIZZLE_RGBA, a_ptr) + LibGL.tex_parameter_i(LibGL::TEXTURE_2D, LibGL::TEXTURE_MIN_FILTER, LibGL::NEAREST) + LibGL.tex_parameter_i(LibGL::TEXTURE_2D, LibGL::TEXTURE_MAG_FILTER, LibGL::NEAREST) + LibGL.use_program shader_program + vao = 0_u32 # required even if not used + LibGL.gen_vertex_arrays(1, pointerof(vao)) + LibGL.bind_vertex_array(vao) + end end diff --git a/src/crab/shaders/gba_colors.frag b/src/crab/shaders/gba_colors.frag new file mode 100644 index 0000000..efc6086 --- /dev/null +++ b/src/crab/shaders/gba_colors.frag @@ -0,0 +1,19 @@ +#version 330 + +in vec2 tex_coord; +out vec4 frag_color; + +uniform sampler2D input_texture; + +void main() { + // Credits to Talarubi and Near for this color-correction algorithm. + // https://byuu.net/video/color-emulation + vec4 color = texture(input_texture, tex_coord); + float lcdGamma = 4.0, outGamma = 2.2; + color.rgb = pow(color.rgb, vec3(lcdGamma)); + frag_color.rgb = pow(vec3( 0 * color.b + 50 * color.g + 255 * color.r, + 30 * color.b + 230 * color.g + 10 * color.r, + 220 * color.b + 10 * color.g + 50 * color.r) / 255, + vec3(1.0 / outGamma)); + frag_color.a = 1.0; +} diff --git a/src/crab/shaders/gba_colors.vert b/src/crab/shaders/gba_colors.vert new file mode 100644 index 0000000..6bbd610 --- /dev/null +++ b/src/crab/shaders/gba_colors.vert @@ -0,0 +1,10 @@ +#version 330 + +out vec2 tex_coord; + +const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); + +void main() { + gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); + tex_coord = (vertices[gl_VertexID] + 1.0) / vec2(2.0, -2.0); +}