SOLVED drawing to surfaces

zendraw

Member
can i set any number of surfaces to draw to before i reset? or do i have to constantly reset after each set?
GML:
surface_set_target(surf1);



surface_set_target(surf2);



surface_set_target(surf3);



surface_reset_target();
 

zendraw

Member
will it work tho? i have a 512x256 loop and in every iteration it sets and resets betwean 43 surfaces and in some of them there are things that shuldnt be there. shuld i make it perform a single iteration per frame or set a single surface per frame?
 

Sybok

Member
To save taking time out of your day, I have copied the appropriate part of the manual which @Nocturne kindly linked also.

One thing that should be noted is that surfaces are stacked so you cannot jump from target to target and then reset to the normal draw target at the end, but rather you must open and close rendering targets. For example, this will not work correctly:

surface_set_target(surf1);
draw_text(32, 32, "surface1");
surface_set_target(surf2);
draw_text(32, 64, "surface2");
surface_reset_target();

Instead, you must reset the target for each of the surfaces that you set
In short, no it won’t work.
 

zendraw

Member
no you didnt understand me. im alredy doing it right, but like i sayd its a 512x256 loop with a switch loop in it that decides on which surface to draw. and the resault is that some images turn out right, some dont, in the sense that there are extra dots on them, i draw dots on them.
 

Bart

WiseBart
You can call surface_set_target multiple times and then call surface_reset_target only once after that. Nothing prevents you from doing so.
But in the end, you'll end up drawing to the wrong render target/surface.

In practice you should always pair a call to surface_set_target with a call to surface_reset_target and put some code to draw stuff in between:
GML:
// Current render target is "< some > surface"

// Draw some stuff on "<some> surface" here...

surface_set_target(surf1); // Current render target is surf1 from here on, until we call surface_reset_target
// In that case the render target returns to the surface previously set as the render target, in this case "< some > surface"

// Draw some stuff on surf1 here...

surface_reset_target(); // From here on, use "< some > surface" again for drawing

// Draw stuff on "<some> surface" here...

surface_set_target(surf2); // From here on, draw to surf2 until we call surface_reset_target

// Draw some stuff on surf2

surface_reset_target();

// We're back where we were, namely on "<some> surface"
You can find more info on how this works in this tech blog article on using surfaces.

If it still doesn't work the way you want it, it may be interesting to post the full code that you're using.
 
Last edited:

zendraw

Member
it doesnt work doing a couple of surface_set_target and then a single surface_reset_target. it gives an error.
 

TsukaYuriko

☄️
Forum Staff
Moderator
You have to reset exactly as many times as you set. When these resets happen doesn't matter, as long as it's within the same frame. You can set multiple times and then reset exactly that many times, or reset a few times in between, as long as the amount of sets and resets is the same. It's like parentheses - you can put as many as you want, but you have to close all of them.
 

zendraw

Member
the second part of my problem is that it draws extra stuff that it shuldnt draw.
there are 43 surface create and surface free functions before the code, and 43 surface save and surface free functions after. also the switch statements have more cases that i removed otherwise the code wuld be very long.
GML:
    var i=0;
    var j=0;
    var s=0;
    var ac=0;
    var sg=0;
    
    repeat (global.stageh)
    {
        i=0;
        repeat (global.stagew)
        {
            ac=actor[i, j];
            sg=stage[i, j];
            if (ac)
            {
                #region //set actor surface
                
                switch (ac)
                {
                    default: surface_set_target(surf); break;
                }
                
                #endregion
                
                draw_sprite_ext(spix, 0, i, j, 1, 1, 0, global.color[ac], 1);
                
                surface_reset_target();
            } else
            {
                #region //set object surface
                
                switch (sg)
                {
                    default: surface_set_target(surf); break;
                }
                
                #endregion
                
                draw_sprite_ext(spix, 0, i, j, 1, 1, 0, global.color[sg], 1);
                
                surface_reset_target();
            }
            
            i++;
        }
        j++;
    }
in the end this is the resault
sstagedemonstatue.png

it shuld have only the purple dot, the yellow ones shuldnt exist.
 

Homunculus

Member
I don't get it. Why are you using switch if you only have one option? Moreover, the surface you are using is always the same, why do you even need to set it every cycle if it doesn't change?

