Yojimbo2000's Soda with Super Simple Buttons?

I think you’re right about defining the functions externally instead of using closures, too. I don’t see any way to enforce that programmatically, but it should certainly be best practice.

@yojimbo2000 : I would greatly appreciate any insight you have into a couple things:

-it seems like this code should automatically update the blurred texture when you drag the buttons, without needing to change anything else, because the button always passes its current x,y to roundRect. Instead, the buttons always retain the texture from the first location they are in. I don’t get it.

-the two choice buttons don’t render their textures independently, instead they both seem to render the area under the top button. Each button feeds different x,y values to roundRect, so I don’t get it.

Any clue?

Is the latest code in the repo?

This could be to do with how roundedRect caches meshes: it doesn’t notice that the mesh has changed. So you might have to include the texCoords as part of the unique mesh identifier. This would make it cache a lot more meshes though.

Wahoolazuma! You know your own code like the back of your hand! I added texCoord.x and texCoord.y to the identifier label and presto. Live updating and all buttons have independent renders. Nice, nice work, @yojimbo2000.

Glad to help. Is the latest version in the repo yet? Would be interested in trying it.

I’m cleaning it up and fixing a few glitches, will post when ready.

Another kudos I’d like to give is for the concept, observed in roundRect also, of not passing custom fill and stroke values through the button functions, but just using the current system values. Very cool and clean. Switching to that system is one of the main clean-ups I’m doing.

Updated–I think it’s all up to speed now.

@yojimbo2000: I modified your old blurs demo to have a background image that also gets blurred, and it’s here: https://gist.github.com/DolenzSong/449e39ae44192160c09e

When I run this on my iPad 3, I get around 50 fps, rendering the entire screen every draw cycle. Not bad! For my purposes, more than usable.

I’ve also updated my ChoiceMaker (and the gist), so that it also renders a screen blur every draw cycle (for use by the ui pieces). When I run it, I get 0 fps for about five seconds and then it crashes Codea. If I comment out only the screen blurring command the crash doesn’t happen, so I don’t think any of my closure-generation is affecting it.

The demo and the Soda implementations of blur are very different, but I don’t see any reason one should crash and not the other. Can you see what I’m doing wrong?

@UberGoober OK, so your Blurs code posted above is modifying one of the earlier attempts at optimizing that I tried, which takes fewer samples, 5 x 5 = 25. (rather than 14 x 14 = 196)

I’ll look at the latest ChoiceMaker code later

Can I confirm, is the code in the gist the crashing or the non-crashing version, and which bit am I supposed to comment/ un-comment?

The code in the gist is the code that crashes, but it won’t crash for you unless you un-comment line 50 in the uiPieceHandler tab.

OK I got it, I need to uncomment line 50 in uiPieceHandler to see the crashing.

The demo and the Soda implementations of blur are very different, but I don’t see any reason one should crash and not the other.

Seriously?? :s Meshes, shaders, and images are complex objects that take up memory. Ideally, you should set them up in, well, setup. In Soda, and in your blurs demo, the blurring meshes are setup once. Each cycle, all you then do is draw the image.

In choicemaker on the other hand (if you uncomment line 50 of uiPieceHandler), you set up new meshes, images, shaders etc, every single frame! This is completely needless, and it’s no wonder it grinds to a halt and crashes Codea (does the same on an iPad Air btw).

If you really are committed to a fire-and-forget ellipse analogy, then you’re going to have to come up with much stronger caching capabilities for your blur function, as with roundedRect, so that your blur function recognises that the size of the mesh is the same and doesn’t attempt to recreate it every frame.

BTW, I thought of a much better way to handle shifting texcoords when an element moves. At the moment, you recreate a new roundedrect for each position and cache it, potentially a massive memory leak. A better solution would be to offset the texcoords of the shape by the position, calculating the correct texcoords in the vertex shader.

Ok I think I get it, that’s a very helpful explanation, it will help me figure out how to fix it.

As to the texCoords, I assume that would be done by a translate() statement somewhere?

Please forgive me if I seem to be missing huge crucial things, I am, as you say, untrained. :slight_smile:

