zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

context_desktop.go (14187B)


      1 // Copyright 2014 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 //go:build (darwin || freebsd || linux || windows) && !android && !ios
     16 // +build darwin freebsd linux windows
     17 // +build !android
     18 // +build !ios
     19 
     20 package opengl
     21 
     22 import (
     23 	"errors"
     24 	"fmt"
     25 
     26 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     27 	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
     28 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     29 )
     30 
     31 type (
     32 	textureNative      uint32
     33 	renderbufferNative uint32
     34 	framebufferNative  uint32
     35 	shader             uint32
     36 	program            uint32
     37 	buffer             uint32
     38 )
     39 
     40 func (t textureNative) equal(rhs textureNative) bool {
     41 	return t == rhs
     42 }
     43 
     44 func (r renderbufferNative) equal(rhs renderbufferNative) bool {
     45 	return r == rhs
     46 }
     47 
     48 func (f framebufferNative) equal(rhs framebufferNative) bool {
     49 	return f == rhs
     50 }
     51 
     52 func (s shader) equal(rhs shader) bool {
     53 	return s == rhs
     54 }
     55 
     56 func (b buffer) equal(rhs buffer) bool {
     57 	return b == rhs
     58 }
     59 
     60 func (u uniformLocation) equal(rhs uniformLocation) bool {
     61 	return u == rhs
     62 }
     63 
     64 func (p program) equal(rhs program) bool {
     65 	return p == rhs
     66 }
     67 
     68 var InvalidTexture textureNative
     69 
     70 type (
     71 	uniformLocation int32
     72 	attribLocation  int32
     73 )
     74 
     75 type programID uint32
     76 
     77 const (
     78 	invalidTexture     = 0
     79 	invalidFramebuffer = (1 << 32) - 1
     80 	invalidUniform     = -1
     81 )
     82 
     83 func getProgramID(p program) programID {
     84 	return programID(p)
     85 }
     86 
     87 const (
     88 	zero             = operation(gl.ZERO)
     89 	one              = operation(gl.ONE)
     90 	srcAlpha         = operation(gl.SRC_ALPHA)
     91 	dstAlpha         = operation(gl.DST_ALPHA)
     92 	oneMinusSrcAlpha = operation(gl.ONE_MINUS_SRC_ALPHA)
     93 	oneMinusDstAlpha = operation(gl.ONE_MINUS_DST_ALPHA)
     94 	dstColor         = operation(gl.DST_COLOR)
     95 )
     96 
     97 type contextImpl struct {
     98 	init bool
     99 }
    100 
    101 func (c *context) reset() error {
    102 	if !c.init {
    103 		// Note that this initialization must be done after Loop is called.
    104 		if err := gl.Init(); err != nil {
    105 			return fmt.Errorf("opengl: initializing error %v", err)
    106 		}
    107 		c.init = true
    108 	}
    109 
    110 	c.locationCache = newLocationCache()
    111 	c.lastTexture = invalidTexture
    112 	c.lastFramebuffer = invalidFramebuffer
    113 	c.lastViewportWidth = 0
    114 	c.lastViewportHeight = 0
    115 	c.lastCompositeMode = driver.CompositeModeUnknown
    116 	gl.Enable(gl.BLEND)
    117 	gl.Enable(gl.SCISSOR_TEST)
    118 
    119 	c.blendFunc(driver.CompositeModeSourceOver)
    120 
    121 	f := int32(0)
    122 	gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f)
    123 	c.screenFramebuffer = framebufferNative(f)
    124 	return nil
    125 }
    126 
    127 func (c *context) blendFunc(mode driver.CompositeMode) {
    128 	if c.lastCompositeMode == mode {
    129 		return
    130 	}
    131 	c.lastCompositeMode = mode
    132 	s, d := mode.Operations()
    133 	s2, d2 := convertOperation(s), convertOperation(d)
    134 	gl.BlendFunc(uint32(s2), uint32(d2))
    135 }
    136 
    137 func (c *context) scissor(x, y, width, height int) {
    138 	gl.Scissor(int32(x), int32(y), int32(width), int32(height))
    139 }
    140 
    141 func (c *context) newTexture(width, height int) (textureNative, error) {
    142 	var t uint32
    143 	gl.GenTextures(1, &t)
    144 	// TODO: Use gl.IsTexture
    145 	if t <= 0 {
    146 		return 0, errors.New("opengl: creating texture failed")
    147 	}
    148 	texture := textureNative(t)
    149 	c.bindTexture(texture)
    150 
    151 	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
    152 	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
    153 	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
    154 	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
    155 
    156 	gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
    157 	// If data is nil, this just allocates memory and the content is undefined.
    158 	// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
    159 	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil)
    160 	return texture, nil
    161 }
    162 
    163 func (c *context) bindFramebufferImpl(f framebufferNative) {
    164 	gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f))
    165 }
    166 
    167 func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte {
    168 	gl.Flush()
    169 	c.bindFramebuffer(f.native)
    170 	pixels := make([]byte, 4*width*height)
    171 	gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels))
    172 	return pixels
    173 }
    174 
    175 func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
    176 	gl.Flush()
    177 	c.bindFramebuffer(f.native)
    178 	gl.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer))
    179 	gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil)
    180 	gl.BindBuffer(gl.PIXEL_PACK_BUFFER, 0)
    181 }
    182 
    183 func (c *context) activeTexture(idx int) {
    184 	gl.ActiveTexture(gl.TEXTURE0 + uint32(idx))
    185 }
    186 
    187 func (c *context) bindTextureImpl(t textureNative) {
    188 	gl.BindTexture(gl.TEXTURE_2D, uint32(t))
    189 }
    190 
    191 func (c *context) deleteTexture(t textureNative) {
    192 	tt := uint32(t)
    193 	if !gl.IsTexture(tt) {
    194 		return
    195 	}
    196 	if c.lastTexture == t {
    197 		c.lastTexture = invalidTexture
    198 	}
    199 	gl.DeleteTextures(1, &tt)
    200 }
    201 
    202 func (c *context) isTexture(t textureNative) bool {
    203 	panic("opengl: isTexture is not implemented")
    204 }
    205 
    206 func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
    207 	var r uint32
    208 	gl.GenRenderbuffersEXT(1, &r)
    209 	if r <= 0 {
    210 		return 0, errors.New("opengl: creating renderbuffer failed")
    211 	}
    212 
    213 	renderbuffer := renderbufferNative(r)
    214 	c.bindRenderbuffer(renderbuffer)
    215 
    216 	// GL_STENCIL_INDEX8 might not be available with OpenGL 2.1.
    217 	// https://www.khronos.org/opengl/wiki/Image_Format
    218 	gl.RenderbufferStorageEXT(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, int32(width), int32(height))
    219 
    220 	return renderbuffer, nil
    221 }
    222 
    223 func (c *context) bindRenderbufferImpl(r renderbufferNative) {
    224 	gl.BindRenderbufferEXT(gl.RENDERBUFFER, uint32(r))
    225 }
    226 
    227 func (c *context) deleteRenderbuffer(r renderbufferNative) {
    228 	rr := uint32(r)
    229 	if !gl.IsRenderbufferEXT(rr) {
    230 		return
    231 	}
    232 	if c.lastRenderbuffer.equal(r) {
    233 		c.lastRenderbuffer = 0
    234 	}
    235 	gl.DeleteRenderbuffersEXT(1, &rr)
    236 }
    237 
    238 func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
    239 	var f uint32
    240 	gl.GenFramebuffersEXT(1, &f)
    241 	// TODO: Use gl.IsFramebuffer
    242 	if f <= 0 {
    243 		return 0, errors.New("opengl: creating framebuffer failed: gl.IsFramebuffer returns false")
    244 	}
    245 	c.bindFramebuffer(framebufferNative(f))
    246 	gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0)
    247 	s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER)
    248 	if s != gl.FRAMEBUFFER_COMPLETE {
    249 		if s != 0 {
    250 			return 0, fmt.Errorf("opengl: creating framebuffer failed: %v", s)
    251 		}
    252 		if e := gl.GetError(); e != gl.NO_ERROR {
    253 			return 0, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
    254 		}
    255 		return 0, fmt.Errorf("opengl: creating framebuffer failed: unknown error")
    256 	}
    257 	return framebufferNative(f), nil
    258 }
    259 
    260 func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
    261 	c.bindFramebuffer(f)
    262 
    263 	gl.FramebufferRenderbufferEXT(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
    264 	if s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
    265 		return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
    266 	}
    267 	return nil
    268 }
    269 
    270 func (c *context) setViewportImpl(width, height int) {
    271 	gl.Viewport(0, 0, int32(width), int32(height))
    272 }
    273 
    274 func (c *context) deleteFramebuffer(f framebufferNative) {
    275 	ff := uint32(f)
    276 	if !gl.IsFramebufferEXT(ff) {
    277 		return
    278 	}
    279 	if c.lastFramebuffer == f {
    280 		c.lastFramebuffer = invalidFramebuffer
    281 		c.lastViewportWidth = 0
    282 		c.lastViewportHeight = 0
    283 	}
    284 	gl.DeleteFramebuffersEXT(1, &ff)
    285 }
    286 
    287 func (c *context) newVertexShader(source string) (shader, error) {
    288 	return c.newShader(gl.VERTEX_SHADER, source)
    289 }
    290 
    291 func (c *context) newFragmentShader(source string) (shader, error) {
    292 	return c.newShader(gl.FRAGMENT_SHADER, source)
    293 }
    294 
    295 func (c *context) newShader(shaderType uint32, source string) (shader, error) {
    296 	s := gl.CreateShader(shaderType)
    297 	if s == 0 {
    298 		return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
    299 	}
    300 	cSources, free := gl.Strs(source + "\x00")
    301 	gl.ShaderSource(uint32(s), 1, cSources, nil)
    302 	free()
    303 	gl.CompileShader(s)
    304 
    305 	var v int32
    306 	gl.GetShaderiv(s, gl.COMPILE_STATUS, &v)
    307 	if v == gl.FALSE {
    308 		var l int32
    309 		var log []byte
    310 		gl.GetShaderiv(uint32(s), gl.INFO_LOG_LENGTH, &l)
    311 		if l != 0 {
    312 			log = make([]byte, l)
    313 			gl.GetShaderInfoLog(s, l, nil, (*uint8)(gl.Ptr(log)))
    314 		}
    315 		return 0, fmt.Errorf("opengl: shader compile failed: %s", log)
    316 	}
    317 	return shader(s), nil
    318 }
    319 
    320 func (c *context) deleteShader(s shader) {
    321 	gl.DeleteShader(uint32(s))
    322 }
    323 
    324 func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
    325 	p := gl.CreateProgram()
    326 	if p == 0 {
    327 		return 0, errors.New("opengl: glCreateProgram failed")
    328 	}
    329 
    330 	for _, shader := range shaders {
    331 		gl.AttachShader(p, uint32(shader))
    332 	}
    333 
    334 	for i, name := range attributes {
    335 		l, free := gl.Strs(name + "\x00")
    336 		gl.BindAttribLocation(p, uint32(i), *l)
    337 		free()
    338 	}
    339 
    340 	gl.LinkProgram(p)
    341 	var v int32
    342 	gl.GetProgramiv(p, gl.LINK_STATUS, &v)
    343 	if v == gl.FALSE {
    344 		var l int32
    345 		var log []byte
    346 		gl.GetProgramiv(p, gl.INFO_LOG_LENGTH, &l)
    347 		if l != 0 {
    348 			log = make([]byte, l)
    349 			gl.GetProgramInfoLog(p, l, nil, (*uint8)(gl.Ptr(log)))
    350 		}
    351 		return 0, fmt.Errorf("opengl: program error: %s", log)
    352 	}
    353 	return program(p), nil
    354 }
    355 
    356 func (c *context) useProgram(p program) {
    357 	gl.UseProgram(uint32(p))
    358 }
    359 
    360 func (c *context) deleteProgram(p program) {
    361 	c.locationCache.deleteProgram(p)
    362 
    363 	if !gl.IsProgram(uint32(p)) {
    364 		return
    365 	}
    366 	gl.DeleteProgram(uint32(p))
    367 }
    368 
    369 func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
    370 	l, free := gl.Strs(location + "\x00")
    371 	uniform := uniformLocation(gl.GetUniformLocation(uint32(p), *l))
    372 	free()
    373 	return uniform
    374 }
    375 
    376 func (c *context) uniformInt(p program, location string, v int) bool {
    377 	l := int32(c.locationCache.GetUniformLocation(c, p, location))
    378 	if l == invalidUniform {
    379 		return false
    380 	}
    381 	gl.Uniform1i(l, int32(v))
    382 	return true
    383 }
    384 
    385 func (c *context) uniformFloat(p program, location string, v float32) bool {
    386 	l := int32(c.locationCache.GetUniformLocation(c, p, location))
    387 	if l == invalidUniform {
    388 		return false
    389 	}
    390 	gl.Uniform1f(l, v)
    391 	return true
    392 }
    393 
    394 func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool {
    395 	l := int32(c.locationCache.GetUniformLocation(c, p, location))
    396 	if l == invalidUniform {
    397 		return false
    398 	}
    399 
    400 	base := typ.Main
    401 	len := int32(1)
    402 	if base == shaderir.Array {
    403 		base = typ.Sub[0].Main
    404 		len = int32(typ.Length)
    405 	}
    406 
    407 	switch base {
    408 	case shaderir.Float:
    409 		gl.Uniform1fv(l, len, (*float32)(gl.Ptr(v)))
    410 	case shaderir.Vec2:
    411 		gl.Uniform2fv(l, len, (*float32)(gl.Ptr(v)))
    412 	case shaderir.Vec3:
    413 		gl.Uniform3fv(l, len, (*float32)(gl.Ptr(v)))
    414 	case shaderir.Vec4:
    415 		gl.Uniform4fv(l, len, (*float32)(gl.Ptr(v)))
    416 	case shaderir.Mat2:
    417 		gl.UniformMatrix2fv(l, len, false, (*float32)(gl.Ptr(v)))
    418 	case shaderir.Mat3:
    419 		gl.UniformMatrix3fv(l, len, false, (*float32)(gl.Ptr(v)))
    420 	case shaderir.Mat4:
    421 		gl.UniformMatrix4fv(l, len, false, (*float32)(gl.Ptr(v)))
    422 	default:
    423 		panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
    424 	}
    425 	return true
    426 }
    427 
    428 func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) {
    429 	gl.VertexAttribPointer(uint32(index), int32(size), gl.FLOAT, false, int32(stride), uintptr(offset))
    430 }
    431 
    432 func (c *context) enableVertexAttribArray(index int) {
    433 	gl.EnableVertexAttribArray(uint32(index))
    434 }
    435 
    436 func (c *context) disableVertexAttribArray(index int) {
    437 	gl.DisableVertexAttribArray(uint32(index))
    438 }
    439 
    440 func (c *context) newArrayBuffer(size int) buffer {
    441 	var b uint32
    442 	gl.GenBuffers(1, &b)
    443 	gl.BindBuffer(gl.ARRAY_BUFFER, b)
    444 	gl.BufferData(gl.ARRAY_BUFFER, size, nil, gl.DYNAMIC_DRAW)
    445 	return buffer(b)
    446 }
    447 
    448 func (c *context) newElementArrayBuffer(size int) buffer {
    449 	var b uint32
    450 	gl.GenBuffers(1, &b)
    451 	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b)
    452 	gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size, nil, gl.DYNAMIC_DRAW)
    453 	return buffer(b)
    454 }
    455 
    456 func (c *context) bindArrayBuffer(b buffer) {
    457 	gl.BindBuffer(gl.ARRAY_BUFFER, uint32(b))
    458 }
    459 
    460 func (c *context) bindElementArrayBuffer(b buffer) {
    461 	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, uint32(b))
    462 }
    463 
    464 func (c *context) arrayBufferSubData(data []float32) {
    465 	gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
    466 }
    467 
    468 func (c *context) elementArrayBufferSubData(data []uint16) {
    469 	gl.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, len(data)*2, gl.Ptr(data))
    470 }
    471 
    472 func (c *context) deleteBuffer(b buffer) {
    473 	bb := uint32(b)
    474 	gl.DeleteBuffers(1, &bb)
    475 }
    476 
    477 func (c *context) drawElements(len int, offsetInBytes int) {
    478 	gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, uintptr(offsetInBytes))
    479 }
    480 
    481 func (c *context) maxTextureSizeImpl() int {
    482 	s := int32(0)
    483 	gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s)
    484 	return int(s)
    485 }
    486 
    487 func (c *context) getShaderPrecisionFormatPrecision() int {
    488 	// glGetShaderPrecisionFormat is not defined at OpenGL 2.0. Assume that desktop environments always have
    489 	// enough highp precision.
    490 	return highpPrecision
    491 }
    492 
    493 func (c *context) flush() {
    494 	gl.Flush()
    495 }
    496 
    497 func (c *context) needsRestoring() bool {
    498 	return false
    499 }
    500 
    501 func (c *context) texSubImage2D(t textureNative, args []*driver.ReplacePixelsArgs) {
    502 	c.bindTexture(t)
    503 	for _, a := range args {
    504 		gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(a.Pixels))
    505 	}
    506 }
    507 
    508 func (c *context) enableStencilTest() {
    509 	gl.Enable(gl.STENCIL_TEST)
    510 }
    511 
    512 func (c *context) disableStencilTest() {
    513 	gl.Disable(gl.STENCIL_TEST)
    514 }
    515 
    516 func (c *context) beginStencilWithEvenOddRule() {
    517 	gl.Clear(gl.STENCIL_BUFFER_BIT)
    518 	gl.StencilFunc(gl.ALWAYS, 0x00, 0xff)
    519 	gl.StencilOp(gl.KEEP, gl.KEEP, gl.INVERT)
    520 	gl.ColorMask(false, false, false, false)
    521 }
    522 
    523 func (c *context) endStencilWithEvenOddRule() {
    524 	gl.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
    525 	gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
    526 	gl.ColorMask(true, true, true, true)
    527 }