Edit: Sorry didn't see you removed parts of the code. Assuming the surface that gets set is in fact different depending on the values of sg and ac, you have the right surface_set / surface_reset count. It may be a problem related to your conditions rather than surfaces themselves. That if(ac) is really suspect, especially since inside you use the same value in the switch statement, indicating that it is not a boolean value. What kind of value does ac hold?
 
Last edited:

zendraw

Member
as i sayd in that comment, i deleted the 43, in total, cases in the switch statements to reduce code length for the post. they are basicly case splayer:
surface_set_target(surfplayer); break;
there are 43 surfaces that are created before the loop and then saved and freed after the loops.
 

Sabnock

Member
surface_set_target() sets the target surface to draw to. So multiple surface_set_target() calls will result in the only last one set being the surface drawn to. so you have to set a target, draw to it and then reset as i understand it.
 
Last edited:

Sabnock

Member
However, the manual says this


Description

With this function you set all further drawing to the target surface rather than the screen and in this way you can tell GameMaker Studio 2 to only draw specific things to the specified surface. Please note that if you do not call surface_reset_target after you have finished, nothing will be drawn on the screen as all further drawing (even in other instances) will be done on the surface. You should also realise that nothing will be seen if the surface itself is not drawn on the screen in the draw event of an instance. You can check the return value of this function too as a debug tool to check whether the surface target was set or not, with a return value of 0 being a failure to set the target and any other positive value being a success.
One thing that should be noted is that surfaces are stacked so you cannot jump from target to target and then reset to the normal draw target at the end, but rather you must open and close rendering targets. For example, this will not work correctly:
surface_set_target(surf1);
draw_text(32, 32, "surface1");
surface_set_target(surf2);
draw_text(32, 64, "surface2");
surface_reset_target();
Instead, you must reset the target for each of the surfaces that you set, much like you must use opening and closing brackets {} for code blocks. So the above should be written as either this:
surface_set_target(surf1);
draw_text(32, 32, "surface1");
surface_reset_target();
surface_set_target(surf2);
draw_text(32, 64, "surface2");
surface_reset_target();
or as this:
surface_set_target(surf1);
draw_text(32, 32, "surface1");
surface_set_target(surf2);
draw_text(32, 64, "surface2");
surface_reset_target();
surface_reset_target();
NOTE: When working with surfaces there is the possibility that they can cease to exist at any time due to them being stored in texture memory. You should ALWAYS check that a surface exists using surface_exists before referencing them directly. For further information see Surfaces.
so it looks like you can nest targets. Hopefully a less vague answer for you :D
 
Last edited:

zendraw

Member
ac simply holds a sprite index. sg aswell. they are ac=actor like player and enemies and sg=stage like walls doors etc. i will look more in the code.
 

Homunculus

Member
if(ac) then doesn't really look right to me. What's the value if ac is not an actor sprite? Undefined? -1? If that's the case, it should be something like if(!is_undefined(ac)) or if(ac >= 0) . Remember that 0 may be (and generally is) a valid sprite index as well.

You also don't show where spix comes from, and that's relevant since I guess either the first or the second one is supposed to draw the pixels that are not yellow (as you said)
 

zendraw

