Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.84k stars 819 forks source link

Problems with draw calls #918

Closed Creta5164 closed 7 years ago

Creta5164 commented 7 years ago

Hi, I am using starling 2.1.

When I created a new class that inherits Sprite, and when I declare a Quad type of variable, I found the Draw call number incremented by class objects.

Does this work normally? If it works normally, I want to know the principle of working.

Should I use Image (with TextureAtlas) instead of Quad?

(translated by Naver NMT translator : http://labspace.naver.com/nmt/)

저는 Sprite를 상속하는 새 Class를 만들고, 그 안에서 Quad 타입의 변수를 선언할 때, Class 객체를 선언할 때마다 Draw call이 1씩 증가하는 것을 발견했습니다.

이것은 정상적으로 작동하는 것인가요? 정상적으로 작동하는 것이라면, 저는 작동하는 원리를 알고 싶습니다.

저는 Quad 대신에 Image (TextureAtlas와 함께)를 사용해야 하나요?

PrimaryFeather commented 7 years ago

Could you maybe post some sample code that creates those higher number of draw calls than expected? I need to know what exactly you are doing, then it will become clear to me where those draw calls are coming from. Hopefully, that is! :wink: Thanks in advance!

Creta5164 commented 7 years ago

case of only 1 draw calls Main.as class

private var q:Quad;
private var q1:Quad;
private var q2:Quad;

...

private function init():void
{
    q = new Quad(1, 1);
    addChild(q);
    q1 = new Quad(1, 1);
    addChild(q1);
    q2 = new Quad(1, 1);
    addChild(q2);
}

it's will be make results of 1 draw call.

however, if I make new Classes with overriding Sprite, and...

newClass.as with override Sprite

private var q:Quad;
...

private function init():void
{
    q = new Quad(1, 1);
    this.addChild(q);
}

and now create it

Main.as Class

private var q:newClass;
private var q1:newClass;
private var q2:newClass;

...

private function init():void
{
    q = new newClass();
    addChild(q);
    q1 = new newClass();
    addChild(q1);
    q2 = new newClass();
    addChild(q2);
}

then, it's make 3 draw calls (so sad)

Creta5164 commented 7 years ago

ah, sorry daniel. I need have more test and tell you.

somthing strange, i have confuse :|

Creta5164 commented 7 years ago

It seems to be happening only under certain conditions. I think I could have made some elementary mistakes :{

if i was doing to test project was not happened it :| But in a project that I really wanted to make, the problem arose.

Main.as (new Starling(Main, stage))

    public class Main extends Sprite
    {
        ///에셋 매니저입니다.
        private var assetLoader:AssetManager;
        private static var _assets:AssetManager;
        private static var _UITexture:TextureAtlas;
        private static var _QuadTex;Texture;        //@@NOTE : this statics have get method.

        private static var topMenu:TopMenu;                 //상단 메뉴

        private var LOADING:LoadingScreen;                  //로딩화면

        public var musicSource:Sound;                       //음악
        public var musicChannel:SoundChannel;               //음악 채널
        private var musicSourceRequest:URLRequest;          //음악 로더

        public var noteMapData:MapData;                     //맵 데이터

        public var root_AsyncedProject:File;                //동기화 된 프로젝트 폴더

        public function Main():void
        {
            assetLoader = new AssetManager();

            assetLoader.enqueue(File.applicationDirectory.resolvePath("Assets/Font"));

            assetLoader.loadQueue(SYSTEM_LOAD);
        }

        private function SYSTEM_LOAD(value:Number):void
        {
            if (value == 1)
            {
                LOADING = new ProgramLoading();
                addChild(LOADING);

                assetLoader.enqueue(File.applicationDirectory.resolvePath("Assets/Textures"));
                assetLoader.loadQueue(assets_load);
            }
        }

        private function assets_load(value:Number):void
        {
            if (value == 1)
            {
                start(assetLoader);

                LOADING.remove();
            }
        }

        public function start(asset:AssetManager):void
        {
            _assets = asset;
            _UITexture = new TextureAtlas(_assets.getTexture("UITexture"), _assets.getXml("UITexture"));
            _QuadTex = _UITexture.getTexture("Square");//@@NOTE : this is simple square.

            createTopmenu();
        }

        private function createTopmenu():void
        {
            topMenu = new TopMenu();
            addChild(topMenu);
        }
.
.
.

TopMenu.as

    public class TopMenu extends Sprite
    {
        private var background:Image;

        private var File_menu:TopMenuList;

        private var selector:File;

        public function TopMenu():void
        {
            background = new Image(Main.QuadTex);
            background.width = 1280;
            background.height = 40;
            background.color = UIColors.main;
            background.textureSmoothing = "none";
            addChild(background);

            File_menu = new TopMenuList("파일", {
                items:[
                    {
                        name:"새로 만들기",
                        event:CreateNewFunc
                    },
                    {
                        name:"노트 저장하기",
                        event:SaveDocumentFunc
                    },
                    {
                        name:"노트 불러오기",
                        event:LoadDocumentFunc
                    }
                ]
            });
        }

        addChild(File_menu);
        //@@NOTE : this Obejct was make lists.
.
.
.
.

TopMenuList.as

        public function TopMenuList(Name:String = "폼 메뉴", subMenu:Object = null):void 
        {
            w = 94;
            h = 40;
            sw = 300;

            title = Name;
            if (subMenu != null)
            {
                list = subMenu;
            }
            else
            {
                list = {
                    items:[
                        {
                            "name":"(항목 없음)"
                        }
                    ]
                }
            }

            //서브 메뉴 효과
            subMenuCurssor = new Image(Main.QuadTex);
            subMenuCurssor.width = sw;
            subMenuCurssor.height = h;
            subMenuCurssor.color = UIColors.focus;
            subMenuCurssor.visible = false;
            subMenuCurssor.scaleY = 0;
            subMenuCurssor.textureSmoothing = "none";
            addChild(subMenuCurssor);

            //버튼 이벤트 생성
            button = new Image(Main.QuadTex);
            button.width = w;
            button.height = h;
            button.color = UIColors.main;
            button.textureSmoothing = "none";
            addChild(button);

            //텍스트 생성
            titleText = StaticText.generateText(94, 27, title, 25, UIColors.accent);
            ///@@NOTE : StaticText was make text sprite from BitmapFont.
            titleText.y = 6.5;
            titleText.touchable = false;
            addChild(titleText);

            generateMenu();
        }

        //메뉴를 생성합니다.
        private function generateMenu():void
        {
            subBackground = new Image(Main.QuadTex);
            subBackground.width = sw;
            subBackground.height = list.items.length * h;
            subBackground.color = UIColors.border;
            subBackground.textureSmoothing = "none";
            subBackground.y = h;
            //subBackground.scaleX = 0;
            subBackground.scaleY = 0;
            addChildAt(subBackground, 0);

            items = new Vector.<Sprite>();

            var i:int;
            for (i = 0; i < list.items.length; i += 1)
            {
                items[i] = StaticText.generateText(280, 27, list.items[i].name != null ? list.items[i].name : "(항목 없음)", 25, UIColors.accent);
                items[i].alignPivot("left", "top");
                items[i].x = 10;
                items[i].y = h + (i * h) + 6.5;
                items[i].visible = false;
                items[i].touchable = false;
                addChildAt(items[i], getChildIndex(subMenuCurssor) + 1);
            }
        }

StaticText.as

        public static function generateText(width:Number, height:Number, text:String = "", size:Number = 20, color:uint = 0x0):Sprite
        {
            return TextField.getBitmapFont("SpoqaHanSans_bitmap").createSprite(width, height, text, new TextFormat("", size, color));
        }

In my line of common sense, draw calls is supposed to be 2, but this makes 4. What is the problem, and how can I solve this?

Creta5164 commented 7 years ago

Ah! finally, i got it know this what happening!

I found it a problem in the sequence of code execution.

A - B - A : 3 draw calls A - A - B : 2 draw calls

i got it this solution to solved my draw calls problem.

But when there are two Texture (Bitmap Font, Atlases, etc.), does the increase in the order in which the draw increases according to the above mentioned order?

If this isn't the case, I'd like to know what the normal method is.

Thank you for answering, daniel :D

JohnBlackburne commented 7 years ago

Yes. They way it works is, to determine whether it needs another draw call, it looks at what has changed. If everything (e.g. the Texture in this case) is the same then it can batch together two or more objects into one draw call. But if you change something, such as the Texture, then it needs a new draw call.

It does this in the order you draw things. If you want to take advantage of batching you need to think about this order, perhaps rearrange things so objects sharing a texture are drawn together, such as by putting them all in a layer using a Sprite to contain them.

PrimaryFeather commented 7 years ago

Yes, John explained this correctly! Starling just draws one object after the other, in the order that's indicated by the display list (one child after another). And when, between one child and the next, the "state" changes (i.e. the texture / blend mode, etc.), a draw call will be executed.

There is some more information about how this works to be found in this document: http://wiki.starling-framework.org/manual/performance_optimization#minimize_state_changes

I hope that helps!

Creta5164 commented 7 years ago

Thank you for all your answers!

So, is it the right way to use the A and B characters in the example Bitmap Font(using createSprite) and Texture Atlas to use the sentence specified in the Texture Atlas?

I'd like to know if there's any other way.

PrimaryFeather commented 7 years ago

I'm not sure I understand what exactly you are asking! Could you maybe rephrase that question?