3D Problem with MRT, surfaces crashing the game when setting twice

Discussion in 'Programming' started by BlueHarrier, Sep 10, 2019.

  1. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    Hi again, community.

    Today I'm having issues with surfaces and Multi Render Targets, in this case, the game just crashes when trying to set the second target slot twice (reseting the target in between, obviously), and I don't know why. For some reason (and it's not the first time this happens to me), the game doesn't crash when using the debugger, but the application surface becomes completely white.

    This is the fragment where it crashes (Draw End):
    Code:
    var
    mat=matrix_build(x, y, z, 0, 0, 0, 2, 2, 2);
    matrix_set(matrix_world, mat)
    gpu_set_cullmode(cull_clockwise)
    
    shader_set(sh3d_lightsource)
    
    var
    rel_uv=shader_get_uniform(sh3d_lightsource, "u_RelTexCoords"),
    z_vals=shader_get_uniform(sh3d_lightsource, "u_ZVals"),
    ul_color=shader_get_uniform(sh3d_lightsource, "u_LightColor"),
    brightness=shader_get_uniform(sh3d_lightsource, "u_LightBrightness");
    
    shader_set_uniform_f_array(rel_uv, sprite_get_uvs(tex_lamp, 0))
    shader_set_uniform_f(z_vals, camera.z_near, camera.z_far)
    shader_set_uniform_f(ul_color, color_get_red(l_color)/255, color_get_green(l_color)/255, color_get_blue(l_color)/255)
    shader_set_uniform_f(brightness, 0.5)
    
    surface_set_target_ext(0, application_surface)
    surface_set_target_ext(1, global.light_surf)
    
    camera_reload()
    
    model_draw("lamp.dat", tex_lamp, 0)
    
    surface_reset_target()
    
    shader_reset()
    
    gpu_set_cullmode(cull_noculling)
    
    matrix_set(matrix_world, matrix_build_identity())
    And the error given is:
    Code:
    ############################################################################################
    ERROR in
    action number 1
    of Create Event
    for object obj_light:
    
    Cannot free this surface - call surface_reset_target before calling surface_free
    ############################################################################################
    
    This problem only occurs when there are TWO or more instances of this object in the room, when there's only one, there is no problem. Any ideas of what could it be?
     
    Last edited: Sep 10, 2019
  2. Binsk

    Binsk Member

    Joined:
    Jun 22, 2016
    Posts:
    583
    Why are you resetting the shader BEFORE you draw your model?
     
  3. Ido-f

    Ido-f Member

    Joined:
    Feb 19, 2018
    Posts:
    124
    Try adding another surface_reset_target in there.
    According to this blog post, you need a matching surface_reset_target call for each surface_set_target call, rather than only one at the end.
     
  4. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    Uhm... That's right, but the problem's still there (and I don't understand how was it working without the shader, but it did).
     
  5. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    It makes a stack error, I'm only setting one stack of surfaces.

    Code:
    ___________________________________________
    ############################################################################################
    FATAL ERROR in
    action number 1
    of Draw Event
    for object obj_light:
    
    Unbalanced surface stack. you are trying to pop a surface that has not been set.
     at gml_Object_obj_light_Draw_0 (line 29) - surface_reset_target()
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Object_obj_light_Draw_0 (line 29)
    
    If I had set the targets twice, it'd work, but it's not the case...
     
  6. Binsk

    Binsk Member

    Joined:
    Jun 22, 2016
    Posts:
    583
    @Ido-f For future reference, since he's using surface_set_target_ext it only needs to be called once. It's just how MRTs work.

    Is that the full error on the first post? I don't see a line number or anything. Is it possible a function you are calling, such as camera_reload or model_draw, is doing anything with the surfaces in their scripts?

    By itself I'm not seeing anything wrong with that create event code.
     
    Ido-f likes this.
  7. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    Good news, I fixed it, I know how, but it doesn't seem logic to me.

    By removing the first surface_set_target_ext, which sets up the application surface, the error changes, now, it returns a surface stack error (the same as my last reply), and removing that line solves the error. It seems that Game Maker only pushes surfaces that are set on the first slot, by reseting the target, it will reset slots 1, 2 and 3 to -1, and the slot 0 will become the application surface. What doesn't make sense is why does it crashes when everything was correct, because with other objects that doesn't requiere 2 surfaces I need to reset the target.
     
  8. Ido-f

    Ido-f Member

    Joined:
    Feb 19, 2018
    Posts:
    124
    @BlueHarrier, I think that the error might be caused because you're doing surfaces and draw things in the create event instead of the draw event.
     
  9. Binsk

    Binsk Member

    Joined:
    Jun 22, 2016
    Posts:
    583
    Can you humor me and try something? I'm wondering if the application surface hasn't been created yet since you are calling this in the create event instead of the draw event.

    Try putting all this code in alarm 0 or something similar instead of the create event and set the alarm to 1 in the create event so that it has an offset of a step.

    EDIT: Reading through the manual it states that the application_surface is created on the first draw event call in each room. This may be the issue, then.
     
  10. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    Yeah, I forgot about mentioning that, even if GM error says that the event failing is the Create Event, I'm operating in the Draw End Event, and that's the code I'm showing. The create event hasn't got any surface function as they are controled by managers. I've seen errors like this one before, and they are shown as the create event, even if they're not.

    Sorry, I should have said it before.
     
    Last edited: Sep 10, 2019
  11. Binsk

    Binsk Member

    Joined:
    Jun 22, 2016
    Posts:
    583
    Mind showing us the create code just in case?
     
  12. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    Roger roger

    Create Event:
    Code:
    ///@desc Setup
    
    var
    format;
    
    vertex_format_begin()
    vertex_format_add_position_3d()
    vertex_format_add_texcoord()
    vertex_format_add_color()
    vertex_format_add_normal()
    format=vertex_format_end()
    
    model_call_ext("lamp.dat", format)      //This function calls the model manager to avoid loading a model twice (uses saved model buffer files)
    sprite_call(sprite_index)                //This one fetches a sprite to the graphic memory
    
    z=-21
    l_att=[0.5, 0.0005, 0.00000001]
    l_color=make_color_rgb(255, 100, 100)
     
  13. Binsk

    Binsk Member

    Joined:
    Jun 22, 2016
    Posts:
    583
    I'm at a loss. I honestly can't think of anything other than throwing out some highly unlikely possibilities. My remaining thoughts are:

    1) Are you sure that your game is using the application_surface? Did you disable it at some point?
    2) If you switch the application_surface out for your own surface does the error go away?
    3) Do surfaces, application or otherwise, ever get recreated / modified in any way by any other objects?
    4) On the same idea as (3), have you checked creation order? Are you sure it isn't just happenstance that it is 2+ objects? If placed in the room via editor, what happens if you remove them all and add them in again so the order changes?

    Those are all the remaining things I can think of. Not much but it's what I've got. Maybe someone else has some ideas. -.-'
     
  14. BlueHarrier

    BlueHarrier Member

    Joined:
    Mar 31, 2017
    Posts:
    25
    As I said early, I fixed the problem, and I was only looking for an explanation of why did that happen, and I have a theory:

    Basically, that "light_surf" is a bloom post-proccessed surface, that is created and secured on a Pre-Draw event in an object that acts as a graphic manager (it's literally called like that). That object makes the maintenance of all surface used in the game to work, from cleaning them each frame to making sure that they exist. It also clears the application surface and draws it manually on the screen buffer (Draw GUI, I have disabled the automatic drawing of the surface because of the post-proccess effects that are added to final render).

    The error came when I used a double render target for drawing a lamp with an HLSL shader using 2 output targets: one for the common render, and the other one for the bloom surface. When setting the two targets, drawing and then reseting the target once work perfectly well, but when the code is executed two or more times, it returns an error that doesn't even make sense (because the error we got is one that should be given when you use surface_free(surf) with a non existing surface), happening the same with the event and the line.

    THEORY:
    I think this is just an engine error that can be solved manually. For some reason, surface_reset_target() does not reset the surfaces stored in another target index than 0, so the way I reseted the surface is manually, by doing surface_set_target_ext(1, -1). -1 is the void value, and the engine takes it perfectly well, with no memory increase and no performance impact, so everything seems correct.

    These are the two summarized codes, the one that failed, and the other that worked:
    Code:
    /* CRASH */
    [...]
    surface_set_target_ext(0, surf1)
    surface_set_target_ext(1, surf2)
    
    //Draw stuff
    
    surface_reset_target()
    
    /* WORKS */
    [...]
    //Here we assume that the first target is already set, if not, I don't know how to fix this, in my case it is.
    surface_set_target_ext(1, surf2)
    
    //Draw stuff
    
    surface_set_target_ext(1, -1)
    P.S.: Should I edit the thread and say this and even report this to the technical support?
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice