| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- import os
- import platform
- import pytest
- from _pytest.outcomes import Failed
- from panda3d import core
- SHADERS_DIR = core.Filename.from_os_specific(os.path.dirname(__file__))
- # This is the template for the shader that is used by run_cg_test.
- # We render this to an nx1 texture, where n is the number of lines in the body.
- # An assert
- CG_VERTEX_TEMPLATE = """//Cg
- void vshader(float4 vtx_position : POSITION, out float4 l_position : POSITION) {{
- l_position = vtx_position;
- }}
- """
- CG_FRAGMENT_TEMPLATE = """//Cg
- {preamble}
- float4 _assert(bool cond) {{
- return float4(cond.x, 1, 1, 1);
- }}
- float4 _assert(bool2 cond) {{
- return float4(cond.x, cond.y, 1, 1);
- }}
- float4 _assert(bool3 cond) {{
- return float4(cond.x, cond.y, cond.z, 1);
- }}
- float4 _assert(bool4 cond) {{
- return float4(cond.x, cond.y, cond.z, cond.w);
- }}
- #define assert(cond) {{ if ((int)l_vpos.x == __LINE__ - line_offset) o_color = _assert(cond); }}
- void fshader(in float4 l_vpos : VPOS, out float4 o_color : COLOR) {{
- o_color = float4(1, 1, 1, 1);
- if ((int)l_vpos.x == 0) {{
- o_color = float4(0, 0, 0, 0);
- }}
- const int line_offset = __LINE__;
- {body}
- }}
- """
- def run_cg_test(gsg, body, preamble="", inputs={},
- state=core.RenderState.make_empty()):
- """ Runs a Cg test on the given GSG. The given body is executed in the
- main function and should call assert(). The preamble should contain all
- of the shader inputs. """
- if not gsg.supports_basic_shaders:
- pytest.skip("basic shaders not supported")
- __tracebackhide__ = True
- preamble = preamble.strip()
- body = body.rstrip().lstrip('\n')
- num_lines = body.count('\n') + 1
- vertex_code = CG_VERTEX_TEMPLATE.format(preamble=preamble, body=body)
- code = CG_FRAGMENT_TEMPLATE.format(preamble=preamble, body=body)
- shader = core.Shader.make(core.Shader.SL_Cg, vertex_code, code)
- if not shader:
- pytest.fail("error compiling shader:\n" + code)
- result = core.Texture("")
- fbprops = core.FrameBufferProperties()
- fbprops.force_hardware = True
- fbprops.set_rgba_bits(8, 8, 8, 8)
- fbprops.srgb_color = False
- engine = gsg.get_engine()
- buffer = engine.make_output(
- gsg.pipe,
- 'buffer',
- 0,
- fbprops,
- core.WindowProperties.size(core.Texture.up_to_power_2(num_lines + 1), 1),
- core.GraphicsPipe.BF_refuse_window,
- gsg
- )
- buffer.add_render_texture(result, core.GraphicsOutput.RTM_copy_ram, core.GraphicsOutput.RTP_color)
- buffer.set_clear_color_active(True)
- buffer.set_clear_color((0, 0, 0, 0))
- engine.open_windows()
- # Build up the shader inputs
- attrib = core.ShaderAttrib.make(shader)
- for name, value in inputs.items():
- attrib = attrib.set_shader_input(name, value)
- state = state.set_attrib(attrib)
- scene = core.NodePath("root")
- scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always))
- format = core.GeomVertexFormat.get_v3()
- vdata = core.GeomVertexData("tri", format, core.Geom.UH_static)
- vdata.unclean_set_num_rows(3)
- vertex = core.GeomVertexWriter(vdata, "vertex")
- vertex.set_data3(-1, -1, 0)
- vertex.set_data3(3, -1, 0)
- vertex.set_data3(-1, 3, 0)
- tris = core.GeomTriangles(core.Geom.UH_static)
- tris.add_next_vertices(3)
- geom = core.Geom(vdata)
- geom.add_primitive(tris)
- gnode = core.GeomNode("tri")
- gnode.add_geom(geom, state)
- scene.attach_new_node(gnode)
- scene.set_two_sided(True)
- camera = scene.attach_new_node(core.Camera("camera"))
- camera.node().get_lens(0).set_near_far(-10, 10)
- camera.node().set_cull_bounds(core.OmniBoundingVolume())
- region = buffer.make_display_region()
- region.active = True
- region.camera = camera
- try:
- engine.render_frame()
- except AssertionError as exc:
- assert False, "Error executing shader:\n" + code
- finally:
- engine.remove_window(buffer)
- # Download the texture to check whether the assertion triggered.
- triggered = tuple(result.get_ram_image())
- if triggered[0]:
- pytest.fail("control check failed")
- if not all(triggered[4:]):
- count = 0
- lines = body.split('\n')
- formatted = ''
- for i, line in enumerate(lines):
- offset = (i + 1) * 4
- x = triggered[offset + 2] == 0
- y = triggered[offset + 1] == 0
- z = triggered[offset] == 0
- w = triggered[offset + 3] == 0
- if x or y or z or w:
- count += 1
- else:
- continue
- formatted += '=> ' + line
- components = ''
- if x:
- components += 'x'
- if y:
- components += 'y'
- if z:
- components += 'z'
- if w:
- components += 'w'
- formatted += f' <= {components} components don\'t match'
- formatted += '\n'
- pytest.fail("{0} Cg assertions triggered:\n{1}".format(count, formatted))
- def run_cg_compile_check(gsg, shader_path, expect_fail=False):
- """Compile supplied Cg shader path and check for errors"""
- shader = core.Shader.load(shader_path, core.Shader.SL_Cg)
- # assert shader.is_prepared(gsg.prepared_objects)
- if expect_fail:
- assert shader is None
- else:
- assert shader is not None
- def test_cg_compile_error(gsg):
- """Test getting compile errors from bad Cg shaders"""
- shader_path = core.Filename(SHADERS_DIR, 'cg_bad.sha')
- run_cg_compile_check(gsg, shader_path, expect_fail=True)
- def test_cg_from_file(gsg):
- """Test compiling Cg shaders from files"""
- shader_path = core.Filename(SHADERS_DIR, 'cg_simple.sha')
- run_cg_compile_check(gsg, shader_path)
- def test_cg_test(gsg):
- "Test to make sure that the Cg tests work correctly."
- run_cg_test(gsg, "assert(true);")
- def test_cg_test_fail(gsg):
- "Same as above, but making sure that the failure case works correctly."
- with pytest.raises(Failed):
- run_cg_test(gsg, "assert(false);")
- def test_cg_sampler(gsg):
- tex1 = core.Texture("tex1-ubyte-rgba8")
- tex1.setup_1d_texture(1, core.Texture.T_unsigned_byte, core.Texture.F_rgba8)
- tex1.set_clear_color((0, 2 / 255.0, 1, 1))
- tex2 = core.Texture("tex2-float-rgba32")
- tex2.setup_2d_texture(1, 1, core.Texture.T_float, core.Texture.F_rgba32)
- tex2.set_clear_color((1.0, 2.0, -3.14, 0.0))
- tex3 = core.Texture("tex3-float-r32")
- tex3.setup_3d_texture(1, 1, 1, core.Texture.T_float, core.Texture.F_r32)
- tex3.set_clear_color((0.5, 0.0, 0.0, 1.0))
- preamble = """
- uniform sampler1D tex1;
- uniform sampler2D tex2;
- uniform sampler3D tex3;
- """
- code = """
- assert(tex1D(tex1, 0) == float4(0, 2 / 255.0, 1, 1));
- assert(tex2D(tex2, float2(0, 0)) == float4(1.0, 2.0, -3.14, 0.0));
- assert(abs(tex3D(tex3, float3(0, 0, 0)).r - 0.5) < 0.01);
- """
- run_cg_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2, 'tex3': tex3})
- def test_cg_int(gsg):
- inputs = dict(
- zero=0,
- ten=10,
- intmax=0x7fffffff,
- intmin=-0x7fffffff,
- )
- preamble = """
- uniform int zero;
- uniform int intmax;
- uniform int intmin;
- """
- code = """
- assert(zero == 0);
- assert(intmax == 0x7fffffff);
- assert(intmin == -0x7fffffff);
- """
- run_cg_test(gsg, code, preamble, inputs)
- def test_cg_state_material(gsg):
- mat = core.Material("mat")
- mat.ambient = (1, 2, 3, 4)
- mat.diffuse = (5, 6, 7, 8)
- mat.emission = (9, 10, 11, 12)
- mat.specular = (13, 14, 15, 0)
- mat.shininess = 16
- preamble = """
- uniform float4x4 attr_material;
- """
- code = """
- assert(attr_material[0] == float4(1, 2, 3, 4));
- assert(attr_material[1] == float4(5, 6, 7, 8));
- assert(attr_material[2] == float4(9, 10, 11, 12));
- assert(attr_material[3].rgb == float3(13, 14, 15));
- assert(attr_material[3].w == 16);
- """
- node = core.NodePath("state")
- node.set_material(mat)
- run_cg_test(gsg, code, preamble, state=node.get_state())
- def test_cg_state_fog(gsg):
- fog = core.Fog("fog")
- fog.color = (1, 2, 3, 4)
- fog.exp_density = 0.5
- fog.set_linear_range(6, 10)
- preamble = """
- uniform float4 attr_fog;
- uniform float4 attr_fogcolor;
- """
- code = """
- assert(attr_fogcolor == float4(1, 2, 3, 4));
- assert(attr_fog[0] == 0.5);
- assert(attr_fog[1] == 6);
- assert(attr_fog[2] == 10);
- assert(attr_fog[3] == 0.25);
- """
- node = core.NodePath("state")
- node.set_fog(fog)
- run_cg_test(gsg, code, preamble, state=node.get_state())
- def test_cg_texpad_texpix(gsg):
- tex = core.Texture("test")
- tex.setup_2d_texture(16, 32, core.Texture.T_unsigned_byte, core.Texture.F_rgba)
- tex.auto_texture_scale = core.ATS_pad
- tex.set_size_padded(10, 30)
- preamble = """
- uniform float3 texpad_test;
- uniform float2 texpix_test;
- """
- code = """
- assert(texpad_test == float3(10 * 0.5 / 16, 30 * 0.5 / 32, 0.5));
- assert(texpix_test == float2(1.0 / 16, 1.0 / 32));
- """
- run_cg_test(gsg, code, preamble, inputs={"test": tex})
- def test_cg_alight(gsg):
- alight = core.AmbientLight("alight")
- alight.set_color((1, 2, 3, 4))
- np = core.NodePath(alight)
- preamble = """
- uniform float4 alight_test;
- """
- code = """
- assert(alight_test == float4(1, 2, 3, 4));
- """
- run_cg_test(gsg, code, preamble, inputs={"test": np})
- def test_cg_satten(gsg):
- spot = core.Spotlight("spot")
- spot.set_attenuation((1, 2, 3))
- spot.set_exponent(4)
- np = core.NodePath(spot)
- preamble = """
- uniform float4 satten_test;
- """
- code = """
- assert(satten_test == float4(1, 2, 3, 4));
- """
- run_cg_test(gsg, code, preamble, inputs={"test": np})
|