Debug Blenderplayer? (mat.getShader() crashes BGE)

I’m having an issue with my GLSL material where it works in Embedded player but not in Blenderplayer.exe

Whenever I try to run .\blenderplayer.exe mygame.blend through Windows Powershell, it just closes powershell before running the game (then crashes).

You can’t really debug the Blenderplayer unless you lay down a bunch of print statements.

Here is a tip:
Assuming you’re running the .blend correctly through the blenderplayer, you should make sure that none of your Python code calls into the Blender code (i.e. bpy). To have a standalone runtime, you must only use the game engine API.

nah, the script that I’ve singled out as causing the problem is only using the “GameLogic” module.

also, lacking print statements isn’t really my problem. The problem is that my console closes after the game crashes, not staying open like I want it to.

EDIT: you’re right actually, I tried redirecting stderr to a text file and it showed nothing. It’s clearly an error with my python script. I’m trying to render a pass that uses different GLSL shaders than the original render like in https://blenderartists.org/forum/showthread.php?397594-Render-Passes-in-BGE but it just crashes my game.

if you create a batch file, you can pass parameters to the blender player to display the debug properties in the blenderplayer.

doesn’t seem to be working. maybe the stderr isn’t actually going to my text file? here’s the command I’m running:

& .\blenderplayer.exe -d G:\mygame.blend 2>&1 >> .\log.txt

here’s my output

read file G:\mygame.blend
  Version 278 sub 0 date 2016-10-24 12:20 hash e8299c8
GL_VENDOR: NVIDIA Corporation
GL_RENDERER: GeForce GTX 1060 6GB/PCIe/SSE2
GL_VERSION:  4.5.0 NVIDIA 376.33
Supported Extensions...
 GL_ARB_shader_objects supported?       yes.
 GL_ARB_vertex_shader supported?        yes.
 ----------Details----------
  Max uniform components.4096
  Max varying floats.124
  Max vertex texture units.32
  Max combined texture units.192


 GL_ARB_fragment_shader supported?      yes.
 ----------Details----------
  Max uniform components.4096


 GL_ARB_texture_cube_map supported?     yes.
 ----------Details----------
  Max cubemap size.32768


 GL_ARB_multitexture supported?         yes.
 ----------Details----------
  Max texture units available.  4


 GL_ARB_texture_env_combine supported?  yes.
 GL_ARB_texture_non_power_of_two supported  yes.

what i’m saying is if you just enable the debug properties in the blender player and have your glsl set properties on an object, you can see the values while you are playing.

@Nicholas_A:
I think he’s having issue with his whole game crashing, not with wanting to have access to debug info.

@FarkR:
Oh, render passes, great fun!
Blender generates a ‘crash.txt’ (or some name like that) in the tmp folder (C:/tmp) have a look at that. It may give you some ideas.
Keeping the shell open after blender crashes is hard in Windows.

Also, you should use bge.logic rather than GameLogic. GameLogic is the ancient way of accessing it.

If you want to upload your script, I’ll take a look at it. If you’re using a buffer object you may be segfaulting it if you try access a value out of bounds, or if you’re trying to squish in a texture larger than the buffer.

If you become desparate, start commenting things out and re-running it until you find out what function/line is causing issues.

I’m not using any textures, I’ll just let you know now.
I’m not seeing anything in C:/tmp/, except for my past renders pngs

Here’s my code, sorry it’s painful to read:

import GameLogic

scene = GameLogic.getCurrentScene()

controller = GameLogic.getCurrentController()

obj = controller.owner


VertexShader = """
   // this vertex shader should be assigned 
   // to "VertexShader" in the script above


   void main() 
   {
      gl_Position = gl_ModelViewProjectionMatrix * 
         (vec4(1.0, 1.0, 1.0, 1.0) * gl_Vertex);
   }
"""
VertexSaber = """
   varying vec4 position_in_view_space;
   // this vertex shader should be assigned 
   // to "VertexShader" in the script above


   void main() 
   {
      position_in_view_space = gl_ModelViewMatrix * gl_Vertex;
      gl_Position = gl_ModelViewProjectionMatrix * 
         (vec4(1.0, 1.0, 1.0, 1.0) * gl_Vertex);
   }
"""


FragmentShader = """
   void main()
   {   
      gl_FragColor = vec4(%s, 1.0);
         // this fragment shader just sets the output color to opaque
         // red (red = 1.0, green = 0.0, blue = 0.0, alpha = 1.0)
   }
"""