No, it would be a change somewhere in the vertex shader. I’ll see whether I can work something up.

Ok, this is what I came up with, for live updating full-screen blur texture being used on multiple panels. It dispenses with texCoords altogether, calculating them in the vertex shader. Runs at 60 fps on an Air, but maybe only 40 or so on an iPad 3? Drag the panels to move them around.

--# Main
--Gaussian blur
--adapted by Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/ 

supportedOrientations(LANDSCAPE_ANY)
displayMode(OVERLAY)
function setup()
    downSample = 0.15
    local dimensions = vec2(WIDTH, HEIGHT) * downSample --down sampled
    
    blurTex = {} --image textures
    blurMesh = {} --meshes
    for i=1,2 do --2 passes, 1: horizontal, 2: vertical
        blurTex[i]=image(dimensions.x, dimensions.y)   
        local m =mesh()
        m.texture=blurTex[i]
        m:addRect(dimensions.x/2, dimensions.y/2,dimensions.x, dimensions.y)
        m.shader=shader(Gaussian.vs[i], Gaussian.fs)
        blurMesh[i] = m
    end
    
    blurTex[3]=image(dimensions.x, dimensions.y) --this third image is the texture used by all panels

    button1 = Panel(WIDTH/2, HEIGHT/2,blurTex[3], color(240))
    button2 = Panel(WIDTH* 0.7, HEIGHT * 0.3, blurTex[3], color(200))
    
    pos={x1=200, y2=200, x3=800}
    tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong})

    profiler.init()

end

function draw()
    --do all drawing, downscaled
    setContext(blurTex[1])
    pushMatrix()
    scale(downSample)
    drawing()
    popMatrix()
    setContext()
    --horizontal blur pass
    setContext(blurTex[2])
    blurMesh[1]:draw() 
    setContext()
    --vertical blur pass
    setContext(blurTex[3])
    blurMesh[2]:draw() 
    
    --draw to screen
    setContext()
    drawing()
    button1:draw()
    button2:draw()
    
    profiler.draw()
end

function drawing()
    background(50)
    
    sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200)
    sprite("Small World:Store Extra Large",pos.x3,200,300)
    sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2)
end

function touched(t)
    if not button2:touched(t) then button1:touched(t) end
end

profiler={}

function profiler.init(quiet)    
    profiler.del=0
    profiler.c=0
    profiler.fps=0
    profiler.mem=0
    if not quiet then
        parameter.watch("profiler.fps")
        parameter.watch("profiler.mem")
    end
end

function profiler.draw()
    profiler.del = profiler.del +  DeltaTime
    profiler.c = profiler.c + 1
    if profiler.c==10 then
        profiler.fps=profiler.c/profiler.del
        profiler.del=0
        profiler.c=0
        profiler.mem=collectgarbage("count", 2)
    end
end
--# Panel
Panel = class()

function Panel:init(x,y,tex, col)
    self.x, self.y = x,y
    local w,h = 400,300
    self.ww, self.hh = w/2, h/2
    local m = mesh()
    m.shader = shader(texOffset.vert, texOffset.frag)
    m.texture = tex
    m:addRect(0,0, w,h)

    m:setRectColor(1, col) --darken the panel slightly
    m.shader.downSample = vec2(1/WIDTH, 1/HEIGHT) --used to convert position into texCoords
    self.mesh = m
end

function Panel:draw()
    pushMatrix()
    translate(self.x, self.y)
    self.mesh.shader.modelMatrix = modelMatrix() --pass the translation into the shader
    self.mesh:draw()
    popMatrix()
end

function Panel:touched(t)
    if t.x > self.x-self.ww and t.x < self.x+self.ww and t.y > self.y-self.hh and t.y < self.y+self.hh then
        self.x = self.x + t.deltaX
        self.y = self.y + t.deltaY
        return true
    end
end


