Exemple #1
0
def test_cannot_use_unresolved_globals():
    def compute_shader(index: ("input", "GlobalInvocationId", ivec3), ):
        color = stdlib + 1.0  # noqa

    with raises(pyshader.ShaderError) as info:
        pyshader.python2shader(compute_shader)

    assert "color = stdlib + 1.0" in str(info.value).lower()
Exemple #2
0
def test_fail_unvalid_stlib_name():
    def compute_shader(index: ("input", "GlobalInvocationId", ivec3), ):
        color = stdlib.foo  # noqa

    with raises(pyshader.ShaderError) as info:
        pyshader.python2shader(compute_shader)

    assert "color = stdlib.foo" in str(info.value).lower()
Exemple #3
0
def test_cannot_assign_same_slot():
    def compute_shader(
            index: ("input", "GlobalInvocationId", ivec3),
            data1: ("buffer", 0, Array(i32)),
            data2: ("buffer", 0, Array(i32)),
    ):
        data2[index.x] = data1[index.x]

    with raises(pyshader.ShaderError) as err:
        pyshader.python2shader(compute_shader).to_spirv()
    assert "already taken" in str(err.value)
def test_andor1():
    # Implicit conversion to truth values is not supported

    def compute_shader(
            index_xyz: ("input", "GlobalInvocationId", ivec3),
            data2: ("buffer", 1, Array(f32)),
    ):
        index = index_xyz.x
        if index < 5:
            val = f32(index - 3) and 99.0
        else:
            val = f32(index - 6) and 99.0
        data2[index] = val

    with pytest.raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader).to_spirv()
Exemple #5
0
def test_cannot_call_non_funcs():
    def compute_shader1(
            index: ("input", "GlobalInvocationId", ivec3),
            tex: ("texture", 0, "2d rg32i"),
    ):
        a = 1.0
        a(1.0)

    def compute_shader2(
            index: ("input", "GlobalInvocationId", ivec3),
            tex: ("texture", 0, "2d rg32i"),
    ):
        a = 1.0 ()  # noqa

    with raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader1)
    with raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader2)
Exemple #6
0
def test_no_duplicate_constants():
    def vertex_shader():
        positions = [vec2(0.0, 1.0), vec2(0.0, 1.0), vec2(0.0, 1.0)]  # noqa

    m = pyshader.python2shader(vertex_shader)
    text = pyshader.dev.disassemble(m.to_spirv())
    # One for 1.0, one for 0.0
    assert text.count("OpConstant %float") == 2
    # One for the vector, one for the array
    assert text.count("OpConstantComposite") == 2
Exemple #7
0
def test_cannot_add_int_and_floats():
    def compute_shader1(index: ("input", "GlobalInvocationId", ivec3), ):
        foo = 3.0
        bar = foo + index.x  # noqa

    x = pyshader.python2shader(compute_shader1)
    with raises(pyshader.ShaderError) as info:
        x.to_spirv()
    err = info.value.args[0]
    assert "source file" in err.lower()
    assert "test_py.py" in err.lower()
    assert "bar = foo + index.x" in err.lower()
Exemple #8
0
def test_errror_reports_the_correct_name2():
    # ... and sometimes that name is an array (a VariableAccessId internally)

    def compute_shader1(index: ("input", "GlobalInvocationId", ivec3), ):
        foo = [1, 2, 3]
        bar = foo  # noqa
        spam = foo[0] + 1.0  # noqa

    def compute_shader2(index: ("input", "GlobalInvocationId", ivec3), ):
        foo = [1, 2, 3]
        bar = foo  # noqa
        spam = bar[0] + 1.0  # noqa

    with raises(pyshader.ShaderError) as info1:
        pyshader.python2shader(compute_shader1).to_spirv()
    assert "spam = foo[0] + 1.0" in str(info1.value).lower()
    assert "variables: foo[0], 1.0" in str(info1.value).lower()

    with raises(pyshader.ShaderError) as info2:
        pyshader.python2shader(compute_shader2).to_spirv()
    assert "spam = bar[0] + 1.0" in str(info2.value).lower()
    assert "variables: bar[0], 1.0" in str(info2.value).lower()