FragmentSaber = """
   varying vec4 position_in_view_space;
   void main()
   {   
      vec3 colors = vec3(%s);
      float dist = distance(position_in_view_space, 
               vec4(0.0, 0.0, 0.0, 1.0));
      //gl_FragColor = vec4(colors * (dist / 20.0), 1.0);
      gl_FragColor = vec4(colors, 1.0);
   }
"""


if "RenderToTexture" in obj:
 made_invisible = list()
 for object in [o for o in obj.scene.objects if o.visible]:
  if "hideFromFilter" in object:
   object.visible = False
   made_invisible.append(object)
  else:
   clip = "0, 0, 0"
   for mesh in object.meshes:
    for mat in mesh.materials:
     shader = mat.getShader()
     vertex = shader.getVertexProg()
     fragment = shader.getFragmentProg()
     if "saberBlade" in object and mat.emit == 2.0:
      shader.setSource(VertexSaber, FragmentSaber % object["saberColor"], 1)
     else:
      shader.setSource(VertexShader, FragmentShader % clip, 1)


 obj["RenderToTexture"].refresh(True)
 
 for object in scene.objects:
  for mesh in object.meshes:
   for mat in mesh.materials:
    shader = mat.getShader()
    if ("hideFromFilter" in object):
     None #shader.setSource(object["vertex"], object["fragment"], 1)
    else:
     shader.delSource()


 for object in made_invisible:
  object.visible = True


else:
    import VideoTexture
    objList = scene.objects
    camName = obj['cam']
    cam = objList[camName]
    matID = VideoTexture.materialID(obj, "MA" + obj['material'])
    renderToTexture = VideoTexture.Texture(obj, matID)
    renderToTexture.source = VideoTexture.ImageRender(scene,cam)
    obj["RenderToTexture"] = renderToTexture



come to think of it, maybe it’s because I’m formatting the GLSL shader. I’ll mess around with that and see what happens.

i second this. who even hears of GameLogic anymore? and more importantly, why is it working at ALL!?

well changing the line to

import bge.logic as GameLogic

doesn’t fix much either. I don’t see why it should.

So I messed around with commenting out lines and such, and it appears that blender crashes whenever I run material.getShader()

Does anyone know of a bug like this? I’m almost positive that this is the function causing it to crash.

fyi

When you run the blenderplayer from a commend console (cmd) it will stay open even when the blenderplayer exits regardless of crash or not. It is a separate process.

A batch/command file will not keep the console open as the per default closes the console when there is no further command to process. You can indeed add commands that prevent closing.

I can’t remember discovering crashes on getShader(). I guess it is a bug (crashes are always bugs).

I ran some quick print test and crashed, it seems mat.getShader() object is missing str or other magic methods that python uses, it is definitely a bug

Side note;


for object in made_invisible:
  object.visible = True
else:

the else probably doesn’t do what you expect. The else will always run as you never use a break in the for loop.

I’m struggling to understand the flow of your code. Does it run every frame? If so, you’re re-compiling the shaders every frame and your performance will degrade over time. (I’d estimate you’ll drop below 60FPS after 5 minutes)

I’d suggest investigating module mode. This allows you to run specific functions:
A basic framework would be:


def init(cont):
   assign_shaders()
   set_up_render_to_texture()
   cont.script = __name__ + '.run'  # This changes the function the python controller runs to the function below

def run(cont):
    set_objs_invisible()
    refresh_texture()
    set_objs_visible()

Put the controller in module mode and give it the value <script_name>.init

Sorry, that’ wasn’t about the crash. BUT. I suspect because you’re recompiling the shaders each frame, something is going wrong there - probably running out of GPU vram. Re-allocating the RenderToTexture every frame isn’t helping anything either. We had huge issues when recompiling shaders every frame. It behaved very erratically (it didn’t actually crash, but simply re-ordering the order of when shaders/textures were assigned would break things)

I don’t have a simple example on hand (my renderpass/render-to-texture system is designed to handle a system with ~12 passes that need to be run at different times, combined in funny ways etc). It’s object oriented, but unless you’re experienced at python, it probably won’t help much. I’ll see if I can put together a nice renderpass example.

Are you sure you’re looking at that else statement correctly?
It’s on a different whitespace level than the recursion, so it’s more like this

if "RenderToTexture" in obj:
 renderframe()
else:
 applyRenderToTexture()

And yes, these script is being run every frame by an always sensor with pulsing mode turned on

