Texture GetData strange results on desktop and android.

Hello,

I was trying modify a texture data on runtime to make some funny outlines, color changes and just generally messing about. The texture draws empty on android but works fine on desktop.

Found this:

And i also found tihs:
That hints that getting setting data would work fine on android and ios.

This code dont work on Android:

    public Texture2D CreateModifiedTexture(Texture2D texture)
    {
        Color[] data;
        data = new Color[texture.Width * texture.Height];
        texture.GetData(data);

        for (int i = 0; i < data.Length; i++)
            data[i] *= 2;

        texture.SetData(data);
        return texture;
    }

This code works on Android:

    public Texture2D CreateModifiedTexture(Texture2D texture)
    {
        Color[] data;
        data = new Color[texture.Width * texture.Height];
        texture.GetData(data);

        for (int i = 0; i < data.Length; i++)
            data[i] *= 2;

        Texture2D result = new Texture2D(GraphicsDevice, texture.Width, texture.Height);
        result.SetData(data);
        return result;
    }

Questions
Well whats going on =)
Do i have to use some other approach on Android/IOS?
What is the best way to modify pixelinformation in runtime overall?

In short in the first example the texture passed was probably null.

I think whoever wrote this code is missing the fundamental difference between get and set data. I say this because of the way the code is structured.

Anyway i made you a example at the bottom, as for the question.
Easier to just explain it step by step… looking to the second method.

public Texture2D CreateModifiedTexture(Texture2D texture)

We pass in a texture to the method normally when you pass a texture you expect it to not be null which is probably why the first method didn’t work.

Then in the next line we make a color array but we haven’t filled it up yet with any actual colors. So the array is initialized but not the colors in it so that the array is defined but its data is only declared.

Color[] data = new Color[texture.Width * texture.Height];

Then we come to this line …

texture.GetData(data);

^^^ this would take the pixels in the texture provided its not null and put it in the data array basically filling it up with colors… right…

then he did this…

for (int i = 0; i < data.Length; i++)
     data[i] *= 2;

Which does the exact same thing but overwrites whats in the arrays data that he just filled up ? That is from whatever pixel data was in the texture, provided it was not null ?

Ok then he makes a brand new texture so i guess he didn’t need the original one after all.

Texture2D result = new Texture2D(GraphicsDevice, texture.Width, texture.Height);

He takes the data array and sets all the pixels that is in it on to the newly created texture.

result.SetData(data);

then he returns the data. … ok now that you can see everything that was done here… i hope you can see how most of it doesn’t make much sense.
In short this entire method doesn’t actually use get data at all.

.
.
Here are a few practical use methods.
The first 3 are basically all you need for setting or getting data.

You mostly only set data its kinda dumb to load dumb little one pixel dot textures or checkerboard textures that you can just generate.
Unless this is some sort of editor were you are doing image processing and need complete control as sort of the case were you wish to grey scale or monochrome a image and need to get the data modify it then reset it.

        //
        public Color[] CreateColorArrayViaGetDataFromTexture(Texture2D texture)
        {
            Color[] data = new Color[texture.Width * texture.Height];
            texture.GetData(data);
            return data;
        }
        public Texture2D CreateTextureViaSetDataFromColorArray(int width, int height, Color[] data)
        {
            Texture2D result = new Texture2D(GraphicsDevice, width, height);
            result.SetData(data);
            return result;
        }
        public Texture2D ModifyTextureViaSetDataFromColorArray(Texture2D texture, Color[] data)
        {
            texture.SetData(data);
            return texture;
        }

        // these are related to the topic i made them on the fly to show you just reuse the first 3 methods over and over.

        public Texture2D CreateGreyScaleCopy(Texture2D texture)
        {
            var data = CreateColorArrayViaGetDataFromTexture(texture);
            // gray scale the color pixels.
            for (int i = 0; i < data.Length; i++)
            {
                var c = data[i];
                int n = (c.R + c.G + c.B) / 3;
                data[i] = new Color(n, n, n, 255);
            }
            // we said we would copy it so.
            return CreateTextureViaSetDataFromColorArray(texture.Width, texture.Height, data);
        }

        public Texture2D ModifyTextureToTransparentBlack(Texture2D texture)
        {
            Color[] data = new Color[texture.Width * texture.Height];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = new Color(0, 0, 0, 0);
            }
            // we said we would modify it so.
            return ModifyTextureViaSetDataFromColorArray(texture, data);
        }

        // here is a couple i keep around and use that are helpful.

        public Texture2D TextureDotCreate( Color c)
        {
            Color[] data = new Color[1];
            data[0] = c;
            return CreateTextureViaSetDataFromColorArray(1, 1, data);
        }

        public Texture2D CreateCheckerBoard( int w, int h, Color c0, Color c1)
        {
            Color[] data = new Color[w * h];
            for (int x = 0; x < w; x++)
            {
                for (int y = 0; y < h; y++)
                {
                    int index = y * w + x;
                    Color c = c0;
                    if ((y % 2 == 0))
                    {
                        if ((x % 2 == 0))
                            c = c0;
                        else
                            c = c1;
                    }
                    else
                    {
                        if ((x % 2 == 0))
                            c = c1;
                        else
                            c = c0;
                    }
                    data[index] = c;
                }
            }
            return CreateTextureViaSetDataFromColorArray(w, h, data);
        }

