[Mesa-dev] [RFC shader-db] Add support for shadertoy tests

Dylan Baker baker.dylan.c at gmail.com
Tue Jun 23 16:27:17 PDT 2015


I have a couple of python pointers for you, feel free to take them or
leave them.

Dylan

On Tue, Jun 16, 2015 at 03:46:50PM -0400, Rob Clark wrote:
> Attached script grabs shaders from shadertoy, and dumps them out as
> .shader_test files which can be run through shader-db for compiler
> testing.
> 
> shadertoy only gives you a fragment shader (which works based on
> gl_FragCoord), so a generic vertex shader is used.  And a blurb is
> inserted for the pre-defined uniforms and main() function (which just
> calls shadertoy mainImage() fxn).
> 
> ---
> TODO I guess we'd actually have to parse the shader to figure out if
> the sampler uniforms were meant to be 2D/cube/etc.  Maybe we just
> commit samplers we get from the script and massage them by hand?
> 
> PS. don't make fun of my py too much.. I'm a newb and figuring it
> out as I go

I'm trying not to make fun, but I do have quite a few pointers for you.

> 
>  grab-shadertoy.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100755 grab-shadertoy.py
> 
> diff --git a/grab-shadertoy.py b/grab-shadertoy.py
> new file mode 100755
> index 0000000..74e9d10
> --- /dev/null
> +++ b/grab-shadertoy.py
> @@ -0,0 +1,63 @@
> +#!/usr/bin/env python3
> +
> +
> +import requests, json

You're not actually using json

> +
> +url = 'https://www.shadertoy.com/api/v1/shaders'
> +key = '?key=NdnKw7'
> +
> +# Get the list of shaders
> +r = requests.get(url + key)
> +j = r.json()
> +print('Found ' + str(j['Shaders']) + ' shaders')

If you use format you can avoid calling str() on everything, and make
things more readable using format rather than concatenation:
print('Found {} shaders'.format(j['Shaders']))

> +
> +shader_ids = j['Results']
> +for id in shader_ids:
> +    print('Fetching shader: ' + str(id))
> +    r = requests.get(url + '/' + id + key)
> +    j = r.json()
> +    s = j['Shader']
> +    info = s['info']
> +    print('Name: ' + info['name'])
> +    print('Description: ' + info['description'])
> +    i = 0;

python has a cool builtin called enumerate for doing this:
for i, p in enmerate(s['renderpass']):

Also, I know it's easy to forget, but python doesn't use ';' at the end
of lines, it allows them, but they look weird to pythonistas

> +    for p in s['renderpass']:
> +        fobj = open('shaders/shadertoy/' + str(id) + '_' + str(i) + '.shader_test', 'w')

with str.format this would look like:
with open('shaders/shadertoy/{}_{}.shader_test'.format(id, i), 'w') as fobj:

> +        #print('Inputs: ' + str(p['inputs']))
> +        #print('Outputs: ' + str(p['outputs']))
> +        fobj.write('[require]\n')
> +        fobj.write('GLSL >= 1.30\n')
> +        fobj.write('\n');
> +        fobj.write('[fragment shader]\n')
> +        fobj.write('#version 130\n')
> +        # Shadertoy inserts some uniforms, so we need to do the same:
> +        fobj.write('uniform vec3      iResolution;\n');
> +        fobj.write('uniform float     iGlobalTime;\n');
> +        fobj.write('uniform float     iChannelTime[4];\n');
> +        fobj.write('uniform vec4      iMouse;\n');
> +        fobj.write('uniform vec4      iDate;\n');
> +        fobj.write('uniform float     iSampleRate;\n');
> +        fobj.write('uniform vec3      iChannelResolution[4];\n');
> +        # TODO probably need to parse the shader to figure out if 2d/cubemap/etc
> +        fobj.write('uniform sampler2D iChannel0;\n');
> +        fobj.write('uniform sampler2D iChannel1;\n');
> +        fobj.write('uniform sampler2D iChannel2;\n');
> +        fobj.write('uniform sampler2D iChannel3;\n');
> +        # Actual shadertoy shader body:
> +        fobj.write(p['code'])
> +        # Shadertoy shader uses mainImage(out vec4 fragColor, in vec2 fragCoord)
> +        # so we need to insert a main:
> +        fobj.write('\nvoid main() { mainImage(gl_FragColor, gl_FragCoord.xy); }\n')
> +        fobj.write('\n\n')
> +        # And a generic vertex shader:
> +        fobj.write('[vertex shader]\n')
> +        fobj.write('#version 130\n')
> +        fobj.write('in vec2 position;\n')
> +        fobj.write('\n')
> +        fobj.write('void main()\n')
> +        fobj.write('{\n')
> +        fobj.write('   gl_Position = vec4(position, 0.0, 1.0);\n')
> +        fobj.write('}\n')
> +
> +        fobj.close()
> +        i = 1 + i

You can simplify this quite a bit:

import textwrap

header = textwrap.dedent("""\
    [require]
    GLSL >= 1.30

    [fragment shader]
    #version 130
    uniform vec3      iResolution;
    uniform float     iGlobalTime;
    uniform float     iChannelTime[4];
    uniform vec4      iMouse;
    uniform vec4      iDate;
    uniform float     iSampleRate;
    uniform vec3      iChannelResolution[4];
    uniform sampler2D iChannel0;
    uniform sampler2D iChannel1;
    uniform sampler2D iChannel2;
    uniform sampler2D iChannel3;
""")

footer = textwarp.dedent("""\
    void main() { mainImage(gl_FragColor, gl_FragCoord.xy); }

    [vertex shader]
    #version 130
    in vec2 position;

    void main()
    {
       gl_Position = vec4(position, 0.0, 1.0);
    }
""")

for id in shader_id:
    ...
    
    for i, p in enumerate(s['renderpass']):
        with open('shaders/shadertoy/{}_{}.shader_test'.format(id, i), 'w') as f:
            f.write(header)
            f.write(p['code'])
            f.write(footer)

Also, I recommend using `with open(...)` since it automatically closes
the fd at the end of the block.

You should probably also ensure that shaders/shadertoy exists, I don't
think file.write will succeed if it doesn't.
Something like this should do:

import os
if not os.path.exists('shaders/shadertoy'):
    os.makedirs('shaders/shadertoy')

> -- 
> 2.4.2
> 
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20150623/2f37a2f8/attachment-0001.sig>


More information about the mesa-dev mailing list