are the init() and run() functions automatically called the way I’d want them to in BGE (where init is called and runtime and run is called each frame)? If this is the case, how does it change the fact that my shaders are recompiled each frame?

I’m not running it in a batch file. I am simply opening windows power shell and "cd"ing to the blender player executable directory. Power shell still closes after a crash.

Here’s a blend showing off the module mode as I described above:
test.blend (453 KB)

Arrgh! Why are there single-space indents. That changes things completely. It is python convention to use four spaces per indent - as this makes it actually readable where things are at a glance - that said, copy-pasting into firefox is doing the same for me! So I’m hoping that’s the issue. (I use spaces rather than tabs, so don’t normally have this issue)

Can we please invest in some functions? Things like this are a good start:


import bge

def generate_render_to_texture(obj, camera, material_name):
    matID = bge.texture.materialID(obj, "MA" + material_name)
    renderToTexture = bge.texture.Texture(obj, matID)
    renderToTexture.source = bge.texture.ImageRender(scene, cam)
    return renderToTexture

Yes, VideoTexture is also bge.texture
So I just import bge, and then use bge.texture, bge.render, etc. This is both simpler and shorter than GameLogic and VideoTexture

No matter how I see it, the script is running:


shader.setSource(VertexSaber, FragmentSaber % object["saberColor"], 1)

Every frame. Which is probably not a good thing. You do delete the source at the end of each frame, but as I’ve said, recompiling shaders every frame has caused issues for me in the past. It is better to have two objects, one which displays in one mode, one which displays in the other. In my current system. Objects do not have any rendering cost if they’re invisible, so having duplicate objects has (almost) no performance loss if one of them is always invisible.
This also simplifies things: once you’ve set up the system at the game start, all you ever do is toggle visibility - presumably based on a game property.

Some other issues:


for object in [o for o in obj.scene.objects if o.visible]:

‘object’ is a python keyboard already. This is a bit like typing:


print = "Oh no"

And then (possibly) wondering why your code is failing to print, except that ‘object’ is used far less commonly used.

Perhaps a good case study for this is my ObjectsInMirror blend:
https://blenderartists.org/forum/showthread.php?400715-Mirror-help
Pretty much all that needs changing is from ImageMirror to ImageRender and you have a basic renderpass system.

The code for the mirror with some extra comments is (there is also an attached blend in the above link)


import bge

def init(cont):
    # Runs on first frame
    make_mirror_texture(cont)

    # Set the controller to run the update method after it is initilized
    cont.script = __name__+'.update'
    
def update(cont):
    # Runs every frame except for the first frame
    make_mirror_obj_visible(cont)
    update_mirror_texture(cont)
    make_mirror_obj_invisible(cont)

def make_mirror_obj_visible(cont):
    # Any objects that should be visible in the mirror are made visible here, all other objects are made invisible
    for obj in cont.owner.scene.objects:
        if 'MirrorOnly' in obj or 'Mirror' in obj:
            obj.visible = True
        else:
            obj.visible = False

def make_mirror_obj_invisible(cont):
    # Objects that are only visible in the mirror are made invisible here, and other objects are made visible
    # A possible change to make to this is to make it only make visible ones that it made invisible earlier
    for obj in cont.owner.scene.objects:
        if 'MirrorOnly' in obj:
            obj.visible = False
        else:
            obj.visible = True

def make_mirror_texture(cont):
    '''Allocates a texture for the mirror'''
    obj = cont.owner
    mat_id = bge.texture.materialID(obj, "MA" + cont.owner['material'])
    tex = bge.texture.Texture(obj, mat_id, 0)  # 0 is the first texture slot

    # Set it up as a mirror, assigning the textures source as an ImageMirror
    tex.source = bge.texture.ImageMirror(obj.scene, obj.scene.active_camera, obj, mat_id)

    # Can restrict mirror resolution with the following line:
    # tex.source.capsize = [REFLECTION_MAX_SIZE, REFLECTION_MAX_SIZE]
    obj['reflection_tex'] = tex
    
def update_mirror_texture(cont):
    # Refresh the mirror texture
    obj = cont.owner
    obj['reflection_tex'].refresh(False)

On a possible relevant side-note: what blender version are you using? Some of the 2.7x series have issues with ImageRender and ImageMirror.

This sounds good.
I’ll look into applying it later when I get home, I’ll remember to use four spaces next time :stuck_out_tongue:

I appreciate everyone’s help so far, thanks a lot!

Also, I’m on 2.78