Problem with shader

I am trying to implement 2d light. I port 2d light from libgdx https://gist.github.com/mattdesl/5286905 . When i try it in libgdx i can have about 70 lights, before fps go down. But when i try defold i can have 5 light before fps go down. The problem is with shadow fragment shader. If i comment line in shader(distance = min(distance, dst)) fps is ok. If watch in profiler, the problem is with vsync. What i am doing wrong, how can i fixed it.

#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP 
#endif
#define PI 3.14

varying vec2 vTexCoord0;
varying LOWP vec4 vColor;

uniform sampler2D u_texture;
uniform vec2 resolution;

//for debugging; use a constant value in final release
uniform float upScale;

//alpha threshold for our occlusion map
const float THRESHOLD = 0.75;


void main(void) {
  float distance = 1.0;
  
  for (float y=0.0; y<resolution.y; y+=1.0) {
    	//rectangular to polar filter
		vec2 norm = vec2(vTexCoord0.s, y/resolution.y) * 2.0 - 1.0;
		float theta = PI*1.5 + norm.x * PI; 
		float r = (1.0 + norm.y) * 0.5;
		
		//coord which we will sample from occlude map
		vec2 coord = vec2(-r * sin(theta), -r * cos(theta))/2.0 + 0.5;
		
		//sample the occlusion map
		vec4 data = texture2D(u_texture, coord);
		
		//the current distance is how far from the top we've come
		float dst = y/resolution.y / upScale;
		
		//if we've hit an opaque fragment (occluder), then get new distance
		//if the new distance is below the current, then we'll use that for our ray
		float caster = data.a;
		if (caster > THRESHOLD) {
                       //if comment this line all work correct
			distance = min(distance, dst);
  		}
  } 
  gl_FragColor = vec4(vec3(distance), 1.0);
}

module that render light

local Target_helper = require "helpers.render.render_target.render_target_helper"
local Screen_helper = require "helpers.render.screen.screen_helper"
local M = {}
M.__index = M

local add_light_hash=hash("add_light")

local function init_render_targets(self,size)
	self.light_size=size
	self.pred = render.predicate({"screen"})
	self.block_light_pred = render.predicate({"block_light"})
	self.occlusion_map_target=Target_helper.create_default("2dlight_occlusion_map",size,size,false)
	
	local shadow_color_params = {
    	format = render.FORMAT_RGBA,
    	width = size,
    	height = size,
    	min_filter = render.FILTER_LINEAR,
    	mag_filter = render.FILTER_LINEAR,
    	u_wrap = render.WRAP_REPEAT,
    	v_wrap = render.WRAP_REPEAT }
	self.shadow_map_target=Target_helper.new_with_params("2dlight_shadow_map",size,1,shadow_color_params,nil)
	self.light_map_target=Target_helper.create_default("2dlight_light_map",size,size,false)
	self.clear_color = vmath.vector4(0, 0, 0, 1)
	
	self.shadow_map_constants = render.constant_buffer()
    self.shadow_map_constants.resolution = vmath.vector4(size, size, 0, 0)

end

local function enable_blend(self)
	render.enable_state(render.STATE_BLEND)
	if(self.additive) then
    	render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE)
    else
    	render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
	end
end

