// Sets up anything that wouldn't have been loaded from disk, including // all opengl data, and sets up finalizers for that data. func (d *Dictionary) setupGlStuff() { d.dlists = make(map[string]uint32) d.strs = make(map[string]strBuffer) d.pars = make(map[string]strBuffer) // TODO: This finalizer is untested runtime.SetFinalizer(d, func(d *Dictionary) { render.Queue(func() { for _, v := range d.dlists { gl.DeleteLists(gl.Uint(v), 1) } }) }) render.Queue(func() { gl.Enable(gl.TEXTURE_2D) gl.GenTextures(1, (*gl.Uint)(&d.texture)) gl.BindTexture(gl.TEXTURE_2D, gl.Uint(d.texture)) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) glu.Build2DMipmaps(gl.TEXTURE_2D, 4, d.data.Dx, d.data.Dy, gl.RGBA, d.data.Pix) gl.Disable(gl.TEXTURE_2D) }) }
// Creates a LosTexture with the specified size, which must be a power of two. func MakeLosTexture() *LosTexture { var lt LosTexture lt.pix = make([]byte, LosTextureSizeSquared) lt.p2d = make([][]byte, LosTextureSize) lt.rec = make(chan gl.Texture, 1) for i := 0; i < LosTextureSize; i++ { lt.p2d[i] = lt.pix[i*LosTextureSize : (i+1)*LosTextureSize] } render.Queue(func() { gl.Enable(gl.TEXTURE_2D) tex := gl.GenTexture() tex.Bind(gl.TEXTURE_2D) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, len(lt.p2d), len(lt.p2d), 0, gl.ALPHA, gl.BYTE, lt.pix) lt.rec <- tex runtime.SetFinalizer(<, losTextureFinalize) }) return < }
// Launch this in its own go-routine if you want to occassionally // delete textures that haven't been used in a while. func (m *Manager) Scavenger() { var unused []string for { time.Sleep(time.Minute) unused = unused[0:0] m.mutex.RLock() for s, d := range m.registry { if generation-d.accessed >= 2 { unused = append(unused, s) } } m.mutex.RUnlock() m.mutex.Lock() generation++ if len(unused) == 0 { m.mutex.Unlock() continue } var unused_data []*Data for _, s := range unused { unused_data = append(unused_data, m.registry[s]) m.deleted[s] = m.registry[s] delete(m.registry, s) } render.Queue(func() { for _, d := range unused_data { d.texture.Delete() d.texture = 0 } }) m.mutex.Unlock() } }
func (m *Manager) LoadSprite(path string) (*Sprite, error) { // We can't run this during an init() function because it will get queued to // run before the opengl context is created, so we just check here and run // it if we haven't run it before. gen_tex_once.Do(func() { render.Queue(func() { gl.Enable(gl.TEXTURE_2D) error_texture = gl.GenTexture() error_texture.Bind(gl.TEXTURE_2D) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) pink := []byte{255, 0, 255, 255} glu.Build2DMipmaps(gl.TEXTURE_2D, 4, 1, 1, gl.RGBA, pink) }) }) path = filepath.Clean(path) err := m.loadSharedSprite(path) if err != nil { return nil, err } var s Sprite m.mutex.Lock() s.shared = m.shared[path] m.mutex.Unlock() s.anim_node = s.shared.anim_start s.state_node = s.shared.state_start return &s, nil }
// Updates OpenGl with any changes that have been made to the texture. // OpenGl calls in this method are run on the render thread func (lt *LosTexture) Remap() { if !lt.ready() { return } render.Queue(func() { gl.Enable(gl.TEXTURE_2D) lt.tex.Bind(gl.TEXTURE_2D) gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, len(lt.p2d), len(lt.p2d), gl.ALPHA, lt.pix) }) }
func (s *sheet) loadRoutine() { ready := make(chan bool, 1) pixer := make(chan []byte) for load := range s.load_chan { if load { go s.compose(pixer) go func() { render.Queue(func() { s.makeTexture(pixer) ready <- true }) }() } else { go func() { <-ready render.Queue(func() { s.texture.Delete() s.texture = 0 }) }() } } }
func setupTextureList() { textureListSync.Do(func() { render.Queue(func() { textureList = gl.GenLists(1) gl.NewList(textureList, gl.COMPILE) gl.Begin(gl.QUADS) gl.TexCoord2d(0, 0) gl.Vertex2i(0, 0) gl.TexCoord2d(0, -1) gl.Vertex2i(0, 1) gl.TexCoord2d(1, -1) gl.Vertex2i(1, 1) gl.TexCoord2d(1, 0) gl.Vertex2i(1, 0) gl.End() gl.EndList() }) }) }
func main() { defer func() { if r := recover(); r != nil { data := debug.Stack() base.Error().Printf("PANIC: %v\n", r) base.Error().Printf("PANIC: %s\n", string(data)) base.CloseLog() fmt.Printf("PANIC: %s\n", string(data)) } }() base.Log().Printf("Version %s", Version()) sys.Startup() sound.Init() render.Init() render.Queue(func() { sys.CreateWindow(10, 10, wdx, wdy) sys.EnableVSync(true) err := gl.Init() if err != nil { panic(err) } gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) }) base.InitShaders() runtime.GOMAXPROCS(8) ui, err := gui.Make(gin.In(), gui.Dims{wdx, wdy}, filepath.Join(datadir, "fonts", "skia.ttf")) if err != nil { panic(err.Error()) } loadAllRegistries() // TODO: Might want to be able to reload stuff, but this is sensitive because it // is loading textures. We should probably redo the sprite system so that this // is easier to safely handle. game.LoadAllEntities() // Set up editors editors = map[string]house.Editor{ "room": house.MakeRoomEditorPanel(), "house": house.MakeHouseEditorPanel(), } for name, editor := range editors { path := base.GetStoreVal(fmt.Sprintf("last %s path", name)) path = filepath.Join(datadir, path) if path != "" { editor.Load(path) } } editor_name = "room" editor = editors[editor_name] edit_mode := false game.Restart = func() { base.Log().Printf("Restarting...") ui.RemoveChild(game_box) game_box = &lowerLeftTable{gui.MakeAnchorBox(gui.Dims{1024, 768})} err = game.InsertStartMenu(game_box) if err != nil { panic(err) } ui.AddChild(game_box) base.Log().Printf("Restarted") } game.Restart() if base.IsDevel() { ui.AddChild(base.MakeConsole()) } sys.Think() // Wait until now to create the dictionary because the render thread needs // to be running in advance. render.Queue(func() { ui.Draw() }) render.Purge() var profile_output *os.File heap_prof_count := 0 for key_map["quit"].FramePressCount() == 0 { sys.Think() render.Queue(func() { sys.SwapBuffers() ui.Draw() }) render.Purge() for _, child := range game_box.GetChildren() { if gp, ok := child.(*game.GamePanel); ok { game_panel = gp } } if base.IsDevel() { if key_map["cpu profile"].FramePressCount() > 0 { if profile_output == nil { profile_output, err = os.Create(filepath.Join(datadir, "cpu.prof")) if err == nil { err = pprof.StartCPUProfile(profile_output) if err != nil { base.Log().Printf("Unable to start CPU profile: %v\n", err) profile_output.Close() profile_output = nil } base.Log().Printf("profout: %v\n", profile_output) } else { base.Log().Printf("Unable to start CPU profile: %v\n", err) } } else { pprof.StopCPUProfile() profile_output.Close() profile_output = nil } } if key_map["heap profile"].FramePressCount() > 0 { out, err := os.Create(filepath.Join(datadir, fmt.Sprintf("heap-%d.prof", heap_prof_count))) heap_prof_count++ if err == nil { err = pprof.WriteHeapProfile(out) out.Close() if err != nil { base.Warn().Printf("Unable to write heap profile: %v", err) } } else { base.Warn().Printf("Unable to create heap profile: %v", err) } } if key_map["manual mem"].FramePressCount() > 0 { base.Log().Printf(memory.TotalAllocations()) } if key_map["game mode"].FramePressCount()%2 == 1 { base.Log().Printf("Game mode change: %t", edit_mode) if edit_mode { ui.RemoveChild(editor) ui.AddChild(game_box) } else { ui.RemoveChild(game_box) ui.AddChild(editor) } edit_mode = !edit_mode if key_map["row up"].FramePressCount() > 0 { house.Num_rows += 25 } if key_map["row down"].FramePressCount() > 0 { house.Num_rows -= 25 } if key_map["steps up"].FramePressCount() > 0 { house.Num_steps++ } if key_map["steps down"].FramePressCount() > 0 { house.Num_steps-- } if key_map["noise up"].FramePressCount() > 0 { house.Noise_rate += 10 } if key_map["noise down"].FramePressCount() > 0 { house.Noise_rate -= 10 } if key_map["foo"].FramePressCount() > 0 { house.Foo = (house.Foo + 1) % 2 } } if edit_mode { editMode() } else { gameMode() } } // Draw a cursor at the cursor - for testing an osx bug in glop. // zx, zy := gin.In().GetCursor("Mouse").Point() // render.Queue(func() { // gl.Color4ub(255, 0, 0, 255) // gl.Begin(gl.LINES) // { // gl.Vertex2i(int32(zx-25), int32(zy)) // gl.Vertex2i(int32(zx+25), int32(zy)) // gl.Vertex2i(int32(zx), int32(zy-25)) // gl.Vertex2i(int32(zx), int32(zy+25)) // } // gl.End() // }) } }
func losTextureFinalize(lt *LosTexture) { render.Queue(func() { gl.Enable(gl.TEXTURE_2D) lt.tex.Delete() }) }
func handleLoadRequest(req loadRequest) { f, _ := os.Open(req.path) im, _, err := image.Decode(f) f.Close() if err != nil { return } gray := true dx := im.Bounds().Dx() dy := im.Bounds().Dy() for i := 0; i < dx; i++ { for j := 0; j < dy; j++ { r, g, b, _ := im.At(i, j).RGBA() if r != g || g != b { gray = false break } } if !gray { break } } var canvas draw.Image var pix []byte if gray { ga := NewGrayAlpha(im.Bounds()) pix = ga.Pix canvas = ga } else { pix = memory.GetBlock(4 * req.data.dx * req.data.dy) canvas = &image.RGBA{pix, 4 * req.data.dx, im.Bounds()} } draw.Draw(canvas, im.Bounds(), im, image.Point{}, draw.Src) load_mutex.Lock() load_count += len(pix) manual_unlock := false // This prevents us from trying to send too much to opengl in a single // frame. If we go over the threshold then we hold the lock until we're // done sending data to opengl, then other requests will be free to // queue up and they will run on the next frame. if load_count < load_threshold { load_mutex.Unlock() } else { manual_unlock = true } render.Queue(func() { { gl.Enable(gl.TEXTURE_2D) req.data.texture = gl.GenTexture() req.data.texture.Bind(gl.TEXTURE_2D) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) } if gray { glu.Build2DMipmaps(gl.TEXTURE_2D, gl.LUMINANCE_ALPHA, req.data.dx, req.data.dy, gl.LUMINANCE_ALPHA, pix) } else { glu.Build2DMipmaps(gl.TEXTURE_2D, gl.RGBA, req.data.dx, req.data.dy, gl.RGBA, pix) } memory.FreeBlock(pix) if manual_unlock { load_count = 0 load_mutex.Unlock() } }) }
func InitShaders() { render.Queue(func() { vertex_shaders = make(map[string]uint32) fragment_shaders = make(map[string]uint32) shader_progs = make(map[string]uint32) warned_names = make(map[string]bool) RemoveRegistry("shaders") RegisterRegistry("shaders", make(map[string]*shaderDef)) RegisterAllObjectsInDir("shaders", filepath.Join(GetDataDir(), "shaders"), ".json", "json") names := GetAllNamesInRegistry("shaders") for _, name := range names { // Load the shader files shader := Shader{Defname: name} GetObject("shaders", &shader) vdata, err := ioutil.ReadFile(filepath.Join(GetDataDir(), shader.Vertex_path)) if err != nil { Error().Printf("Unable to load vertex shader '%s': %v", shader.Vertex_path, err) continue } fdata, err := ioutil.ReadFile(filepath.Join(GetDataDir(), shader.Fragment_path)) if err != nil { Error().Printf("Unable to load fragment shader '%s': %v", shader.Fragment_path, err) continue } // Create the vertex shader vertex_id, ok := vertex_shaders[shader.Vertex_path] if !ok { vertex_id = uint32(gl.CreateShader(gl.VERTEX_SHADER)) pointer := &vdata[0] length := int32(len(vdata)) gl.ShaderSource(gl.Uint(vertex_id), 1, (**gl.Char)(unsafe.Pointer(&pointer)), (*gl.Int)(&length)) gl.CompileShader(gl.Uint(vertex_id)) var param int32 gl.GetShaderiv(gl.Uint(vertex_id), gl.COMPILE_STATUS, (*gl.Int)(¶m)) if param == 0 { Error().Printf("Failed to compile vertex shader '%s': %v", shader.Vertex_path, param) continue } } // Create the fragment shader fragment_id, ok := fragment_shaders[shader.Fragment_path] if !ok { fragment_id = uint32(gl.CreateShader(gl.FRAGMENT_SHADER)) pointer := &fdata[0] length := int32(len(fdata)) gl.ShaderSource(gl.Uint(fragment_id), 1, (**gl.Char)(unsafe.Pointer(&pointer)), (*gl.Int)(&length)) gl.CompileShader(gl.Uint(fragment_id)) var param int32 gl.GetShaderiv(gl.Uint(fragment_id), gl.COMPILE_STATUS, (*gl.Int)(¶m)) if param == 0 { Error().Printf("Failed to compile fragment shader '%s': %v", shader.Fragment_path, param) continue } } // shader successfully compiled - now link program_id := gl.CreateProgram() gl.AttachShader(program_id, gl.Uint(vertex_id)) gl.AttachShader(program_id, gl.Uint(fragment_id)) gl.LinkProgram(program_id) var param int32 gl.GetProgramiv(program_id, gl.LINK_STATUS, (*gl.Int)(¶m)) if param == 0 { Error().Printf("Failed to link shader '%s': %v", shader.Name, param) continue } vertex_shaders[shader.Vertex_path] = vertex_id fragment_shaders[shader.Fragment_path] = fragment_id shader_progs[shader.Name] = uint32(program_id) } }) }