• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Drawing the application_surface to normal draw event

Kyon

Member
Hi everyone, why do I see nothing when I draw the application_surface to a normal draw event?
It works when using the GUI draw event, but can someone explain to me why it doesnt work in normal draw event? (even when the x and y are fixed to the camera)
 

obscene

Member
Copy it then draw the copy. It's just like that. Basically your trying to draw it on itself and it refuses.
 

Kyon

Member
Copy it then draw the copy. It's just like that. Basically your trying to draw it on itself and it refuses.
Yeah that's true.
What happened is that I thought of a cool application_surface shader, but I got a lot of stuff drawn that I want to be above this effect.
 

RangerX

Member
GMS automatically draws and sort of "lock" the application surface. You can't fool around the draw pipeline with it unless you deactivate that automatic drawing.
If ever you want to be totally free with the application surface and do the hell you want with it and draw it as your see fit, you can use this function:
application_surface_draw_enable()

This function is meant to "unlock" / remove all that is automatic about the application surface (drawing, resizing, etc)
 

Kyon

Member
GMS automatically draws and sort of "lock" the application surface. You can't fool around the draw pipeline with it unless you deactivate that automatic drawing.
If ever you want to be totally free with the application surface and do the hell you want with it and draw it as your see fit, you can use this function:
application_surface_draw_enable()

This function is meant to "unlock" / remove all that is automatic about the application surface (drawing, resizing, etc)
Ah yes that's done already.
Anyway, the application_surface. That doesn't include the GUI right?
Because at the moment I'm changing everything that I've written in the draw event and changing it to draw GUI, but I noticed that when I'm playing with zooming in and out, it's making the GUI smaller in my screen and I'm not sure why.
 
A

Ampersand

Guest
If you need to mess with the application_surface what I like to do is keep that to the Post Draw event. I don't think you can write the application_surface to itself in the draw event, but I may be mistaken.

Not sure what you mean.
Isn't copying a surface to a surface the same as drawing a surface inside a surface?
I believe there are differences to how the memory is handled.

I think the issue is that you're trying to read from the application_surface while it is "open" for writing.
 

obscene

Member
Not sure what you mean.
Isn't copying a surface to a surface the same as drawing a surface inside a surface?
No, it works. I do it all the time. For instance a water reflection effect, a glass refraction effects. I want those drawn at a certain depth so objects in the foreground aren't effected, so I copy the surface, draw the copy right there in the normal draw event and then everything drawn after is unaffected.

I know, I wish someone had told me sooner too. :p
 

Kyon

Member
If you need to mess with the application_surface what I like to do is keep that to the Post Draw event. I don't think you can write the application_surface to itself in the draw event, but I may be mistaken.



I believe there are differences to how the memory is handled.

I think the issue is that you're trying to read from the application_surface while it is "open" for writing.


Just to be clear, I got application_surface_draw_enable(false); at the start of every room.
Anyway, the problem is, I'm trying to draw my surface where my application surface is on (I'm using multiple shaders so I got multiple surfaces).
But it only draws when I'm drawing it on the draw GUI, how can I draw the end result on the normal draw event? (or post draw of whatever, it all doesn't work)
 
A

Ampersand

Guest
Just to be clear, I got application_surface_draw_enable(false); at the start of every room.
Anyway, the problem is, I'm trying to draw my surface where my application surface is on (I'm using multiple shaders so I got multiple surfaces).
But it only draws when I'm drawing it on the draw GUI, how can I draw the end result on the normal draw event? (or post draw of whatever, it all doesn't work)
Yeah, I had plenty of issues with exactly this even when disabling the application_surface drawing. This is why I recommended doing it in Post Draw -- it won't affect your GUI layer and won't give you trouble.
 

RangerX

Member
Post drawn is MEANT to be an event where you apply shaders and stuff to the application surface and then you draw it on screen.
And to answer a previous question of yours Kyon, the GUI layer and the application_surface are basically 2 different layers with both their draw events and GUI layer is always on top. They are also independant from each other in matter of resizing.
 

Kyon