Exemple #9
0
def test_errror_reports_the_correct_name1():
    # Sometimes, an object can be known by multiple names ...

    def compute_shader1(index: ("input", "GlobalInvocationId", ivec3), ):
        foo = 3.0
        bar = foo  # noqa
        spam = foo + 1  # noqa

    def compute_shader2(index: ("input", "GlobalInvocationId", ivec3), ):
        foo = 3.0
        bar = foo  # noqa
        spam = bar + 1  # noqa

    with raises(pyshader.ShaderError) as info1:
        pyshader.python2shader(compute_shader1).to_spirv()
    assert "spam = foo + 1" in str(info1.value).lower()
    assert "variables: foo, 1" in str(info1.value).lower()

    with raises(pyshader.ShaderError) as info2:
        pyshader.python2shader(compute_shader2).to_spirv()
    assert "spam = bar + 1" in str(info2.value).lower()
    assert "variables: bar, 1" in str(info2.value).lower()
Exemple #10
0
def test_bytecode_output_src_opcodes():
    def compute_shader():
        a = 2  # noqa

    m = pyshader.python2shader(compute_shader)
    bc = m.to_bytecode()
    instructions = [x[0] for x in bc]
    assert instructions == [
        "co_src_filename",
        "co_src_linenr",
        "co_entrypoint",
        "co_src_linenr",
        "co_load_constant",
        "co_store_name",
        "co_func_end",
    ]
def test_loop9():
    # This is a very specific shader (volumeslice from pygfx) that produces
    # wrong results at some point, which was the notch needed to implement
    # variables using VarAccessId objects. See #56.
    def compute_shader(
            index_xyz: ("input", "GlobalInvocationId", ivec3),
            data2: ("buffer", 1, Array(f32)),
    ):
        ed2pl = [[0, 4], [0, 3], [0, 5], [0, 2], [1, 5], [1, 3], [1, 4],
                 [1, 2]]
        intersect_flag = [0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0]
        i = 1
        plane_index = ed2pl[i][0]
        vertices = [i, 0, 0, 0, 0, 0]
        i_start = i
        i_last = i
        max_iter = 6
        for iter in range(1, max_iter):
            for i in range(12):
                if i != i_last and intersect_flag[i] == 1:
                    if ed2pl[i][0] == plane_index:
                        vertices[iter] = i
                        plane_index = ed2pl[i][1]
                        i_last = i
                        break
                    elif ed2pl[i][1] == plane_index:
                        vertices[iter] = i
                        plane_index = ed2pl[i][0]
                        i_last = i
                        break
            if i_last == i_start:
                max_iter = iter
                break
        index = index_xyz.x
        data2[index] = f32(vertices[index])

    # On py36 this works, but generates different bytecode ...
    if sys.version_info > (3, 7):
        compute_shader = python2shader_and_validate(compute_shader)
    else:
        compute_shader = pyshader.python2shader(compute_shader)

    skip_if_no_wgpu()
    vertices = generate_list_of_floats_from_shader(6, compute_shader)
    print(vertices)
    assert vertices == [1, 3, 7, 5, 1, 0]
Exemple #12
0
def test_spirv_output_opnames():
    def compute_shader(
            index: ("input", "GlobalInvocationId", ivec3),
            data1: ("buffer", 0, Array(i32)),
    ):
        a = 2
        b = a  # noqa
        c = a + 1  # noqa

    m = pyshader.python2shader(compute_shader)
    text = pyshader.dev.disassemble(m.to_spirv())

    # Check opname
    assert text.count("OpName") == 9
    assert 'OpName %main "main"' in text
    assert 'OpName %index "index"' in text
    assert 'OpName %data1 "data1"' in text
    assert 'OpName %1 "1"' in text
    assert 'OpName %2 "2"' in text
    assert 'OpName %a "a"' in text
    assert 'OpName %b "b"' in text
Exemple #13
0
def test_cannot_use_tuples_in_other_ways():
    def compute_shader1(index: ("input", "GlobalInvocationId", ivec3), ):
        v = 3.0, 4.0  # noqa

    def compute_shader2(index: ("input", "GlobalInvocationId", ivec3), ):
        a = 3.0
        b = 4.0
        v = a, b  # noqa

    def compute_shader3(index: ("input", "GlobalInvocationId", ivec3), ):
        v = vec2(3.0, 4.0)
        a, b = v

    with raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader1)

    with raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader2)

    with raises(pyshader.ShaderError):
        pyshader.python2shader(compute_shader3)
Exemple #14
0
def mesh_renderer(wobject, render_info):
    """ Render function capable of rendering meshes.
    """

    geometry = wobject.geometry
    material = wobject.material  # noqa

    # Get stuff from material

    # ...

    # Get stuff from geometry

    fragment_shader = fragment_shader_simple

    # Use index buffer if present on the geometry
    index_buffer = getattr(geometry, "index", None)
    index_buffer = index_buffer if isinstance(index_buffer,
                                              BaseBuffer) else None

    # Collect vertex buffers
    vertex_buffers = []
    vertex_buffers.append(geometry.positions)
    if getattr(geometry, "texcoords", None) is not None:
        vertex_buffers.append(geometry.texcoords)

    bindings0 = {0: (wgpu.BindingType.uniform_buffer, render_info.stdinfo)}
    bindings1 = {}

    bindings1[0] = wgpu.BindingType.uniform_buffer, material.uniform_buffer

    # Collect texture and sampler
    if material.map is not None:
        if isinstance(material.map, BaseTexture):
            raise TypeError(
                "material.map is a Texture, but must be a TextureView")
        elif not isinstance(material.map, TextureView):
            raise TypeError("material.map must be a TextureView")
        elif getattr(geometry, "texcoords", None) is None:
            raise ValueError(
                "material.map is present, but geometry has no texcoords")
        bindings1[1] = wgpu.BindingType.sampler, material.map
        bindings1[2] = wgpu.BindingType.sampled_texture, material.map
        if "rgba" in material.map.format:
            fragment_shader = fragment_shader_textured_rgba
        else:
            fragment_shader = fragment_shader_textured_gray
        # Use a version of the shader for float textures if necessary
        if "float" in material.map.format:
            if not hasattr(fragment_shader, "float_version"):
                func = fragment_shader.input
                tex_anno = func.__annotations__["t_tex"]
                func.__annotations__["t_tex"] = tex_anno[:2] + ("2d f32", )
                fragment_shader.float_version = python2shader(func)
                func.__annotations__["t_tex"] = tex_anno
            fragment_shader = fragment_shader.float_version

    if index_buffer:
        n = len(index_buffer.data)
    else:
        n = len(vertex_buffers[0].data)

    # Put it together!

    return [{
        "vertex_shader": vertex_shader,
        "fragment_shader": fragment_shader,
        "primitive_topology": wgpu.PrimitiveTopology.triangle_list,
        "indices": (range(n), range(1)),
        "index_buffer": index_buffer,
        "vertex_buffers": vertex_buffers,
        "bindings0": bindings0,
        "bindings1": bindings1,
    }]
Exemple #15
0
def python2shader_and_validate(func):
    m = pyshader.python2shader(func)
    assert m.input is func
    validate_module(m, HASHES)
    return m
Exemple #16
0
def python2shader_and_validate_nochecks(func):
    m = pyshader.python2shader(func)
    assert m.input is func
    validate_module(m, HASHES, check_bytecode=False, check_spirv=False)
    return m