3 Replies Latest reply on Jan 30, 2013 10:01 AM by Outplay (HH)

    Is texture base memory reported incorrectly?

    Outplay (HH)

      Hi,

       

      I've been using Scout to profile a large application and noticed that the GPU texture memory being used is 1.5 times the amount of what it should be! So my question is, is Scout reported the actual texture memory used or is it trying to approxomiate waht the texture memory is by including a guess for mip maps?

       

      I have written an example program to demonstrate what I mean below. In the example program I create a texture that is 64 by 64 pixels which has no mip maps. Therefore the size of this texture in memory should be 64 * 64 * 4 = 16384 bytes (16 KB). However Scout reports that the memory consumed by the texture is 24 KB which is exactly 1.5 times what it should be. I have tested this with many different texture sizes and this is always the case.

       

      Therefore what I want to know is this Scout reported texture memory usage wrong or is the Flash runtime creating this extra memory? This is of great importance to me because I am creating large 2048 by 2048 textures which are using 24 MB instead of 16 MB which is a huge difference.

       

      I am using Adobe Air 3.5.

       

      Here is the simple code which should be able to compiled without dependacies for reference:

       

      package

      {

                import flash.display.BitmapData;

                import flash.display.Sprite;

                import flash.display.Stage3D;

                import flash.display.StageAlign;

                import flash.display.StageScaleMode;

                import flash.display3D.Context3D;

                import flash.display3D.Context3DTextureFormat;

                import flash.display3D.textures.Texture;

                import flash.events.Event;

       

                [SWF(width="640", height="480", frameRate="60", backgroundColor="#000000")]

                public class TextureMemoryBug extends Sprite

                {

                          // the 3d graphics window on the stage

                          private var context3D:Context3D;

       

                          // The Stage3d Texture that uses the above myTextureData

                          private var _texture:Texture;

       

                          public function TextureMemoryBug()

                          {

                                    stage.frameRate = 60;

                                    stage.scaleMode = StageScaleMode.NO_SCALE;

                                    stage.align = StageAlign.TOP_LEFT;

       

                                    addEventListener(Event.ADDED_TO_STAGE, init);

                          }

       

                          private function init(event:Event):void

                          {

                                    removeEventListener(Event.ADDED_TO_STAGE, init);

       

                                    // and request a context3D from Stage3d

                                    stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);

                                    stage.stage3Ds[0].requestContext3D();

                          }

       

                          private function onContext3DCreate(event:Event):void

                          {

                                    if (hasEventListener(Event.ENTER_FRAME))

                                    {

                                              removeEventListener(Event.ENTER_FRAME, enterFrame);

                                    }

       

                                    var stage3D:Stage3D = event.target as Stage3D;

                                    context3D = stage3D.context3D;

       

                                    if (context3D == null)

                                    {

                                              return;

                                    }

       

                                    context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0, false);

       

                                    // Generate test texture bitmap data

                                    var bitmapData:BitmapData = new BitmapData(64, 64, false, 0);

       

                                    // Create texture

                                    _texture = context3D. createTexture(64, 64, Context3DTextureFormat.BGRA, false, 0);

                                    _texture.uploadFromBitmapData(bitmapData, 0);

       

                                    bitmapData.dispose();

       

                                    addEventListener(Event.ENTER_FRAME, enterFrame, false, 0, true);

                          }

       

                          private function enterFrame(e:Event):void

                          {

                                    context3D.clear(1, 1, 1);

       

                                    context3D.present();

                          }

                }

      }

        • 1. Re: Is texture base memory reported incorrectly?
          Michael J.A. Smith Adobe Employee

          Stage3D always allocates memory internally for mipmaps, regardless of whether you upload them to the texture. Although I'm not sure why you're seeing 1.5x, rather than 1.33x.

           

          There will soon be support in Stage3D for rectangle textures, which don't have mipmaps. However, if memory is an issue, a better solution would be to use compressed textures - if you set the texture format to Context3DTextureFormat.COMPRESSED or Context3DTextureFormat.COMPRESSED_ALPHA, then it'll get compressed at runtime, and upload the compressed texture to the GPU. So you can get the advantage of compression even for dynamically generated textures.

           

          btw, if you do this, there's a bug in Scout 1.0 - if you try to replay the runtime compression in the Stage3D Rendering panel, it'll crash (the other panels in Scout will work fine though). This will be fixed in the next release of Scout - sorry for the inconvenience.

          • 2. Re: Is texture base memory reported incorrectly?
            Outplay (HH) Level 1

            Thank you for your answer it is much appreciated.

             

            This is very disappointing news for me. I am making a 2D game with stage3D for the performance boost, which has a huge amount of content being shown at once and needs as much memory as possible to be able to run my game on low memory devices like the iPod 4G and iPad 1.

             

            Therefore using an extra 1.5 times the memory thats required for textures is unacceptable. Especially because I cannot use compressed textures as the visual quality is unacceptable for my needs, the compression is too noisy which is highlighted due to the fact most images are displayed at scale of 1 or above which highlights the compression artefacting more.

             

            Do you have any idea when Adobe will release a fix to not allocate memory for mipmaps for textures that do not use mimapping?

             

            Also is there a bug report for this? Or where do I go to open a bug report for this?

             

            I am quite surprised no one else has found this issue, especially all of the 2D engines like Starling.

             

            Finally as an experiment I tried the dynamic runtime compression (before I was just loading ATF textures in):

            Michael J.A. Smith wrote:

             

            if you set the texture format to Context3DTextureFormat.COMPRESSED or Context3DTextureFormat.COMPRESSED_ALPHA, then it'll get compressed at runtime, and upload the compressed texture to the GPU. So you can get the advantage of compression even for dynamically generated textures.

             

            However, I could not get this to work, the texture does not render. Is there anything I am doing wrong? Code is:

            package

            {

                      import com.adobe.utils.AGALMiniAssembler;

             

                      import flash.display.BitmapData;

                      import flash.display.Sprite;

                      import flash.display.Stage3D;

                      import flash.display.StageAlign;

                      import flash.display.StageScaleMode;

                      import flash.display3D.Context3D;

                      import flash.display3D.Context3DProgramType;

                      import flash.display3D.Context3DTextureFormat;

                      import flash.display3D.Context3DTriangleFace;

                      import flash.display3D.Context3DVertexBufferFormat;

                      import flash.display3D.IndexBuffer3D;

                      import flash.display3D.Program3D;

                      import flash.display3D.VertexBuffer3D;

                      import flash.display3D.textures.Texture;

                      import flash.events.Event;

                      import flash.geom.Matrix3D;

             

                      [SWF(width="1024", height="1024", frameRate="60", backgroundColor="#000000")]

                      public class TextureMemoryBug extends Sprite

                      {

                                // the 3d graphics window on the stage

                                private var context3D:Context3D;

             

                                // The Stage3d Texture that uses the above myTextureData

                                private var _texture:Texture;

             

                                private var _shaderProgram:Program3D;

                                private var _vertexBuffer:VertexBuffer3D;

                                private var _indexBuffer:IndexBuffer3D;

                                private var _indexData:Vector.<uint> = new <uint>[0, 1, 2, 1, 2, 3];

                                private var _vertexData:Vector.<Number> = new <Number>

                                [

                                //          x                    y                    u                    v

                                          -1,                    -1,                    0,                    0,

                                          1,                    -1,                    1,                    0,

                                          -1,                    1,                    0,                    1,

                                          1,                    1,                    1,                    1

                                ];

             

                                public function TextureMemoryBug()

                                {

                                          stage.frameRate = 60;

                                          stage.scaleMode = StageScaleMode.NO_SCALE;

                                          stage.align = StageAlign.TOP_LEFT;

             

                                          addEventListener(Event.ADDED_TO_STAGE, init);

                                }

             

                                private function init(event:Event):void

                                {

                                          removeEventListener(Event.ADDED_TO_STAGE, init);

             

                                          // and request a context3D from Stage3d

                                          stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);

                                          stage.stage3Ds[0].requestContext3D();

                                }

             

                                private function onContext3DCreate(event:Event):void

                                {

                                          if (hasEventListener(Event.ENTER_FRAME))

                                          {

                                                    removeEventListener(Event.ENTER_FRAME, enterFrame);

                                          }

             

                                          var stage3D:Stage3D = event.target as Stage3D;

                                          context3D = stage3D.context3D;

             

                                          if (context3D == null)

                                          {

                                                    return;

                                          }

             

                                          context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0, false);

             

             

                                          var bitmapData:BitmapData = new BitmapData(1024, 1024, true, 0);

             

                                          // Fill image with random noise for test purposes

                                          for(var y:int = 0; y < bitmapData.height; ++ y)

                                          {

                                                    for(var x:int = 0; x < bitmapData.width; ++ x)

                                                    {

                                                              bitmapData.setPixel32(x, y, Math.random() * uint.MAX_VALUE);

                                                    }

                                          }

             

                                          // Create texture

                                           _texture = context3D. createTexture(bitmapData.width, bitmapData.height, Context3DTextureFormat.BGRA, false, 0);                                         // This works

                                          //_texture = context3D. createTexture(bitmapData.width, bitmapData.height, Context3DTextureFormat.COMPRESSED, false, 0);                    // This doesn't work - texture does not show

                                          //_texture = context3D. createTexture(bitmapData.width, bitmapData.height, Context3DTextureFormat.COMPRESSED_ALPHA, false, 0);          // This doesn't work - texture does not show

             

                                          _texture.uploadFromBitmapData(bitmapData, 0);

             

                                          bitmapData.dispose();

             

             

                                          var vertexProgramCode:String =

                                                    "m44 op, va0, vc0 \n" +                              // 4x4 matrix transform to output clipspace

                                                    "mov v0, va1 \n";                                         // Pass texture coordinates to fragment program

             

                                          var fragmentProgramCode:String =

                                                    "tex ft0, v0, fs0 <2d, clamp, linear> \n" +          // sample texture 0

                                                    "mov oc, ft0 \n";                                                                      // Move final colour to output colour

             

                                          var vertexProgramAssembler:AGALMiniAssembler = new AGALMiniAssembler();

                                          vertexProgramAssembler.assemble(Context3DProgramType.VERTEX, vertexProgramCode);

             

                                          var fragmentProgramAssembler:AGALMiniAssembler = new AGALMiniAssembler();

                                          fragmentProgramAssembler.assemble(Context3DProgramType.FRAGMENT, fragmentProgramCode);

             

                                          _shaderProgram = context3D.createProgram();

                                          _shaderProgram.upload(vertexProgramAssembler.agalcode, fragmentProgramAssembler.agalcode);

             

                                          _indexBuffer = context3D.createIndexBuffer(_indexData.length);

                                          _indexBuffer.uploadFromVector(_indexData, 0, _indexData.length);

             

                                          _vertexBuffer = context3D.createVertexBuffer(4, 4);

                                          _vertexBuffer.uploadFromVector(_vertexData, 0, 4);

             

             

                                          addEventListener(Event.ENTER_FRAME, enterFrame, false, 0, true);

                                }

             

                                private function enterFrame(event:Event):void

                                {

                                          context3D.clear(1, 1, 1);

             

                                          context3D.setProgram(_shaderProgram);

             

                                          context3D.setCulling(Context3DTriangleFace.NONE);

             

                                          context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, new Matrix3D(), true);

             

                                          // associate the vertex data with current shader program position

                                          context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);

                                          // tex coord

                                          context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);

             

                                          // which texture should we use?

                                          context3D.setTextureAt(0, _texture);

             

                                          // finally draw the triangles

                                          context3D.drawTriangles(_indexBuffer, 0, 2);

             

                                          context3D.present();

                                }

                      }

            }

            • 3. Re: Is texture base memory reported incorrectly?
              Outplay (HH) Level 1

              I have filed a bug report to Adobe about this. The bug issue can be viewed at: https://bugbase.adobe.com/index.cfm?event=bug&id=3493152

               

              However if the other questions could be answered that would be great