Gallium3D interfaces don't match any particular graphics API 1 to 1. Likewise, conformance tests end up not doing a good coverage of Gallium3D's interface either: sometimes a single Gallium3D feature is tested in many different tests; sometimes a feature ends up not being exercised by any test, so bugs are only detected in applications, where they are much harder to narrow down. And so appeared the need to write some tests at the Gallium3D interface level.
Since the ability to write tests quickly was important, and the running speed not so important, I've decided to write a Python bindings, so that tests could be scripted in Python. These bindings wrap around the pipe driver, so that they look like a pipe driver from the Python script point of view, and look like a state tracker from the pipe driver point of view.
About the tests there is not much to write about yet. I wrote tests for texture formats that allowed to squash the bugs I was searching for, and I imagine that more tests will be added as needs justify it.
However, having a Gallium3D bindings in Python opens several doors. One particularly is that it becomes a nice sandbox to learn Gallium3D. For example. here is the code to draw a single triangle:
def test(dev): ctx = dev.context_create() width = 255 height = 255 # disabled blending/masking blend = Blend() blend.rgb_src_factor = PIPE_BLENDFACTOR_ONE blend.alpha_src_factor = PIPE_BLENDFACTOR_ONE blend.rgb_dst_factor = PIPE_BLENDFACTOR_ZERO blend.alpha_dst_factor = PIPE_BLENDFACTOR_ZERO blend.colormask = PIPE_MASK_RGBA ctx.set_blend(blend) # no-op depth/stencil/alpha depth_stencil_alpha = DepthStencilAlpha() ctx.set_depth_stencil_alpha(depth_stencil_alpha) # rasterizer rasterizer = Rasterizer() rasterizer.front_winding = PIPE_WINDING_CW rasterizer.cull_mode = PIPE_WINDING_NONE rasterizer.bypass_clipping = 1 rasterizer.scissor = 1 #rasterizer.bypass_vs = 1 ctx.set_rasterizer(rasterizer) # viewport (identity, we setup vertices in wincoords) viewport = Viewport() scale = FloatArray(4) scale = 1.0 scale = 1.0 scale = 1.0 scale = 1.0 viewport.scale = scale translate = FloatArray(4) translate = 0.0 translate = 0.0 translate = 0.0 translate = 0.0 viewport.translate = translate ctx.set_viewport(viewport) # samplers sampler = Sampler() sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE sampler.min_img_filter = PIPE_TEX_MIPFILTER_NEAREST sampler.mag_img_filter = PIPE_TEX_MIPFILTER_NEAREST sampler.normalized_coords = 1 ctx.set_sampler(0, sampler) # scissor scissor = Scissor() scissor.minx = 0 scissor.miny = 0 scissor.maxx = width scissor.maxy = height ctx.set_scissor(scissor) clip = Clip() clip.nr = 0 ctx.set_clip(clip) # framebuffer cbuf = dev.texture_create( PIPE_FORMAT_X8R8G8B8_UNORM, width, height, tex_usage=PIPE_TEXTURE_USAGE_DISPLAY_TARGET, ) _cbuf = cbuf.get_surface(usage = PIPE_BUFFER_USAGE_GPU_READ|PIPE_BUFFER_USAGE_GPU_WRITE) fb = Framebuffer() fb.width = width fb.height = height fb.num_cbufs = 1 fb.set_cbuf(0, _cbuf) ctx.set_framebuffer(fb) _cbuf.clear_value = 0x00000000 ctx.surface_clear(_cbuf, _cbuf.clear_value) del _cbuf # vertex shader vs = Shader(' VERT1.1 DCL IN, POSITION, CONSTANT DCL IN, COLOR, CONSTANT DCL OUT, POSITION, CONSTANT DCL OUT, COLOR, CONSTANT 0:MOV OUT, IN 1:MOV OUT, IN 2:END ') ctx.set_vertex_shader(vs) # fragment shader fs = Shader(' FRAG1.1 DCL IN, COLOR, LINEAR DCL OUT, COLOR, CONSTANT 0:MOV OUT, IN 1:END ') ctx.set_fragment_shader(fs) nverts = 3 nattrs = 2 verts = FloatArray(nverts * nattrs * 4) verts[ 0] = 128.0 # x1 verts[ 1] = 32.0 # y1 verts[ 2] = 0.0 # z1 verts[ 3] = 1.0 # w1 verts[ 4] = 1.0 # r1 verts[ 5] = 0.0 # g1 verts[ 6] = 0.0 # b1 verts[ 7] = 1.0 # a1 verts[ 8] = 32.0 # x2 verts[ 9] = 224.0 # y2 verts = 0.0 # z2 verts = 1.0 # w2 verts = 0.0 # r2 verts = 1.0 # g2 verts = 0.0 # b2 verts = 1.0 # a2 verts = 224.0 # x3 verts = 224.0 # y3 verts = 0.0 # z3 verts = 1.0 # w3 verts = 0.0 # r3 verts = 0.0 # g3 verts = 1.0 # b3 verts = 1.0 # a3 ctx.draw_vertices(PIPE_PRIM_TRIANGLES, nverts, nattrs, verts) ctx.flush()
And this is the result:
In summary, you create several state atoms, bind them to the context, and then send the geometry through the pipe driver. Full source available in Mesa3D's git repository.
To use Gallium3D's Python bindings follow these instructions.
BTW, XDS 2008 is happening now. Too bad I couldn't go this year, as I would like to meet everybody. I hope you're having a great time!