I wrote it and yes im fundamentaly confused in life =)

Nopp im sure its not null and that GetData “gets data” i printed it.

Well creating a new texture and using SetData on it and returning it will keep the original unchanged makes some sense to me! But what does not is why one work on Android and the other do not.

I don’t see a difference from your code and mine it’s just more separated, beautiful and readable.

But basically

  1. Init a Array
  2. Fill the Array from a texture with GetData
  3. Do something funny with the data
  4. We store the funny data in the same texture with SetData

This do not work on Android, tested hardware and the simulator the texture is blank. If you run it on desktop it works fine.

Let’s assume im doing something wrong here and if you just answer: should this work regardless of device im trying to run this on?

This code dont work on Android:

Nopp im sure its not null and that GetData “gets data” i printed it

    texture.GetData(data);
    for (int i = 0; i < data.Length; i++)
        data[i] *= 2; 
    // This is a array of colors with minimum and maximum values.
    // What do you expect to see when multiplying a color(255,255,255 ...) by 2 ?

But what does not is why one work on Android and the other do not.

guess.
Probably because the reference memory is pinned and android probably has higher security policys for reference changes to such memory in the scope of a method or application ? Which is why its ok with a new reference instead being passed back out.

I don’t see a difference from your code and mine.

Not much considering i just broke my own rules in writing out two of those methods on the fly for using get set data which my rule is basically don’t use getdata unless i have a really really good reason to do it.

CreateModified

It’s one or the other you either modify a texture or create a new one let me explain why this is important.

What happens when you send the same texture in twice to your second method while reassigning it back to the same texture reference ? When it needs to be disposed ?

mytexture = _CreateModified_Texture(mytexture);

public Texture2D _CreateModified_Texture(Texture2D texture)
Texture2D result = new Texture2D(
return result;
}

Pass the result back into the method again and reassign the new texture to the same reference now how do you dispose the old memory which you just dereferenced and is pinned in memory. How will the garbage collector do it ?. What does the gpu driver do when your application closes but it was never notified that it doesn’t need to keep the texture around for you anymore ?

Which is why the first thing most people ask is why do you need getdata ?
Which is probably why android doesn’t like it. Why shawn hargraves hated it and wanted to remove it in xna.
The only time i have ever used it is for specific image editing or quick operations for clearing alpha off a bunch of images in a folder and such.

I apologize if i ruffled your feathers i was pretty tired when i answered just trying to convey you have to be careful with get and set data even if it works so you should have a need to use it. Which is part of why im also pointing out that you’re multiplying colors by 2.

should this work regardless of device im trying to run this on?

Texture2D.GetData is not supported in GL ES 2. This came up before in a
GitHub issue. What are you using it for specifically? There’s often a
better way to do things that does not rely on pulling texture data from
the GPU (which is typically very slow).

Yes! but isnt the forbidden that mutch more alluring :slight_smile: My plan longterm was to build some prossessing step in the my pipline but I don’t like debugging the piplineprojects. I’m alsow asuming that messing with texture2d during buildtime is ok?. Alsow setdata seams more ok than getdata is that the correct assumption?

As I just wanted to outline a bunch of sprites in a Sprite sheet and get, set data seams to do the trick when it didn’t on android and iOS I started to wonder if I was using the wrong runtime method.

If I got my pants in a twist everytime someone told me I was doing or going to do something stupid I wouldn’t walk straight.

But you should take care of that sleeping problem. I use alcohol but I hear a a bed with a pillow works as well. :slight_smile:

Yes why not take som exampels maybe the thread should be more general but annyways.

Ofcorse if we can do it in buildtime we can do it before starting the game so we can assume that will always be the best option or I’m I using my feet in the wrong way again?

Exampels:
Outlining sprites? Animated, flashing, glories outlines.
Changing eye or clothing color of a sprite let’s say some character creation thing. Where you can upload your logo and get it on a tshirt.
Dynamically added blood or effects when something interacts.