diff options
author | Peter Harris <pharris@opentext.com> | 2014-03-10 13:02:04 -0400 |
---|---|---|
committer | Peter Harris <pharris@opentext.com> | 2014-03-10 13:02:04 -0400 |
commit | c404049a52f7999d716bc0e2cf6c7af30c62f50f (patch) | |
tree | 8d96361d055bd8d829a046893929901c15ff00e5 |
Initial commit
-rw-r--r-- | genshaders.go | 511 | ||||
-rw-r--r-- | geometry.template | 62 | ||||
-rw-r--r-- | glyphs.template | 42 | ||||
-rw-r--r-- | pixel.template | 43 | ||||
-rw-r--r-- | simple.template | 21 | ||||
-rw-r--r-- | vertex.template | 47 |
6 files changed, 726 insertions, 0 deletions
diff --git a/genshaders.go b/genshaders.go new file mode 100644 index 0000000..4943806 --- /dev/null +++ b/genshaders.go @@ -0,0 +1,511 @@ +// genshaders is a tool to generate fragment shaders that implement the +// X11 GC operations, and to generate fragment shaders that implement +// (some of) the RENDER extension blend operations. +// +// Note that some of the generated shaders are much more complex than +// they need to be; genshaders relies on the fxc optimizer to elimiate +// dead code. For example, the entire noop set contains generated code +// for the rest of the GC operations, but fxc optimizes it all to a +// single discard instruction. +// +// Also generated are a few useful vertex and geometry shaders. + +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "os/exec" + "runtime" + "sync" + "text/template" +) + +type compInfo struct { + name string + suffix string +} + +var compile = flag.Bool("compile", false, "Invoke the fxc compiler on each shader") + +func compiler(wg *sync.WaitGroup, log chan string, in chan compInfo) { + var errorBuf bytes.Buffer + for { + ci := <-in + if *compile { + model := ci.suffix + "_4_0" + + out, err := os.Create(ci.name + ".asm") + if err != nil { + panic(err) + } + cmd := exec.Command("fxc", "/T"+model, "/O3", ci.name+".hl"+ci.suffix) + cmd.Stdout = out + errorBuf.Reset() + cmd.Stderr = &errorBuf + err = cmd.Run() + if err != nil { + fmt.Fprintln(os.Stderr, "\nFatal Error while creating asm file", ci.name) + fmt.Fprintln(os.Stderr, err.Error()) + errorBuf.WriteTo(os.Stderr) + os.Exit(1) + } + out.Close() + + cmd = exec.Command("fxc", "/T"+model, "/O3", ci.name+".hl"+ci.suffix, "/Fo"+ci.name+"."+ci.suffix) + cmd.Run() + + out, err = os.Create(ci.name + ".h") + if err != nil { + panic(err) + } + cmd = exec.Command("xxd", "-i", ci.name+"."+ci.suffix) + cmd.Stdout = out + errorBuf.Reset() + cmd.Stderr = &errorBuf + err = cmd.Run() + if err != nil { + fmt.Fprintln(os.Stderr, "\n Fatal Error while xxding", ci.name) + fmt.Fprintln(os.Stderr, err.Error()) + errorBuf.WriteTo(os.Stderr) + os.Exit(1) + } + out.Close() + + log <- ci.name + " Compiled." + } + wg.Done() + } +} + +type GlyphS struct { + Cbuf string + TilePos string + GlyphPos string + OutPos string +} + +func doVert(wg *sync.WaitGroup, log chan string, comp chan compInfo) { + wg.Add(3) + + vt, err := template.ParseFiles("vertex.template") + if err != nil { + panic(err) + } + out, err := os.Create("size_calc.hlvs") + if err != nil { + panic(err) + } + err = vt.Execute(out, nil) + if err != nil { + panic(err) + } + out.Close() + comp <- compInfo{"size_calc", "vs"} + + glypht, err := template.ParseFiles("glyphs.template") + if err != nil { + panic(err) + } + out, err = os.Create("render_glyphs.hlvs") + if err != nil { + panic(err) + } + render := GlyphS{"", "input.glyph_pos", "0", "0"} + err = glypht.Execute(out, &render) + if err != nil { + panic(err) + } + out.Close() + comp <- compInfo{"render_glyphs", "vs"} + + out, err = os.Create("core_glyphs.hlvs") + if err != nil { + panic(err) + } + core := GlyphS{` +cbuffer output_info :register(b1) { + uint2 out_offset; +} +`, + "input.pos - tile_offset", + "input.glyph_pos / (float2)clip_size", + "input.pos - out_offset"} + err = glypht.Execute(out, &core) + if err != nil { + panic(err) + } + out.Close() + comp <- compInfo{"core_glyphs", "vs"} + + log <- "Vertex shaders done." + wg.Done() +} + +func doGeom(wg *sync.WaitGroup, log chan string, comp chan compInfo) { + gt, err := template.ParseFiles("geometry.template") + if err != nil { + panic(err) + } + out, err := os.Create("line2rect.hlgs") + if err != nil { + panic(err) + } + err = gt.Execute(out, nil) + if err != nil { + panic(err) + } + out.Close() + + wg.Add(1) + comp <- compInfo{"line2rect", "gs"} + + log <- "Geometry shader done." + wg.Done() +} + +type pixelInfo struct { + Rop string + Style string + PlaneMask string + ClipMask string + Tex string + ClipMaskBlock string + Load string + RopBlock string + PlaneMaskBlock string +} + +var rop = [...]string{"clear", "and", "andreverse", "copy", "andinverted", "noop", "xor", "or", "nor", "equiv", "invert", "orreverse", "copyinverted", "orinverted", "nand", "set"} +var style = [...]string{"solid", "tile", "stipple", "opaquestip"} +var planemask = [...]string{"fullmask", "planemask"} +var clipmask = [...]string{"unclipped", "clipmask"} + +func forEachCore(cb func(r, s, p, c string)) { + for _, c := range clipmask { + for _, p := range planemask { + for _, s := range style { + for _, r := range rop { + cb(r, s, p, c) + } + } + } + } +} + +func forEachCopyPlane(cb func(r, s, p, c string)) { + for _, c := range clipmask { + for _, p := range planemask { + for _, r := range rop { + cb(r, "copyplane", p, c) + } + } + } +} + +func doPixel(wg *sync.WaitGroup, log chan string, comp chan compInfo) { + pt, err := template.ParseFiles("pixel.template") + if err != nil { + panic(err) + } + + cmask := map[string]string{ + "unclipped": "", + "clipmask": ` // Test clip mask + float4 cm = clipmask.Sample(point_sample, input.clip_pos); + if (cm.r < 0.5) { + discard; + return 0; + } + +`} + pmask := map[string]string{ + "fullmask": "", + "planemask": ` + // planemask + color = (color & planemask) | (out_color & ~planemask); +`} + style := map[string]string{ + "solid": " color = foreground;", + "tile": ` int3 tp; + tp.xy = input.tile_pos.xy % tile_wh.xy; + tp.z = 0; + color = tile.Load(tp);`, + "stipple": ` float4 pt = stipple.Sample(point_sample, input.tile_pos); + if (pt.r < 0.5) { + discard; + return 0; + } + color = foreground;`, + "opaquestip": ` float4 pt = stipple.Sample(point_sample, input.tile_pos); + color = foreground; + if (pt.r < 0.5) + color = background;`, + "copyplane": ` int3 tp; + tp.xy = input.tile_pos.xy % tile_wh.xy; + tp.z = 0; + color = tile.Load(tp); + if (any(color & plane)) + color = foreground; + else + color = background;`, + } + rop := map[string]string{ + "clear": "color = 0", + "and": "color = color & out_color", + "andreverse": "color = color & ~out_color", + "copy": "color = color", + "andinverted": "color = ~color & out_color", + "noop": "discard", + "xor": "color = color ^ out_color", + "or": "color = color | out_color", + "nor": "color = ~color & ~out_color", + "equiv": "color = ~color ^ out_color", + "invert": "color = ~out_color", + "orreverse": "color = color | ~out_color", + "copyinverted": "color = ~color", + "orinverted": "color = ~color | out_color", + "nand": "color = ~color | ~out_color", + "set": "color = 255", + } + + var pi pixelInfo + for pi.ClipMask, pi.ClipMaskBlock = range cmask { + for pi.PlaneMask, pi.PlaneMaskBlock = range pmask { + for pi.Style, pi.Load = range style { + for pi.Rop, pi.RopBlock = range rop { + name := pi.Rop + "_" + pi.Style + "_" + pi.PlaneMask + "_" + pi.ClipMask + pi.Tex = "" + switch pi.Style { + case "stipple", "opaquestip": + pi.Tex = "\nTexture2D stipple : register(t0);" + case "tile", "copyplane": + pi.Tex = "\nTexture2D<uint4> tile : register(t0);" + } + if pi.ClipMask == "clipmask" { + pi.Tex += "\nTexture2D clipmask : register(t1);" + } + + out, err := os.Create(name + ".hlps") + if err != nil { + panic(err) + } + err = pt.Execute(out, &pi) + if err != nil { + panic(err) + } + out.Close() + wg.Add(1) + comp <- compInfo{name, "ps"} + } + } + } + } + + pix_h, err := os.Create("pixel_shaders.h") + if err != nil { + panic(err) + } + fmt.Fprintln(pix_h, "/* Automatically generated file; do not edit */") + fmt.Fprintln(pix_h) + include := func(rop, style, planemask, clipmask string) { + name := rop + "_" + style + "_" + planemask + "_" + clipmask + fmt.Fprintln(pix_h, `#include "`+name+`.h"`) + } + ps := func(rop, style, planemask, clipmask string) { + name := " " + rop + "_" + style + "_" + planemask + "_" + clipmask + "_ps," + fmt.Fprintln(pix_h, name) + } + ps_len := func(rop, style, planemask, clipmask string) { + name := " " + rop + "_" + style + "_" + planemask + "_" + clipmask + "_ps_len," + fmt.Fprintln(pix_h, name) + } + forEachCore(include) + forEachCopyPlane(include) + + fmt.Fprintln(pix_h) + fmt.Fprintln(pix_h, "static const unsigned char *core_ps[] = {") + forEachCore(ps) + fmt.Fprintln(pix_h, "};") + fmt.Fprintln(pix_h) + + fmt.Fprintln(pix_h, "static const unsigned int core_ps_len[] = {") + forEachCore(ps_len) + fmt.Fprintln(pix_h, "};") + fmt.Fprintln(pix_h) + + fmt.Fprintln(pix_h, "static const unsigned char *copyplane_ps[] = {") + forEachCopyPlane(ps) + fmt.Fprintln(pix_h, "};") + fmt.Fprintln(pix_h) + + fmt.Fprintln(pix_h, "static const unsigned int copyplane_ps_len[] = {") + forEachCopyPlane(ps_len) + fmt.Fprintln(pix_h, "};") + + pix_h.Close() + log <- "Pixel shaders done." + wg.Done() +} + +func foreachRenderSimple(cb func(name string, hasmask, srcswiz, dstswiz, maskswiz, compalpha, skip bool)) { + falsetrue := [...]bool{false, true} + for _, srcswizzle := range falsetrue { + for _, dstswizzle := range falsetrue { + for _, hasmask := range falsetrue { + for _, maskswizzle := range falsetrue { + for _, componentalpha := range falsetrue { + skip := maskswizzle && !hasmask + skip = skip || (componentalpha && !hasmask) + + name := "render_simple" + map[bool]string{true: "_sswiz"}[srcswizzle] + + map[bool]string{true: "_dswiz"}[dstswizzle] + + map[bool]string{true: "_mask"}[hasmask] + + map[bool]string{true: "_swiz"}[maskswizzle] + + map[bool]string{true: "_ca"}[componentalpha] + + cb(name, hasmask, srcswizzle, dstswizzle, maskswizzle, componentalpha, skip) + } + } + } + } + } +} + +type simpleRender struct { + Description string + SrcSwiz, Mask, MaskSwiz, CompAlpha string + Return string +} + +func doRender(wg *sync.WaitGroup, log chan string, comp chan compInfo) { + rt, err := template.ParseFiles("simple.template") + if err != nil { + panic(err) + } + + simple_h, err := os.Create("render_simple_shaders.h") + if err != nil { + panic(err) + } + fmt.Fprintln(simple_h, "/* Automatically generated file; do not edit */") + fmt.Fprintln(simple_h) + + foreachRenderSimple(func(name string, hasmask, srcswiz, dstswiz, maskswiz, compalpha, skip bool) { + if skip { + return + } + + fmt.Fprintln(simple_h, `#include "`+name+`.h"`) + + var sr simpleRender + if srcswiz { + sr.Description += " for source swizzled" + } + if hasmask { + sr.Description += " with mask" + } + if maskswiz { + sr.Description += " swizzled" + } + if compalpha { + sr.Description += ", component alpha" + } + if srcswiz { + sr.SrcSwiz = ` + s.a = s.r; + s.rgb = 0;` + } + + if hasmask { + sr.Mask = "\n float4 m = mask.Sample(mask_sample, input.mask_pos);" + if maskswiz { + sr.MaskSwiz = "\n m.a = m.r;" + } + sr.CompAlpha = "\n s *= m.a;" + if compalpha { + sr.CompAlpha = "\n s *= m;" + } + } + sr.Return = "s" + if dstswiz { + sr.Return = "s.a" + } + + out, err := os.Create(name + ".hlps") + if err != nil { + panic(err) + } + err = rt.Execute(out, &sr) + if err != nil { + panic(err) + } + out.Close() + wg.Add(1) + comp <- compInfo{name, "ps"} + }) + + fmt.Fprintln(simple_h) + fmt.Fprintln(simple_h, "static const unsigned char *render_simple[] = {") + foreachRenderSimple(func(name string, hasmask, srcswiz, dstswiz, maskswiz, compalpha, skip bool) { + if skip { + fmt.Fprintln(simple_h, " NULL,") + } else { + fmt.Fprintln(simple_h, " "+name+"_ps,") + } + }) + fmt.Fprintln(simple_h, "};") + fmt.Fprintln(simple_h) + fmt.Fprintln(simple_h, "static const unsigned int render_simple_len[] = {") + foreachRenderSimple(func(name string, hasmask, srcswiz, dstswiz, maskswiz, compalpha, skip bool) { + if skip { + fmt.Fprintln(simple_h, " 0,") + } else { + fmt.Fprintln(simple_h, " "+name+"_ps_len,") + } + }) + fmt.Fprintln(simple_h, "};") + simple_h.Close() + + log <- "Render shaders done." + wg.Done() +} + +func logger(wg *sync.WaitGroup, in chan string) { + for { + msg := <-in + if msg == "" { + wg.Done() + return + } + fmt.Println(msg) + } +} + +func main() { + flag.Parse() + + var wg sync.WaitGroup + log := make(chan string) + go logger(&wg, log) + + // Limit number of concurrent compiles + compile := make(chan compInfo) + for i := 0; i < runtime.NumCPU(); i++ { + go compiler(&wg, log, compile) + } + + wg.Add(4) + go doVert(&wg, log, compile) + go doGeom(&wg, log, compile) + go doPixel(&wg, log, compile) + go doRender(&wg, log, compile) + wg.Wait() + + wg.Add(1) + log <- "Done." + log <- "" + wg.Wait() +} diff --git a/geometry.template b/geometry.template new file mode 100644 index 0000000..f5b3991 --- /dev/null +++ b/geometry.template @@ -0,0 +1,62 @@ +/* Automatically generated file; do not edit */ + +// This geometry shader takes a list of lines as input, and +// generates a (4 point) triangle strip representing a rectangle +// for each. + +struct vertex { + float2 tile_pos :TEXCOORD0; + float2 clip_pos :TEXCOORD1; + float2 out_pos :TEXCOORD2; + float4 pos :SV_POSITION; +}; + +[maxvertexcount(4)] +void main(line vertex input[2], inout TriangleStream<vertex> output) +{ + vertex v; + v.pos.z = 0; + v.pos.w = 1; + + v.pos.x = input[0].pos.x; + v.tile_pos.x = input[0].tile_pos.x; + v.clip_pos.x = input[0].clip_pos.x; + v.out_pos.x = input[0].out_pos.x; + v.pos.y = input[1].pos.y; + v.tile_pos.y = input[1].tile_pos.y; + v.clip_pos.y = input[1].clip_pos.y; + v.out_pos.y = input[1].out_pos.y; + output.Append(v); + + v.pos.x = input[0].pos.x; + v.tile_pos.x = input[0].tile_pos.x; + v.clip_pos.x = input[0].clip_pos.x; + v.out_pos.x = input[0].out_pos.x; + v.pos.y = input[0].pos.y; + v.tile_pos.y = input[0].tile_pos.y; + v.clip_pos.y = input[0].clip_pos.y; + v.out_pos.y = input[0].out_pos.y; + output.Append(v); + + v.pos.x = input[1].pos.x; + v.tile_pos.x = input[1].tile_pos.x; + v.clip_pos.x = input[1].clip_pos.x; + v.out_pos.x = input[1].out_pos.x; + v.pos.y = input[1].pos.y; + v.tile_pos.y = input[1].tile_pos.y; + v.clip_pos.y = input[1].clip_pos.y; + v.out_pos.y = input[1].out_pos.y; + output.Append(v); + + v.pos.x = input[1].pos.x; + v.tile_pos.x = input[1].tile_pos.x; + v.clip_pos.x = input[1].clip_pos.x; + v.out_pos.x = input[1].out_pos.x; + v.pos.y = input[0].pos.y; + v.tile_pos.y = input[0].tile_pos.y; + v.clip_pos.y = input[0].clip_pos.y; + v.out_pos.y = input[0].out_pos.y; + output.Append(v); + + output.RestartStrip(); +} diff --git a/glyphs.template b/glyphs.template new file mode 100644 index 0000000..bf0541f --- /dev/null +++ b/glyphs.template @@ -0,0 +1,42 @@ +/* Automatically generated file; do not edit */ + +// This vertex shader takes inputs in integer pixels, as well as +// a glyph position in integer texels, and outputs +// in floating point coordinates understood by the DX drawing system. + +struct VS_INPUT { + uint2 pos :POSITION; + uint2 glyph_pos :TEXCOORD0; +}; + +struct VS_OUTPUT { + float2 tile_pos :TEXCOORD0; + float2 glyph_pos :TEXCOORD1; + float2 out_pos :TEXCOORD2; + float4 pos :SV_POSITION; +}; + +cbuffer info :register(b0) { + float2 wh; // Half width, half height of target drawable + uint2 tile_offset; + uint2 stipple_size; + uint2 clip_offset; // unused (glyph pos is taken from input.glyph_pos) + uint2 clip_size; // size of the entire glyph atlas +} +{{.Cbuf}} +VS_OUTPUT main(VS_INPUT input) +{ + VS_OUTPUT output; + output.pos.x = input.pos.x / wh.x - 1.0f; + output.pos.y = -(input.pos.y / wh.y - 1.0f); + output.pos.z = 0; + output.pos.w = 1; + + output.tile_pos = {{.TilePos}}; + output.tile_pos /= (float2)stipple_size; + + output.glyph_pos = {{.GlyphPos}}; + output.out_pos = {{.OutPos}}; + + return output; +} diff --git a/pixel.template b/pixel.template new file mode 100644 index 0000000..c985114 --- /dev/null +++ b/pixel.template @@ -0,0 +1,43 @@ +/* Automatically generated file; do not edit */ + +/* {{.Rop}} shader for {{.Style}} with {{.PlaneMask}} and {{.ClipMask}} */ + +SamplerState point_sample; +{{.Tex}} +Texture2D<uint4> output : register(t2); + +cbuffer colors :register(b0) { + uint4 foreground; + uint4 background; + uint4 planemask; +} + +cbuffer tile_stats :register(b1) { + uint4 plane; + uint2 tile_wh; +} + +struct PS_INPUT { + float2 tile_pos : TEXCOORD0; + float2 clip_pos : TEXCOORD1; + float2 out_pos : TEXCOORD2; +}; + +uint4 main(PS_INPUT input) : SV_TARGET +{ +{{.ClipMaskBlock}} uint4 color; + + // Load input color +{{.Load}} + + // Load output color + int3 pos; + pos.xy = input.out_pos.xy; + pos.z = 0; + uint4 out_color = output.Load(pos); + + // rop + {{.RopBlock}}; +{{.PlaneMaskBlock}} + return color; +} diff --git a/simple.template b/simple.template new file mode 100644 index 0000000..2b02782 --- /dev/null +++ b/simple.template @@ -0,0 +1,21 @@ +/* Automatically generated file; do not edit */
+
+/* simple render shader{{.Description}} */
+
+SamplerState src_sample : register(s0);
+SamplerState mask_sample : register(s1);
+
+Texture2D src : register(t0);
+Texture2D mask : register(t1);
+
+struct PS_INPUT {
+ float2 src_pos : TEXCOORD0;
+ float2 mask_pos : TEXCOORD1;
+};
+
+float4 main(PS_INPUT input) : SV_TARGET
+{
+ float4 s = src.Sample(src_sample, input.src_pos);
+{{.SrcSwiz}}{{.Mask}}{{.MaskSwiz}}{{.CompAlpha}}
+ return {{.Return}};
+}
diff --git a/vertex.template b/vertex.template new file mode 100644 index 0000000..72d4c35 --- /dev/null +++ b/vertex.template @@ -0,0 +1,47 @@ +/* Automatically generated file; do not edit */ + +// This vertex shader takes inputs in integer pixels, as well as +// clip, tile, and output offsets in integer pixels, and outputs +// in floating point coordinates understood by the DX drawing system. + +struct VS_INPUT { + uint2 pos :POSITION; +}; + +struct VS_OUTPUT { + float2 tile_pos :TEXCOORD0; + float2 clip_pos :TEXCOORD1; + float2 out_pos :TEXCOORD2; + float4 pos :SV_POSITION; +}; + +cbuffer info :register(b0) { + float2 wh; // Half width, half height of target drawable + uint2 tile_offset; + uint2 stipple_size; // Set to 1 x 1 for tiles + uint2 clip_offset; + uint2 clip_size; +} + +cbuffer output_info :register(b1) { + uint2 out_offset; +} + +VS_OUTPUT main(VS_INPUT input) +{ + VS_OUTPUT output; + output.pos.x = input.pos.x / wh.x - 1.0f; + output.pos.y = -(input.pos.y / wh.y - 1.0f); + output.pos.z = 0; + output.pos.w = 1; + + output.tile_pos = input.pos - tile_offset; + output.tile_pos /= (float2)stipple_size; + + output.clip_pos = input.pos - clip_offset; + output.clip_pos /= (float2)clip_size; + + output.out_pos = input.pos - out_offset; + + return output; +} |