image.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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. package atlas
  15. import (
  16. "fmt"
  17. "image"
  18. "math"
  19. "math/bits"
  20. "runtime"
  21. "sync"
  22. "github.com/hajimehoshi/ebiten/v2/internal/graphics"
  23. "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
  24. "github.com/hajimehoshi/ebiten/v2/internal/packing"
  25. "github.com/hajimehoshi/ebiten/v2/internal/restorable"
  26. "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
  27. )
  28. var (
  29. minSourceSize = 0
  30. minDestinationSize = 0
  31. maxSize = 0
  32. )
  33. func appendDeferred(f func()) {
  34. deferredM.Lock()
  35. defer deferredM.Unlock()
  36. deferred = append(deferred, f)
  37. }
  38. func flushDeferred() {
  39. deferredM.Lock()
  40. fs := deferred
  41. deferred = nil
  42. deferredM.Unlock()
  43. for _, f := range fs {
  44. f()
  45. }
  46. }
  47. // baseCountToPutOnSourceBackend represents the base time duration when the image can be put onto an atlas.
  48. // Actual time duration is increased in an exponential way for each usage as a rendering target.
  49. const baseCountToPutOnSourceBackend = 10
  50. func putImagesOnSourceBackend() {
  51. // The counter usedAsDestinationCount is updated at most once per frame (#2676).
  52. imagesUsedAsDestination.forEach(func(i *Image) {
  53. // This counter is not updated when the backend is created in this frame.
  54. if !i.backendCreatedInThisFrame && i.usedAsDestinationCount < math.MaxInt {
  55. i.usedAsDestinationCount++
  56. }
  57. i.backendCreatedInThisFrame = false
  58. })
  59. imagesUsedAsDestination.clear()
  60. imagesToPutOnSourceBackend.forEach(func(i *Image) {
  61. if i.usedAsSourceCount < math.MaxInt {
  62. i.usedAsSourceCount++
  63. }
  64. if i.usedAsSourceCount >= baseCountToPutOnSourceBackend*(1<<uint(min(i.usedAsDestinationCount, 31))) {
  65. i.putOnSourceBackend()
  66. i.usedAsSourceCount = 0
  67. }
  68. })
  69. imagesToPutOnSourceBackend.clear()
  70. }
  71. // backend is a big texture atlas that can have multiple images.
  72. // backend is a texture in GPU.
  73. type backend struct {
  74. // restorable is an atlas on which there might be multiple images.
  75. restorable *restorable.Image
  76. // page is an atlas map. Each part is called a node.
  77. // If page is nil, the backend's image is isolated and not on an atlas.
  78. page *packing.Page
  79. // source reports whether this backend is mainly used a rendering source, but this is not 100%.
  80. //
  81. // If a non-source (destination) image is used as a source many times,
  82. // the image's backend might be turned into a source backend to optimize draw calls.
  83. source bool
  84. // sourceInThisFrame reports whether this backend is used as a source in this frame.
  85. // sourceInThisFrame is reset every frame.
  86. sourceInThisFrame bool
  87. }
  88. func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) {
  89. if b.page == nil {
  90. return nil, false
  91. }
  92. n := b.page.Alloc(width, height)
  93. if n == nil {
  94. // The page can't be extended anymore. Return as failure.
  95. return nil, false
  96. }
  97. b.restorable = b.restorable.Extend(b.page.Size())
  98. return n, true
  99. }
  100. var (
  101. // backendsM is a mutex for critical sections of the backend and packing.Node objects.
  102. backendsM sync.Mutex
  103. // inFrame indicates whether the current state is in between BeginFrame and EndFrame or not.
  104. // If inFrame is false, function calls on an image should be deferred until the next BeginFrame.
  105. inFrame bool
  106. initOnce sync.Once
  107. // theBackends is a set of atlases.
  108. theBackends []*backend
  109. imagesToPutOnSourceBackend smallImageSet
  110. imagesUsedAsDestination smallImageSet
  111. deferred []func()
  112. // deferredM is a mutex for the slice operations. This must not be used for other usages.
  113. deferredM sync.Mutex
  114. )
  115. // ImageType represents the type of an image.
  116. type ImageType int
  117. const (
  118. // ImageTypeRegular is a regular image, that can be on a big texture atlas (backend).
  119. ImageTypeRegular ImageType = iota
  120. // ImageTypeScreen is a screen image that is not on an atlas.
  121. // A screen image is also unmanaged.
  122. ImageTypeScreen
  123. // ImageTypeVolatile is a volatile image that is cleared every frame.
  124. // A volatile image is also unmanaged.
  125. ImageTypeVolatile
  126. // ImageTypeUnmanaged is an unmanaged image that is not on an atlas.
  127. ImageTypeUnmanaged
  128. )
  129. // Image is a rectangle pixel set that might be on an atlas.
  130. type Image struct {
  131. width int
  132. height int
  133. imageType ImageType
  134. backend *backend
  135. backendCreatedInThisFrame bool
  136. node *packing.Node
  137. // usedAsSourceCount represents how long the image is used as a rendering source and kept not modified with
  138. // DrawTriangles.
  139. // In the current implementation, if an image is being modified by DrawTriangles, the image is separated from
  140. // a restorable image on an atlas by ensureIsolatedFromSource.
  141. //
  142. // The type is int64 instead of int to avoid overflow when comparing the limitation.
  143. //
  144. // usedAsSourceCount is increased if the image is used as a rendering source, or set to 0 if the image is
  145. // modified.
  146. //
  147. // WritePixels doesn't affect this value since WritePixels can be done on images on an atlas.
  148. usedAsSourceCount int64
  149. // usedAsDestinationCount represents how many times an image is used as a rendering destination at DrawTriangles.
  150. // usedAsDestinationCount affects the calculation when to put the image onto a texture atlas again.
  151. //
  152. // usedAsDestinationCount is never reset.
  153. usedAsDestinationCount int
  154. }
  155. // moveTo moves its content to the given image dst.
  156. // After moveTo is called, the image i is no longer available.
  157. //
  158. // moveTo is similar to C++'s move semantics.
  159. func (i *Image) moveTo(dst *Image) {
  160. dst.deallocate()
  161. *dst = *i
  162. // i is no longer available but the finalizer must not be called
  163. // since i and dst share the same backend and the same node.
  164. runtime.SetFinalizer(i, nil)
  165. }
  166. func (i *Image) isOnAtlas() bool {
  167. return i.node != nil
  168. }
  169. func (i *Image) isOnSourceBackend() bool {
  170. if i.backend == nil {
  171. return false
  172. }
  173. return i.backend.source
  174. }
  175. func (i *Image) resetUsedAsSourceCount() {
  176. i.usedAsSourceCount = 0
  177. imagesToPutOnSourceBackend.remove(i)
  178. }
  179. func (i *Image) paddingSize() int {
  180. if i.imageType == ImageTypeRegular {
  181. return 1
  182. }
  183. return 0
  184. }
  185. func (i *Image) ensureIsolatedFromSource(backends []*backend) {
  186. i.resetUsedAsSourceCount()
  187. // imagesUsedAsDestination affects the counter usedAsDestination.
  188. // The larger this counter is, the harder it is for the image to be transferred to the source backend.
  189. imagesUsedAsDestination.add(i)
  190. if i.backend == nil {
  191. // `sourceInThisFrame` of `backends` should be true, so `backends` should be in `bs`.
  192. var bs []*backend
  193. for _, b := range theBackends {
  194. if b.sourceInThisFrame {
  195. bs = append(bs, b)
  196. }
  197. }
  198. i.allocate(bs, false)
  199. i.backendCreatedInThisFrame = true
  200. return
  201. }
  202. if !i.isOnAtlas() {
  203. return
  204. }
  205. // Check if i has the same backend as the given backends.
  206. var needsIsolation bool
  207. for _, b := range backends {
  208. if i.backend == b {
  209. needsIsolation = true
  210. break
  211. }
  212. }
  213. if !needsIsolation {
  214. return
  215. }
  216. newI := NewImage(i.width, i.height, i.imageType)
  217. // Call allocate explicitly in order to have an isolated backend from the specified backends.
  218. // `sourceInThisFrame` of `backends` should be true, so `backends` should be in `bs`.
  219. bs := []*backend{i.backend}
  220. for _, b := range theBackends {
  221. if b.sourceInThisFrame {
  222. bs = append(bs, b)
  223. }
  224. }
  225. newI.allocate(bs, false)
  226. w, h := float32(i.width), float32(i.height)
  227. vs := make([]float32, 4*graphics.VertexFloatCount)
  228. graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1)
  229. is := graphics.QuadIndices()
  230. dr := image.Rect(0, 0, i.width, i.height)
  231. sr := image.Rect(0, 0, i.width, i.height)
  232. newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
  233. newI.moveTo(i)
  234. }
  235. func (i *Image) putOnSourceBackend() {
  236. if i.backend == nil {
  237. i.allocate(nil, true)
  238. return
  239. }
  240. if i.isOnSourceBackend() {
  241. return
  242. }
  243. if !i.canBePutOnAtlas() {
  244. panic("atlas: putOnSourceBackend cannot be called on a image that cannot be on an atlas")
  245. }
  246. if i.imageType != ImageTypeRegular {
  247. panic(fmt.Sprintf("atlas: the image type must be ImageTypeRegular but %d", i.imageType))
  248. }
  249. newI := NewImage(i.width, i.height, ImageTypeRegular)
  250. newI.allocate(nil, true)
  251. w, h := float32(i.width), float32(i.height)
  252. vs := make([]float32, 4*graphics.VertexFloatCount)
  253. graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1)
  254. is := graphics.QuadIndices()
  255. dr := image.Rect(0, 0, i.width, i.height)
  256. sr := image.Rect(0, 0, i.width, i.height)
  257. newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
  258. newI.moveTo(i)
  259. i.usedAsSourceCount = 0
  260. if !i.isOnSourceBackend() {
  261. panic("atlas: i must be on a source backend but not")
  262. }
  263. }
  264. func (i *Image) regionWithPadding() image.Rectangle {
  265. if i.backend == nil {
  266. panic("atlas: backend must not be nil: not allocated yet?")
  267. }
  268. if !i.isOnAtlas() {
  269. return image.Rect(0, 0, i.width+i.paddingSize(), i.height+i.paddingSize())
  270. }
  271. return i.node.Region()
  272. }
  273. // DrawTriangles draws triangles with the given image.
  274. //
  275. // The vertex floats are:
  276. //
  277. // 0: Destination X in pixels
  278. // 1: Destination Y in pixels
  279. // 2: Source X in pixels (the upper-left is (0, 0))
  280. // 3: Source Y in pixels
  281. // 4: Color R [0.0-1.0]
  282. // 5: Color G
  283. // 6: Color B
  284. // 7: Color Y
  285. func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint restorable.Hint) {
  286. backendsM.Lock()
  287. defer backendsM.Unlock()
  288. if !inFrame {
  289. vs := make([]float32, len(vertices))
  290. copy(vs, vertices)
  291. is := make([]uint32, len(indices))
  292. copy(is, indices)
  293. us := make([]uint32, len(uniforms))
  294. copy(us, uniforms)
  295. appendDeferred(func() {
  296. i.drawTriangles(srcs, vs, is, blend, dstRegion, srcRegions, shader, us, fillRule, hint)
  297. })
  298. return
  299. }
  300. i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint)
  301. }
  302. func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint restorable.Hint) {
  303. backends := make([]*backend, 0, len(srcs))
  304. for _, src := range srcs {
  305. if src == nil {
  306. continue
  307. }
  308. if src.backend == nil {
  309. // It is possible to spcify i.backend as a forbidden backend, but this might prevent a good allocation for a source image.
  310. // If the backend becomes the same as i's, i's backend will be changed at ensureIsolatedFromSource.
  311. src.allocate(nil, true)
  312. }
  313. backends = append(backends, src.backend)
  314. src.backend.sourceInThisFrame = true
  315. }
  316. i.ensureIsolatedFromSource(backends)
  317. for _, src := range srcs {
  318. // Compare i and source images after ensuring i is not on an atlas, or
  319. // i and a source image might share the same atlas even though i != src.
  320. if src != nil && i.backend.restorable == src.backend.restorable {
  321. panic("atlas: Image.DrawTriangles: source must be different from the receiver")
  322. }
  323. }
  324. r := i.regionWithPadding()
  325. // TODO: Check if dstRegion does not to violate the region.
  326. dstRegion = dstRegion.Add(r.Min)
  327. dx, dy := float32(r.Min.X), float32(r.Min.Y)
  328. var oxf, oyf float32
  329. if srcs[0] != nil {
  330. r := srcs[0].regionWithPadding()
  331. oxf, oyf = float32(r.Min.X), float32(r.Min.Y)
  332. n := len(vertices)
  333. for i := 0; i < n; i += graphics.VertexFloatCount {
  334. vertices[i] += dx
  335. vertices[i+1] += dy
  336. vertices[i+2] += oxf
  337. vertices[i+3] += oyf
  338. }
  339. if shader.unit == shaderir.Texels {
  340. sw, sh := srcs[0].backend.restorable.InternalSize()
  341. swf, shf := float32(sw), float32(sh)
  342. for i := 0; i < n; i += graphics.VertexFloatCount {
  343. vertices[i+2] /= swf
  344. vertices[i+3] /= shf
  345. }
  346. }
  347. } else {
  348. n := len(vertices)
  349. for i := 0; i < n; i += graphics.VertexFloatCount {
  350. vertices[i] += dx
  351. vertices[i+1] += dy
  352. }
  353. }
  354. var imgs [graphics.ShaderSrcImageCount]*restorable.Image
  355. for i, src := range srcs {
  356. if src == nil {
  357. continue
  358. }
  359. // A source region can be deliberately empty when this is not needed in order to avoid unexpected
  360. // performance issue (#1293).
  361. // TODO: This should no longer be needed but is kept just in case. Remove this later.
  362. if !srcRegions[i].Empty() {
  363. r := src.regionWithPadding()
  364. srcRegions[i] = srcRegions[i].Add(r.Min)
  365. }
  366. imgs[i] = src.backend.restorable
  367. if !src.isOnSourceBackend() && src.canBePutOnAtlas() {
  368. // src might already registered, but assigning it again is not harmful.
  369. imagesToPutOnSourceBackend.add(src)
  370. }
  371. }
  372. i.backend.restorable.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule, hint)
  373. }
  374. // WritePixels replaces the pixels on the image.
  375. func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
  376. backendsM.Lock()
  377. defer backendsM.Unlock()
  378. if !inFrame {
  379. copied := make([]byte, len(pix))
  380. copy(copied, pix)
  381. appendDeferred(func() {
  382. i.writePixels(copied, region)
  383. })
  384. return
  385. }
  386. i.writePixels(pix, region)
  387. }
  388. func (i *Image) writePixels(pix []byte, region image.Rectangle) {
  389. if l := 4 * region.Dx() * region.Dy(); len(pix) != l {
  390. panic(fmt.Sprintf("atlas: len(p) must be %d but %d", l, len(pix)))
  391. }
  392. i.resetUsedAsSourceCount()
  393. if i.backend == nil {
  394. if pix == nil {
  395. return
  396. }
  397. // Allocate as a source as this image will likely be used as a source.
  398. i.allocate(nil, true)
  399. }
  400. r := i.regionWithPadding()
  401. if !region.Eq(image.Rect(0, 0, i.width, i.height)) || i.paddingSize() == 0 {
  402. region = region.Add(r.Min)
  403. if pix == nil {
  404. i.backend.restorable.ClearPixels(region)
  405. return
  406. }
  407. // Copy pixels in the case when pix is modified before the graphics command is executed.
  408. pix2 := graphics.NewManagedBytes(len(pix), func(bs []byte) {
  409. copy(bs, pix)
  410. })
  411. i.backend.restorable.WritePixels(pix2, region)
  412. return
  413. }
  414. // TODO: These loops assume that paddingSize is 1.
  415. // TODO: Is clearing edges explicitly really needed?
  416. const paddingSize = 1
  417. if paddingSize != i.paddingSize() {
  418. panic(fmt.Sprintf("atlas: writePixels assumes the padding is always 1 but the actual padding was %d", i.paddingSize()))
  419. }
  420. pixb := graphics.NewManagedBytes(4*r.Dx()*r.Dy(), func(bs []byte) {
  421. // Clear the edges. bs might not be zero-cleared.
  422. rowPixels := 4 * r.Dx()
  423. for i := 0; i < rowPixels; i++ {
  424. bs[rowPixels*(r.Dy()-1)+i] = 0
  425. }
  426. for j := 1; j < r.Dy(); j++ {
  427. bs[rowPixels*j-4] = 0
  428. bs[rowPixels*j-3] = 0
  429. bs[rowPixels*j-2] = 0
  430. bs[rowPixels*j-1] = 0
  431. }
  432. // Copy the content.
  433. for j := 0; j < region.Dy(); j++ {
  434. copy(bs[4*j*r.Dx():], pix[4*j*region.Dx():4*(j+1)*region.Dx()])
  435. }
  436. })
  437. i.backend.restorable.WritePixels(pixb, r)
  438. }
  439. func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) (ok bool, err error) {
  440. backendsM.Lock()
  441. defer backendsM.Unlock()
  442. if !inFrame {
  443. // Not ready to read pixels. Try this later.
  444. return false, nil
  445. }
  446. // In the tests, BeginFrame might not be called often and then images might not be disposed (#2292).
  447. // To prevent memory leaks, flush the deferred functions here.
  448. flushDeferred()
  449. if i.backend == nil || i.backend.restorable == nil {
  450. for i := range pixels {
  451. pixels[i] = 0
  452. }
  453. return true, nil
  454. }
  455. if err := i.backend.restorable.ReadPixels(graphicsDriver, pixels, region.Add(i.regionWithPadding().Min)); err != nil {
  456. return false, err
  457. }
  458. return true, nil
  459. }
  460. // Deallocate deallocates the internal state.
  461. // Even after this call, the image is still available as a new cleared image.
  462. func (i *Image) Deallocate() {
  463. backendsM.Lock()
  464. defer backendsM.Unlock()
  465. if !inFrame {
  466. appendDeferred(func() {
  467. i.deallocate()
  468. runtime.SetFinalizer(i, nil)
  469. })
  470. return
  471. }
  472. i.deallocate()
  473. runtime.SetFinalizer(i, nil)
  474. }
  475. func (i *Image) deallocate() {
  476. defer func() {
  477. i.backend = nil
  478. i.node = nil
  479. }()
  480. i.resetUsedAsSourceCount()
  481. i.usedAsDestinationCount = 0
  482. imagesUsedAsDestination.remove(i)
  483. if i.backend == nil {
  484. // Not allocated yet.
  485. return
  486. }
  487. if i.isOnAtlas() {
  488. i.backend.page.Free(i.node)
  489. if !i.backend.page.IsEmpty() {
  490. // As this part can be reused, this should be cleared explicitly.
  491. r := i.regionWithPadding()
  492. i.backend.restorable.ClearPixels(r)
  493. return
  494. }
  495. }
  496. i.backend.restorable.Dispose()
  497. for idx, sh := range theBackends {
  498. if sh == i.backend {
  499. copy(theBackends[idx:], theBackends[idx+1:])
  500. theBackends[len(theBackends)-1] = nil
  501. theBackends = theBackends[:len(theBackends)-1]
  502. return
  503. }
  504. }
  505. panic("atlas: backend not found at an image being deallocated")
  506. }
  507. func NewImage(width, height int, imageType ImageType) *Image {
  508. // Actual allocation is done lazily, and the lock is not needed.
  509. return &Image{
  510. width: width,
  511. height: height,
  512. imageType: imageType,
  513. }
  514. }
  515. func (i *Image) canBePutOnAtlas() bool {
  516. if minSourceSize == 0 || minDestinationSize == 0 || maxSize == 0 {
  517. panic("atlas: min*Size or maxSize must be initialized")
  518. }
  519. if i.imageType != ImageTypeRegular {
  520. return false
  521. }
  522. return i.width+i.paddingSize() <= maxSize && i.height+i.paddingSize() <= maxSize
  523. }
  524. func (i *Image) finalize() {
  525. // A function from finalizer must not be blocked, but disposing operation can be blocked.
  526. // Defer this operation until it becomes safe. (#913)
  527. appendDeferred(func() {
  528. i.deallocate()
  529. runtime.SetFinalizer(i, nil)
  530. })
  531. }
  532. func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) {
  533. if i.backend != nil {
  534. panic("atlas: the image is already allocated")
  535. }
  536. runtime.SetFinalizer(i, (*Image).finalize)
  537. if i.imageType == ImageTypeScreen {
  538. if asSource {
  539. panic("atlas: a screen image cannot be created as a source")
  540. }
  541. // A screen image doesn't have a padding.
  542. i.backend = &backend{
  543. restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen),
  544. }
  545. theBackends = append(theBackends, i.backend)
  546. return
  547. }
  548. wp := i.width + i.paddingSize()
  549. hp := i.height + i.paddingSize()
  550. if !i.canBePutOnAtlas() {
  551. if wp > maxSize || hp > maxSize {
  552. panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
  553. }
  554. typ := restorable.ImageTypeRegular
  555. if i.imageType == ImageTypeVolatile {
  556. typ = restorable.ImageTypeVolatile
  557. }
  558. i.backend = &backend{
  559. restorable: restorable.NewImage(wp, hp, typ),
  560. source: asSource && typ == restorable.ImageTypeRegular,
  561. }
  562. theBackends = append(theBackends, i.backend)
  563. return
  564. }
  565. // Check if an existing backend is available.
  566. loop:
  567. for _, b := range theBackends {
  568. if b.source != asSource {
  569. continue
  570. }
  571. for _, bb := range forbiddenBackends {
  572. if b == bb {
  573. continue loop
  574. }
  575. }
  576. if n, ok := b.tryAlloc(wp, hp); ok {
  577. i.backend = b
  578. i.node = n
  579. return
  580. }
  581. }
  582. var width, height int
  583. if asSource {
  584. width, height = minSourceSize, minSourceSize
  585. } else {
  586. width, height = minDestinationSize, minDestinationSize
  587. }
  588. for wp > width {
  589. if width == maxSize {
  590. panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
  591. }
  592. width *= 2
  593. }
  594. for hp > height {
  595. if height == maxSize {
  596. panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
  597. }
  598. height *= 2
  599. }
  600. typ := restorable.ImageTypeRegular
  601. if i.imageType == ImageTypeVolatile {
  602. typ = restorable.ImageTypeVolatile
  603. }
  604. b := &backend{
  605. restorable: restorable.NewImage(width, height, typ),
  606. page: packing.NewPage(width, height, maxSize),
  607. source: asSource,
  608. }
  609. theBackends = append(theBackends, b)
  610. n := b.page.Alloc(wp, hp)
  611. if n == nil {
  612. panic("atlas: Alloc result must not be nil at allocate")
  613. }
  614. i.backend = b
  615. i.node = n
  616. }
  617. func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool) (string, error) {
  618. backendsM.Lock()
  619. defer backendsM.Unlock()
  620. if !inFrame {
  621. panic("atlas: DumpScreenshots must be called in between BeginFrame and EndFrame")
  622. }
  623. return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height))
  624. }
  625. func EndFrame() error {
  626. backendsM.Lock()
  627. defer backendsM.Unlock()
  628. defer func() {
  629. inFrame = false
  630. }()
  631. if !inFrame {
  632. panic("atlas: inFrame must be true in EndFrame")
  633. }
  634. for _, b := range theBackends {
  635. b.sourceInThisFrame = false
  636. }
  637. return nil
  638. }
  639. func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error {
  640. func() {
  641. backendsM.Lock()
  642. defer backendsM.Unlock()
  643. if inFrame {
  644. panic("atlas: inFrame must be false in SwapBuffer")
  645. }
  646. }()
  647. if err := restorable.SwapBuffers(graphicsDriver); err != nil {
  648. return err
  649. }
  650. return nil
  651. }
  652. func floorPowerOf2(x int) int {
  653. if x <= 0 {
  654. return 0
  655. }
  656. return 1 << (bits.Len(uint(x)) - 1)
  657. }
  658. func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
  659. backendsM.Lock()
  660. defer backendsM.Unlock()
  661. if inFrame {
  662. panic("atlas: inFrame must be false in BeginFrame")
  663. }
  664. inFrame = true
  665. var err error
  666. initOnce.Do(func() {
  667. err = restorable.InitializeGraphicsDriverState(graphicsDriver)
  668. if err != nil {
  669. return
  670. }
  671. if len(theBackends) != 0 {
  672. panic("atlas: all the images must be not on an atlas before the game starts")
  673. }
  674. // min*Size and maxSize can already be set for testings.
  675. if minSourceSize == 0 {
  676. minSourceSize = 1024
  677. }
  678. if minDestinationSize == 0 {
  679. minDestinationSize = 16
  680. }
  681. if maxSize == 0 {
  682. maxSize = floorPowerOf2(restorable.MaxImageSize(graphicsDriver))
  683. }
  684. })
  685. if err != nil {
  686. return err
  687. }
  688. // Restore images first before other image manipulations (#2075).
  689. if err := restorable.RestoreIfNeeded(graphicsDriver); err != nil {
  690. return err
  691. }
  692. flushDeferred()
  693. putImagesOnSourceBackend()
  694. return nil
  695. }
  696. func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) (string, error) {
  697. backendsM.Lock()
  698. defer backendsM.Unlock()
  699. if !inFrame {
  700. panic("atlas: DumpImages must be called in between BeginFrame and EndFrame")
  701. }
  702. return restorable.DumpImages(graphicsDriver, dir)
  703. }