mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-13 22:47:26 +01:00
internal/atlas: use texture atlases for rendering destinations
Before this change, texture atlases are created only for rendreing sources. This change enables to use texture atlases even for rendering destinations, so that the number of textures will be drastically reduced. Closes #2581
This commit is contained in:
parent
8f3974eeba
commit
7c0fbce0cf
@ -19,27 +19,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BaseCountToPutOnAtlas = baseCountToPutOnAtlas
|
BaseCountToPutOnSourceBackend = baseCountToPutOnSourceBackend
|
||||||
)
|
)
|
||||||
|
|
||||||
func PutImagesOnAtlasForTesting(graphicsDriver graphicsdriver.Graphics) error {
|
func PutImagesOnSourceBackendForTesting(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
return putImagesOnAtlas(graphicsDriver)
|
return putImagesOnSourceBackend(graphicsDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
oldMinSize int
|
oldMinSourceSize int
|
||||||
|
oldMinDestinationSize int
|
||||||
oldMaxSize int
|
oldMaxSize int
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetImageSizeForTesting(min, max int) {
|
func SetImageSizeForTesting(minSource, minDestination, max int) {
|
||||||
oldMinSize = minSize
|
oldMinSourceSize = minSourceSize
|
||||||
|
oldMinDestinationSize = minDestinationSize
|
||||||
oldMaxSize = maxSize
|
oldMaxSize = maxSize
|
||||||
minSize = min
|
|
||||||
|
minSourceSize = minSource
|
||||||
|
minDestinationSize = minDestination
|
||||||
maxSize = max
|
maxSize = max
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetImageSizeForTesting() {
|
func ResetImageSizeForTesting() {
|
||||||
minSize = oldMinSize
|
minSourceSize = oldMinSourceSize
|
||||||
|
minDestinationSize = oldMinDestinationSize
|
||||||
maxSize = oldMaxSize
|
maxSize = oldMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,16 +52,16 @@ func (i *Image) PaddingSizeForTesting() int {
|
|||||||
return i.paddingSize()
|
return i.paddingSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) IsOnAtlasForTesting() bool {
|
func (i *Image) IsOnSourceBackendForTesting() bool {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
defer backendsM.Unlock()
|
||||||
return i.isOnAtlas()
|
return i.isOnSourceBackend()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) EnsureIsolatedForTesting() {
|
func (i *Image) EnsureIsolatedFromSourceForTesting(backends []*backend) {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
defer backendsM.Unlock()
|
||||||
i.ensureIsolated()
|
i.ensureIsolatedFromSource(backends)
|
||||||
}
|
}
|
||||||
|
|
||||||
var FlushDeferredForTesting = flushDeferred
|
var FlushDeferredForTesting = flushDeferred
|
||||||
|
@ -27,7 +27,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
minSize = 0
|
minSourceSize = 0
|
||||||
|
minDestinationSize = 0
|
||||||
maxSize = 0
|
maxSize = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,25 +107,25 @@ func flushDeferred() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// baseCountToPutOnAtlas represents the base time duration when the image can be put onto an atlas.
|
// baseCountToPutOnSourceBackend represents the base time duration when the image can be put onto an atlas.
|
||||||
// Actual time duration is increased in an exponential way for each usage as a rendering target.
|
// Actual time duration is increased in an exponential way for each usage as a rendering target.
|
||||||
const baseCountToPutOnAtlas = 10
|
const baseCountToPutOnSourceBackend = 10
|
||||||
|
|
||||||
func putImagesOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
|
func putImagesOnSourceBackend(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
for i := range imagesToPutOnAtlas {
|
for i := range imagesToPutOnSourceBackend {
|
||||||
i.usedAsSourceCount++
|
i.usedAsSourceCount++
|
||||||
if i.usedAsSourceCount >= baseCountToPutOnAtlas*(1<<uint(min(i.isolatedCount, 31))) {
|
if i.usedAsSourceCount >= baseCountToPutOnSourceBackend*(1<<uint(min(i.destinationCount, 31))) {
|
||||||
if err := i.putOnAtlas(graphicsDriver); err != nil {
|
if err := i.putOnSourceBackend(graphicsDriver); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.usedAsSourceCount = 0
|
i.usedAsSourceCount = 0
|
||||||
delete(imagesToPutOnAtlas, i)
|
delete(imagesToPutOnSourceBackend, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the images. The images will be registered again when it is used as a rendering source.
|
// Reset the images. The images will be registered again when it is used as a rendering source.
|
||||||
for k := range imagesToPutOnAtlas {
|
for k := range imagesToPutOnSourceBackend {
|
||||||
delete(imagesToPutOnAtlas, k)
|
delete(imagesToPutOnSourceBackend, k)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -136,6 +137,11 @@ type backend struct {
|
|||||||
// page is an atlas map. Each part is called a node.
|
// page is an atlas map. Each part is called a node.
|
||||||
// If page is nil, the backend's image is isolated and not on an atlas.
|
// If page is nil, the backend's image is isolated and not on an atlas.
|
||||||
page *packing.Page
|
page *packing.Page
|
||||||
|
|
||||||
|
// source reports whether this backend is mainly used a rendering source, but this is not 100%.
|
||||||
|
// If a non-source (destination) image is used as a source many times,
|
||||||
|
// the image's backend might be turned into a source backend to optimize draw calls.
|
||||||
|
source bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) {
|
func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) {
|
||||||
@ -157,9 +163,13 @@ var (
|
|||||||
initOnce sync.Once
|
initOnce sync.Once
|
||||||
|
|
||||||
// theBackends is a set of atlases.
|
// theBackends is a set of atlases.
|
||||||
theBackends = []*backend{}
|
theBackends []*backend
|
||||||
|
|
||||||
imagesToPutOnAtlas = map[*Image]struct{}{}
|
// theSourceBackendsForOneFrame is a temporary set of backends that are used as sources in one frame.
|
||||||
|
// theSourceBackendsForOneFrame is reset every frame.
|
||||||
|
theSourceBackendsForOneFrame = map[*backend]struct{}{}
|
||||||
|
|
||||||
|
imagesToPutOnSourceBackend = map[*Image]struct{}{}
|
||||||
|
|
||||||
deferred []func()
|
deferred []func()
|
||||||
|
|
||||||
@ -202,7 +212,7 @@ type Image struct {
|
|||||||
// usedAsSourceCount represents how long the image is used as a rendering source and kept not modified with
|
// usedAsSourceCount represents how long the image is used as a rendering source and kept not modified with
|
||||||
// DrawTriangles.
|
// DrawTriangles.
|
||||||
// In the current implementation, if an image is being modified by DrawTriangles, the image is separated from
|
// In the current implementation, if an image is being modified by DrawTriangles, the image is separated from
|
||||||
// a restorable image on an atlas by ensureIsolated.
|
// a restorable image on an atlas by ensureIsolatedFromSource.
|
||||||
//
|
//
|
||||||
// usedAsSourceCount is increased if the image is used as a rendering source, or set to 0 if the image is
|
// usedAsSourceCount is increased if the image is used as a rendering source, or set to 0 if the image is
|
||||||
// modified.
|
// modified.
|
||||||
@ -210,9 +220,12 @@ type Image struct {
|
|||||||
// WritePixels doesn't affect this value since WritePixels can be done on images on an atlas.
|
// WritePixels doesn't affect this value since WritePixels can be done on images on an atlas.
|
||||||
usedAsSourceCount int
|
usedAsSourceCount int
|
||||||
|
|
||||||
// isolatedCount represents how many times the image on a texture atlas is changed into an isolated image.
|
// destinationCount represents how many times an image on an atlas is used as a rendering destination at DrawTriangles.
|
||||||
// isolatedCount affects the calculation when to put the image onto a texture atlas again.
|
// destinationCount affects the calculation when to put the image onto a texture atlas again.
|
||||||
isolatedCount int
|
//
|
||||||
|
// The current counting way is derived from the old implementation in which a destination image was always isolated
|
||||||
|
// e.g. not on an atlas. This might have to be revisited.
|
||||||
|
destinationCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveTo moves its content to the given image dst.
|
// moveTo moves its content to the given image dst.
|
||||||
@ -232,9 +245,16 @@ func (i *Image) isOnAtlas() bool {
|
|||||||
return i.node != nil
|
return i.node != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) isOnSourceBackend() bool {
|
||||||
|
if i.backend == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return i.backend.source
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Image) resetUsedAsSourceCount() {
|
func (i *Image) resetUsedAsSourceCount() {
|
||||||
i.usedAsSourceCount = 0
|
i.usedAsSourceCount = 0
|
||||||
delete(imagesToPutOnAtlas, i)
|
delete(imagesToPutOnSourceBackend, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) paddingSize() int {
|
func (i *Image) paddingSize() int {
|
||||||
@ -244,11 +264,16 @@ func (i *Image) paddingSize() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) ensureIsolated() {
|
func (i *Image) ensureIsolatedFromSource(backends []*backend) {
|
||||||
i.resetUsedAsSourceCount()
|
i.resetUsedAsSourceCount()
|
||||||
|
|
||||||
if i.backend == nil {
|
if i.backend == nil {
|
||||||
i.allocate(false)
|
// `theSourceBackendsForOneFrame` already includes `backends`.
|
||||||
|
bs := make([]*backend, 0, len(theSourceBackendsForOneFrame))
|
||||||
|
for b := range theSourceBackendsForOneFrame {
|
||||||
|
bs = append(bs, b)
|
||||||
|
}
|
||||||
|
i.allocate(bs, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,10 +281,30 @@ func (i *Image) ensureIsolated() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.destinationCount++
|
||||||
|
|
||||||
|
// Check if i has the same backend as the given backends.
|
||||||
|
var needsIsolation bool
|
||||||
|
for _, b := range backends {
|
||||||
|
if i.backend == b {
|
||||||
|
needsIsolation = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !needsIsolation {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
newI := NewImage(i.width, i.height, i.imageType)
|
newI := NewImage(i.width, i.height, i.imageType)
|
||||||
|
|
||||||
// Call allocate explicitly in order to have an isolated backend.
|
// Call allocate explicitly in order to have an isolated backend from the specified backends.
|
||||||
newI.allocate(false)
|
// `theSourceBackendsForOneFrame` already includes `backends`.
|
||||||
|
bs := make([]*backend, 0, 1+len(theSourceBackendsForOneFrame))
|
||||||
|
bs = append(bs, i.backend)
|
||||||
|
for b := range theSourceBackendsForOneFrame {
|
||||||
|
bs = append(bs, b)
|
||||||
|
}
|
||||||
|
newI.allocate(bs, false)
|
||||||
|
|
||||||
w, h := float32(i.width), float32(i.height)
|
w, h := float32(i.width), float32(i.height)
|
||||||
vs := make([]float32, 4*graphics.VertexFloatCount)
|
vs := make([]float32, 4*graphics.VertexFloatCount)
|
||||||
@ -271,24 +316,26 @@ func (i *Image) ensureIsolated() {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
Height: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
origBackend := i.backend
|
||||||
newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true)
|
newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true)
|
||||||
|
delete(theSourceBackendsForOneFrame, origBackend)
|
||||||
|
|
||||||
newI.moveTo(i)
|
newI.moveTo(i)
|
||||||
i.isolatedCount++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
|
func (i *Image) putOnSourceBackend(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
if i.backend == nil {
|
if i.backend == nil {
|
||||||
i.allocate(true)
|
i.allocate(nil, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.isOnAtlas() {
|
if i.isOnSourceBackend() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !i.canBePutOnAtlas() {
|
if !i.canBePutOnAtlas() {
|
||||||
panic("atlas: putOnAtlas cannot be called on a image that cannot be on an atlas")
|
panic("atlas: putOnSourceBackend cannot be called on a image that cannot be on an atlas")
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.imageType != ImageTypeRegular {
|
if i.imageType != ImageTypeRegular {
|
||||||
@ -296,6 +343,7 @@ func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newI := NewImage(i.width, i.height, ImageTypeRegular)
|
newI := NewImage(i.width, i.height, ImageTypeRegular)
|
||||||
|
newI.allocate(nil, true)
|
||||||
|
|
||||||
w, h := float32(i.width), float32(i.height)
|
w, h := float32(i.width), float32(i.height)
|
||||||
vs := make([]float32, 4*graphics.VertexFloatCount)
|
vs := make([]float32, 4*graphics.VertexFloatCount)
|
||||||
@ -331,8 +379,13 @@ func (i *Image) processSrc(src *Image) {
|
|||||||
if src.disposed {
|
if src.disposed {
|
||||||
panic("atlas: the drawing source image must not be disposed (DrawTriangles)")
|
panic("atlas: the drawing source image must not be disposed (DrawTriangles)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if src.backend == nil {
|
if src.backend == nil {
|
||||||
src.allocate(true)
|
backends := make([]*backend, 0, 1)
|
||||||
|
if i.backend != nil {
|
||||||
|
backends = append(backends, i.backend)
|
||||||
|
}
|
||||||
|
src.allocate(backends, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare i and source images after ensuring i is not on an atlas, or
|
// Compare i and source images after ensuring i is not on an atlas, or
|
||||||
@ -364,13 +417,21 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
|||||||
if i.disposed {
|
if i.disposed {
|
||||||
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
|
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
|
||||||
}
|
}
|
||||||
if keepOnAtlas {
|
|
||||||
if i.backend == nil {
|
backends := make([]*backend, 0, len(srcs))
|
||||||
i.allocate(true)
|
for _, src := range srcs {
|
||||||
|
if src == nil {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
if src.backend == nil {
|
||||||
i.ensureIsolated()
|
// It is possible to spcify i.backend as a forbidden backend, but this might prevent a good allocation for a source image.
|
||||||
|
// If the backend becomes the same as i's, this will be changed later.
|
||||||
|
src.allocate(nil, true)
|
||||||
}
|
}
|
||||||
|
backends = append(backends, src.backend)
|
||||||
|
theSourceBackendsForOneFrame[src.backend] = struct{}{}
|
||||||
|
}
|
||||||
|
i.ensureIsolatedFromSource(backends)
|
||||||
|
|
||||||
for _, src := range srcs {
|
for _, src := range srcs {
|
||||||
i.processSrc(src)
|
i.processSrc(src)
|
||||||
@ -437,9 +498,9 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
|||||||
if src == nil {
|
if src == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !src.isOnAtlas() && src.canBePutOnAtlas() {
|
if !src.isOnSourceBackend() && src.canBePutOnAtlas() {
|
||||||
// src might already registered, but assigning it again is not harmful.
|
// src might already registered, but assigning it again is not harmful.
|
||||||
imagesToPutOnAtlas[src] = struct{}{}
|
imagesToPutOnSourceBackend[src] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,7 +527,8 @@ func (i *Image) writePixels(pix []byte, x, y, width, height int) {
|
|||||||
if pix == nil {
|
if pix == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.allocate(true)
|
// Allocate as a source as this image will likely be used as a source.
|
||||||
|
i.allocate(nil, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
px, py, pw, ph := i.regionWithPadding()
|
px, py, pw, ph := i.regionWithPadding()
|
||||||
@ -594,18 +656,18 @@ func (i *Image) dispose(markDisposed bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i.backend.restorable.Dispose()
|
i.backend.restorable.Dispose()
|
||||||
index := -1
|
|
||||||
|
delete(theSourceBackendsForOneFrame, i.backend)
|
||||||
|
|
||||||
for idx, sh := range theBackends {
|
for idx, sh := range theBackends {
|
||||||
if sh == i.backend {
|
if sh == i.backend {
|
||||||
index = idx
|
theBackends = append(theBackends[:idx], theBackends[idx+1:]...)
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if index == -1 {
|
|
||||||
panic("atlas: backend not found at an image being disposed")
|
panic("atlas: backend not found at an image being disposed")
|
||||||
}
|
}
|
||||||
theBackends = append(theBackends[:index], theBackends[index+1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewImage(width, height int, imageType ImageType) *Image {
|
func NewImage(width, height int, imageType ImageType) *Image {
|
||||||
// Actual allocation is done lazily, and the lock is not needed.
|
// Actual allocation is done lazily, and the lock is not needed.
|
||||||
@ -617,8 +679,8 @@ func NewImage(width, height int, imageType ImageType) *Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) canBePutOnAtlas() bool {
|
func (i *Image) canBePutOnAtlas() bool {
|
||||||
if minSize == 0 || maxSize == 0 {
|
if minSourceSize == 0 || minDestinationSize == 0 || maxSize == 0 {
|
||||||
panic("atlas: minSize or maxSize must be initialized")
|
panic("atlas: min*Size or maxSize must be initialized")
|
||||||
}
|
}
|
||||||
if i.imageType != ImageTypeRegular {
|
if i.imageType != ImageTypeRegular {
|
||||||
return false
|
return false
|
||||||
@ -626,7 +688,7 @@ func (i *Image) canBePutOnAtlas() bool {
|
|||||||
return i.width+2*i.paddingSize() <= maxSize && i.height+2*i.paddingSize() <= maxSize
|
return i.width+2*i.paddingSize() <= maxSize && i.height+2*i.paddingSize() <= maxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) allocate(putOnAtlas bool) {
|
func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) {
|
||||||
if i.backend != nil {
|
if i.backend != nil {
|
||||||
panic("atlas: the image is already allocated")
|
panic("atlas: the image is already allocated")
|
||||||
}
|
}
|
||||||
@ -634,6 +696,9 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
runtime.SetFinalizer(i, (*Image).MarkDisposed)
|
runtime.SetFinalizer(i, (*Image).MarkDisposed)
|
||||||
|
|
||||||
if i.imageType == ImageTypeScreen {
|
if i.imageType == ImageTypeScreen {
|
||||||
|
if asSource {
|
||||||
|
panic("atlas: a screen image cannot be created as a source")
|
||||||
|
}
|
||||||
// A screen image doesn't have a padding.
|
// A screen image doesn't have a padding.
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen),
|
restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen),
|
||||||
@ -641,7 +706,7 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !putOnAtlas || !i.canBePutOnAtlas() {
|
if !i.canBePutOnAtlas() {
|
||||||
if i.width+2*i.paddingSize() > maxSize || i.height+2*i.paddingSize() > maxSize {
|
if i.width+2*i.paddingSize() > maxSize || i.height+2*i.paddingSize() > maxSize {
|
||||||
panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
|
panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
|
||||||
}
|
}
|
||||||
@ -652,11 +717,23 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
}
|
}
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
restorable: restorable.NewImage(i.width+2*i.paddingSize(), i.height+2*i.paddingSize(), typ),
|
restorable: restorable.NewImage(i.width+2*i.paddingSize(), i.height+2*i.paddingSize(), typ),
|
||||||
|
source: asSource && typ == restorable.ImageTypeRegular,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if an existing backend is available.
|
||||||
|
loop:
|
||||||
for _, b := range theBackends {
|
for _, b := range theBackends {
|
||||||
|
if b.source != asSource {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, bb := range forbiddenBackends {
|
||||||
|
if b == bb {
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if n, ok := b.tryAlloc(i.width+2*i.paddingSize(), i.height+2*i.paddingSize()); ok {
|
if n, ok := b.tryAlloc(i.width+2*i.paddingSize(), i.height+2*i.paddingSize()); ok {
|
||||||
i.backend = b
|
i.backend = b
|
||||||
i.node = n
|
i.node = n
|
||||||
@ -664,7 +741,12 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width, height := minSize, minSize
|
var width, height int
|
||||||
|
if asSource {
|
||||||
|
width, height = minSourceSize, minSourceSize
|
||||||
|
} else {
|
||||||
|
width, height = minDestinationSize, minDestinationSize
|
||||||
|
}
|
||||||
for i.width+2*i.paddingSize() > width {
|
for i.width+2*i.paddingSize() > width {
|
||||||
if width == maxSize {
|
if width == maxSize {
|
||||||
panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
|
panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
|
||||||
@ -685,6 +767,7 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
b := &backend{
|
b := &backend{
|
||||||
restorable: restorable.NewImage(width, height, typ),
|
restorable: restorable.NewImage(width, height, typ),
|
||||||
page: packing.NewPage(width, height, maxSize),
|
page: packing.NewPage(width, height, maxSize),
|
||||||
|
source: asSource,
|
||||||
}
|
}
|
||||||
theBackends = append(theBackends, b)
|
theBackends = append(theBackends, b)
|
||||||
|
|
||||||
@ -711,6 +794,11 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
theTemporaryBytes.resetAtFrameEnd()
|
theTemporaryBytes.resetAtFrameEnd()
|
||||||
|
|
||||||
|
for b := range theSourceBackendsForOneFrame {
|
||||||
|
delete(theSourceBackendsForOneFrame, b)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,9 +826,12 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
panic("atlas: all the images must be not on an atlas before the game starts")
|
panic("atlas: all the images must be not on an atlas before the game starts")
|
||||||
}
|
}
|
||||||
|
|
||||||
// minSize and maxSize can already be set for testings.
|
// min*Size and maxSize can already be set for testings.
|
||||||
if minSize == 0 {
|
if minSourceSize == 0 {
|
||||||
minSize = 1024
|
minSourceSize = 1024
|
||||||
|
}
|
||||||
|
if minDestinationSize == 0 {
|
||||||
|
minDestinationSize = 16
|
||||||
}
|
}
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = floorPowerOf2(restorable.MaxImageSize(graphicsDriver))
|
maxSize = floorPowerOf2(restorable.MaxImageSize(graphicsDriver))
|
||||||
@ -756,7 +847,7 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flushDeferred()
|
flushDeferred()
|
||||||
if err := putImagesOnAtlas(graphicsDriver); err != nil {
|
if err := putImagesOnSourceBackend(graphicsDriver); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +27,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minImageSizeForTesting = 1024
|
minSourceImageSizeForTesting = 1024
|
||||||
|
minDestinationImageSizeForTesting = 256
|
||||||
maxImageSizeForTesting = 4096
|
maxImageSizeForTesting = 4096
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
atlas.SetImageSizeForTesting(minImageSizeForTesting, maxImageSizeForTesting)
|
atlas.SetImageSizeForTesting(minSourceImageSizeForTesting, minDestinationImageSizeForTesting, maxImageSizeForTesting)
|
||||||
defer atlas.ResetImageSizeForTesting()
|
defer atlas.ResetImageSizeForTesting()
|
||||||
t.MainWithRunLoop(m)
|
t.MainWithRunLoop(m)
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ func quadVertices(sw, sh, x, y int, scalex float32) []float32 {
|
|||||||
|
|
||||||
const bigSize = 2049
|
const bigSize = 2049
|
||||||
|
|
||||||
func TestEnsureIsolated(t *testing.T) {
|
func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
|
||||||
// Create img1 and img2 with this size so that the next images are allocated
|
// Create img1 and img2 with this size so that the next images are allocated
|
||||||
// with non-upper-left location.
|
// with non-upper-left location.
|
||||||
img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular)
|
img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular)
|
||||||
@ -97,7 +98,7 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
dx1 = size * 3 / 4
|
dx1 = size * 3 / 4
|
||||||
dy1 = size * 3 / 4
|
dy1 = size * 3 / 4
|
||||||
)
|
)
|
||||||
// img4.EnsureIsolated() should be called.
|
// img4.ensureIsolatedFromSource() should be called.
|
||||||
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
|
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dr := graphicsdriver.Region{
|
dr := graphicsdriver.Region{
|
||||||
@ -107,11 +108,11 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
Height: size,
|
Height: size,
|
||||||
}
|
}
|
||||||
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img4.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img4.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make img3 isolated before getting pixels.
|
// img5 is not allocated now, but is allocated at DrawTriangles.
|
||||||
vs = quadVertices(0, 0, size/2, size/2, 1)
|
vs = quadVertices(0, 0, size/2, size/2, 1)
|
||||||
dr = graphicsdriver.Region{
|
dr = graphicsdriver.Region{
|
||||||
X: 0,
|
X: 0,
|
||||||
@ -120,7 +121,7 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
Height: size / 2,
|
Height: size / 2,
|
||||||
}
|
}
|
||||||
img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReputOnAtlas(t *testing.T) {
|
func TestReputOnSourceBackend(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
@ -161,7 +162,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +183,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile)
|
img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile)
|
||||||
defer img3.MarkDisposed()
|
defer img3.MarkDisposed()
|
||||||
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
||||||
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,23 +197,23 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
Height: size,
|
Height: size,
|
||||||
}
|
}
|
||||||
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use img1 as a render source.
|
// Use img1 as a render source.
|
||||||
// Use the doubled count since img1 was on a texture atlas and became an isolated image once.
|
// Use the doubled count since img1 was on a texture atlas and became an isolated image once.
|
||||||
// Then, img1 requires longer time to recover to be on a texture atlas again.
|
// Then, img1 requires longer time to recover to be on a texture atlas again.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +237,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
|
|
||||||
// img1 is on an atlas again.
|
// img1 is on an atlas again.
|
||||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,39 +261,39 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
|
|
||||||
// Use img1 as a render target again.
|
// Use img1 as a render target again.
|
||||||
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use img1 as a render source, but call WritePixels.
|
// Use img1 as a render source, but call WritePixels.
|
||||||
// Now use 4x count as img1 became an isolated image again.
|
// Now use 4x count as img1 became an isolated image again.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas*4; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*4; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
|
||||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// img1 is not on an atlas due to WritePixels.
|
// img1 is not on an atlas due to WritePixels.
|
||||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use img3 as a render source. As img3 is volatile, img3 is never on an atlas.
|
// Use img3 as a render source. As img3 is volatile, img3 is never on an atlas.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,7 +315,7 @@ func TestExtend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
img0.WritePixels(p0, 0, 0, w0, h0)
|
img0.WritePixels(p0, 0, 0, w0, h0)
|
||||||
|
|
||||||
const w1, h1 = minImageSizeForTesting + 1, 100
|
const w1, h1 = minSourceImageSizeForTesting + 1, 100
|
||||||
img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular)
|
img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
@ -511,11 +512,11 @@ func TestDisposeImmediately(t *testing.T) {
|
|||||||
// This tests restorable.Image.ClearPixels is called but WritePixels is not called.
|
// This tests restorable.Image.ClearPixels is called but WritePixels is not called.
|
||||||
|
|
||||||
img0 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
img0 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
||||||
img0.EnsureIsolatedForTesting()
|
img0.EnsureIsolatedFromSourceForTesting(nil)
|
||||||
defer img0.MarkDisposed()
|
defer img0.MarkDisposed()
|
||||||
|
|
||||||
img1 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
img1 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
||||||
img1.EnsureIsolatedForTesting()
|
img1.EnsureIsolatedFromSourceForTesting(nil)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
// img0 and img1 should share the same backend in 99.9999% possibility.
|
// img0 and img1 should share the same backend in 99.9999% possibility.
|
||||||
@ -528,10 +529,10 @@ func TestExtendWithBigImage(t *testing.T) {
|
|||||||
|
|
||||||
img0.WritePixels(make([]byte, 4*1*1), 0, 0, 1, 1)
|
img0.WritePixels(make([]byte, 4*1*1), 0, 0, 1, 1)
|
||||||
|
|
||||||
img1 := atlas.NewImage(minImageSizeForTesting+1, minImageSizeForTesting+1, atlas.ImageTypeRegular)
|
img1 := atlas.NewImage(minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
img1.WritePixels(make([]byte, 4*(minImageSizeForTesting+1)*(minImageSizeForTesting+1)), 0, 0, minImageSizeForTesting+1, minImageSizeForTesting+1)
|
img1.WritePixels(make([]byte, 4*(minSourceImageSizeForTesting+1)*(minSourceImageSizeForTesting+1)), 0, 0, minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue #1217
|
// Issue #1217
|
||||||
@ -554,7 +555,7 @@ func Disable_TestMinImageSize(t *testing.T) {
|
|||||||
|
|
||||||
// This tests that extending a backend works correctly.
|
// This tests that extending a backend works correctly.
|
||||||
// Though the image size is minimum size of the backend, extending the backend happens due to the paddings.
|
// Though the image size is minimum size of the backend, extending the backend happens due to the paddings.
|
||||||
s := minImageSizeForTesting
|
s := minSourceImageSizeForTesting
|
||||||
img := atlas.NewImage(s, s, atlas.ImageTypeRegular)
|
img := atlas.NewImage(s, s, atlas.ImageTypeRegular)
|
||||||
defer img.MarkDisposed()
|
defer img.MarkDisposed()
|
||||||
img.WritePixels(make([]byte, 4*s*s), 0, 0, s, s)
|
img.WritePixels(make([]byte, 4*s*s), 0, 0, s, s)
|
||||||
@ -585,7 +586,7 @@ func TestMaxImageSizeExceeded(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Issue #1421
|
// Issue #1421
|
||||||
func TestDisposedAndReputOnAtlas(t *testing.T) {
|
func TestDisposedAndReputOnSourceBackend(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
@ -605,35 +606,35 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
|||||||
Height: size,
|
Height: size,
|
||||||
}
|
}
|
||||||
src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use src as a render source.
|
// Use src as a render source.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas/2; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before PutImagesOnAtlasForTesting, dispose the image.
|
// Before PutImagesOnSourceBackendForTesting, dispose the image.
|
||||||
src.MarkDisposed()
|
src.MarkDisposed()
|
||||||
|
|
||||||
// Force to dispose the image.
|
// Force to dispose the image.
|
||||||
atlas.FlushDeferredForTesting()
|
atlas.FlushDeferredForTesting()
|
||||||
|
|
||||||
// Confirm that PutImagesOnAtlasForTesting doesn't panic.
|
// Confirm that PutImagesOnSourceBackendForTesting doesn't panic.
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue #1456
|
// Issue #1456
|
||||||
func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
@ -655,36 +656,36 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
|||||||
|
|
||||||
// Use src2 as a rendering target, and make src2 an independent image.
|
// Use src2 as a rendering target, and make src2 an independent image.
|
||||||
src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the count without using src2 as a rendering source.
|
// Update the count without using src2 as a rendering source.
|
||||||
// This should not affect whether src2 is on an atlas or not.
|
// This should not affect whether src2 is on an atlas or not.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the count with using src2 as a rendering source.
|
// Update the count with using src2 as a rendering source.
|
||||||
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
|
for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ {
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
|
||||||
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if got, want := src2.IsOnAtlasForTesting(), true; got != want {
|
if got, want := src2.IsOnSourceBackendForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user