--# Gaussian
Gaussian = {
vs = { -- horizontal pass vertex shader
[[
uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec2 texCoord;
 
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
 
void main()
{
    gl_Position = modelViewProjection * position;
    vTexCoord = texCoord;
    v_blurTexCoords[ 0] = vTexCoord + vec2(-0.028, 0.0);
    v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024, 0.0);
    v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020, 0.0);
    v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016, 0.0);
    v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012, 0.0);
    v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008, 0.0);
    v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004, 0.0);
    v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004, 0.0);
    v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008, 0.0);
    v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012, 0.0);
    v_blurTexCoords[10] = vTexCoord + vec2( 0.016, 0.0);
    v_blurTexCoords[11] = vTexCoord + vec2( 0.020, 0.0);
    v_blurTexCoords[12] = vTexCoord + vec2( 0.024, 0.0);
    v_blurTexCoords[13] = vTexCoord + vec2( 0.028, 0.0);
}]],
-- vertical pass vertex shader
 [[
uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec2 texCoord;
 
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
 
void main()
{
    gl_Position = modelViewProjection * position;
    vTexCoord = texCoord;
    v_blurTexCoords[ 0] = vTexCoord + vec2(0.0, -0.028);
    v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024);
    v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020);
    v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016);
    v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012);
    v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008);
    v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004);
    v_blurTexCoords[ 7] = vTexCoord + vec2(0.0,  0.004);
    v_blurTexCoords[ 8] = vTexCoord + vec2(0.0,  0.008);
    v_blurTexCoords[ 9] = vTexCoord + vec2(0.0,  0.012);
    v_blurTexCoords[10] = vTexCoord + vec2(0.0,  0.016);
    v_blurTexCoords[11] = vTexCoord + vec2(0.0,  0.020);
    v_blurTexCoords[12] = vTexCoord + vec2(0.0,  0.024);
    v_blurTexCoords[13] = vTexCoord + vec2(0.0,  0.028);
}]]},
--fragment shader
fs = [[precision mediump float;
 
uniform lowp sampler2D texture;
 
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
 
void main()
{
    gl_FragColor = vec4(0.0);
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 0])*0.0044299121055113265;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 1])*0.00895781211794;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 2])*0.0215963866053;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 3])*0.0443683338718;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 4])*0.0776744219933;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 5])*0.115876621105;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])*0.147308056121;
    gl_FragColor += texture2D(texture, vTexCoord         )*0.159576912161;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])*0.147308056121;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])*0.115876621105;
    gl_FragColor += texture2D(texture, v_blurTexCoords[ 9])*0.0776744219933;
    gl_FragColor += texture2D(texture, v_blurTexCoords[10])*0.0443683338718;
    gl_FragColor += texture2D(texture, v_blurTexCoords[11])*0.0215963866053;
    gl_FragColor += texture2D(texture, v_blurTexCoords[12])*0.00895781211794;
    gl_FragColor += texture2D(texture, v_blurTexCoords[13])*0.0044299121055113265;
}]]
}

--# TexOffset
texOffset={
vert=[[
uniform mat4 modelViewProjection;
uniform mat4 modelMatrix;
uniform vec2 downSample;

attribute vec4 position;
attribute vec4 color; //look ma, no texCoords!

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    vColor = color;
    
    vec4 pos = modelMatrix * position; //translate vertex pos
    vTexCoord = pos.xy * downSample; //convert translated point to texCoord
    gl_Position = modelViewProjection * position;
}
]],
frag=[[
precision highp float;

uniform lowp sampler2D texture;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    gl_FragColor = texture2D( texture, vTexCoord ) * vColor;
}
]]
}

If you find the above code running slow on older hardware, try lowering the downSample variable to see if that helps.

Wow, @yojimbo2000 , that’s a lot of work! I am very much thank you, so much my English bad go. I probably won’t have a chance to run it for a couple days, but I wanted to thank you right away.

Hi @yojimbo2000, long time no forum! Been working, and using my Codea time to turn my existing app Universal. (Which it is now, yay!)

So, here’s the current state of this thing: https://gist.github.com/DolenzSong/ef91fd371fd13fe1c00a

Everything works great except the button textures don’t update when I change backgrounds.

I need a way to manually update the cached texture, so I can do that after a scene transition. Is that possible?