from panda3d import core import struct import pytest from _pytest.outcomes import Failed # This is the template for the compute shader that is used by run_glsl_test. # It defines an assert() macro that writes failures to a buffer, indexed by # line number. # The reset() function serves to prevent the _triggered variable from being # optimized out in the case that the assertions are being optimized out. GLSL_COMPUTE_TEMPLATE = """#version {version} {extensions} layout(local_size_x = 1, local_size_y = 1) in; {preamble} layout(r8ui) uniform writeonly uimageBuffer _triggered; void _reset() {{ imageStore(_triggered, 0, uvec4(0, 0, 0, 0)); memoryBarrier(); }} void _assert(bool cond, int line) {{ if (!cond) {{ imageStore(_triggered, line, uvec4(1)); }} }} #define assert(cond) _assert(cond, __LINE__) void main() {{ _reset(); {body} }} """ def run_glsl_test(gsg, body, preamble="", inputs={}, version=150, exts=set()): """ Runs a GLSL 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_compute_shaders or not gsg.supports_glsl: pytest.skip("compute shaders not supported") if not gsg.supports_buffer_texture: pytest.skip("buffer textures not supported") exts = exts | {'GL_ARB_compute_shader', 'GL_ARB_shader_image_load_store'} missing_exts = sorted(ext for ext in exts if not gsg.has_extension(ext)) if missing_exts: pytest.skip("missing extensions: " + ' '.join(missing_exts)) extensions = '' for ext in exts: extensions += '#extension {ext} : require\n'.format(ext=ext) __tracebackhide__ = True preamble = preamble.strip() body = body.rstrip().lstrip('\n') code = GLSL_COMPUTE_TEMPLATE.format(version=version, extensions=extensions, preamble=preamble, body=body) line_offset = code[:code.find(body)].count('\n') + 1 shader = core.Shader.make_compute(core.Shader.SL_GLSL, code) assert shader, code # Create a buffer to hold the results of the assertion. We use one byte # per line of shader code, so we can show which lines triggered. result = core.Texture("") result.set_clear_color((0, 0, 0, 0)) result.setup_buffer_texture(code.count('\n'), core.Texture.T_unsigned_byte, core.Texture.F_r8i, core.GeomEnums.UH_static) # Build up the shader inputs attrib = core.ShaderAttrib.make(shader) for name, value in inputs.items(): attrib = attrib.set_shader_input(name, value) attrib = attrib.set_shader_input('_triggered', result) # Run the compute shader. engine = core.GraphicsEngine.get_global_ptr() try: engine.dispatch_compute((1, 1, 1), attrib, gsg) except AssertionError as exc: assert False, "Error executing compute shader:\n" + code # Download the texture to check whether the assertion triggered. assert engine.extract_texture_data(result, gsg) triggered = result.get_ram_image() if any(triggered): count = len(triggered) - triggered.count(0) lines = body.split('\n') formatted = '' for i, line in enumerate(lines): if triggered[i + line_offset]: formatted += '=> ' + line + '\n' else: formatted += ' ' + line + '\n' pytest.fail("{0} GLSL assertions triggered:\n{1}".format(count, formatted)) def test_glsl_test(gsg): "Test to make sure that the GLSL tests work correctly." run_glsl_test(gsg, "assert(true);") def test_glsl_test_fail(gsg): "Same as above, but making sure that the failure case works correctly." with pytest.raises(Failed): run_glsl_test(gsg, "assert(false);") def test_glsl_sampler(gsg): tex1 = core.Texture("") 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.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)) preamble = """ uniform sampler1D tex1; uniform sampler2D tex2; """ code = """ assert(texelFetch(tex1, 0, 0) == vec4(0, 2 / 255.0, 1, 1)); assert(texelFetch(tex2, ivec2(0, 0), 0) == vec4(1.0, 2.0, -3.14, 0.0)); """ run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}) def test_glsl_image(gsg): tex1 = core.Texture("") 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.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)) preamble = """ layout(rgba8) uniform image1D tex1; layout(rgba32f) uniform image2D tex2; """ code = """ assert(imageLoad(tex1, 0) == vec4(0, 2 / 255.0, 1, 1)); assert(imageLoad(tex2, ivec2(0, 0)) == vec4(1.0, 2.0, -3.14, 0.0)); """ run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}) def test_glsl_ssbo(gsg): from struct import pack num1 = pack('