Libgdx Рендеринг растрового шрифта в текстуру растрового изображения приводит к чрезвычайно медленному рендерингу

#libgdx #scene2d

Вопрос:

Я использую libgdx scene2d для визуализации 2d-актеров. Некоторые из этих актеров первоначально включали актеров меток scene2d для рендеринга статического текста. Метки работают нормально, но рисование ~20 из них на экране сразу снижает частоту кадров на 10-15 кадров, что приводит к заметному ухудшению визуализации при перетаскивании.

Я пытаюсь избежать надписей, предварительно отрисовав текст в текстуры и отрисовав текстуры как актеры изображения scene2d. Я создаю текстуру, используя приведенный ниже код:

 BitmapFont font = manager.get(baseNameFont,BitmapFont.class);  GlyphLayout gl = new GlyphLayout(font,"Test Text");   int textWidth = (int)gl.width;  int textHeight = (int)gl.height;  LOGGER.info("textHeight: {}",textHeight);  //int width = Gdx.graphics.getWidth();  int width = textWidth;  //int height = 500;  int height = textHeight;   SpriteBatch spriteBatch = new SpriteBatch();   FrameBuffer m_fbo = new FrameBuffer(Pixmap.Format.RGB565, width,height, false);  m_fbo.begin();  Gdx.gl.glClearColor(1f,1f,1f,0f);  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);  Matrix4 normalProjection = new Matrix4()   .setToOrtho2D(0, 0, width, height);  spriteBatch.setProjectionMatrix(normalProjection);   spriteBatch.begin();   font.draw(spriteBatch,gl,0,height);    spriteBatch.end();//finish write to buffer   Pixmap pm = ScreenUtils.getFrameBufferPixmap(0, 0, (int) width, (int) height);//write frame buffer to Pixmap   m_fbo.end();   m_fbo.dispose();  m_fbo = null;  spriteBatch.dispose();   Texture texture = new Texture(pm);  textTexture = new TextureRegion(texture);  textTexture.flip(false,true);  manager.add(texture);  

Я предполагал и читал, что текстуры часто бывают быстрее. Однако, когда я заменил метки текстурой, это оказало такое же, если не худшее, влияние на частоту кадров. Как ни странно, я не испытываю этого при добавлении текстур из файла, что заставляет меня думать, что я делаю что-то не так в своем коде. Есть ли другой способ, которым я должен предварительно отрисовывать эти фрагменты текста?

Комментарии:

1. Вы сказали, что у вас 20 этикеток. Вы создаете 20 таких полноэкранных текстур, чтобы рисовать все сразу? Это почти наверняка будет иметь худшую производительность, чем рисование отдельных меток. Вы бы обменяли рисование умеренного количества вершин на рисование многих, многих фрагментов.

2. Нет. Я в основном начинаю с нуля, чтобы действительно понять, в чем проблема. Я удалил все метки и во время инициализации отрисовываю один фрагмент текста в одну текстуру. Затем я заменил все эти метки изображениями scene2d, которые все указывают на эту единственную текстуру. Так что теперь я рендерю 20 копий одной и той же текстуры вместо 20 разных меток. Без изображений мой FPS составляет 60 (но со многими другими текстурами на основе файлов). Как только я добавляю 20 копий одной и той же предварительно отрисованной текстовой текстуры, она падает до 40-45, что создает заметное заикание.

3. @Tenfour04 Я должен отметить, что на самом деле существует, вероятно, 100 изображений текстуры, однако они не видны , и поэтому я предполагаю, что Libgdx не вызывает на них методы рисования. Я считаю, что это точно, потому что были бы более серьезные проблемы, если бы я пытался отобразить 100 меток одновременно.

4. Под 100 изображениями вы подразумеваете 100 экземпляров изображений в вашем графике scene2d? Я не думаю, что scene2d выполняет отбор актеров с помощью усеченного представления, поэтому, если они добавляются на график, они рисуются. Конечно, если они находятся за пределами экрана, как только их вершины будут спроецированы в шейдере вершин, у них не будет фрагментов для рисования с помощью шейдера фрагментов, поэтому это не должно иметь большого влияния в этом смысле. Но если это 100 различных экземпляров текстур, то будет происходить много подкачки текстур, даже если эти текстуры не видны.

5. @Tenfour04 Я думаю, что понял эту часть. Я предположил, что исходный код, который рисовал ярлыки, был отбракован. Это было неверно. Это означало, что было нарисовано много дополнительных меток. Ключ в том, что не все дополнительные метки были нарисованы из-за логики в моем коде, которая сделала некоторые из них невидимыми. Однако, когда я добавил тест текстуры, я не применил ту же логику видимости. Таким образом, все актеры рисовали текстуру. Таким образом, в то время как код метки рисовал некоторое подмножество меток (все еще слишком много), код текстуры рисовал изображение для всех 200 актеров. Так что производительность была на самом деле хуже.

Ответ №1:

Я не тестировал это, но я думаю, что вы можете включить отбраковку для всей сцены, установив ее корневой вид, чтобы cullingArea он соответствовал ширине и высоте видового экрана. Я бы сделал это resize после обновления окна просмотра сцены на случай, если обновление повлияет на мировую ширину и высоту окна просмотра.

 @Override public void resize(int width, int height) {  //...  stage.getViewport().update(width, height, true);  stage.getRoot().setCullingArea(  new Rectangle(0f, 0f, stage.getViewport().getWorldWidth(), stage.getViewport().getWorldHeight())  ); }  

Он сможет отбирать только тех актеров, у которых правильно заданы x, y, ширина и высота. Это правда, я думаю о чем угодно в пакете пользовательского интерфейса scene2d, но для ваших собственных пользовательских актеров вам нужно будет сделать это самостоятельно.

Если дочерний элемент вашего корневого представления представляет собой группу, охватывающую не только экран и множество участников, вы также можете захотеть отбраковать ее дочерних элементов, чтобы, даже если группа в целом не отбракована корневым представлением, группа все равно могла отбраковать часть своих собственных дочерних элементов. Чтобы определить прямоугольник отбора для этого, я думаю, что вы пересечете прямоугольник размера группы с прямоугольником мирового размера окна просмотра, смещенным на-x и-y позиции группы (поскольку прямоугольник отбора относительно позиции группы, на которую он установлен).

Комментарии:

1. Это не учитывает тот факт, что область отбора изменяется при перемещении камеры. X,y прямоугольника должны измениться. Таким образом, это выглядит примерно так: mainStage.getRoot().setCullingArea( новый прямоугольник(camera.position.x-mainStage.getViewport().getWorldWidth()/2, camera.position.y-mainStage.getViewport().getWorldHeight()/2, mainStage.getViewport().getWorldWidth (), mainStage.getViewport().getWorldHeight()) ); Это не учитывает масштаб, поэтому мне придется поработать над этим, но все идет в правильном направлении. Спасибо!

2. Ах да, я забыл упомянуть об этом. Я использую scene2d исключительно для пользовательского интерфейса, поэтому я не перемещаю его камеру.