twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

graphics.go (10021B)


      1 // Copyright 2018 The Ebiten Authors
      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 package opengl
     16 
     17 import (
     18 	"fmt"
     19 
     20 	"github.com/hajimehoshi/ebiten/v2/internal/affine"
     21 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     22 	"github.com/hajimehoshi/ebiten/v2/internal/graphics"
     23 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     24 	"github.com/hajimehoshi/ebiten/v2/internal/thread"
     25 )
     26 
     27 var theGraphics Graphics
     28 
     29 func Get() *Graphics {
     30 	return &theGraphics
     31 }
     32 
     33 type Graphics struct {
     34 	state   openGLState
     35 	context context
     36 
     37 	nextImageID driver.ImageID
     38 	images      map[driver.ImageID]*Image
     39 
     40 	nextShaderID driver.ShaderID
     41 	shaders      map[driver.ShaderID]*Shader
     42 
     43 	// drawCalled is true just after Draw is called. This holds true until ReplacePixels is called.
     44 	drawCalled bool
     45 }
     46 
     47 func (g *Graphics) SetThread(thread *thread.Thread) {
     48 	g.context.t = thread
     49 }
     50 
     51 func (g *Graphics) Begin() {
     52 	// Do nothing.
     53 }
     54 
     55 func (g *Graphics) End() {
     56 	// Call glFlush to prevent black flicking (especially on Android (#226) and iOS).
     57 	// TODO: examples/sprites worked without this. Is this really needed?
     58 	g.context.flush()
     59 }
     60 
     61 func (g *Graphics) SetTransparent(transparent bool) {
     62 	// Do nothings.
     63 }
     64 
     65 func (g *Graphics) checkSize(width, height int) {
     66 	if width < 1 {
     67 		panic(fmt.Sprintf("opengl: width (%d) must be equal or more than %d", width, 1))
     68 	}
     69 	if height < 1 {
     70 		panic(fmt.Sprintf("opengl: height (%d) must be equal or more than %d", height, 1))
     71 	}
     72 	m := g.context.getMaxTextureSize()
     73 	if width > m {
     74 		panic(fmt.Sprintf("opengl: width (%d) must be less than or equal to %d", width, m))
     75 	}
     76 	if height > m {
     77 		panic(fmt.Sprintf("opengl: height (%d) must be less than or equal to %d", height, m))
     78 	}
     79 }
     80 
     81 func (g *Graphics) genNextImageID() driver.ImageID {
     82 	id := g.nextImageID
     83 	g.nextImageID++
     84 	return id
     85 }
     86 
     87 func (g *Graphics) InvalidImageID() driver.ImageID {
     88 	return -1
     89 }
     90 
     91 func (g *Graphics) genNextShaderID() driver.ShaderID {
     92 	id := g.nextShaderID
     93 	g.nextShaderID++
     94 	return id
     95 }
     96 
     97 func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
     98 	i := &Image{
     99 		id:       g.genNextImageID(),
    100 		graphics: g,
    101 		width:    width,
    102 		height:   height,
    103 	}
    104 	w := graphics.InternalImageSize(width)
    105 	h := graphics.InternalImageSize(height)
    106 	g.checkSize(w, h)
    107 	t, err := g.context.newTexture(w, h)
    108 	if err != nil {
    109 		return nil, err
    110 	}
    111 	i.textureNative = t
    112 	g.addImage(i)
    113 	return i, nil
    114 }
    115 
    116 func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
    117 	g.checkSize(width, height)
    118 	i := &Image{
    119 		id:       g.genNextImageID(),
    120 		graphics: g,
    121 		width:    width,
    122 		height:   height,
    123 		screen:   true,
    124 	}
    125 	g.addImage(i)
    126 	return i, nil
    127 }
    128 
    129 func (g *Graphics) addImage(img *Image) {
    130 	if g.images == nil {
    131 		g.images = map[driver.ImageID]*Image{}
    132 	}
    133 	if _, ok := g.images[img.id]; ok {
    134 		panic(fmt.Sprintf("opengl: image ID %d was already registered", img.id))
    135 	}
    136 	g.images[img.id] = img
    137 }
    138 
    139 func (g *Graphics) removeImage(img *Image) {
    140 	delete(g.images, img.id)
    141 }
    142 
    143 // Reset resets or initializes the current OpenGL state.
    144 func (g *Graphics) Reset() error {
    145 	return g.state.reset(&g.context)
    146 }
    147 
    148 func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
    149 	// Note that the vertices passed to BufferSubData is not under GC management
    150 	// in opengl package due to unsafe-way.
    151 	// See BufferSubData in context_mobile.go.
    152 	g.context.arrayBufferSubData(vertices)
    153 	g.context.elementArrayBufferSubData(indices)
    154 }
    155 
    156 func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
    157 	destination := g.images[dst]
    158 	source := g.images[src]
    159 
    160 	g.drawCalled = true
    161 
    162 	if err := destination.setViewport(); err != nil {
    163 		return err
    164 	}
    165 	g.context.blendFunc(mode)
    166 
    167 	program := g.state.programs[programKey{
    168 		useColorM: colorM != nil,
    169 		filter:    filter,
    170 		address:   address,
    171 	}]
    172 
    173 	uniforms := []uniformVariable{}
    174 
    175 	vw := destination.framebuffer.width
    176 	vh := destination.framebuffer.height
    177 	uniforms = append(uniforms, uniformVariable{
    178 		name:  "viewport_size",
    179 		value: []float32{float32(vw), float32(vh)},
    180 		typ:   shaderir.Type{Main: shaderir.Vec2},
    181 	}, uniformVariable{
    182 		name: "source_region",
    183 		value: []float32{
    184 			sourceRegion.X,
    185 			sourceRegion.Y,
    186 			sourceRegion.X + sourceRegion.Width,
    187 			sourceRegion.Y + sourceRegion.Height,
    188 		},
    189 		typ: shaderir.Type{Main: shaderir.Vec4},
    190 	})
    191 
    192 	if colorM != nil {
    193 		// ColorM's elements are immutable. It's OK to hold the reference without copying.
    194 		esBody, esTranslate := colorM.UnsafeElements()
    195 		uniforms = append(uniforms, uniformVariable{
    196 			name:  "color_matrix_body",
    197 			value: esBody,
    198 			typ:   shaderir.Type{Main: shaderir.Mat4},
    199 		}, uniformVariable{
    200 			name:  "color_matrix_translation",
    201 			value: esTranslate,
    202 			typ:   shaderir.Type{Main: shaderir.Vec4},
    203 		})
    204 	}
    205 
    206 	if filter != driver.FilterNearest {
    207 		sw, sh := source.framebufferSize()
    208 		uniforms = append(uniforms, uniformVariable{
    209 			name:  "source_size",
    210 			value: []float32{float32(sw), float32(sh)},
    211 			typ:   shaderir.Type{Main: shaderir.Vec2},
    212 		})
    213 	}
    214 
    215 	if filter == driver.FilterScreen {
    216 		scale := float32(destination.width) / float32(source.width)
    217 		uniforms = append(uniforms, uniformVariable{
    218 			name:  "scale",
    219 			value: scale,
    220 			typ:   shaderir.Type{Main: shaderir.Float},
    221 		})
    222 	}
    223 
    224 	var imgs [graphics.ShaderImageNum]textureVariable
    225 	for i := range imgs {
    226 		if i == 0 {
    227 			imgs[i].valid = true
    228 			imgs[i].native = source.textureNative
    229 		}
    230 	}
    231 
    232 	if err := g.useProgram(program, uniforms, imgs); err != nil {
    233 		return err
    234 	}
    235 
    236 	g.context.drawElements(indexLen, indexOffset*2) // 2 is uint16 size in bytes
    237 
    238 	// glFlush() might be necessary at least on MacBook Pro (a smilar problem at #419),
    239 	// but basically this pass the tests (esp. TestImageTooManyFill).
    240 	// As glFlush() causes performance problems, this should be avoided as much as possible.
    241 	// Let's wait and see, and file a new issue when this problem is newly foung.
    242 	return nil
    243 }
    244 
    245 func (g *Graphics) SetVsyncEnabled(enabled bool) {
    246 	// Do nothing
    247 }
    248 
    249 func (g *Graphics) FramebufferYDirection() driver.YDirection {
    250 	return driver.Upward
    251 }
    252 
    253 func (g *Graphics) NeedsRestoring() bool {
    254 	return g.context.needsRestoring()
    255 }
    256 
    257 func (g *Graphics) IsGL() bool {
    258 	return true
    259 }
    260 
    261 func (g *Graphics) HasHighPrecisionFloat() bool {
    262 	return g.context.hasHighPrecisionFloat()
    263 }
    264 
    265 func (g *Graphics) MaxImageSize() int {
    266 	return g.context.getMaxTextureSize()
    267 }
    268 
    269 func (g *Graphics) NewShader(program *shaderir.Program) (driver.Shader, error) {
    270 	s, err := newShader(g.genNextShaderID(), g, program)
    271 	if err != nil {
    272 		return nil, err
    273 	}
    274 	g.addShader(s)
    275 	return s, nil
    276 }
    277 
    278 func (g *Graphics) addShader(shader *Shader) {
    279 	if g.shaders == nil {
    280 		g.shaders = map[driver.ShaderID]*Shader{}
    281 	}
    282 	if _, ok := g.shaders[shader.id]; ok {
    283 		panic(fmt.Sprintf("opengl: shader ID %d was already registered", shader.id))
    284 	}
    285 	g.shaders[shader.id] = shader
    286 }
    287 
    288 func (g *Graphics) removeShader(shader *Shader) {
    289 	delete(g.shaders, shader.id)
    290 }
    291 
    292 func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error {
    293 	d := g.images[dst]
    294 	s := g.shaders[shader]
    295 
    296 	g.drawCalled = true
    297 
    298 	if err := d.setViewport(); err != nil {
    299 		return err
    300 	}
    301 	g.context.blendFunc(mode)
    302 
    303 	us := make([]uniformVariable, graphics.PreservedUniformVariablesNum+len(uniforms))
    304 
    305 	{
    306 		const idx = graphics.DestinationTextureSizeUniformVariableIndex
    307 		w, h := d.framebufferSize()
    308 		us[idx].name = "U0"
    309 		us[idx].value = []float32{float32(w), float32(h)}
    310 		us[idx].typ = s.ir.Uniforms[0]
    311 	}
    312 	{
    313 		sizes := make([]float32, 2*len(srcs))
    314 		for i, src := range srcs {
    315 			if img := g.images[src]; img != nil {
    316 				w, h := img.framebufferSize()
    317 				sizes[2*i] = float32(w)
    318 				sizes[2*i+1] = float32(h)
    319 			}
    320 
    321 		}
    322 		const idx = graphics.TextureSizesUniformVariableIndex
    323 		us[idx].name = fmt.Sprintf("U%d", idx)
    324 		us[idx].value = sizes
    325 		us[idx].typ = s.ir.Uniforms[idx]
    326 	}
    327 	{
    328 		voffsets := make([]float32, 2*len(offsets))
    329 		for i, o := range offsets {
    330 			voffsets[2*i] = o[0]
    331 			voffsets[2*i+1] = o[1]
    332 		}
    333 		const idx = graphics.TextureSourceOffsetsUniformVariableIndex
    334 		us[idx].name = fmt.Sprintf("U%d", idx)
    335 		us[idx].value = voffsets
    336 		us[idx].typ = s.ir.Uniforms[idx]
    337 	}
    338 	{
    339 		origin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)}
    340 		const idx = graphics.TextureSourceRegionOriginUniformVariableIndex
    341 		us[idx].name = fmt.Sprintf("U%d", idx)
    342 		us[idx].value = origin
    343 		us[idx].typ = s.ir.Uniforms[idx]
    344 	}
    345 	{
    346 		size := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)}
    347 		const idx = graphics.TextureSourceRegionSizeUniformVariableIndex
    348 		us[idx].name = fmt.Sprintf("U%d", idx)
    349 		us[idx].value = size
    350 		us[idx].typ = s.ir.Uniforms[idx]
    351 	}
    352 
    353 	for i, v := range uniforms {
    354 		const offset = graphics.PreservedUniformVariablesNum
    355 		us[i+offset].name = fmt.Sprintf("U%d", i+offset)
    356 		us[i+offset].value = v
    357 		us[i+offset].typ = s.ir.Uniforms[i+offset]
    358 	}
    359 
    360 	var ts [graphics.ShaderImageNum]textureVariable
    361 	for i, src := range srcs {
    362 		if src == g.InvalidImageID() {
    363 			continue
    364 		}
    365 		ts[i].valid = true
    366 		ts[i].native = g.images[src].textureNative
    367 	}
    368 
    369 	if err := g.useProgram(s.p, us, ts); err != nil {
    370 		return err
    371 	}
    372 	g.context.drawElements(indexLen, indexOffset*2) // 2 is uint16 size in bytes
    373 
    374 	return nil
    375 }