Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No rendering when running on separate goroutine #467

Open
bejaneps opened this issue Jul 10, 2020 · 3 comments
Open

No rendering when running on separate goroutine #467

bejaneps opened this issue Jul 10, 2020 · 3 comments

Comments

@bejaneps
Copy link

bejaneps commented Jul 10, 2020

So, basically I'm following justforfunc series, and started to build flappy gopher game. I've run into an issue that after running a renderer in a separate goroutine it doesn't copy texture to window.

Minimal reproduction

  1. Build game: go build *.go
  2. Run it: ./main
  3. Draws only Flappy Gopher title on window, no bird and background.

Goversion: 1.14.4
OS: Elementary OS Juno

Code
main.go

package main

import (
	"context"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/pkg/errors"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/ttf"
)

var (
	pathToFonts     = filepath.Join("res", "fonts", "flappy.ttf")
	pathToBgImage   = filepath.Join("res", "images", "background.png")
	pathToBirdImage = filepath.Join("res", "images", "bird_frame_%d.png")
)

func main() {
	if err := run(); err != nil {
		log.Println(err)
		os.Exit(2)
	}
}

func run() error {
	var err error

	// initialize all dependencies for SDL
	err = sdl.Init(sdl.INIT_EVERYTHING)
	if err != nil {
		return errors.Errorf("couldn't init SDL: %v", err)
	}
	defer sdl.Quit()

	// initialize fonts that will be used
	err = ttf.Init()
	if err != nil {
		return errors.Errorf("couldn't init TTF: %v", err)
	}
	defer ttf.Quit()

	// create a window
	w, r, err := sdl.CreateWindowAndRenderer(800, 600, sdl.WINDOW_SHOWN)
	if err != nil {
		return errors.Errorf("couldn't create window: %v", err)
	}
	defer r.Destroy()
	defer w.Destroy()

	// draw title
	err = drawTitle(r, "Flappy Gopher")
	if err != nil {
		return errors.Errorf("couldn't draw title: %v", err)
	}

	sdl.Delay(1000)

	// initialize new scene
	scene, err := newScene(r)
	if err != nil {
		return errors.Errorf("couldn't create scene: %v", err)
	}
	defer scene.destroy()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

        // problem code
	runtime.LockOSThread()
	errc := scene.run(ctx, r)
	for {
		select {
		case err := <-errc:
			return err
		case <-time.After(5 * time.Second):
			return nil
		}
	}
}

func drawTitle(r *sdl.Renderer, title string) error {
	r.Clear()

	// open and load font
	font, err := ttf.OpenFont(pathToFonts, 20)
	if err != nil {
		return errors.Errorf("couldn't open font: %v", err)
	}
	defer font.Close()

	// render title
	c := sdl.Color{R: 255, G: 100, B: 0, A: 255}
	surface, err := font.RenderUTF8Solid(title, c)
	if err != nil {
		return errors.Errorf("couldn't render title")
	}
	defer surface.Free()

	// create texture from rendered title
	texture, err := r.CreateTextureFromSurface(surface)
	if err != nil {
		return errors.Errorf("couldn't create font texture: %v", err)
	}
	defer texture.Destroy()

	err = r.Copy(texture, nil, nil)
	if err != nil {
		return errors.Errorf("coldn't copy font texture: %v", err)
	}

	r.Present()

	return nil
}

scene.go

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/pkg/errors"
	"github.com/veandco/go-sdl2/img"
	"github.com/veandco/go-sdl2/sdl"
)

type scene struct {
	time int

	bg    *sdl.Texture
	birds []*sdl.Texture
}

func newScene(r *sdl.Renderer) (*scene, error) {
	// open and load background image
	bg, err := img.LoadTexture(r, pathToBgImage)
	if err != nil {
		return nil, errors.Errorf("couldn't load background image: %v", err)
	}

	// open and load bird images
	var birds []*sdl.Texture
	for i := 1; i <= 4; i++ {
		path := fmt.Sprintf(pathToBirdImage, i)

		bird, err := img.LoadTexture(r, path)
		if err != nil {
			return nil, errors.Errorf("couldn't load bird image: %v", err)
		}

		birds = append(birds, bird)
	}

	return &scene{bg: bg, birds: birds}, nil
}

// problem method
func (s *scene) run(ctx context.Context, r *sdl.Renderer) <-chan error {
	errc := make(chan error)

	go func() {
		defer close(errc)
		tick := time.Tick(10 * time.Millisecond)
		for {
			select {
			case <-ctx.Done():
				return
			case <-tick:
				if err := s.paint(r); err != nil {
					errc <- err
				}
			}

		}
	}()

	return errc
}

func (s *scene) paint(r *sdl.Renderer) error {
	s.time++
	r.Clear()

	// copy backround image
	err := r.Copy(s.bg, nil, nil)
	if err != nil {
		return errors.Errorf("couldn't copy background image: %v", err)
	}

	// place bird in the middle of window
	// and draw different bird images every second
	rect := &sdl.Rect{W: 50, H: 43, X: 10, Y: 300 - 43/2}
	i := s.time / 10 % len(s.birds)
	err = r.Copy(s.birds[i], nil, rect)
	if err != nil {
		return errors.Errorf("couldn't copy bird image: %v", err)
	}

	r.Present()

	return nil
}

func (s *scene) destroy() {
	s.bg.Destroy()

	for _, v := range s.birds {
		v.Destroy()
	}
}

Note: I tried to use directly scene.paint() method and it draws background and bird successfully, but when it's running inside scene.run() method, it basically shows black screen.

@bejaneps
Copy link
Author

Up

@veeableful
Copy link
Contributor

Hi @bejaneps, sorry for the wait. Could you post a minimal example that reproduces the issue? I don't have all the assets so it would be very helpful if you can share a tiny project that requires no assets and reproduces the same issue so I can see what's going on.

@Noofbiz
Copy link

Noofbiz commented Jul 15, 2020

I'm not sure if SDL supports using Renderer.Paint off the main thread. The only things I'm seeing that allow you to do so is via OpenGL contexts, not directly using the Renderer. Here and here are folks doing it with an OpenGL context, but I haven't seen anything using the built in Renderer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants