package graphics import ( "fmt" "log" "github.com/go-gl/gl/v3.3-core/gl" "github.com/veandco/go-sdl2/sdl" ) // CreateTextureFromSurface uploads an SDL surface to an OpenGL texture and returns the texture ID. func CreateTextureFromSurface(surface *sdl.Surface) (uint32, error) { if surface == nil { return 0, fmt.Errorf("CreateTextureFromSurface: input surface is nil") } var textureID uint32 var err error surfaceToUpload := surface var convertedSurface *sdl.Surface = nil sdlFormatEnum := surface.Format.Format var dataFormat uint32 var internalFormat int32 = gl.RGBA8 if sdlFormatEnum == sdl.PIXELFORMAT_INDEX8 { convertedSurface, err = surface.ConvertFormat(sdl.PIXELFORMAT_ARGB8888, 0) if err != nil { return 0, fmt.Errorf("failed to convert INDEX8 surface to ARGB8888: %w", err) } defer convertedSurface.Free() surfaceToUpload = convertedSurface sdlFormatEnum = surfaceToUpload.Format.Format } switch sdlFormatEnum { case sdl.PIXELFORMAT_ARGB8888: dataFormat = gl.BGRA case sdl.PIXELFORMAT_ABGR8888: dataFormat = gl.RGBA case sdl.PIXELFORMAT_RGBA8888: dataFormat = gl.RGBA case sdl.PIXELFORMAT_BGRA8888: dataFormat = gl.BGRA default: return 0, fmt.Errorf("unhandled SDL pixel format for OpenGL upload: %s", sdl.GetPixelFormatName(uint(sdlFormatEnum))) } for errCode := gl.GetError(); errCode != gl.NO_ERROR; errCode = gl.GetError() { log.Printf("OpenGL error pending before GenTextures: 0x%X", errCode) } gl.GenTextures(1, &textureID) if errCode := gl.GetError(); errCode != gl.NO_ERROR { return 0, fmt.Errorf("OpenGL error 0x%X after GenTextures", errCode) } if textureID == 0 { return 0, fmt.Errorf("gl.GenTextures returned textureID 0") } gl.BindTexture(gl.TEXTURE_2D, textureID) 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_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) expectedPitch := surfaceToUpload.W * int32(surfaceToUpload.Format.BytesPerPixel) if int32(surfaceToUpload.Pitch) != expectedPitch { gl.PixelStorei(gl.UNPACK_ROW_LENGTH, surfaceToUpload.Pitch/int32(surfaceToUpload.Format.BytesPerPixel)) } else { gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) } gl.TexImage2D( gl.TEXTURE_2D, 0, internalFormat, surfaceToUpload.W, surfaceToUpload.H, 0, dataFormat, gl.UNSIGNED_BYTE, gl.Ptr(surfaceToUpload.Pixels()), ) gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4) gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) if glErr := gl.GetError(); glErr != gl.NO_ERROR { gl.DeleteTextures(1, &textureID) return 0, fmt.Errorf("OpenGL error 0x%X after TexImage2D", glErr) } gl.BindTexture(gl.TEXTURE_2D, 0) return textureID, nil } // CheckGLError queries OpenGL for errors and returns the first one found. func CheckGLError() error { errCode := gl.GetError() if errCode == gl.NO_ERROR { return nil } switch errCode { case gl.INVALID_ENUM: return fmt.Errorf("INVALID_ENUM") case gl.INVALID_VALUE: return fmt.Errorf("INVALID_VALUE") case gl.INVALID_OPERATION: return fmt.Errorf("INVALID_OPERATION") case gl.STACK_OVERFLOW: return fmt.Errorf("STACK_OVERFLOW") case gl.STACK_UNDERFLOW: return fmt.Errorf("STACK_UNDERFLOW") case gl.OUT_OF_MEMORY: return fmt.Errorf("OUT_OF_MEMORY") case gl.INVALID_FRAMEBUFFER_OPERATION: return fmt.Errorf("INVALID_FRAMEBUFFER_OPERATION") default: return fmt.Errorf("Unknown GL error: 0x%X", errCode) } }