Member
if (ac) simply means if ac is a positive number, thus above 0, which is the case with the sprite`s order. if ac is negative, thus 0, we draw sg, and with sg it doesnt matter if its 0 or positive. and yes 0 is the first sprite index in the sprite resourses. spix is a sprite, a single pixel sprite. and we draw that sprite with a preset color from global.color array. dont know thou why actor in the code i posted with gml style is shown purple, its a simple 2d array like stage.

basicallly you havbe 2 2d arrays which hold sprite indexes and based on these indexes you set which surface to draw on, the color is automatically set based again on the sprite indexes. the sprite indexes go from 0-N. cant explain it in a more simple way whats going on. and i dont see an obvious way how it wuld drawthings with a diffrent color, mind you, and from diffrent checks unless it cant handle surface set and reset that fast. you basicallly have 512x256=131072 surface set and surface resets in a single frame. maybe thats a problem?
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
When you create the surfaces, do you clear them first (using draw_clear_alpha())? If not, and you don't draw to them, then they will contain pixel data in the form of whatever was previously in the vram they currently occupy. This could give you those yellow pixels...
 

Homunculus

Member
You shouldn't rely on the sprite order though for this kind of stuff, especially when you use 0, which is valid as a sprite, to mark a place in the array as empty. It's really bad if you ask me, but it's your choice. I'd double check though that the condition is running as expected, just throw a couple of show_debug_messages in there to identify when you have an ac and when you have an sg. Am I wrong assuming that you draw yellow in one case, and purple in another?
 

zendraw

Member
no i dont clear them, shuld i write 1 clearing function after all have been created or after each surface creation? and im almost sure that wuld be the case, in the save folder surfaces that are saved but have nothing drawn on them are pure black.

the code im pretty sure is fine, its quite simple code. i might put tthem in an array in the future but its not a big game so who knows.
 

Homunculus

Member
Each surface had to be cleared independently. I get it's quite simple code for you, but we have to make a lot of assumptions from what we see. So, first of all, what determines if a point is yellow or purple? Which seems to be your main problem so far.
 

zendraw

Member
im confident the clearing thing wuld fix the issue, ill try it later today. as i sayd, theglobal.colour array, which is set based on the sprites aswell. so
global.color[s_player]=c_blue; so when were drawing the player sprite we set the player color aswell.
 

Homunculus

Member
you make so many assumptions with this code I can’t even start to comprehend them. ac is a sprite, it is used as is in an if statement assuming that sprite index 0 is never in the array, you are then not drawing ac at all but another sprite, using the sprite index as an array index to get a color you apply as a blend mode. To draw a colored pixel.

I sincerely hope the clearing fixes your problem, but I’m sorry, I give up on this...
 
Last edited:

zendraw

Member
i dont understand what the difficulty is for you? the sprites in the resourse tree are like a prebuild array ready for you to use. and when ac is 0, thus there is not an actor at that position, draw whatever element the stage has at that position. this is the setup there is no assumption, this is not a programming sesion, its practice.
 

Sabnock

Member
OP was

"can i set any number of surfaces to draw to before i reset? or do i have to constantly reset after each set?"

Did this get answered? 🤔😌
 

Hyomoto

Member
The answer is that surface calls are stacked. So you must call surface reset for each surface set. You do not have to do them in order, but you must do them.

GML:
surface_set_target();
    // draw
    surface_set_target();
        //draw
    surface_reset_target();
    // draw
surface_reset_target()

// or

surface_set_target();
// draw
surface_reset_target();
surface_set_target();
//draw
surface_reset_target();
All the manual is saying is that `reset` will `pop` the last surface set off the stack, and that you must pop all surfaces set.
 

Homunculus

Member
i dont understand what the difficulty is for you? the sprites in the resourse tree are like a prebuild array ready for you to use. and when ac is 0, thus there is not an actor at that position, draw whatever element the stage has at that position. this is the setup there is no assumption, this is not a programming sesion, its practice.
Relying on the order of the resources in the resource tree, and their numbering as well, is simply a bad practice in my opinion. It's not set in stone to begin with, you may very well find out that a new version or a specific export does not number or order things that way. Or, you may simply forget about this when creating a new sprite and wonder why your project no longer works for hours.

Then you have an array of sprite indexes (which are simply numbers) where the positions you consider "empty" are simply marked as 0, possibly another sprite index, which is also used as an implied boolean. That same sprite index is subsequently used as an array index. Again, not bad per se, but another thing that makes your code even more cryptic and difficult to read and debug (for me, but for you as well in the long run).

But the real problem is that you explain none of the above, and instead post an image with a few yellow pixels telling us that they should be purple. Not mentioning however why or where in the above routine one color or the other should be set.

This is why I give up, since you asked. And I'm telling you this not because I want to rant or bother you, but because, to me at least, 1. you are not explaining your code / problem to us efficiently at all, and 2. because from my little experience, when I see a lot of things I consider bad practices being used all in one place, that could end up making debugging more difficult than it actually should be, I have to point that out.

There, I said it, you are free to ignore everything I said though, I don't hold any absolute truth, but I still wanted to get it out, because this is something I see in many other topics of yours.
 
Last edited:

zendraw

Member
i can see your frustration, but if its becouse of little experience then maybe get more experience and get more comfortable around code, i dont have problem you judging my code, but you wont learn this way anyway. you wont find by the book practices anywhere.
 
Top