diff --git a/gpu/backend.go b/gpu/backend.go index bf84b02b..bc3a3cb0 100644 --- a/gpu/backend.go +++ b/gpu/backend.go @@ -23,7 +23,7 @@ type Backend interface { NilTexture() Texture NewFramebuffer() Framebuffer NewBuffer(typ BufferType, data []byte) Buffer - NewProgram(vertexShader, fragmentShader string, attribMap []string) (Program, error) + NewProgram(vertexShader, fragmentShader ShaderSources, attribMap []string) (Program, error) SetupVertexArray(slot int, size int, dataType DataType, stride, offset int) DepthFunc(f DepthFunc) @@ -39,6 +39,32 @@ type Backend interface { BlendFunc(sfactor, dfactor BlendFactor) } +type ShaderSources struct { + GLES2 string + HLSL []byte + Uniforms []UniformLocation + Inputs []InputLocation +} + +type UniformLocation struct { + Name string + Type DataType + Size int + Offset int +} + +type InputLocation struct { + // For GLSL. + Name string + Location int + // For HLSL. + Semantic string + SemanticIndex int + + Type DataType + Size int +} + type BlendFactor uint8 type DrawMode uint8 diff --git a/gpu/build.go b/gpu/build.go new file mode 100644 index 00000000..59d7fef7 --- /dev/null +++ b/gpu/build.go @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// +build ignore + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "go/format" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + "text/template" +) + +// This program generates shader variants for +// multiple GPU backends (OpenGL ES, Direct3D 11...) +// from a single source. + +type shaderArgs struct { + FetchColorExpr string + Header string +} + +// InputLocation matches gpu.InputLocation. +type InputLocation struct { + Name string + Location int + Semantic string + SemanticIndex int + + Type DataType + Size int +} + +type DataType uint8 + +// UniformLocation matches gpu.UniformLocation. +type UniformLocation struct { + Name string + Type DataType + Size int + Offset int +} + +const ( + DataTypeFloat DataType = iota + DataTypeShort +) + +func main() { + if err := generate(); err != nil { + fmt.Fprintf(os.Stderr, "gpu generate: %v\n", err) + os.Exit(1) + } +} + +func generate() error { + tmp, err := ioutil.TempDir("", "gpu-generate") + if err != nil { + return err + } + defer os.RemoveAll(tmp) + glslcc, err := exec.LookPath("glslcc") + if err != nil { + return err + } + fxc, err := exec.LookPath("fxc") + fxcFound := err == nil + shaders, err := filepath.Glob("shaders/*") + if err != nil { + return err + } + var out bytes.Buffer + out.WriteString("// Code generated by build.go. DO NOT EDIT.\n\n") + out.WriteString("package gpu\n\n") + + out.WriteString("var (\n") + + for _, shader := range shaders { + const nvariants = 2 + var variants [nvariants]struct { + gles2 string + hlslSrc string + hlsl []byte + inputs []InputLocation + uniforms []UniformLocation + } + args := [nvariants]shaderArgs{ + { + FetchColorExpr: `color.color`, + Header: `layout(binding=0) uniform Color { vec4 color; } color;`, + }, + { + FetchColorExpr: `texture(tex, vUV)`, + Header: `layout(binding=0) uniform sampler2D tex;`, + }, + } + for i := range args { + gles2, reflect, err := convertShader(tmp, glslcc, shader, "gles", "100", &args[i], false) + if err != nil { + return err + } + // Make the GL ES 2 source compatible with desktop GL 3. + gles2 = "#version 100\n" + gles2 + inputs, uniforms, err := parseReflection(reflect) + if err != nil { + return err + } + hlsl, _, err := convertShader(tmp, glslcc, shader, "hlsl", "40", &args[i], false) + if err != nil { + return err + } + var hlslProf string + switch filepath.Ext(shader) { + case ".frag": + hlslProf = "ps" + case ".vert": + hlslProf = "vs" + default: + return fmt.Errorf("unrecognized shader type %s", shader) + } + var hlslc []byte + if fxcFound { + hlslc, err = compileHLSL(tmp, fxc, hlsl, "main", hlslProf+"_4_0") + if err != nil { + return err + } + } + variants[i].gles2 = gles2 + variants[i].hlslSrc = hlsl + variants[i].hlsl = hlslc + variants[i].inputs = inputs + variants[i].uniforms = uniforms + } + name := filepath.Base(shader) + name = strings.ReplaceAll(name, ".", "_") + fmt.Fprintf(&out, "\tshader_%s = ", name) + // If the shader don't use the variant arguments, output + // only a single version. + multiVariant := variants[0].gles2 != variants[1].gles2 + if multiVariant { + fmt.Fprintf(&out, "[...]ShaderSources{\n") + } + for _, src := range variants { + fmt.Fprintf(&out, "ShaderSources{\n") + if len(src.inputs) > 0 { + fmt.Fprintf(&out, "Inputs: []InputLocation{\n") + for _, inp := range src.inputs { + fmt.Fprintf(&out, "{Name: %q, Location: %d, Semantic: %q, ", inp.Name, inp.Location, inp.Semantic) + fmt.Fprintf(&out, "SemanticIndex: %d, Type: %d, Size: %d},\n", inp.SemanticIndex, inp.Type, inp.Size) + } + fmt.Fprintf(&out, "},\n") + } + if len(src.uniforms) > 0 { + fmt.Fprintf(&out, "Uniforms: []UniformLocation{\n") + for _, u := range src.uniforms { + fmt.Fprintf(&out, "{Name: %q, Type: %d, Size: %d, Offset: %d},\n", u.Name, u.Type, u.Size, u.Offset) + } + fmt.Fprintf(&out, "},\n") + } + fmt.Fprintf(&out, "GLES2: %#v,\n", src.gles2) + fmt.Fprintf(&out, "/*\n%s\n*/\n", src.hlslSrc) + fmt.Fprintf(&out, "HLSL: %#v,\n", src.hlsl) + fmt.Fprintf(&out, "}") + if multiVariant { + fmt.Fprintf(&out, ",") + } + fmt.Fprintf(&out, "\n") + if !multiVariant { + break + } + } + if multiVariant { + fmt.Fprintf(&out, "}\n") + } + } + out.WriteString(")") + gosrc, err := format.Source(out.Bytes()) + if err != nil { + return fmt.Errorf("shader.go: %v", err) + } + return ioutil.WriteFile("shaders.go", gosrc, 0644) +} + +func parseReflection(jsonData []byte) ([]InputLocation, []UniformLocation, error) { + type InputReflection struct { + ID int `json:"id"` + Name string `json:"name"` + Location int `json:"location"` + Semantic string `json:"semantic"` + SemanticIndex int `json:"semantic_index"` + Type string `json:"type"` + } + type UniformMemberReflection struct { + Name string `json:"name"` + Type string `json:"type"` + Offset int `json:"offset"` + Size int `json:"size"` + } + type UniformBufferReflection struct { + ID int `json:"id"` + Name string `json:"name"` + Set int `json:"set"` + Binding int `json:"binding"` + Size int `json:"block_size"` + Members []UniformMemberReflection `json:"members"` + } + type shaderReflection struct { + Inputs []InputReflection `json:"inputs"` + UniformBuffers []UniformBufferReflection `json:"uniform_buffers"` + } + type shaderMetadata struct { + VS shaderReflection `json:"vs"` + FS shaderReflection `json:"fs"` + } + var reflect shaderMetadata + if err := json.Unmarshal(jsonData, &reflect); err != nil { + return nil, nil, fmt.Errorf("parseReflection: %v", err) + } + var inputs []InputLocation + inputRef := reflect.VS.Inputs + for _, input := range inputRef { + dataType, dataSize, err := parseDataType(input.Type) + if err != nil { + return nil, nil, err + } + inputs = append(inputs, InputLocation{ + Name: input.Name, + Location: input.Location, + Semantic: input.Semantic, + SemanticIndex: input.SemanticIndex, + Type: dataType, + Size: dataSize, + }) + } + sort.Slice(inputs, func(i, j int) bool { + return inputs[i].Location < inputs[j].Location + }) + var ublocks []UniformLocation + shaderBlocks := reflect.VS.UniformBuffers + if len(shaderBlocks) == 0 { + shaderBlocks = reflect.FS.UniformBuffers + } + for _, block := range shaderBlocks { + for _, member := range block.Members { + dataType, size, err := parseDataType(member.Type) + if err != nil { + return nil, nil, err + } + ublocks = append(ublocks, UniformLocation{ + // Synthetic name generated by glslcc. + Name: fmt.Sprintf("_%d.%s", block.ID, member.Name), + Type: dataType, + Size: size, + Offset: member.Offset, + }) + } + } + return inputs, ublocks, nil +} + +func parseDataType(t string) (DataType, int, error) { + switch t { + case "float": + return DataTypeFloat, 1, nil + case "float2": + return DataTypeFloat, 2, nil + case "float3": + return DataTypeFloat, 3, nil + case "float4": + return DataTypeFloat, 4, nil + default: + return 0, 0, fmt.Errorf("unsupported input data type: %s", t) + } +} + +func compileHLSL(tmp, fxc, src, entry, profile string) ([]byte, error) { + tmpfile := filepath.Join(tmp, "shader.hlsl") + if err := ioutil.WriteFile(tmpfile, []byte(src), 0644); err != nil { + return nil, err + } + outFile := filepath.Join(tmp, "shader.bin") + cmd := exec.Command(fxc, + "/T", profile, + "/E", entry, + "/nologo", + "/Fo", outFile, + tmpfile, + ) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return nil, err + } + return ioutil.ReadFile(outFile) +} + +func convertShader(tmp, glslcc, path, lang, profile string, args *shaderArgs, flattenUBOs bool) (string, []byte, error) { + shaderTmpl, err := template.ParseFiles(path) + if err != nil { + return "", nil, err + } + var buf bytes.Buffer + if err := shaderTmpl.Execute(&buf, args); err != nil { + return "", nil, err + } + tmppath := filepath.Join(tmp, filepath.Base(path)) + if err := ioutil.WriteFile(tmppath, buf.Bytes(), 0644); err != nil { + return "", nil, err + } + defer os.Remove(tmppath) + var progFlag string + var progSuffix string + switch filepath.Ext(path) { + case ".vert": + progFlag = "--vert" + progSuffix = "vs" + case ".frag": + progFlag = "--frag" + progSuffix = "fs" + default: + return "", nil, fmt.Errorf("unrecognized shader type: %s", path) + } + cmd := exec.Command(glslcc, + "--silent", + "--optimize", + "--reflect", + "--output", filepath.Join(tmp, "shader"), + "--lang", lang, + "--profile", profile, + progFlag, tmppath, + ) + if flattenUBOs { + cmd.Args = append(cmd.Args, "--flatten-ubos") + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return "", nil, fmt.Errorf("%s: %v", path, err) + } + f, err := os.Open(filepath.Join(tmp, "shader_"+progSuffix)) + if err != nil { + return "", nil, err + } + defer f.Close() + defer os.Remove(f.Name()) + src, err := ioutil.ReadAll(f) + if err != nil { + return "", nil, err + } + reflect, err := ioutil.ReadFile(filepath.Join(tmp, "shader_"+progSuffix+".json")) + if err != nil { + return "", nil, err + } + return string(src), reflect, nil +} diff --git a/gpu/gen.go b/gpu/gen.go new file mode 100644 index 00000000..f2e3c26a --- /dev/null +++ b/gpu/gen.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gpu + +//go:generate go run build.go diff --git a/gpu/gl/backend.go b/gpu/gl/backend.go index 858c4bce..c1b2d02b 100644 --- a/gpu/gl/backend.go +++ b/gpu/gl/backend.go @@ -284,8 +284,8 @@ func (b *Backend) DepthFunc(f gpu.DepthFunc) { b.funcs.DepthFunc(glfunc) } -func (b *Backend) NewProgram(vssrc, fssrc string, attr []string) (gpu.Program, error) { - p, err := CreateProgram(b.funcs, vssrc, fssrc, attr) +func (b *Backend) NewProgram(vssrc, fssrc gpu.ShaderSources, attr []string) (gpu.Program, error) { + p, err := CreateProgram(b.funcs, vssrc.GLES2, fssrc.GLES2, attr) if err != nil { return nil, err } diff --git a/gpu/gpu.go b/gpu/gpu.go index 6fd29ab2..bc1f89af 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -13,7 +13,6 @@ import ( "image" "image/color" "math" - "strings" "time" "unsafe" @@ -376,7 +375,7 @@ func (r *renderer) release() { } func newBlitter(ctx Backend) *blitter { - prog, err := createColorPrograms(ctx, blitVSrc, blitFSrc) + prog, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag) if err != nil { panic(err) } @@ -398,14 +397,14 @@ func newBlitter(ctx Backend) *blitter { case materialTexture: uTex := prog.UniformFor("tex") prog.Uniform1i(uTex, 0) - b.vars[i].uUVScale = prog.UniformFor("uvScale") - b.vars[i].uUVOffset = prog.UniformFor("uvOffset") + b.vars[i].uUVScale = prog.UniformFor("uniforms.uvScale") + b.vars[i].uUVOffset = prog.UniformFor("uniforms.uvOffset") case materialColor: - b.vars[i].uColor = prog.UniformFor("color") + b.vars[i].uColor = prog.UniformFor("color.color") } - b.vars[i].z = prog.UniformFor("z") - b.vars[i].uScale = prog.UniformFor("scale") - b.vars[i].uOffset = prog.UniformFor("offset") + b.vars[i].z = prog.UniformFor("uniforms.z") + b.vars[i].uScale = prog.UniformFor("uniforms.scale") + b.vars[i].uOffset = prog.UniformFor("uniforms.offset") } return b } @@ -417,28 +416,14 @@ func (b *blitter) release() { } } -func createColorPrograms(ctx Backend, vsSrc, fsSrc string) ([2]Program, error) { +func createColorPrograms(ctx Backend, vsSrc ShaderSources, fsSrc [2]ShaderSources) ([2]Program, error) { var prog [2]Program - frep := strings.NewReplacer( - "HEADER", ` -uniform sampler2D tex; -`, - "GET_COLOR", `texture2D(tex, vUV)`, - ) - fsSrcTex := frep.Replace(fsSrc) var err error - prog[materialTexture], err = ctx.NewProgram(vsSrc, fsSrcTex, blitAttribs) + prog[materialTexture], err = ctx.NewProgram(vsSrc, fsSrc[materialTexture], blitAttribs) if err != nil { return prog, err } - frep = strings.NewReplacer( - "HEADER", ` -uniform vec4 color; -`, - "GET_COLOR", `color`, - ) - fsSrcCol := frep.Replace(fsSrc) - prog[materialColor], err = ctx.NewProgram(vsSrc, fsSrcCol, blitAttribs) + prog[materialColor], err = ctx.NewProgram(vsSrc, fsSrc[materialColor], blitAttribs) if err != nil { prog[materialTexture].Release() return prog, err @@ -949,43 +934,3 @@ func fillContourMaxY(maxy float32, verts []byte) { bo.PutUint32(verts[i+off:], math.Float32bits(maxy)) } } - -const blitVSrc = ` -#version 100 - -precision highp float; - -uniform float z; -uniform vec2 scale; -uniform vec2 offset; - -attribute vec2 pos; - -attribute vec2 uv; -uniform vec2 uvScale; -uniform vec2 uvOffset; - -varying vec2 vUV; - -void main() { - vec2 p = pos; - p *= scale; - p += offset; - gl_Position = vec4(p, z, 1); - vUV = uv*uvScale + uvOffset; -} -` - -const blitFSrc = ` -#version 100 - -precision mediump float; - -varying vec2 vUV; - -HEADER - -void main() { - gl_FragColor = GET_COLOR; -} -` diff --git a/gpu/path.go b/gpu/path.go index 1275dc24..9bdea64a 100644 --- a/gpu/path.go +++ b/gpu/path.go @@ -91,7 +91,7 @@ func newPather(ctx Backend) *pather { } func newCoverer(ctx Backend) *coverer { - prog, err := createColorPrograms(ctx, coverVSrc, coverFSrc) + prog, err := createColorPrograms(ctx, shader_cover_vert, shader_cover_frag) if err != nil { panic(err) } @@ -104,29 +104,29 @@ func newCoverer(ctx Backend) *coverer { case materialTexture: uTex := prog.UniformFor("tex") prog.Uniform1i(uTex, 0) - c.vars[i].uUVScale = prog.UniformFor("uvScale") - c.vars[i].uUVOffset = prog.UniformFor("uvOffset") + c.vars[i].uUVScale = prog.UniformFor("uniforms.uvScale") + c.vars[i].uUVOffset = prog.UniformFor("uniforms.uvOffset") case materialColor: - c.vars[i].uColor = prog.UniformFor("color") + c.vars[i].uColor = prog.UniformFor("color.color") } uCover := prog.UniformFor("cover") prog.Uniform1i(uCover, 1) - c.vars[i].z = prog.UniformFor("z") - c.vars[i].uScale = prog.UniformFor("scale") - c.vars[i].uOffset = prog.UniformFor("offset") - c.vars[i].uCoverUVScale = prog.UniformFor("uvCoverScale") - c.vars[i].uCoverUVOffset = prog.UniformFor("uvCoverOffset") + c.vars[i].z = prog.UniformFor("uniforms.z") + c.vars[i].uScale = prog.UniformFor("uniforms.scale") + c.vars[i].uOffset = prog.UniformFor("uniforms.offset") + c.vars[i].uCoverUVScale = prog.UniformFor("uniforms.uvCoverScale") + c.vars[i].uCoverUVOffset = prog.UniformFor("uniforms.uvCoverOffset") } return c } func newStenciler(ctx Backend) *stenciler { defFBO := ctx.DefaultFramebuffer() - prog, err := ctx.NewProgram(stencilVSrc, stencilFSrc, pathAttribs) + prog, err := ctx.NewProgram(shader_stencil_vert, shader_stencil_frag, pathAttribs) if err != nil { panic(err) } - iprog, err := ctx.NewProgram(intersectVSrc, intersectFSrc, intersectAttribs) + iprog, err := ctx.NewProgram(shader_intersect_vert, shader_intersect_frag, intersectAttribs) if err != nil { panic(err) } @@ -149,11 +149,11 @@ func newStenciler(ctx Backend) *stenciler { defFBO: defFBO, prog: prog, iprog: iprog, - uScale: prog.UniformFor("scale"), - uOffset: prog.UniformFor("offset"), - uPathOffset: prog.UniformFor("pathOffset"), - uIntersectUVScale: iprog.UniformFor("uvScale"), - uIntersectUVOffset: iprog.UniformFor("uvOffset"), + uScale: prog.UniformFor("uniforms.scale"), + uOffset: prog.UniformFor("uniforms.offset"), + uPathOffset: prog.UniformFor("uniforms.pathOffset"), + uIntersectUVScale: iprog.UniformFor("uvparams.scale"), + uIntersectUVOffset: iprog.UniformFor("uvparams.offset"), indexBuf: indexBuf, } } @@ -328,220 +328,3 @@ func (c *coverer) cover(z float32, mat materialType, col [4]float32, scale, off, p.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y) c.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4) } - -const stencilVSrc = ` -#version 100 - -precision highp float; - -uniform vec2 scale; -uniform vec2 offset; -uniform vec2 pathOffset; - -attribute vec2 corner; -attribute float maxy; -attribute vec2 from; -attribute vec2 ctrl; -attribute vec2 to; - -varying vec2 vFrom; -varying vec2 vCtrl; -varying vec2 vTo; - -void main() { - // Add a one pixel overlap so curve quads cover their - // entire curves. Could use conservative rasterization - // if available. - vec2 from = from + pathOffset; - vec2 ctrl = ctrl + pathOffset; - vec2 to = to + pathOffset; - float maxy = maxy + pathOffset.y; - vec2 pos; - if (corner.x > 0.0) { - // East. - pos.x = max(max(from.x, ctrl.x), to.x)+1.0; - } else { - // West. - pos.x = min(min(from.x, ctrl.x), to.x)-1.0; - } - if (corner.y > 0.0) { - // North. - pos.y = maxy + 1.0; - } else { - // South. - pos.y = min(min(from.y, ctrl.y), to.y) - 1.0; - } - vFrom = from-pos; - vCtrl = ctrl-pos; - vTo = to-pos; - pos *= scale; - pos += offset; - gl_Position = vec4(pos, 1, 1); -} -` - -const stencilFSrc = ` -#version 100 - -precision mediump float; - -varying vec2 vFrom; -varying vec2 vCtrl; -varying vec2 vTo; - -uniform sampler2D areaLUT; - -void main() { - float dx = vTo.x - vFrom.x; - // Sort from and to in increasing order so the root below - // is always the positive square root, if any. - // We need the direction of the curve below, so this can't be - // done from the vertex shader. - bool increasing = vTo.x >= vFrom.x; - vec2 left = increasing ? vFrom : vTo; - vec2 right = increasing ? vTo : vFrom; - - // The signed horizontal extent of the fragment. - vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5); - // Find the t where the curve crosses the middle of the - // extent, x₀. - // Given the Bézier curve with x coordinates P₀, P₁, P₂ - // where P₀ is at the origin, its x coordinate in t - // is given by: - // - // x(t) = 2(1-t)tP₁ + t²P₂ - // - // Rearranging: - // - // x(t) = (P₂ - 2P₁)t² + 2P₁t - // - // Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq") - // for robustnesss, - // - // t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀)) - // - // which simplifies to - // - // t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀)) - // - // Setting v = P₂-P₁, - // - // t = x₀/(P₁±√(P₁²+(v-P₁)x₀)) - // - // t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where - // the control point lies before the start point or after the end point. - // It can then be shown that only the positive square root is valid. - float midx = mix(extent.x, extent.y, 0.5); - float x0 = midx - left.x; - vec2 p1 = vCtrl - left; - vec2 v = right - vCtrl; - float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0)); - // Find y(t) on the curve. - float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t); - // And the slope. - vec2 d_half = mix(p1, v, t); - float dy = d_half.y/d_half.x; - // Together, y and dy form a line approximation. - - // Compute the fragment area above the line. - // The area is symmetric around dy = 0. Scale slope with extent width. - float width = extent.y - extent.x; - dy = abs(dy*width); - - vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy); - sides = clamp(sides+0.5, 0.0, 1.0); - - float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w); - area *= width; - - // Work around issue #13. - if (width == 0.0) - area = 0.0; - - gl_FragColor.r = area; -} -` - -const coverVSrc = ` -#version 100 - -precision highp float; - -uniform float z; -uniform vec2 scale; -uniform vec2 offset; -uniform vec2 uvScale; -uniform vec2 uvOffset; -uniform vec2 uvCoverScale; -uniform vec2 uvCoverOffset; - -attribute vec2 pos; - -varying vec2 vCoverUV; - -attribute vec2 uv; -varying vec2 vUV; - -void main() { - gl_Position = vec4(pos*scale + offset, z, 1); - vUV = uv*uvScale + uvOffset; - vCoverUV = uv*uvCoverScale+uvCoverOffset; -} -` - -const coverFSrc = ` -#version 100 - -precision mediump float; - -// Use high precision to be pixel accurate for -// large cover atlases. -varying highp vec2 vCoverUV; -uniform sampler2D cover; -varying vec2 vUV; - -HEADER - -void main() { - gl_FragColor = GET_COLOR; - float cover = abs(texture2D(cover, vCoverUV).r); - gl_FragColor *= cover; -} -` - -const intersectVSrc = ` -#version 100 - -precision highp float; - -attribute vec2 pos; -attribute vec2 uv; - -uniform vec2 uvScale; -uniform vec2 uvOffset; - -varying vec2 vUV; - -void main() { - vec2 p = pos; - p.y = -p.y; - gl_Position = vec4(p, 0, 1); - vUV = uv*uvScale + uvOffset; -} -` - -const intersectFSrc = ` -#version 100 - -precision mediump float; - -// Use high precision to be pixel accurate for -// large cover atlases. -varying highp vec2 vUV; -uniform sampler2D cover; - -void main() { - float cover = abs(texture2D(cover, vUV).r); - gl_FragColor.r = cover; -} -` diff --git a/gpu/shaders.go b/gpu/shaders.go new file mode 100644 index 00000000..0d104d1b --- /dev/null +++ b/gpu/shaders.go @@ -0,0 +1,574 @@ +// Code generated by build.go. DO NOT EDIT. + +package gpu + +var ( + shader_blit_frag = [...]ShaderSources{ + ShaderSources{ + Uniforms: []UniformLocation{ + {Name: "_12.color", Type: 0, Size: 4, Offset: 0}, + }, + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 color;\n};\n\nuniform Color color;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = color.color;\n}\n\n", + /* + cbuffer Color : register(b0) + { + float4 color_color : packoffset(c0); + }; + + + static float4 fragColor; + static float2 vUV; + + struct SPIRV_Cross_Input + { + float2 vUV : TEXCOORD0; + }; + + struct SPIRV_Cross_Output + { + float4 fragColor : SV_Target0; + }; + + void frag_main() + { + fragColor = color_color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; + } + + */ + HLSL: []byte(nil), + }, + ShaderSources{ + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n}\n\n", + /* + Texture2D tex : register(t0); + SamplerState _tex_sampler : register(s0); + + static float4 fragColor; + static float2 vUV; + + struct SPIRV_Cross_Input + { + float2 vUV : TEXCOORD0; + }; + + struct SPIRV_Cross_Output + { + float4 fragColor : SV_Target0; + }; + + void frag_main() + { + fragColor = tex.Sample(_tex_sampler, vUV); + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; + } + + */ + HLSL: []byte(nil), + }, + } + shader_blit_vert = ShaderSources{ + Inputs: []InputLocation{ + {Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0, Size: 2}, + {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0, Size: 2}, + }, + Uniforms: []UniformLocation{ + {Name: "_15.z", Type: 0, Size: 1, Offset: 0}, + {Name: "_15.scale", Type: 0, Size: 2, Offset: 8}, + {Name: "_15.offset", Type: 0, Size: 2, Offset: 16}, + {Name: "_15.uvScale", Type: 0, Size: 2, Offset: 24}, + {Name: "_15.uvOffset", Type: 0, Size: 2, Offset: 32}, + }, + GLES2: "#version 100\n\nstruct Block\n{\n float z;\n vec2 scale;\n vec2 offset;\n vec2 uvScale;\n vec2 uvOffset;\n};\n\nuniform Block uniforms;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\n\nvoid main()\n{\n vec2 p = pos;\n p *= uniforms.scale;\n p += uniforms.offset;\n gl_Position = vec4(p, uniforms.z, 1.0);\n vUV = (uv * uniforms.uvScale) + uniforms.uvOffset;\n}\n\n", + /* + cbuffer Block : register(b0) + { + float uniforms_z : packoffset(c0); + float2 uniforms_scale : packoffset(c0.z); + float2 uniforms_offset : packoffset(c1); + float2 uniforms_uvScale : packoffset(c1.z); + float2 uniforms_uvOffset : packoffset(c2); + }; + + + static float4 gl_Position; + static float2 pos; + static float2 vUV; + static float2 uv; + + struct SPIRV_Cross_Input + { + float2 pos : POSITION; + float2 uv : NORMAL; + }; + + struct SPIRV_Cross_Output + { + float2 vUV : TEXCOORD0; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + float2 p = pos; + p *= uniforms_scale; + p += uniforms_offset; + gl_Position = float4(p, uniforms_z, 1.0f); + vUV = (uv * uniforms_uvScale) + uniforms_uvOffset; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + pos = stage_input.pos; + uv = stage_input.uv; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vUV = vUV; + return stage_output; + } + + */ + HLSL: []byte(nil), + } + shader_cover_frag = [...]ShaderSources{ + ShaderSources{ + Uniforms: []UniformLocation{ + {Name: "_12.color", Type: 0, Size: 4, Offset: 0}, + }, + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 color;\n};\n\nuniform Color color;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vCoverUV;\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = color.color;\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n", + /* + cbuffer Color : register(b0) + { + float4 color_color : packoffset(c0); + }; + + Texture2D cover : register(t1); + SamplerState _cover_sampler : register(s1); + + static float4 fragColor; + static float2 vCoverUV; + static float2 vUV; + + struct SPIRV_Cross_Input + { + float2 vCoverUV : TEXCOORD0; + float2 vUV : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 fragColor : SV_Target0; + }; + + void frag_main() + { + fragColor = color_color; + float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x); + fragColor *= cover_1; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vCoverUV = stage_input.vCoverUV; + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; + } + + */ + HLSL: []byte(nil), + }, + ShaderSources{ + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nvarying vec2 vUV;\nvarying highp vec2 vCoverUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n", + /* + Texture2D tex : register(t0); + SamplerState _tex_sampler : register(s0); + Texture2D cover : register(t1); + SamplerState _cover_sampler : register(s1); + + static float4 fragColor; + static float2 vUV; + static float2 vCoverUV; + + struct SPIRV_Cross_Input + { + float2 vCoverUV : TEXCOORD0; + float2 vUV : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 fragColor : SV_Target0; + }; + + void frag_main() + { + fragColor = tex.Sample(_tex_sampler, vUV); + float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x); + fragColor *= cover_1; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vUV = stage_input.vUV; + vCoverUV = stage_input.vCoverUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; + } + + */ + HLSL: []byte(nil), + }, + } + shader_cover_vert = ShaderSources{ + Inputs: []InputLocation{ + {Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0, Size: 2}, + {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0, Size: 2}, + }, + Uniforms: []UniformLocation{ + {Name: "_19.z", Type: 0, Size: 1, Offset: 0}, + {Name: "_19.scale", Type: 0, Size: 2, Offset: 8}, + {Name: "_19.offset", Type: 0, Size: 2, Offset: 16}, + {Name: "_19.uvScale", Type: 0, Size: 2, Offset: 24}, + {Name: "_19.uvOffset", Type: 0, Size: 2, Offset: 32}, + {Name: "_19.uvCoverScale", Type: 0, Size: 2, Offset: 40}, + {Name: "_19.uvCoverOffset", Type: 0, Size: 2, Offset: 48}, + }, + GLES2: "#version 100\n\nstruct Block\n{\n float z;\n vec2 scale;\n vec2 offset;\n vec2 uvScale;\n vec2 uvOffset;\n vec2 uvCoverScale;\n vec2 uvCoverOffset;\n};\n\nuniform Block uniforms;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\nvarying vec2 vCoverUV;\n\nvoid main()\n{\n gl_Position = vec4((pos * uniforms.scale) + uniforms.offset, uniforms.z, 1.0);\n vUV = (uv * uniforms.uvScale) + uniforms.uvOffset;\n vCoverUV = (uv * uniforms.uvCoverScale) + uniforms.uvCoverOffset;\n}\n\n", + /* + cbuffer Block : register(b0) + { + float uniforms_z : packoffset(c0); + float2 uniforms_scale : packoffset(c0.z); + float2 uniforms_offset : packoffset(c1); + float2 uniforms_uvScale : packoffset(c1.z); + float2 uniforms_uvOffset : packoffset(c2); + float2 uniforms_uvCoverScale : packoffset(c2.z); + float2 uniforms_uvCoverOffset : packoffset(c3); + }; + + + static float4 gl_Position; + static float2 pos; + static float2 vUV; + static float2 uv; + static float2 vCoverUV; + + struct SPIRV_Cross_Input + { + float2 pos : POSITION; + float2 uv : NORMAL; + }; + + struct SPIRV_Cross_Output + { + float2 vCoverUV : TEXCOORD0; + float2 vUV : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = float4((pos * uniforms_scale) + uniforms_offset, uniforms_z, 1.0f); + vUV = (uv * uniforms_uvScale) + uniforms_uvOffset; + vCoverUV = (uv * uniforms_uvCoverScale) + uniforms_uvCoverOffset; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + pos = stage_input.pos; + uv = stage_input.uv; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vUV = vUV; + stage_output.vCoverUV = vCoverUV; + return stage_output; + } + + */ + HLSL: []byte(nil), + } + shader_intersect_frag = ShaderSources{ + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vUV;\n\nvoid main()\n{\n float cover_1 = abs(texture2D(cover, vUV).x);\n gl_FragData[0].x = cover_1;\n}\n\n", + /* + Texture2D cover : register(t0); + SamplerState _cover_sampler : register(s0); + + static float2 vUV; + static float4 fragColor; + + struct SPIRV_Cross_Input + { + float2 vUV : TEXCOORD0; + }; + + struct SPIRV_Cross_Output + { + float4 fragColor : SV_Target0; + }; + + void frag_main() + { + float cover_1 = abs(cover.Sample(_cover_sampler, vUV).x); + fragColor.x = cover_1; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; + } + + */ + HLSL: []byte(nil), + } + shader_intersect_vert = ShaderSources{ + Inputs: []InputLocation{ + {Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0, Size: 2}, + {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0, Size: 2}, + }, + Uniforms: []UniformLocation{ + {Name: "_40.scale", Type: 0, Size: 2, Offset: 0}, + {Name: "_40.offset", Type: 0, Size: 2, Offset: 8}, + }, + GLES2: "#version 100\n\nstruct Block\n{\n vec2 scale;\n vec2 offset;\n};\n\nuniform Block uvparams;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\n\nvoid main()\n{\n vec2 p = pos;\n p.y = -p.y;\n gl_Position = vec4(p, 0.0, 1.0);\n vUV = (uv * uvparams.scale) + uvparams.offset;\n}\n\n", + /* + cbuffer Block : register(b0) + { + float2 uvparams_scale : packoffset(c0); + float2 uvparams_offset : packoffset(c0.z); + }; + + + static float4 gl_Position; + static float2 pos; + static float2 vUV; + static float2 uv; + + struct SPIRV_Cross_Input + { + float2 pos : POSITION; + float2 uv : NORMAL; + }; + + struct SPIRV_Cross_Output + { + float2 vUV : TEXCOORD0; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + float2 p = pos; + p.y = -p.y; + gl_Position = float4(p, 0.0f, 1.0f); + vUV = (uv * uvparams_scale) + uvparams_offset; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + pos = stage_input.pos; + uv = stage_input.uv; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vUV = vUV; + return stage_output; + } + + */ + HLSL: []byte(nil), + } + shader_stencil_frag = ShaderSources{ + GLES2: "#version 100\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D areaLUT;\n\nvarying vec2 vTo;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n gl_FragData[0].x = area;\n}\n\n", + /* + Texture2D areaLUT : register(t0); + SamplerState _areaLUT_sampler : register(s0); + + static float2 vTo; + static float2 vFrom; + static float2 vCtrl; + static float4 fragCover; + + struct SPIRV_Cross_Input + { + float2 vFrom : TEXCOORD0; + float2 vCtrl : TEXCOORD1; + float2 vTo : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float4 fragCover : SV_Target0; + }; + + void frag_main() + { + float dx = vTo.x - vFrom.x; + bool increasing = vTo.x >= vFrom.x; + bool2 _35 = increasing.xx; + float2 left = float2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y); + bool2 _41 = increasing.xx; + float2 right = float2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y); + float2 extent = clamp(float2(vFrom.x, vTo.x), (-0.5f).xx, 0.5f.xx); + float midx = lerp(extent.x, extent.y, 0.5f); + float x0 = midx - left.x; + float2 p1 = vCtrl - left; + float2 v = right - vCtrl; + float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0))); + float y = lerp(lerp(left.y, vCtrl.y, t), lerp(vCtrl.y, right.y, t), t); + float2 d_half = lerp(p1, v, t.xx); + float dy = d_half.y / d_half.x; + float width = extent.y - extent.x; + dy = abs(dy * width); + float4 sides = float4((dy * 0.5f) + y, (dy * (-0.5f)) + y, (0.5f - y) / dy, ((-0.5f) - y) / dy); + sides = clamp(sides + 0.5f.xxxx, 0.0f.xxxx, 1.0f.xxxx); + float area = 0.5f * ((((sides.z - (sides.z * sides.y)) + 1.0f) - sides.x) + (sides.x * sides.w)); + area *= width; + if (width == 0.0f) + { + area = 0.0f; + } + fragCover.x = area; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + vTo = stage_input.vTo; + vFrom = stage_input.vFrom; + vCtrl = stage_input.vCtrl; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragCover = fragCover; + return stage_output; + } + + */ + HLSL: []byte(nil), + } + shader_stencil_vert = ShaderSources{ + Inputs: []InputLocation{ + {Name: "corner", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0, Size: 2}, + {Name: "maxy", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0, Size: 1}, + {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0, Size: 2}, + {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0, Size: 2}, + {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0, Size: 2}, + }, + Uniforms: []UniformLocation{ + {Name: "_15.scale", Type: 0, Size: 2, Offset: 0}, + {Name: "_15.offset", Type: 0, Size: 2, Offset: 8}, + {Name: "_15.pathOffset", Type: 0, Size: 2, Offset: 16}, + }, + GLES2: "#version 100\n\nstruct Block\n{\n vec2 scale;\n vec2 offset;\n vec2 pathOffset;\n};\n\nuniform Block uniforms;\n\nattribute vec2 from;\nattribute vec2 ctrl;\nattribute vec2 to;\nattribute float maxy;\nattribute vec2 corner;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\nvarying vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + uniforms.pathOffset;\n vec2 ctrl_1 = ctrl + uniforms.pathOffset;\n vec2 to_1 = to + uniforms.pathOffset;\n float maxy_1 = maxy + uniforms.pathOffset.y;\n vec2 pos;\n if (corner.x > 0.0)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n if (corner.y > 0.0)\n {\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos *= uniforms.scale;\n pos += uniforms.offset;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n", + /* + cbuffer Block : register(b0) + { + float2 uniforms_scale : packoffset(c0); + float2 uniforms_offset : packoffset(c0.z); + float2 uniforms_pathOffset : packoffset(c1); + }; + + + static float4 gl_Position; + static float2 from; + static float2 ctrl; + static float2 to; + static float maxy; + static float2 corner; + static float2 vFrom; + static float2 vCtrl; + static float2 vTo; + + struct SPIRV_Cross_Input + { + float2 corner : POSITION; + float maxy : NORMAL; + float2 from : TEXCOORD0; + float2 ctrl : TEXCOORD1; + float2 to : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float2 vFrom : TEXCOORD0; + float2 vCtrl : TEXCOORD1; + float2 vTo : TEXCOORD2; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + float2 from_1 = from + uniforms_pathOffset; + float2 ctrl_1 = ctrl + uniforms_pathOffset; + float2 to_1 = to + uniforms_pathOffset; + float maxy_1 = maxy + uniforms_pathOffset.y; + float2 pos; + if (corner.x > 0.0f) + { + pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0f; + } + else + { + pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0f; + } + if (corner.y > 0.0f) + { + pos.y = maxy_1 + 1.0f; + } + else + { + pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0f; + } + vFrom = from_1 - pos; + vCtrl = ctrl_1 - pos; + vTo = to_1 - pos; + pos *= uniforms_scale; + pos += uniforms_offset; + gl_Position = float4(pos, 1.0f, 1.0f); + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + from = stage_input.from; + ctrl = stage_input.ctrl; + to = stage_input.to; + maxy = stage_input.maxy; + corner = stage_input.corner; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vFrom = vFrom; + stage_output.vCtrl = vCtrl; + stage_output.vTo = vTo; + return stage_output; + } + + */ + HLSL: []byte(nil), + } +) diff --git a/gpu/shaders/blit.frag b/gpu/shaders/blit.frag new file mode 100644 index 00000000..907f0107 --- /dev/null +++ b/gpu/shaders/blit.frag @@ -0,0 +1,15 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location=0) in vec2 vUV; + +{{.Header}} + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = {{.FetchColorExpr}}; +} diff --git a/gpu/shaders/blit.vert b/gpu/shaders/blit.vert new file mode 100644 index 00000000..58d2325b --- /dev/null +++ b/gpu/shaders/blit.vert @@ -0,0 +1,27 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision highp float; + +layout(binding = 0) uniform Block { + float z; + vec2 scale; + vec2 offset; + vec2 uvScale; + vec2 uvOffset; +} uniforms; + +layout(location = 0) in vec2 pos; + +layout(location = 1) in vec2 uv; + +layout(location = 0) out vec2 vUV; + +void main() { + vec2 p = pos; + p *= uniforms.scale; + p += uniforms.offset; + gl_Position = vec4(p, uniforms.z, 1); + vUV = uv*uniforms.uvScale + uniforms.uvOffset; +} diff --git a/gpu/shaders/cover.frag b/gpu/shaders/cover.frag new file mode 100644 index 00000000..5a051f8e --- /dev/null +++ b/gpu/shaders/cover.frag @@ -0,0 +1,22 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +{{.Header}} + +// Use high precision to be pixel accurate for +// large cover atlases. +layout(location = 0) in highp vec2 vCoverUV; +layout(location = 1) in vec2 vUV; + +layout(binding = 1) uniform sampler2D cover; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = {{.FetchColorExpr}}; + float cover = abs(texture(cover, vCoverUV).r); + fragColor *= cover; +} diff --git a/gpu/shaders/cover.vert b/gpu/shaders/cover.vert new file mode 100644 index 00000000..369443d2 --- /dev/null +++ b/gpu/shaders/cover.vert @@ -0,0 +1,28 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision highp float; + +layout(binding = 0) uniform Block { + float z; + vec2 scale; + vec2 offset; + vec2 uvScale; + vec2 uvOffset; + vec2 uvCoverScale; + vec2 uvCoverOffset; +} uniforms; + +layout(location = 0) in vec2 pos; + +layout(location = 0) out vec2 vCoverUV; + +layout(location = 1) in vec2 uv; +layout(location = 1) out vec2 vUV; + +void main() { + gl_Position = vec4(pos*uniforms.scale + uniforms.offset, uniforms.z, 1); + vUV = uv*uniforms.uvScale + uniforms.uvOffset; + vCoverUV = uv*uniforms.uvCoverScale+uniforms.uvCoverOffset; +} diff --git a/gpu/shaders/intersect.frag b/gpu/shaders/intersect.frag new file mode 100644 index 00000000..df2a41f5 --- /dev/null +++ b/gpu/shaders/intersect.frag @@ -0,0 +1,18 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +// Use high precision to be pixel accurate for +// large cover atlases. +layout(location = 0) in highp vec2 vUV; + +layout(binding = 0) uniform sampler2D cover; + +layout(location = 0) out vec4 fragColor; + +void main() { + float cover = abs(texture(cover, vUV).r); + fragColor.r = cover; +} diff --git a/gpu/shaders/intersect.vert b/gpu/shaders/intersect.vert new file mode 100644 index 00000000..3ce6eb23 --- /dev/null +++ b/gpu/shaders/intersect.vert @@ -0,0 +1,22 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision highp float; + +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; + +layout(binding = 0) uniform Block { + vec2 scale; + vec2 offset; +} uvparams; + +layout(location = 0) out vec2 vUV; + +void main() { + vec2 p = pos; + p.y = -p.y; + gl_Position = vec4(p, 0, 1); + vUV = uv*uvparams.scale + uvparams.offset; +} diff --git a/gpu/shaders/stencil.frag b/gpu/shaders/stencil.frag new file mode 100644 index 00000000..78499ff0 --- /dev/null +++ b/gpu/shaders/stencil.frag @@ -0,0 +1,83 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location=0) in vec2 vFrom; +layout(location=1) in vec2 vCtrl; +layout(location=2) in vec2 vTo; + +layout(binding=0) uniform sampler2D areaLUT; + +layout(location = 0) out vec4 fragCover; + +void main() { + float dx = vTo.x - vFrom.x; + // Sort from and to in increasing order so the root below + // is always the positive square root, if any. + // We need the direction of the curve below, so this can't be + // done from the vertex shader. + bool increasing = vTo.x >= vFrom.x; + vec2 left = increasing ? vFrom : vTo; + vec2 right = increasing ? vTo : vFrom; + + // The signed horizontal extent of the fragment. + vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5); + // Find the t where the curve crosses the middle of the + // extent, x₀. + // Given the Bézier curve with x coordinates P₀, P₁, P₂ + // where P₀ is at the origin, its x coordinate in t + // is given by: + // + // x(t) = 2(1-t)tP₁ + t²P₂ + // + // Rearranging: + // + // x(t) = (P₂ - 2P₁)t² + 2P₁t + // + // Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq") + // for robustnesss, + // + // t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀)) + // + // which simplifies to + // + // t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀)) + // + // Setting v = P₂-P₁, + // + // t = x₀/(P₁±√(P₁²+(v-P₁)x₀)) + // + // t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where + // the control point lies before the start point or after the end point. + // It can then be shown that only the positive square root is valid. + float midx = mix(extent.x, extent.y, 0.5); + float x0 = midx - left.x; + vec2 p1 = vCtrl - left; + vec2 v = right - vCtrl; + float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0)); + // Find y(t) on the curve. + float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t); + // And the slope. + vec2 d_half = mix(p1, v, t); + float dy = d_half.y/d_half.x; + // Together, y and dy form a line approximation. + + // Compute the fragment area above the line. + // The area is symmetric around dy = 0. Scale slope with extent width. + float width = extent.y - extent.x; + dy = abs(dy*width); + + vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy); + sides = clamp(sides+0.5, 0.0, 1.0); + + float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w); + area *= width; + + // Work around issue #13. + if (width == 0.0) + area = 0.0; + + fragCover.r = area; +} diff --git a/gpu/shaders/stencil.vert b/gpu/shaders/stencil.vert new file mode 100644 index 00000000..2b5cdf5a --- /dev/null +++ b/gpu/shaders/stencil.vert @@ -0,0 +1,53 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision highp float; + +layout(binding = 0) uniform Block { + vec2 scale; + vec2 offset; + vec2 pathOffset; +} uniforms; + +layout(location=0) in vec2 corner; +layout(location=1) in float maxy; +layout(location=2) in vec2 from; +layout(location=3) in vec2 ctrl; +layout(location=4) in vec2 to; + +layout(location=0) out vec2 vFrom; +layout(location=1) out vec2 vCtrl; +layout(location=2) out vec2 vTo; + +void main() { + // Add a one pixel overlap so curve quads cover their + // entire curves. Could use conservative rasterization + // if available. + vec2 from = from + uniforms.pathOffset; + vec2 ctrl = ctrl + uniforms.pathOffset; + vec2 to = to + uniforms.pathOffset; + float maxy = maxy + uniforms.pathOffset.y; + vec2 pos; + if (corner.x > 0.0) { + // East. + pos.x = max(max(from.x, ctrl.x), to.x)+1.0; + } else { + // West. + pos.x = min(min(from.x, ctrl.x), to.x)-1.0; + } + if (corner.y > 0.0) { + // North. + pos.y = maxy + 1.0; + } else { + // South. + pos.y = min(min(from.y, ctrl.y), to.y) - 1.0; + } + vFrom = from-pos; + vCtrl = ctrl-pos; + vTo = to-pos; + pos *= uniforms.scale; + pos += uniforms.offset; + gl_Position = vec4(pos, 1, 1); +} +