twitchapon-anim

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

images.go (7041B)


      1 // Copyright 2017 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 restorable
     16 
     17 import (
     18 	"path/filepath"
     19 
     20 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     21 	"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
     22 )
     23 
     24 // forceRestoring reports whether restoring forcely happens or not.
     25 var forceRestoring = false
     26 
     27 // needsRestoring reports whether restoring process works or not.
     28 func needsRestoring() bool {
     29 	if forceRestoring {
     30 		return true
     31 	}
     32 	return graphicscommand.NeedsRestoring()
     33 }
     34 
     35 // EnableRestoringForTesting forces to enable restoring for testing.
     36 func EnableRestoringForTesting() {
     37 	forceRestoring = true
     38 }
     39 
     40 // images is a set of Image objects.
     41 type images struct {
     42 	images      map[*Image]struct{}
     43 	shaders     map[*Shader]struct{}
     44 	lastTarget  *Image
     45 	contextLost bool
     46 }
     47 
     48 // theImages represents the images for the current process.
     49 var theImages = &images{
     50 	images:  map[*Image]struct{}{},
     51 	shaders: map[*Shader]struct{}{},
     52 }
     53 
     54 // ResolveStaleImages flushes the queued draw commands and resolves
     55 // all stale images.
     56 //
     57 // ResolveStaleImages is intended to be called at the end of a frame.
     58 func ResolveStaleImages() error {
     59 	if err := graphicscommand.FlushCommands(); err != nil {
     60 		return err
     61 	}
     62 	if !needsRestoring() {
     63 		return nil
     64 	}
     65 	return theImages.resolveStaleImages()
     66 }
     67 
     68 // RestoreIfNeeded restores the images.
     69 //
     70 // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
     71 func RestoreIfNeeded() error {
     72 	if !needsRestoring() {
     73 		return nil
     74 	}
     75 
     76 	if !forceRestoring {
     77 		var r bool
     78 
     79 		if canDetectContextLostExplicitly {
     80 			r = theImages.contextLost
     81 		} else {
     82 			// As isInvalidated() is expensive, call this only for one image.
     83 			// This assumes that if there is one image that is invalidated, all images are invalidated.
     84 			for img := range theImages.images {
     85 				// The screen image might not have a texture. Skip this.
     86 				if img.screen {
     87 					continue
     88 				}
     89 				var err error
     90 				r, err = img.isInvalidated()
     91 				if err != nil {
     92 					return err
     93 				}
     94 				break
     95 			}
     96 		}
     97 
     98 		if !r {
     99 			return nil
    100 		}
    101 	}
    102 
    103 	err := graphicscommand.ResetGraphicsDriverState()
    104 	if err == driver.GraphicsNotReady {
    105 		return nil
    106 	}
    107 	if err != nil {
    108 		return err
    109 	}
    110 	return theImages.restore()
    111 }
    112 
    113 // DumpImages dumps all the current images to the specified directory.
    114 //
    115 // This is for testing usage.
    116 func DumpImages(dir string) error {
    117 	for img := range theImages.images {
    118 		if err := img.Dump(filepath.Join(dir, "*.png"), false); err != nil {
    119 			return err
    120 		}
    121 	}
    122 	return nil
    123 }
    124 
    125 // add adds img to the images.
    126 func (i *images) add(img *Image) {
    127 	i.images[img] = struct{}{}
    128 }
    129 
    130 func (i *images) addShader(shader *Shader) {
    131 	i.shaders[shader] = struct{}{}
    132 }
    133 
    134 // remove removes img from the images.
    135 func (i *images) remove(img *Image) {
    136 	i.makeStaleIfDependingOn(img)
    137 	delete(i.images, img)
    138 }
    139 
    140 func (i *images) removeShader(shader *Shader) {
    141 	i.makeStaleIfDependingOnShader(shader)
    142 	delete(i.shaders, shader)
    143 }
    144 
    145 // resolveStaleImages resolves stale images.
    146 func (i *images) resolveStaleImages() error {
    147 	i.lastTarget = nil
    148 	for img := range i.images {
    149 		if err := img.resolveStale(); err != nil {
    150 			return err
    151 		}
    152 	}
    153 	return nil
    154 }
    155 
    156 // makeStaleIfDependingOn makes all the images stale that depend on target.
    157 //
    158 // When target is modified, all images depending on target can't be restored with target.
    159 // makeStaleIfDependingOn is called in such situation.
    160 func (i *images) makeStaleIfDependingOn(target *Image) {
    161 	if target == nil {
    162 		panic("restorable: target must not be nil at makeStaleIfDependingOn")
    163 	}
    164 	if i.lastTarget == target {
    165 		return
    166 	}
    167 	i.lastTarget = target
    168 	for img := range i.images {
    169 		img.makeStaleIfDependingOn(target)
    170 	}
    171 }
    172 
    173 // makeStaleIfDependingOn makes all the images stale that depend on shader.
    174 func (i *images) makeStaleIfDependingOnShader(shader *Shader) {
    175 	if shader == nil {
    176 		panic("restorable: shader must not be nil at makeStaleIfDependingOnShader")
    177 	}
    178 	for img := range i.images {
    179 		img.makeStaleIfDependingOnShader(shader)
    180 	}
    181 }
    182 
    183 // restore restores the images.
    184 //
    185 // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
    186 func (i *images) restore() error {
    187 	if !needsRestoring() {
    188 		panic("restorable: restore cannot be called when restoring is disabled")
    189 	}
    190 
    191 	// Dispose all the shaders ahead of restoring. A current shader ID and a new shader ID can be duplicated.
    192 	for s := range i.shaders {
    193 		if needsDisposingWhenRestoring {
    194 			s.shader.Dispose()
    195 		}
    196 		s.shader = nil
    197 	}
    198 	for s := range i.shaders {
    199 		s.restore()
    200 	}
    201 
    202 	// Dispose all the images ahead of restoring. A current texture ID and a new texture ID can be duplicated.
    203 	// TODO: Write a test to confirm that ID duplication never happens.
    204 	for i := range i.images {
    205 		if needsDisposingWhenRestoring {
    206 			i.image.Dispose()
    207 		}
    208 		i.image = nil
    209 	}
    210 
    211 	// Let's do topological sort based on dependencies of drawing history.
    212 	// It is assured that there are not loops since cyclic drawing makes images stale.
    213 	type edge struct {
    214 		source *Image
    215 		target *Image
    216 	}
    217 	images := map[*Image]struct{}{}
    218 	for i := range i.images {
    219 		if !i.priority {
    220 			images[i] = struct{}{}
    221 		}
    222 	}
    223 	edges := map[edge]struct{}{}
    224 	for t := range images {
    225 		for s := range t.dependingImages() {
    226 			edges[edge{source: s, target: t}] = struct{}{}
    227 		}
    228 	}
    229 
    230 	sorted := []*Image{}
    231 	for i := range i.images {
    232 		if i.priority {
    233 			sorted = append(sorted, i)
    234 		}
    235 	}
    236 	for len(images) > 0 {
    237 		// current repesents images that have no incoming edges.
    238 		current := map[*Image]struct{}{}
    239 		for i := range images {
    240 			current[i] = struct{}{}
    241 		}
    242 		for e := range edges {
    243 			if _, ok := current[e.target]; ok {
    244 				delete(current, e.target)
    245 			}
    246 		}
    247 		for i := range current {
    248 			delete(images, i)
    249 			sorted = append(sorted, i)
    250 		}
    251 		removed := []edge{}
    252 		for e := range edges {
    253 			if _, ok := current[e.source]; ok {
    254 				removed = append(removed, e)
    255 			}
    256 		}
    257 		for _, e := range removed {
    258 			delete(edges, e)
    259 		}
    260 	}
    261 
    262 	for _, img := range sorted {
    263 		if err := img.restore(); err != nil {
    264 			return err
    265 		}
    266 	}
    267 
    268 	i.contextLost = false
    269 
    270 	return nil
    271 }
    272 
    273 // InitializeGraphicsDriverState initializes the graphics driver state.
    274 func InitializeGraphicsDriverState() error {
    275 	return graphicscommand.ResetGraphicsDriverState()
    276 }
    277 
    278 // OnContextLost is called when the context lost is detected in an explicit way.
    279 func OnContextLost() {
    280 	if !canDetectContextLostExplicitly {
    281 		panic("restorable: OnContextLost cannot be called in this environment")
    282 	}
    283 	theImages.contextLost = true
    284 }