summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Harris <pharris@opentext.com>2014-03-10 13:02:04 -0400
committerPeter Harris <pharris@opentext.com>2014-03-10 13:02:04 -0400
commitc404049a52f7999d716bc0e2cf6c7af30c62f50f (patch)
tree8d96361d055bd8d829a046893929901c15ff00e5
Initial commit
-rw-r--r--genshaders.go511
-rw-r--r--geometry.template62
-rw-r--r--glyphs.template42
-rw-r--r--pixel.template43
-rw-r--r--simple.template21
-rw-r--r--vertex.template47
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;
+}