mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-29 20:35:13 +01:00
move gb color correction to gpu, unify display logic for gb and gba
This commit is contained in:
parent
be81650f54
commit
f2151a0a25
11 changed files with 208 additions and 225 deletions
145
src/crab/common/display.cr
Normal file
145
src/crab/common/display.cr
Normal file
|
@ -0,0 +1,145 @@
|
|||
require "lib_gl"
|
||||
|
||||
class Display
|
||||
enum Console
|
||||
GB
|
||||
GBA
|
||||
|
||||
def width : Int32
|
||||
case self
|
||||
in GB then 160
|
||||
in GBA then 240
|
||||
end
|
||||
end
|
||||
|
||||
def height : Int32
|
||||
case self
|
||||
in GB then 144
|
||||
in GBA then 160
|
||||
end
|
||||
end
|
||||
|
||||
def shader : String
|
||||
case self
|
||||
in GB then "gb_colors.frag"
|
||||
in GBA then "gba_colors.frag"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SCALE = 4
|
||||
SHADERS = "src/crab/common/shaders"
|
||||
|
||||
@microseconds = 0
|
||||
@frames = 0
|
||||
@last_time = Time.utc
|
||||
@seconds : Int32 = Time.utc.second
|
||||
|
||||
@blend : Bool = false
|
||||
|
||||
def initialize(@console : Console)
|
||||
@window = SDL::Window.new(window_title(59.7), console.width * SCALE, console.height * SCALE, flags: SDL::Window::Flags::OPENGL)
|
||||
setup_gl
|
||||
end
|
||||
|
||||
def draw(framebuffer : Slice(UInt16)) : Nil
|
||||
LibGL.tex_image_2d(LibGL::TEXTURE_2D, 0, LibGL::RGB5, @console.width, @console.height, 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)
|
||||
update_draw_count
|
||||
end
|
||||
|
||||
def toggle_blending : Nil
|
||||
if @blend
|
||||
LibGL.disable(LibGL::BLEND)
|
||||
else
|
||||
LibGL.enable(LibGL::BLEND)
|
||||
end
|
||||
@blend = !@blend
|
||||
end
|
||||
|
||||
private def window_title(fps : Float) : String
|
||||
"crab - #{fps.round(1)} fps"
|
||||
end
|
||||
|
||||
private def update_draw_count : Nil
|
||||
current_time = Time.utc
|
||||
@microseconds += (current_time - @last_time).microseconds
|
||||
@last_time = current_time
|
||||
@frames += 1
|
||||
if current_time.second != @seconds
|
||||
fps = @frames * 1_000_000 / @microseconds
|
||||
@window.title = window_title(fps)
|
||||
@microseconds = 0
|
||||
@frames = 0
|
||||
@seconds = current_time.second
|
||||
end
|
||||
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
|
||||
LibGL.get_shader_info_log(shader, log_length, pointerof(log_length), s) if log_length > 0
|
||||
abort "Error compiling shader: #{s}"
|
||||
end
|
||||
shader
|
||||
end
|
||||
|
||||
private def setup_gl : Nil
|
||||
{% if flag?(:darwin) %}
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_FLAGS, LibSDL::GLcontextFlag::FORWARD_COMPATIBLE_FLAG)
|
||||
{% end %}
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_PROFILE_MASK, LibSDL::GLprofile::PROFILE_CORE)
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_MAJOR_VERSION, 3)
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_MINOR_VERSION, 3)
|
||||
|
||||
{% unless flag?(:darwin) %}
|
||||
# todo: proper debug messages for mac
|
||||
LibGL.enable(LibGL::DEBUG_OUTPUT)
|
||||
LibGL.enable(LibGL::DEBUG_OUTPUT_SYNCHRONOUS)
|
||||
LibGL.debug_message_callback(->Display.callback, nil)
|
||||
{% end %}
|
||||
|
||||
LibSDL.gl_create_context @window
|
||||
LibSDL.gl_set_swap_interval(0) # disable vsync
|
||||
shader_program = LibGL.create_program
|
||||
|
||||
puts "OpenGL version: #{String.new(LibGL.get_string(LibGL::VERSION))}"
|
||||
puts "Shader language version: #{String.new(LibGL.get_string(LibGL::SHADING_LANGUAGE_VERSION))}"
|
||||
|
||||
LibGL.blend_func(LibGL::SRC_ALPHA, LibGL::ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
vert_shader_id = compile_shader(File.read("#{SHADERS}/identity.vert"), LibGL::VERTEX_SHADER)
|
||||
frag_shader_id = compile_shader(File.read("#{SHADERS}/#{@console.shader}"), 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, vert_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 in modern opengl
|
||||
LibGL.gen_vertex_arrays(1, pointerof(vao))
|
||||
LibGL.bind_vertex_array(vao)
|
||||
end
|
||||
|
||||
protected def self.callback(source : UInt32, type : UInt32, id : UInt32, severity : UInt32, length : Int32, message : Pointer(UInt8), userParam : Pointer(Void)) : Nil
|
||||
puts "OpenGL debug message: #{String.new message}"
|
||||
end
|
||||
end
|
20
src/crab/common/shaders/gb_colors.frag
Normal file
20
src/crab/common/shaders/gb_colors.frag
Normal file
|
@ -0,0 +1,20 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D input_texture;
|
||||
|
||||
mat3 m = mat3(
|
||||
26, 0, 6,
|
||||
4, 24, 4,
|
||||
2, 8, 22
|
||||
);
|
||||
|
||||
void main() {
|
||||
// Credits to [unknown] and Near for this color-correction algorithm.
|
||||
// https://byuu.net/video/color-emulation
|
||||
vec4 color = texture(input_texture, tex_coord);
|
||||
frag_color.rgb = min(vec3(30), m * color.rgb / 32);
|
||||
frag_color.a = 0.5;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
require "stumpy_png"
|
||||
|
||||
module GB
|
||||
class Display
|
||||
WIDTH = 160
|
||||
HEIGHT = 144
|
||||
|
||||
PIXELFORMAT_BGR5 = 357764866
|
||||
TEXTUREACCESS_STREAMING = 1
|
||||
|
||||
@window : SDL::Window
|
||||
@renderer : SDL::Renderer
|
||||
@texture : Pointer(LibSDL::Texture)
|
||||
|
||||
@title : String
|
||||
|
||||
@fps = 30
|
||||
@seconds : Int32 = Time.utc.second
|
||||
|
||||
def initialize(gb : GB, headless : Bool)
|
||||
@title = gb.cartridge.title
|
||||
flags = headless ? SDL::Window::Flags::HIDDEN : SDL::Window::Flags::SHOWN
|
||||
@window = SDL::Window.new(window_title, WIDTH * DISPLAY_SCALE, HEIGHT * DISPLAY_SCALE, flags: flags)
|
||||
@renderer = SDL::Renderer.new @window
|
||||
@renderer.logical_size = {WIDTH, HEIGHT}
|
||||
@texture = LibSDL.create_texture @renderer, PIXELFORMAT_BGR5, TEXTUREACCESS_STREAMING, WIDTH, HEIGHT
|
||||
end
|
||||
|
||||
def window_title : String
|
||||
"CryBoy - #{@title} - #{@fps} fps"
|
||||
end
|
||||
|
||||
def draw(framebuffer : Slice(UInt16)) : Nil
|
||||
LibSDL.update_texture @texture, nil, framebuffer, WIDTH * sizeof(UInt16)
|
||||
@renderer.clear
|
||||
@renderer.copy @texture
|
||||
@renderer.present
|
||||
@fps += 1
|
||||
if Time.utc.second != @seconds
|
||||
@window.title = window_title
|
||||
@fps = 0
|
||||
@seconds = Time.utc.second
|
||||
end
|
||||
end
|
||||
|
||||
def write_png(framebuffer : Array(RGB)) : Nil
|
||||
canvas = StumpyPNG::Canvas.new WIDTH, HEIGHT
|
||||
HEIGHT.times do |row|
|
||||
WIDTH.times do |col|
|
||||
rgb = framebuffer[row * WIDTH + col]
|
||||
color = StumpyPNG::RGBA.from_rgb8(rgb.red, rgb.green, rgb.blue)
|
||||
canvas[col, row] = color
|
||||
end
|
||||
end
|
||||
StumpyPNG.write(canvas, "out.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
###############################################################################
|
||||
# Method for drawing all tiles in vram
|
||||
|
||||
# @all_tiles_window = SDL::Window.new("ALL TILES", 128 * scale, 192 * scale)
|
||||
# @all_tiles_renderer = SDL::Renderer.new @all_tiles_window
|
||||
# @all_tiles_renderer.logical_size = {128, 192}
|
||||
|
||||
# # a method for showing all tiles in vram for debugging
|
||||
# def draw_all_tiles(memory : Memory)
|
||||
# (0...24).each do |y|
|
||||
# (0...16).each do |x|
|
||||
# tile_ptr = 0x8000 + (y * 16 * 16) + (x * 16)
|
||||
# (0...8).each do |tile_row|
|
||||
# byte_1 = memory[tile_ptr + 2 * tile_row]
|
||||
# byte_2 = memory[tile_ptr + 2 * tile_row + 1]
|
||||
# (0...8).each do |tile_col|
|
||||
# lsb = (byte_1 >> (7 - tile_col)) & 0x1
|
||||
# msb = (byte_2 >> (7 - tile_col)) & 0x1
|
||||
# @all_tiles_renderer.draw_color = @colors[(msb << 1) | lsb]
|
||||
# @all_tiles_renderer.draw_point((8 * x + tile_col), (8 * y + tile_row))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# @all_tiles_renderer.present
|
||||
# end
|
|
@ -203,10 +203,10 @@ module GB
|
|||
arr = @pram
|
||||
end
|
||||
color = @cgb_ptr.value ? pixel.color : palette[pixel.color]
|
||||
@framebuffer[Display::WIDTH * @ly + @lx] = arr.to_unsafe.as(UInt16*)[4 * pixel.palette + color]
|
||||
@framebuffer[WIDTH * @ly + @lx] = arr.to_unsafe.as(UInt16*)[4 * pixel.palette + color]
|
||||
end
|
||||
@lx += 1
|
||||
if @lx == Display::WIDTH
|
||||
if @lx == WIDTH
|
||||
self.mode_flag = 0
|
||||
end
|
||||
if window_enabled? && @ly >= @wy && @lx + 7 >= @wx && !@fetching_window && @window_trigger
|
||||
|
@ -243,7 +243,7 @@ module GB
|
|||
if @cycle_counter == 456
|
||||
@cycle_counter = 0
|
||||
@ly += 1
|
||||
if @ly == Display::HEIGHT # final row of screen complete
|
||||
if @ly == HEIGHT # final row of screen complete
|
||||
self.mode_flag = 1 # switch to vblank
|
||||
@gb.interrupts.vblank_interrupt = true
|
||||
@gb.display.draw @framebuffer # render at vblank
|
||||
|
|
|
@ -2,7 +2,6 @@ require "sdl"
|
|||
require "./apu"
|
||||
require "./cartridge"
|
||||
require "./cpu"
|
||||
require "./display"
|
||||
require "./interrupts"
|
||||
require "./joypad"
|
||||
require "./mbc/*"
|
||||
|
@ -44,7 +43,7 @@ module GB
|
|||
@scheduler = Scheduler.new
|
||||
@interrupts = Interrupts.new
|
||||
@apu = APU.new self, @headless, @sync
|
||||
@display = Display.new self, @headless
|
||||
@display = Display.new Display::Console::GB
|
||||
@joypad = Joypad.new self
|
||||
@ppu = @fifo ? FifoPPU.new self : ScanlinePPU.new self
|
||||
@timer = Timer.new self
|
||||
|
|
|
@ -111,7 +111,10 @@ module GB
|
|||
@ran_bios : Bool # determine if colors should be adjusted for cgb
|
||||
@cgb_ptr : Pointer(Bool)
|
||||
|
||||
@framebuffer = Slice(UInt16).new Display::WIDTH * Display::HEIGHT
|
||||
WIDTH = 160
|
||||
HEIGHT = 144
|
||||
|
||||
@framebuffer = Slice(UInt16).new WIDTH * HEIGHT
|
||||
|
||||
@pram = Bytes.new 64
|
||||
@palette_index : UInt8 = 0
|
||||
|
@ -160,6 +163,30 @@ module GB
|
|||
|
||||
def initialize(@gb : GB)
|
||||
@cgb_ptr = gb.cgb_ptr
|
||||
unless @cgb_ptr.value # fill default color palettes
|
||||
# {% if flag? :pink %}
|
||||
# @palettes[0] = @obj_palettes[0] = @obj_palettes[1] = [
|
||||
# RGB.new(0xFF, 0xF6, 0xD3), RGB.new(0xF9, 0xA8, 0x75),
|
||||
# RGB.new(0xEB, 0x6B, 0x6F), RGB.new(0x7C, 0x3F, 0x58),
|
||||
# ]
|
||||
# {% elsif flag? :graphics_test %}
|
||||
# @palettes[0] = @obj_palettes[0] = @obj_palettes[1] = [
|
||||
# RGB.new(0xFF, 0xFF, 0xFF), RGB.new(0xAA, 0xAA, 0xAA),
|
||||
# RGB.new(0x55, 0x55, 0x55), RGB.new(0x00, 0x00, 0x00),
|
||||
# ]
|
||||
# @pram[0] = @pram[1] = @obj_pram[0] = @obj_pram[1] = 0xFF
|
||||
# @pram.to_unsafe.as(UInt16*)[0] = 0x7FFF;
|
||||
# {% else %}
|
||||
# @palettes[0] = @obj_palettes[0] = @obj_palettes[1] = [
|
||||
# RGB.new(0xE0, 0xF8, 0xCF), RGB.new(0x86, 0xC0, 0x6C),
|
||||
# RGB.new(0x30, 0x68, 0x50), RGB.new(0x07, 0x17, 0x20),
|
||||
# ]
|
||||
# {% end %}
|
||||
@pram.to_unsafe.as(UInt16*)[0] = @obj_pram.to_unsafe.as(UInt16*)[0] = @obj_pram.to_unsafe.as(UInt16*)[4] = 0x6BDF
|
||||
@pram.to_unsafe.as(UInt16*)[1] = @obj_pram.to_unsafe.as(UInt16*)[1] = @obj_pram.to_unsafe.as(UInt16*)[5] = 0x3ABF
|
||||
@pram.to_unsafe.as(UInt16*)[2] = @obj_pram.to_unsafe.as(UInt16*)[2] = @obj_pram.to_unsafe.as(UInt16*)[6] = 0x35BD
|
||||
@pram.to_unsafe.as(UInt16*)[3] = @obj_pram.to_unsafe.as(UInt16*)[3] = @obj_pram.to_unsafe.as(UInt16*)[7] = 0x2CEF
|
||||
end
|
||||
@ran_bios = @cgb_ptr.value
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ module GB
|
|||
end
|
||||
|
||||
# color idx, BG-to-OAM priority bit
|
||||
@scanline_color_vals = Array(Tuple(UInt8, Bool)).new Display::WIDTH, {0_u8, false}
|
||||
@scanline_color_vals = Array(Tuple(UInt8, Bool)).new WIDTH, {0_u8, false}
|
||||
|
||||
def scanline
|
||||
@current_window_line = 0 if @ly == 0
|
||||
|
@ -32,7 +32,7 @@ module GB
|
|||
tile_data_table = bg_window_tile_data == 0 ? 0x1000 : 0x0000 # 0x9000 : 0x8000
|
||||
tile_row_window = @current_window_line & 7
|
||||
tile_row = (@ly.to_u16 + @scy) & 7
|
||||
Display::WIDTH.times do |x|
|
||||
WIDTH.times do |x|
|
||||
if window_enabled? && @ly >= @wy && x + 7 >= @wx && @window_trigger
|
||||
should_increment_window_line = true
|
||||
tile_num_addr = window_map + ((x + 7 - @wx) >> 3) + ((@current_window_line >> 3) * 32)
|
||||
|
@ -57,9 +57,9 @@ module GB
|
|||
color = (msb << 1) | lsb
|
||||
@scanline_color_vals[x] = {color, @vram[1][tile_num_addr] & 0x80 > 0}
|
||||
if @cgb_ptr.value
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[4 * (@vram[1][tile_num_addr] & 0b111) + color]
|
||||
@framebuffer[WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[4 * (@vram[1][tile_num_addr] & 0b111) + color]
|
||||
else
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[@bgp[color]]
|
||||
@framebuffer[WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[@bgp[color]]
|
||||
end
|
||||
elsif bg_display? || @cgb_ptr.value
|
||||
tile_num_addr = background_map + (((x + @scx) >> 3) & 0x1F) + ((((@ly.to_u16 + @scy) >> 3) * 32) & 0x3FF)
|
||||
|
@ -84,9 +84,9 @@ module GB
|
|||
color = (msb << 1) | lsb
|
||||
@scanline_color_vals[x] = {color, @vram[1][tile_num_addr] & 0x80 > 0}
|
||||
if @cgb_ptr.value
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[4 * (@vram[1][tile_num_addr] & 0b111) + color]
|
||||
@framebuffer[WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[4 * (@vram[1][tile_num_addr] & 0b111) + color]
|
||||
else
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[@bgp[color]]
|
||||
@framebuffer[WIDTH * @ly + x] = @pram.to_unsafe.as(UInt16*)[@bgp[color]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -97,7 +97,7 @@ module GB
|
|||
bytes = sprite.bytes @ly, sprite_height
|
||||
8.times do |col|
|
||||
x = col + sprite.x - 8
|
||||
next unless 0 <= x < Display::WIDTH # only render sprites on screen
|
||||
next unless 0 <= x < WIDTH # only render sprites on screen
|
||||
if sprite.x_flip?
|
||||
lsb = (@vram[@cgb_ptr.value ? sprite.bank_num : 0][bytes[0]] >> col) & 0x1
|
||||
msb = (@vram[@cgb_ptr.value ? sprite.bank_num : 0][bytes[1]] >> col) & 0x1
|
||||
|
@ -112,12 +112,12 @@ module GB
|
|||
# objects are always on top of bg/window color 0
|
||||
# objects are on top of bg/window colors 1-3 if bg_priority and object priority are both unset
|
||||
if !bg_display? || @scanline_color_vals[x][0] == 0 || (!@scanline_color_vals[x][1] && sprite.priority == 0)
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @obj_pram.to_unsafe.as(UInt16*)[4 * sprite.cgb_palette_number + color]
|
||||
@framebuffer[WIDTH * @ly + x] = @obj_pram.to_unsafe.as(UInt16*)[4 * sprite.cgb_palette_number + color]
|
||||
end
|
||||
else
|
||||
if sprite.priority == 0 || @scanline_color_vals[x][0] == 0
|
||||
palette = sprite.dmg_palette_number == 0 ? @obp0 : @obp1
|
||||
@framebuffer[Display::WIDTH * @ly + x] = @obj_pram.to_unsafe.as(UInt16*)[palette[color]]
|
||||
@framebuffer[WIDTH * @ly + x] = @obj_pram.to_unsafe.as(UInt16*)[palette[color]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -146,7 +146,7 @@ module GB
|
|||
if @cycle_counter >= 204 # end of hblank reached
|
||||
@cycle_counter -= 204 # reset cycle_counter, saving extra cycles
|
||||
@ly += 1
|
||||
if @ly == Display::HEIGHT # final row of screen complete
|
||||
if @ly == HEIGHT # final row of screen complete
|
||||
self.mode_flag = 1 # switch to vblank
|
||||
@gb.interrupts.vblank_interrupt = true
|
||||
@gb.display.draw @framebuffer # render at vblank
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
require "lib_gl"
|
||||
|
||||
module GBA
|
||||
class Display
|
||||
WIDTH = 240
|
||||
HEIGHT = 160
|
||||
SCALE = 4
|
||||
|
||||
@microseconds = 0
|
||||
@frames = 0
|
||||
@last_time = Time.utc
|
||||
@seconds : Int32 = Time.utc.second
|
||||
|
||||
@blend : Bool = false
|
||||
|
||||
def initialize
|
||||
@window = SDL::Window.new(window_title(59.7), WIDTH * SCALE, HEIGHT * SCALE, flags: SDL::Window::Flags::OPENGL)
|
||||
setup_gl
|
||||
end
|
||||
|
||||
def draw(framebuffer : Slice(UInt16)) : Nil
|
||||
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)
|
||||
update_draw_count
|
||||
end
|
||||
|
||||
def toggle_blending : Nil
|
||||
if @blend
|
||||
LibGL.disable(LibGL::BLEND)
|
||||
else
|
||||
LibGL.enable(LibGL::BLEND)
|
||||
end
|
||||
@blend = !@blend
|
||||
end
|
||||
|
||||
private def window_title(fps : Float) : String
|
||||
"crab - #{fps.round(1)} fps"
|
||||
end
|
||||
|
||||
private def update_draw_count : Nil
|
||||
current_time = Time.utc
|
||||
@microseconds += (current_time - @last_time).microseconds
|
||||
@last_time = current_time
|
||||
@frames += 1
|
||||
if current_time.second != @seconds
|
||||
fps = @frames * 1_000_000 / @microseconds
|
||||
@window.title = window_title(fps)
|
||||
@microseconds = 0
|
||||
@frames = 0
|
||||
@seconds = current_time.second
|
||||
end
|
||||
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
|
||||
LibGL.get_shader_info_log(shader, log_length, pointerof(log_length), s) if log_length > 0
|
||||
abort "Error compiling shader: #{s}"
|
||||
end
|
||||
shader
|
||||
end
|
||||
|
||||
private def setup_gl : Nil
|
||||
{% if flag?(:darwin) %}
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_FLAGS, LibSDL::GLcontextFlag::FORWARD_COMPATIBLE_FLAG)
|
||||
{% end %}
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_PROFILE_MASK, LibSDL::GLprofile::PROFILE_CORE)
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_MAJOR_VERSION, 3)
|
||||
LibSDL.gl_set_attribute(LibSDL::GLattr::SDL_GL_CONTEXT_MINOR_VERSION, 3)
|
||||
|
||||
{% unless flag?(:darwin) %}
|
||||
# todo: proper debug messages for mac
|
||||
LibGL.enable(LibGL::DEBUG_OUTPUT)
|
||||
LibGL.enable(LibGL::DEBUG_OUTPUT_SYNCHRONOUS)
|
||||
LibGL.debug_message_callback(->Display.callback, nil)
|
||||
{% end %}
|
||||
|
||||
LibSDL.gl_create_context @window
|
||||
LibSDL.gl_set_swap_interval(0) # disable vsync
|
||||
shader_program = LibGL.create_program
|
||||
|
||||
puts "OpenGL version: #{String.new(LibGL.get_string(LibGL::VERSION))}"
|
||||
puts "Shader language version: #{String.new(LibGL.get_string(LibGL::SHADING_LANGUAGE_VERSION))}"
|
||||
|
||||
LibGL.blend_func(LibGL::SRC_ALPHA, LibGL::ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
vert_shader_id = compile_shader(File.read("src/crab/gba/shaders/gba_colors.vert"), LibGL::VERTEX_SHADER)
|
||||
frag_shader_id = compile_shader(File.read("src/crab/gba/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, vert_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 in modern opengl
|
||||
LibGL.gen_vertex_arrays(1, pointerof(vao))
|
||||
LibGL.bind_vertex_array(vao)
|
||||
end
|
||||
|
||||
protected def self.callback(source : UInt32, type : UInt32, id : UInt32, severity : UInt32, length : Int32, message : Pointer(UInt8), userParam : Pointer(Void)) : Nil
|
||||
puts "OpenGL debug message: #{String.new message}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,6 @@ require "./keypad"
|
|||
require "./bus"
|
||||
require "./interrupts"
|
||||
require "./cpu"
|
||||
require "./display"
|
||||
require "./ppu"
|
||||
require "./apu"
|
||||
require "./dma"
|
||||
|
@ -50,7 +49,7 @@ module GBA
|
|||
@bus = Bus.new self, @bios_path
|
||||
@interrupts = Interrupts.new self
|
||||
@cpu = CPU.new self
|
||||
@display = Display.new
|
||||
@display = Display.new Display::Console::GBA
|
||||
@ppu = PPU.new self
|
||||
@apu = APU.new self
|
||||
@dma = DMA.new self
|
||||
|
|
Loading…
Add table
Reference in a new issue