Member
Yeah, I had plenty of issues with exactly this even when disabling the application_surface drawing. This is why I recommended doing it in Post Draw -- it won't affect your GUI layer and won't give you trouble.
Post drawn is MEANT to be an event where you apply shaders and stuff to the application surface and then you draw it on screen.
And to answer a previous question of yours Kyon, the GUI layer and the application_surface are basically 2 different layers with both their draw events and GUI layer is always on top. They are also independant from each other in matter of resizing.
aaaah but that's the problem, Post-Draw isn't working for me, when I'm drawing my application_surface-surface there it doesn't show anything. :(
What am I missing haha
 

RangerX

Member
Sorry for the silly question but do you draw anything on it in the normal draw events?
Also, how do you draw it after? You might draw it outside the screen or something
Another tidbits, there's a known bug on certain Win10 builds... do you have V-sync On? If not, turn it on and you might see the application surface when you draw it.
 

Kyon

Member
Ok I'm lost, and it's killing me that I don't get this haha.
I never really worked with other draw events, so everything in my game is in the normal draw event.
What I want to achieve is a bloom effect. I got this idea for a bloom effect and I want it over my application surface, but I want some stuff to be drawn outside of it. (like messageboxes above people's heads or anything)
Luckily in my current project I'm almost doing/drawing everything out of one object. oControl.
What would be the best way to approach this? (not the shader, but to separate some drawings from the application surface, hopefully without using draw GUI)


Thanks in advance :)

Sorry for the silly question but do you draw anything on it in the normal draw events?
Also, how do you draw it after? You might draw it outside the screen or something
Another tidbits, there's a known bug on certain Win10 builds... do you have V-sync On? If not, turn it on and you might see the application surface when you draw it.
Also even without vSync on, the post-draw event suddenly worked. But it works exactly like Draw GUI. So a lot of my code won't work because I use x/y's that match the camera and stuff.
 
Last edited by a moderator:
A

Ampersand

Guest
aaaah but that's the problem, Post-Draw isn't working for me, when I'm drawing my application_surface-surface there it doesn't show anything. :(
What am I missing haha
Also make sure you're grabbing the window offsets. Lemme grab some code from a project real quick...

Post Draw
Code:
//Prep Surface Draw
draw_enable_alphablend(0) ;
var offset = application_get_position() ;
//Set Shader
if(shader_on) {
    shader_set(sha_example);
    //set shader uniforms
}
//Draw Screen
draw_surface(application_surface, offset[0], offset[1]) ;
//Reset alpha blend
draw_enable_alphablend(1) ;
//Reset Shader
if(shader_on) {
    shader_reset() ;
}
You have to remember that Post Draw draws things at raw display coordinates, not at position/view/window coordinates.

I had to change and cut it a bit from the project I pulled it from... Please let me know if this does not plug and play.
 
Last edited by a moderator:

Hyomoto

Member
If you disable the application surface drawing automatically you need to understand what each of the draw events are actually doing. That section of the manual can be a little confusing, so let's take a look:

The pre-draw event deals with the 'back buffer', or the location in memory in which what you actually see drawn on the screen is stored. Basically, your window is blank until you draw something to the back buffer. And what you draw there stays until you write over it. By default this is when the application surface is cleared. Interestingly, if we were to use:
Code:
surface_set_target( application_surface );
We won't see anything drawn to the screen. It's not listed here, but the surface stack is also reset before the normal draw phase begins. We would have to put that into the draw_begin event if we wanted to mimic how the program normally operates. The draw events happen as expected, but something to note is that these events happen for each layer. Draw begin is run for each object on each layer, then draw step, then draw end. This can cause some sorting anomalies if you aren't paying attention to it but should generally have the expected results. For example, if you had two layers "A" and "B", and you had an object on "A" that has a draw end event, it will draw over objects on layer "B" despite layer B being 'higher'. But, for the most part it should behave as expected. Lastly, the post-draw event. This is the only time you can draw to the actual screen. It doesn't matter how much you've done during every other draw event, if you have application surface rendering turned off you'll never see any of it if you don't draw here. This is a good step to use 'compositing', if you have multiple layers you want to combine together, though it should be mentioned the application_surface seems to have a unique property: it always blends at an alpha of 1. It also, when drawn automatically, tries to fit to the room then scale to the window.

However, looking at this chart we can see why the application surface can be drawn during the GUI steps. At that point it's already been drawn to the back buffer, and you are using it to draw to the GUI layer. Now, personally I think the GUI layer confuses things a bit, but the idea is pretty much the same except there is no post-draw for it. The major 'feature' is simply that all coordinates are absolute. When you draw to the application surface everything is relative to it's position in the room. Since the GUI is a static resolution, this isn't the case. There's also the minor benefit that the GUI can be in a different resolution than the game but to be completely honest the GUI doesn't do anything you can't accomplish in the normal draw pipeline. I don't know if any of this helps, but hopefully I covered something you wanted to know.
 

Kyon

Member
If you disable the application surface drawing automatically you need to understand what each of the draw events are actually doing. That section of the manual can be a little confusing, so let's take a look:
The pre-draw event deals with the 'back buffer', or the location in memory in which what you actually see drawn on the screen is stored. Basically, your window is blank until you draw something to the back buffer. And what you draw there stays until you write over it. By default this is when the application surface is cleared. Interestingly, if we were to use:
Code:
surface_set_target( application_surface );
We won't see anything drawn to the screen. It's not listed here, but the surface stack is also reset before the normal draw phase begins. We would have to put that into the draw_begin event if we wanted to mimic how the program normally operates. The draw events happen as expected, but something to note is that these events happen for each layer. Draw begin is run for each object on each layer, then draw step, then draw end. This can cause some sorting anomalies if you aren't paying attention to it but should generally have the expected results. For example, if you had two layers "A" and "B", and you had an object on "A" that has a draw end event, it will draw over objects on layer "B" despite layer B being 'higher'. But, for the most part it should behave as expected. Lastly, the post-draw event. This is the only time you can draw to the actual screen. It doesn't matter how much you've done during every other draw event, if you have application surface rendering turned off you'll never see any of it if you don't draw here. This is a good step to use 'compositing', if you have multiple layers you want to combine together, though it should be mentioned the application_surface seems to have a unique property: it always blends at an alpha of 1. It also, when drawn automatically, tries to fit to the room then scale to the window.

However, looking at this chart we can see why the application surface can be drawn during the GUI steps. At that point it's already been drawn to the back buffer, and you are using it to draw to the GUI layer. Now, personally I think the GUI layer confuses things a bit, but the idea is pretty much the same except there is no post-draw for it. The major 'feature' is simply that all coordinates are absolute. When you draw to the application surface everything is relative to it's position in the room. Since the GUI is a static resolution, this isn't the case. There's also the minor benefit that the GUI can be in a different resolution than the game but to be completely honest the GUI doesn't do anything you can't accomplish in the normal draw pipeline. I don't know if any of this helps, but hopefully I covered something you wanted to know.
Thanks for that explanation!
But I'm still lost on how to draw my shader on everything on the application surface, except some stuff like on this picture, the text-balloon, and the text itself:
 

obscene

Member
Just draw that stuff afterwards. Easiest way would be to just make those objects not visible, and then after your effect do something like with (par_gui) event_perform(ev_draw,0); You might need transform_translate drawing to -view_xview -view_yview or something first if you are using a draw gui or post draw event.
 

Hyomoto

Member
@Kyon - Ah, okay, I see what you want. So, there are different ways to accomplish this but the most obvious one is 'compositing'. Think about the screen like a deck of cards. By default GameMaker draws everything to a single card, which is then displayed to the player. However, we could instead draw a lot of things to different cards, and make ourselves a stack. Then we would draw that stack, from the bottom to the top, to the screen. Personally I use this probably more than I should. However, there is a way to handle this you may not have considered. This is going to take a wild swing in complexity, so please bear with me for a moment! You need a persistent 'scene' object that will set up your rooms to handle this compositing, the important part is during the room start event:
Code:
var _layers    = layer_get_all();

for ( var _i = 0; _i < array_length_1d( _layers ); _i++ ) {
    var _layer    = _layers[ array_length_1d( _layers ) - _i - 1 ];
    var _type    = string_copy( layer_get_name( _layer ), 1, 6 );
 
    if _type == "TilesF" { //floor
        layer_script_begin( _layer, scr_render_floor );
        layer_script_end( _layer, scr_render_reset );
     
    } else if _type == "TilesS" { //shadow
        layer_script_begin( _layer, scr_render_shadow );
        layer_script_end( _layer, scr_render_reset );
     
    } else if _type == "TilesO" { //overlay
        layer_script_begin( _layer, scr_render_overlay );
        layer_script_end( _layer, scr_render_reset );
    
    } else if _type == "TilesR" { //roof
        layer_script_begin( _layer, scr_render_roof );
        layer_script_end( _layer, scr_render_reset );
     
    }
 
}
I apologize for not cleaning this up more, it's an excerpt from the render I wrote for my Final Fantasy project. Basically, each room is made up of a series of layers. Because I wasn't sure how many layers any particular scene would have, I just gave them generic names followed by a number. What this does is attach a script to each layer based on it's naming scheme, please refer to the manual for more information about these specific commands, that allows you to run code just before the draw event is called for objects on that layer. The scripts themselves are quite simple:
Code:
///@func scr_render_overlay();
if event_type == ev_draw {
    surface_set_target( world.surfaces[ world.surfaceOverlay ] );
 
}
Code:
///@func scr_render_reset();
if event_type == ev_draw {
    surface_reset_target();
    shader_reset();
     
}
The short version is that each layer is given a surface to draw to. The floor layers draw to the floor surface, the shadow layers draw to the shadow surface. Then when it comes time to finally draw everything to the screen, I have an object that is higher than anything else, thus is drawn last, that performs this:
Code:
if update {
    surface_set_target( surface );
        for ( var _i = 0; _i < array_length_1d( surfaces ); _i++ ) {
            if surfaceHide[ _i ] { continue }
         
            if _i == surfaceRoof && flags & world_flags.inside { continue } // skip roof if should be hidden
         
            if surfaceType[ _i ] == 0 {
                if colorFlash == undefined { pal_swap( palSource, pal( palIndex ) ) } // set palette swap if color transition is happening, otherwise use default coloring
                else { pal_swap( colorSource, colorFlash ) }
             
                if flags & world_flags.wrap {
                    draw_surface_tiled( surfaces[ _i ], camera.x, camera.y ); // if this surface should wrap, tile it
             
                } else {
                    draw_surface( surfaces[ _i ], camera.x, camera.y ); // otherwise draw the surface
             
                }
                shader_reset(); // reset shader
     
            } else if surfaces[ _i ] != undefined {
                with ( surfaces[ _i ] ) { event_perform( ev_draw, 0 ) } // draws instance layers
         
            }
     
        }
    surface_reset_target();

}
draw_surface( surface, 0, 0 );
This is a LOT to unpack but you have asked a complex question so buckle in! I've left this code mostly unmodified so I can show you how I solved some parts of the question you have. As I say above, I've broken down my room into a series of layers that are all drawn to a number of surfaces. I have a floor layer, a shadow layer, an overlay layer and a roof layer. When it comes time to draw everything to the screen there are two layers that have been left out: the UI and the INSTANCE layers. The UI has all of what you'd expect, dialogue boxes, menus, cursors, etc... and the INSTANCE layer is all the characters on screen. If we go back to my card analogy above the game is stacked like this:
Code:
COMPOSITING
UI
Roof
Overlay
INSTANCE
Shadow
Floor
The block of code above runs through that stack and draws each one of those layers to a final 'composited' image, that is ready to be used during the post-draw step. This allows each layer to be run through their own shader and effects to ensure the final image looks just like what I need it to. Once it is sent to the back buffer I can now apply any 'full screen' post-processing, such as a CRT shader. The point being, and hopefully what resonates with your question, is that I only apply effects to the layers that need them. The UI is not colored by the world, nor are the characters. And everything is put together into a final image at the end. There is one more thing that I will talk about very briefly here at the end, and that is I also only draw my layers once for those that don't change. I noticed that there was a lot of pointless redrawing of things on the screen that aren't changing, so to make it so maps of any size could be drawn more quickly, the layers themselves are only drawn once, then hidden, and are only redrawn if something changes or the surface is lost from VRAM. While this is beyond your question's scope, it should show that the GM draw pipeline has a lot of potential once you really dig into it and take control of how things are drawn to the screen.

Anyways, that is a LOT of information and I wouldn't be surprised if you have questions so feel free to ask them and I'll do my best to help out.
 
Last edited:

Kyon

Member
@Kyon - Ah, okay, I see what you want. So, there are two ways to accomplish this but the most obvious one is 'compositing'. Think about the screen like a deck of cards. By default GameMaker draws everything to a single card, which is then displayed to the player. However, we could instead draw a lot of things to different cards, and make ourselves a stack. Then we would draw that stack, from the bottom to the top, to the screen. Personally I use this probably more than I should. However, there is a way to handle this you may not have considered. This is going to take a wild swing in complexity, so please bear with me for a moment! You need a persistent 'scene' object that will set up your rooms to handle this compositing, the important part is during the room start event:
Code:
var _layers    = layer_get_all();

for ( var _i = 0; _i < array_length_1d( _layers ); _i++ ) {
    var _layer    = _layers[ array_length_1d( _layers ) - _i - 1 ];
    var _type    = string_copy( layer_get_name( _layer ), 1, 6 );
 
    if _type == "TilesF" { //floor
        layer_script_begin( _layer, scr_render_floor );
        layer_script_end( _layer, scr_render_reset );
    
    } else if _type == "TilesS" { //shadow
        layer_script_begin( _layer, scr_render_shadow );
        layer_script_end( _layer, scr_render_reset );
    
    } else if _type == "TilesO" { //overlay
        layer_script_begin( _layer, scr_render_overlay );
        layer_script_end( _layer, scr_render_reset );
   
    } else if _type == "TilesR" { //roof
        layer_script_begin( _layer, scr_render_roof );
        layer_script_end( _layer, scr_render_reset );
    
    }
 
}
I apologize for not cleaning this up more, it's an excerpt from the render I wrote for my Final Fantasy project. Basically, each room is made up of a series of layers. Because I wasn't sure how many layers any particular scene would have, I just gave them generic names followed by a number. What this does is attach a script to each layer based on it's naming scheme, please refer to the manual for more information about these specific commands, that allows you to run code just before the draw event is called for objects on that layer. The scripts themselves are quite simple:
Code:
///@func scr_render_overlay();
if event_type == ev_draw {
    surface_set_target( world.surfaces[ world.surfaceOverlay ] );
 
}
Code:
///@func scr_render_reset();
if event_type == ev_draw {
    surface_reset_target();
    shader_reset();
    
}
The short version is that each layer is given a surface to draw to. The floor layers draw to the floor surface, the shadow layers draw to the shadow surface. Then when it comes time to finally draw everything to the screen, I have an object that is higher than anything else, thus is drawn last, that performs this:
Code:
if update {
    surface_set_target( surface );
        for ( var _i = 0; _i < array_length_1d( surfaces ); _i++ ) {
            if surfaceHide[ _i ] { continue }
        
            if _i == surfaceRoof && flags & world_flags.inside { continue } // skip roof if should be hidden
        
            if surfaceType[ _i ] == 0 {
                if colorFlash == undefined { pal_swap( palSource, pal( palIndex ) ) } // set palette swap if color transition is happening, otherwise use default coloring
                else { pal_swap( colorSource, colorFlash ) }
            
                if flags & world_flags.wrap {
                    draw_surface_tiled( surfaces[ _i ], camera.x, camera.y ); // if this surface should wrap, tile it
            
                } else {
                    draw_surface( surfaces[ _i ], camera.x, camera.y ); // otherwise draw the surface
            
                }
                shader_reset(); // reset shader
    
            } else if surfaces[ _i ] != undefined {
                with ( surfaces[ _i ] ) { event_perform( ev_draw, 0 ) } // draws instance layers
        
            }
    
        }
    surface_reset_target();

}
draw_surface( surface, 0, 0 );
This is a LOT to unpack but you have asked a complex question so buckle in! I've left this code mostly unmodified so I can show you how I solved some parts of the question you have. As I say above, I've broken down my room into a series of layers that are all drawn to a number of surfaces. I have a floor layer, a shadow layer, an overlay layer and a roof layer. When it comes time to draw everything to the screen there are two layers that have been left out: the UI and the INSTANCE layers. The UI has all of what you'd expect, dialogue boxes, menus, cursors, etc... and the INSTANCE layer is all the characters on screen. If we go back to my card analogy above the game is stacked like this:
Code:
COMPOSITING
UI
Roof
Overlay
INSTANCE
Shadow
Floor
The block of code above runs through that stack and draws each one of those layers to a final 'composited' image, that is ready to be used during the post-draw step. This allows each layer to be run through their own shader and effects to ensure the final image looks just like what I need it to. Once it is sent to the back buffer I can now apply any 'full screen' post-processing, such as a CRT shader. The point being, and hopefully what resonates with your question, is that I only apply effects to the layers that need them. The UI is not colored by the world, nor are the characters. And everything is put together into a final image at the end. There is one more thing that I will talk about very briefly here at the end, and that is I also only draw my layers once for those that don't change. I noticed that there was a lot of pointless redrawing of things on the screen that aren't changing, so to make it so maps of any size could be drawn more quickly, the layers themselves are only drawn once, then hidden, and are only redrawn if something changes or the surface is lost from VRAM. While this is beyond your question's scope, it should show that the GM draw pipeline has a lot of potential once you really dig into it and take control of how things are drawn to the screen.

Anyways, that is a LOT of information and I wouldn't be surprised if you have questions so feel free to ask them and I'll do my best to help out.
yikes.
Well first of all I want to thank you for this!! I really learned something.
I'd like to try something else first before rewriting all my draw codes. I want to transpose the x and y's so it matches the post-draw.
What I did is this:
Code:
X   ((msgX-cam_x)/(zoom))*displayratio   Y   ((msgY-cam_y)/(zoom))*displayratio
msgX/msgY being the x/y of the sprite I'm drawing.
cam_x/cam_y being the camera x and y position
zoom being my zoom variable, 1 is regular view, .5 is zoomed in.
displayratio = if fullscreen=true{display_get_width()/application_surface width;}else{1}

So this works. But one thing is happening and that is that the sprite I'm drawing is twitching/shaking.
In this gif you can see it very clear because the framerate is half the framerate in-game. In-game it's moving way faster, almost if it's drawing the sprite twice on two different positions.
Any idea what's causing this? Is it because it can't handle small decimals? Should I round/floor the code?





Also, I notice I'm having trouble with explaining this in English, sorry if it's unclear.
 

Hyomoto

Member
Well, I can take a stab at it and say the most likely reason is because your coordinates are not the same. I'd love to say yes, rounding it will fix that. If you are rounding the position of the screen elsewhere, then it should but if not it may make it worse. Instead I'll try to explain why and see if that helps you find it.

When Gamemaker draws to the screen, the pixels cannot be 'between' pixels, they have to be drawn to exact coordinates. So, the position on screen of a pixel might be 224, but the position in memory is actually 224.059. It comes down to rounding errors. In fact, some of the other issues you've had in your other topics are most likely a result of this as well. What you want is for the rounding errors to be the same everywhere, when they aren't you get jitter: the rounding errors are different between the different layers. I'm not entirely sure this is your culprit; but if you are drawing this during the post draw event, the screen coordinates are not exactly the same as they are during previous steps since you are drawing relative to the back buffer rather than the application surface. That could account for the rounding errors.

Rounding it could fix this instance, but you might also need to round coordinates everywhere else to ensure they are precise. Hopefully it turns out to be super easy to fix, and hopefully this helps!
 

Kyon

Member
rounding didn't work unfortunately. I even copied everything and put it in the Draw GUI instead, but no difference.
The sprite is the only thing doing that shaking between pixels at the moment. Everything else works. (but this one has the complex code though)
I'm not sure I REALLY understand what's happening though. I don't get how it's still glitching even though I've put round() before it.
Even when the camera isn't moving at all, and no zooming or anything it still does this.


Also a thing to note is that this happens in both full-screen and windowed.
So in windowed the variable displayratio = 1; but still shaking.




EDIT:
It's solved, sort of, the shaking came from the zoom variable.
I had that the zoom kind of zooms in when the player is further away from the screen, but this was barely visible haha. the code was something like this:
+.15-(oPlayer.depth/2000)
Anyway, with this removed it doesn't shake anymore, so I'll keep it like this for now.
 
Last edited:
Top