异步字段生成和 TextureRegion 渲染问题

Async fields generation and TextureRegion rendering problem

我有一段代码可以渲染一个网格,我将从中渲染一个迷宫。 我的构建函数:

fun buildAsync(): Deferred<RegularMaze> {
        return KtxAsync.async(newSingleThreadAsyncContext()) {
            addEmptyFields()
            enableLeftBordersRender()
            enableBottomBordersRender()

            regularMazeService.convertFieldsToMaze(fields, colsNo, rowsNo)
        }
    }

它看起来像这样:

但是当我在 async 部分之前移动 addEmptyFields() 时,它呈现正确

还有我的Wallclass

class Wall (width: Float, height: Float, x: Float = 0F, y: Float = 0F, rotation: Float = 0F) : BasePart() {

    private val textureRegion: TextureRegion
    private val size: Size = Size(width, height)
    private val position: Position = Position(x, y, rotation)

    var relatedFieldIndex: Int? = null

    var shouldBeDraw = true

    init {
        inject()

        val wallTexture = assetsHelper.getTextureFromAsset(TextureAsset.WALL)
        wallTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat)
        textureRegion = TextureRegion(wallTexture, 0, 0, size.widthInt, size.heightInt)

        setBounds(
            position.x,
            position.y,
            width,
            height
        )

        rotateBy(position.rotation)
    }

    override fun draw(batch: Batch, parentAlpha: Float) {
        super.draw(batch, parentAlpha)
        if (shouldBeDraw) {
            batch.draw(textureRegion, position.x, position.y, 0F, 0F, size.width, size.height, 1F, 1F, position.rotation)
        }
    }

    class Size (val width: Float, val height: Float) {
        val widthInt: Int
            get() = ceil(width).toInt()

        val heightInt: Int
            get() = ceil(height).toInt()
    }

    data class Position(val x: Float, val y: Float, val rotation: Float)
}

[EDIT/UPDATE]

我发现了一些奇怪的事情,当我在 async 之前创建一个没有尺寸的 Wall 它开始工作时。 "Working"代码:

fun buildAsync(): Deferred<RegularMaze> {
        Wall(0f,0f) // <---- new line
        return KtxAsync.async(newSingleThreadAsyncContext()) {
            addEmptyFields()
            enableLeftBordersRender()
            enableBottomBordersRender()

            regularMazeService.convertFieldsToMaze(fields, colsNo, rowsNo)
        }
    }

为什么?

您 运行 陷入并发问题。 "fixed" 代码可能由于在执行协程或在主线程上执行某些关键操作时出现轻微延迟而意外运行,而不是因为它解决了实际问题。

根据经验,永远不要修改不同线程池上的渲染线程访问的任何内容。我的猜测是,在执行协程时某些东西修改或访问了 regularMazeService,或者 assetsHelper.getTextureFromAsset(TextureAsset.WALL) 无法在后台线程中加载纹理,因为它缺少 OpenGL 上下文。

如果您想在单独的线程上执行某些操作的昂贵部分 - 没关系,但任何修改渲染线程状态的操作都应该在 on 渲染线程。例如:

fun buildAsync(): Deferred<RegularMaze> {
    return KtxAsync.async(asyncContext) {
        onRenderingThread {
            addEmptyFields()
        }
        enableLeftBordersRender()
        enableBottomBordersRender()

        regularMazeService.convertFieldsToMaze(fields, colsNo, rowsNo)
    }
}

如果您的代码的任何部分是通过使用自定义线程启动的协程执行的:

  • 需要访问 OpenGL 上下文(例如纹理加载)。
  • 从呈现线程访问或修改数据。
  • 根据渲染增量时间执行任何类型的渲染或更新。

你应该把它移到 onRenderingThread 块中。

此外,您应该重用协程上下文。不要使用 newSingleThreadAsyncContext() 为每个协程启动新线程 - 分配 AsyncContext 并重用其实例。


如果您手动加载纹理,我鼓励您使用 ktx-assets-async 模块中可用的 AssetStorage。它利用协同程序实现真正的并行资产加载。