Asyn c генерация полей и проблема рендеринга TextureRegion - PullRequest
0 голосов
/ 20 апреля 2020

У меня есть кусок кода для рендеринга сетки, из которой я буду рендерить лабиринт. Моя функция сборки:

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

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

И это выглядит так: enter image description here

Но когда я двигаюсь addEmptyFields() до секции async, она рендерится правильно enter image description here

И мой Wall класс

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)
}

[РЕДАКТИРОВАТЬ / ОБНОВИТЬ]

Я обнаружил кое-что странно, когда я создаю один Wall без измерений до async, он начинает работать. «Рабочий» код:

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

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

Почему?

1 Ответ

0 голосов
/ 26 апреля 2020

У вас проблемы с параллелизмом. «Исправленный» код, вероятно, работает случайно из-за небольшой задержки при выполнении сопрограммы или выполнении какой-либо важной операции в главном потоке, а не потому, что он решает актуальную проблему.

Как правило, вам следует никогда не изменяйте ничего, к чему обращается поток рендеринга в различных пулах потоков. Я предполагаю, что что-то изменяет или обращается к regularMazeService во время выполнения сопрограммы, или assetsHelper.getTextureFromAsset(TextureAsset.WALL) не может загрузить текстуру в фоновом потоке, поскольку в нем отсутствует контекст OpenGL.

Если есть некоторые дорогая часть операции, которую вы хотите выполнить в отдельном потоке - это нормально, но все, что изменяет состояние потока рендеринга, должно выполняться на потоке рендеринга. Например:

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

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

Если какая-либо часть вашего кода, выполненная через сопрограмму, запущена с пользовательским потоком:

  • Требуется доступ к контексту OpenGL (например, загрузка текстуры).
  • Получает доступ или изменяет данные из потока рендеринга.
  • Выполняет любые виды рендеринга или обновления, полагаясь на время дельты рендеринга.

Вы должны переместить его в onRenderingThread block.

Кроме того, вы должны повторно использовать контексты сопрограмм. Не запускайте новый поток для каждой сопрограммы с newSingleThreadAsyncContext() - присвойте AsyncContext и повторно используйте его экземпляр.


Если вы загружаете текстуры вручную, я рекомендую вам использовать AssetStorage в ktx-assets-async модуль. Он использует сопрограммы для истинной параллельной загрузки активов.

...