package main import "base:runtime" import "core:c" import "core:log" import "core:math/linalg/glsl" import "core:mem/virtual" import "core:slice" import gl "vendor:OpenGL" import "vendor:glfw" @(export) NvOptimusEnablement: u32 = 1 @(export) AmdPowerXpressRequestHighPerformance: int = 1 TARGET_FPS :: 60 vertices := []struct { x, y: f32, u, v: f32, }{{-1.0, -1.0, 0.0, 1.0}, {1.0, -1.0, 1.0, 1.0}, {-1.0, 1.0, 0.0, 0.0}, {1.0, 1.0, 1.0, 0.0}} indices := []u32{0, 1, 2, 2, 3, 1} Bitmap :: struct { width: int, height: int, buffer: []u8, _internal: struct { texture: u32, }, } create_bitmap :: proc(width, height: int) -> Bitmap { bitmap: Bitmap = { width = width, height = height, buffer = make([]u8, width * height * 4), } gl.GenTextures(1, &bitmap._internal.texture) gl.BindTexture(gl.TEXTURE_2D, bitmap._internal.texture) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, i32(width), i32(height), 0, gl.BGRA, gl.UNSIGNED_BYTE, nil) gl.TexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, i32(width), i32(height), gl.BGRA, gl.UNSIGNED_BYTE, raw_data(bitmap.buffer), ) gl.BindBuffer(gl.TEXTURE_BUFFER, 0) return bitmap } destroy_bitmap :: proc(bitmap: ^Bitmap) { gl.DeleteTextures(1, &bitmap._internal.texture) delete(bitmap.buffer) } main :: proc() { context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "MAIN") log.info("booting up...") if ok := glfw.Init(); !ok { log.panic("fail :: init glfw") } defer glfw.Terminate() glfw.WindowHint(glfw.RESIZABLE, false) glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 4) glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 1) glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, true) glfw.WindowHint(glfw.OPENGL_DEBUG_CONTEXT, true) glfw.SetErrorCallback(proc "c" (error: c.int, description: cstring) { context = runtime.default_context() context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "GLFW") log.errorf("%d:%s", error, description) }) window := glfw.CreateWindow(1280, 720, "Cellular Automata", nil, nil) if window == nil { log.panic("fail :: create window") } defer glfw.DestroyWindow(window) glfw.MakeContextCurrent(window) glfw.SwapInterval(0) glfw.SetKeyCallback(window, key_callback) gl.load_up_to(4, 1, glfw.gl_set_proc_address) log.debugf("GL Context: %s", gl.GetString(gl.VERSION)) /*if glfw.ExtensionSupported("GL_KHR_debug") != 0 { // NOTE: Not supported on macos? gl.Enable(gl.DEBUG_OUTPUT) gl.Enable(gl.DEBUG_OUTPUT_SYNCHRONOUS) gl.DebugMessageCallback(debug_message_proc, nil) gl.DebugMessageControl(gl.DONT_CARE, gl.DONT_CARE, gl.DONT_CARE, 0, nil, true) gl.DebugMessageInsert( gl.DEBUG_SOURCE_APPLICATION, gl.DEBUG_TYPE_ERROR, 0, gl.DEBUG_SEVERITY_NOTIFICATION, -1, "Vary dangerous error", ) }*/ width, height := glfw.GetFramebufferSize(window) gl.Viewport(0, 0, width, height) log.debugf("viewport %dx%d", width, height) frame_vb, frame_ib: u32 gl.GenBuffers(1, &frame_vb) gl.GenBuffers(1, &frame_ib) gl.BindBuffer(gl.ARRAY_BUFFER, frame_vb) gl.BufferData(gl.ARRAY_BUFFER, size_of(vertices[0]) * len(vertices), raw_data(vertices), gl.STATIC_DRAW) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, frame_ib) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(indices[0]) * len(indices), raw_data(indices), gl.STATIC_DRAW) bitmap := create_bitmap(int(width), int(height)) defer destroy_bitmap(&bitmap) log.debugf("bitmap: %dx%d, %d bytes", bitmap.width, bitmap.height, len(bitmap.buffer)) program, ok := gl.load_shaders_file("src/simple.vs", "src/simple.fs") //msg, _, _, _ := gl.get_last_error_messages() //log.errorf("msg: %s", msg) assert(ok, "shader failed") log.infof("program %d %b", program, ok) mvp_location := gl.GetUniformLocation(program, "MVP") vpos_location := gl.GetAttribLocation(program, "vPos") uv_location := gl.GetAttribLocation(program, "vTexCoord") log.debugf("locs %d %d %d", mvp_location, vpos_location, uv_location) frame_vao: u32 gl.GenVertexArrays(1, &frame_vao) gl.BindVertexArray(frame_vao) gl.BindBuffer(gl.ARRAY_BUFFER, frame_vb) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, frame_ib) gl.EnableVertexAttribArray(u32(vpos_location)) gl.EnableVertexAttribArray(u32(uv_location)) gl.VertexAttribPointer(u32(vpos_location), 2, gl.FLOAT, false, size_of(vertices[0]), 0) gl.VertexAttribPointer(u32(uv_location), 2, gl.FLOAT, false, size_of(vertices[0]), size_of(f32) * 2) gl.BindVertexArray(0) gl.ClearColor(0.5, 0.5, 1.0, 1.0) projection: glsl.mat4 projection = glsl.mat4Ortho3d(-1, 1, -1, 1, 1, -1) app_memory: virtual.Arena if err := virtual.arena_init_static(&app_memory); err != .None { log.panicf("fail :: arena_init, %v", err) } defer virtual.arena_destroy(&app_memory) app: ^App { context.allocator = virtual.arena_allocator(&app_memory) app = app_init() } log.debugf("app_memory: %v", app_memory) max_tick := 1.0 / TARGET_FPS log.debugf("max_tick: %f (target %d fps)", max_tick, TARGET_FPS) tick := glfw.GetTime() draws_tick := glfw.GetTime() draws: int for !glfw.WindowShouldClose(window) { gl.Clear(gl.COLOR_BUFFER_BIT) gl.UseProgram(program) defer gl.UseProgram(0) gl.UniformMatrix4fv(mvp_location, 1, false, &projection[0, 0]) { new_tick := glfw.GetTime() gl.BindTexture(gl.TEXTURE_2D, bitmap._internal.texture) if (new_tick - tick > max_tick) { tick = new_tick slice.fill(bitmap.buffer, 0) { context.allocator = virtual.arena_allocator(&app_memory) app_update_and_draw(app, &bitmap) } gl.TexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, i32(bitmap.width), i32(bitmap.height), gl.BGRA, gl.UNSIGNED_INT_8_8_8_8_REV, raw_data(bitmap.buffer), ) } defer gl.BindTexture(gl.TEXTURE_2D, 0) gl.BindVertexArray(frame_vao) defer gl.BindVertexArray(0) gl.DrawElements(gl.TRIANGLES, i32(len(indices)), gl.UNSIGNED_INT, nil) } glfw.SwapBuffers(window) glfw.PollEvents() draws += 1 new_draws_tick := glfw.GetTime() if (new_draws_tick - draws_tick > 1) { draws_tick = new_draws_tick log.debugf("draws: %d", draws) draws = 0 } } } debug_message_proc :: proc "c" ( source: u32, type: u32, id: u32, severity: u32, length: i32, message: cstring, user_param: rawptr, ) { context = runtime.default_context() context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "GL") log.debugf("%d %d %d %d %d %s %p", source, type, id, severity, length, message, user_param) } key_callback :: proc "c" (window: glfw.WindowHandle, key, scancode, action, mods: i32) { // Exit program on escape pressed if key == glfw.KEY_ESCAPE { glfw.SetWindowShouldClose(window, true) } }