local function draw_occlusion(self,light)
	local width=render.get_width()
	local height=render.get_height()
	render.set_viewport(0, 0, self.light_size, self.light_size)
	render.set_projection(vmath.matrix4_orthographic(-self.light_size/2+light.position.x, self.light_size/2+light.position.x, -self.light_size/2+light.position.y, self.light_size/2+light.position.y, -1, 1))
	render.enable_render_target(self.occlusion_map_target.target)
	enable_blend(self)
	render.clear({[render.BUFFER_COLOR_BIT] = vmath.vector4(0,0,0,0), [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
	render.draw(self.block_light_pred)
	enable_blend(self)
	render.disable_render_target(self.occlusion_map_target.target)
	
	--self.screen_helper:draw_in(self.occlusion_map_target.target,0,0,256,256)
end

local function draw_shadow_map(self)

	render.enable_render_target(self.shadow_map_target.target)
	render.enable_material("2d_light_shadow")
	self.screen_helper:draw(self.occlusion_map_target.target,self.shadow_map_constants,vmath.vector4(0,0,0,0))
	render.disable_material("2d_light_shadow")
	render.disable_render_target(self.shadow_map_target.target)
	--self.screen_helper:draw_in(self.shadow_map_target.target,0,0,256,256)
end

local function draw_light_map(self,light)
	local width=render.get_width()
	local height=render.get_height()
	local width_scale = width/self.light_size
	local height_scale = height/self.light_size
	render.set_viewport(0, 0, render.get_window_width(), render.get_height())
	local x=0.01 * width_scale-light.position.x/width*0.02*width_scale + 0.01
	local y=0.01 * height_scale-light.position.y/height*0.02*height_scale + 0.01
	
	local projection=vmath.matrix4_orthographic(x-0.01*width_scale,x+0.01*width_scale,y-0.01*height_scale,y+0.01*height_scale,-1,1)
	render.set_projection(projection)
	
	--print(light.color)
	render.enable_material("light_map")
	render.enable_texture(0, self.shadow_map_target.target, render.BUFFER_COLOR_BIT)
	
	render.draw(self.pred,light.const)
	render.disable_texture(0,self.shadow_map_target.target)
	render.disable_material("light_map")
end


function M.new(light_size, up_scale, additive, soft_shadows)
	local self = setmetatable({},M)
	self.screen_helper=Screen_helper.new(render)
	self.lights={}
	init_render_targets(self,light_size)
    return self
end

local function draw_light(self,light)
	draw_occlusion(self,light)
	draw_shadow_map(self)
	--draw_light_map(self,light)
	render.set_viewport(0, 0, render.get_window_width(), render.get_height())
end

function M:draw()
	for key,light in pairs(self.lights) do
		draw_light(self,light)
	end
end

function M:handle_message(message_id,message)
	if(message_id==add_light_hash)then
		local light_constants = render.constant_buffer()
    	light_constants.resolution = vmath.vector4(self.light_size, self.light_size, 0, 0)
   		light_constants.vColor = message.color
		local light={position=message.position,size=message.size,color=message.color,const=light_constants}
		self.lights[message.light_id]=light
	end
end



return M

1 Like

I do not know why this is happening.
But maybe it’s because of OpenGL ES.
At least under your link and it is written:
“However, as these features are not supported in OpenGL ES, I chose not to use them.”

I think this is because the shader operator “if” is always expensive, and apparently GLSL ES did not optimize it as GLSL.

This is not gl_es issue, because i test on pc. The problem is with defold, or with my code. I use same shader in libgdx and in defold.Libgdx 70 light sources, defold 6 light sources.

The problem with shadow shader if i use y+=10 instead of y+=1 i can have a lot of light sources. Also if i change projection to bigger, light not work correct but i can have a lot of light sources too. Why shader can’be so slow? As i said early libgdx is about x10 faster. Where is defold perfomance? I tryed web profiler but it is broken.It show me 41ms per frame when game profiler show me 16ms. In game profiler Engine use 84ms while graphics 0.7ms. What mean engine?Is shaders time in engine or in graphics?

local function draw_shadow_map(self)
	self.projection=vmath.matrix4_orthographic(0,0.02,0,0.02,-1,1)
	render.set_projection(self.projection)
	render.enable_render_target(self.shadow_map_target.target)
	render.clear({[render.BUFFER_COLOR_BIT] = vmath.vector4(0,0,0,0), [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
	render.enable_material("2d_light_shadow")
	render.enable_texture(0,self.occlusion_map_target.target, render.BUFFER_COLOR_BIT)
	--self.screen_helper:draw(self.occlusion_map_target.target,self.shadow_map_constants,vmath.vector4(0,0,0,0))
	render.draw(self.pred,self.shadow_map_constants)
	render.disable_texture(0,self.occlusion_map_target.target)
	render.disable_material("2d_light_shadow")
	render.disable_render_target(self.shadow_map_target.target)
	self.screen_helper:draw_in(self.shadow_map_target.target,0,0,256,256)
end

I don’t have enough experience with shader coding to answer this. @Mathias_Westerdahl, @sven, @Andreas_Tadic, do you have any ideas?

I’m sure it’s not broken. You are likely reading the values wrong. Post a screenshot and someone like @jakob.pogulis, @sven or @Mathias_Westerdahl can help you interpret the readout.

2 Likes

Web profiler with no light sources(pic1) game profiler show 16ms
Web profiler with 10 light sources(pic2)


Well, it’s hard to say without investigating properly.

You say you get performance back if you comment out the line below?

distance = min(distance, dst);

Seems strange that that line alone would affect performance, unless the output of the shader is affecting some next step in you render chain.
Btw, I cannot see that you upload the constant upScale? Perhaps you do that elsewhere?

As for the performance, it’s always good to keep an eye on the number of draw calls. In your scenario with 0 lights, you have 6 draw calls, and when you have 10 lights, you have 42 draw calls. Although not ideal, it shouldn’t drop a modern PC to 100ms. A more interesting thing is that you only get 41 ms when using no lights? Perhaps that’s a clue? Btw, do you know the number of draw calls in your libgdx example?

Can I ask what your settings are for your game.project: update_frequency and variable_dt ?

Since your libgdx example ran on the same machine without problems, I really think it should have run fine in Defold too. :confused:

Tomorrow, more people will be back to work, and hopefully we can figure out something new about this.

1 Like

Thx, for your reply.

Now I changed uniform to const value 1.It not used for now, but i want use it in future.

update_frequency 60
variable_dt not checked

I do not understand it too.Because all work happened in shader.
I hope for your help, because i have no idea why it happened.:slight_smile:
I upload defold/libgdx sources, and libgdx jar here.

2 Likes

@Mathias_Westerdahl any idea? :thinking:

No, nothing new I’m sorry to say :confused:

Have you tested running one of the tutorials? Do they run at full framerate? (<16ms/frame)

the web profiler said 42ms=( . But in game profiler said 16ms, and it feels like 60 fps.

What happens if you set variable_dt to true in your game.project file? What will each profiler report?

I tried the code you shared on my Macbook Pro. I added 9 lightsources and fired up the web profiler:

I’m not really sure what this is an indication of though… :slight_smile: Some issue with the shader on certain GPUs? In that case why is it working well with libgdx? @Mathias_Westerdahl, @sven, @Ragnar_Svensson, any idea?

1 Like

Would you mind giving this a try?

  • Create a new script file; glversion.script
  • Replace its content with:
if not ffi then
    ffi = package.preload.ffi()
end
ffi.cdef[[
const char* glGetString(unsigned int);
]]
print("OpenGL Version: " .. ffi.string(ffi.C.glGetString(7938)))
  • Attach the script to one of your gameobjects and run the game.
  • Hopefully the “Console” panel in the editor should print something along the lines of: DEBUG:SCRIPT: OpenGL Version: 2.1 INTEL-10.22.25

This should determine what version of the OpenGL context the engine has created. I’m not sure if it will be relevant information, but couldn’t hurt to try.

(I haven’t tried this on Windows, but I think it should work.)

ERROR:SCRIPT: glversion.script:7: cannot resolve symbol ‘glGetString’

Does it behave better/worse/same in fullscreen mode? (Idea from Win 10 vsync issue)

same. But in fullscreen mode alt+tab not work.(the screen became black, and nothing happened)

Are there any vsync options available in the os or per app? (See this thread for some vsync issues)

Next idea I have is to try the app at another Windows machine, and also on another Windows version… :confused: