shader.go (6094B)
1 // Copyright 2020 The Ebiten Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ebiten 16 17 import ( 18 "bytes" 19 "fmt" 20 "go/parser" 21 "go/token" 22 "strings" 23 24 "github.com/hajimehoshi/ebiten/v2/internal/graphics" 25 "github.com/hajimehoshi/ebiten/v2/internal/mipmap" 26 "github.com/hajimehoshi/ebiten/v2/internal/shader" 27 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 28 ) 29 30 var shaderSuffix string 31 32 func init() { 33 shaderSuffix = ` 34 var __imageDstTextureSize vec2 35 36 // imageSrcTextureSize returns the destination image's texture size in pixels. 37 func imageDstTextureSize() vec2 { 38 return __imageDstTextureSize 39 } 40 ` 41 42 shaderSuffix += fmt.Sprintf(` 43 var __textureSizes [%[1]d]vec2 44 45 // imageSrcTextureSize returns the source image's texture size in pixels. 46 // As an image is a part of internal texture, the texture is usually bigger than the image. 47 // The texture's size is useful when you want to calculate pixels from texels. 48 func imageSrcTextureSize() vec2 { 49 return __textureSizes[0] 50 } 51 52 // The unit is the source texture's texel. 53 var __textureSourceOffsets [%[2]d]vec2 54 55 // The unit is the source texture's texel. 56 var __textureSourceRegionOrigin vec2 57 58 // The unit is the source texture's texel. 59 var __textureSourceRegionSize vec2 60 61 // imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture. 62 // The unit is the source texture's texel. 63 // 64 // As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. 65 func imageSrcRegionOnTexture() (vec2, vec2) { 66 return __textureSourceRegionOrigin, __textureSourceRegionSize 67 } 68 `, graphics.ShaderImageNum, graphics.ShaderImageNum-1) 69 70 for i := 0; i < graphics.ShaderImageNum; i++ { 71 pos := "pos" 72 if i >= 1 { 73 // Convert the position in texture0's texels to the target texture texels. 74 pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i) 75 } 76 // __t%d is a special variable for a texture variable. 77 shaderSuffix += fmt.Sprintf(` 78 func imageSrc%[1]dUnsafeAt(pos vec2) vec4 { 79 // pos is the position in texels of the source texture (= 0th image's texture). 80 return texture2D(__t%[1]d, %[2]s) 81 } 82 83 func imageSrc%[1]dAt(pos vec2) vec4 { 84 // pos is the position in texels of the source texture (= 0th image's texture). 85 return texture2D(__t%[1]d, %[2]s) * 86 step(__textureSourceRegionOrigin.x, pos.x) * 87 (1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) * 88 step(__textureSourceRegionOrigin.y, pos.y) * 89 (1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y)) 90 } 91 `, i, pos) 92 } 93 94 shaderSuffix += ` 95 func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { 96 return mat4( 97 2/__imageDstTextureSize.x, 0, 0, 0, 98 0, 2/__imageDstTextureSize.y, 0, 0, 99 0, 0, 1, 0, 100 -1, -1, 0, 1, 101 ) * vec4(position, 0, 1), texCoord, color 102 } 103 ` 104 } 105 106 // Shader represents a compiled shader program. 107 // 108 // For the details about the shader, see https://ebiten.org/documents/shader.html. 109 type Shader struct { 110 shader *mipmap.Shader 111 uniformNames []string 112 uniformTypes []shaderir.Type 113 } 114 115 // NewShader compiles a shader program in the shading language Kage, and retruns the result. 116 // 117 // If the compilation fails, NewShader returns an error. 118 // 119 // For the details about the shader, see https://ebiten.org/documents/shader.html. 120 func NewShader(src []byte) (*Shader, error) { 121 var buf bytes.Buffer 122 buf.Write(src) 123 buf.WriteString(shaderSuffix) 124 125 fs := token.NewFileSet() 126 f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors) 127 if err != nil { 128 return nil, err 129 } 130 131 const ( 132 vert = "__vertex" 133 frag = "Fragment" 134 ) 135 s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum) 136 if err != nil { 137 return nil, err 138 } 139 140 if s.VertexFunc.Block == nil { 141 return nil, fmt.Errorf("ebiten: vertex shader entry point '%s' is missing", vert) 142 } 143 if s.FragmentFunc.Block == nil { 144 return nil, fmt.Errorf("ebiten: fragment shader entry point '%s' is missing", frag) 145 } 146 147 return &Shader{ 148 shader: mipmap.NewShader(s), 149 uniformNames: s.UniformNames, 150 uniformTypes: s.Uniforms, 151 }, nil 152 } 153 154 // Dispose disposes the shader program. 155 // After disposing, the shader is no longer available. 156 func (s *Shader) Dispose() { 157 s.shader.MarkDisposed() 158 s.shader = nil 159 } 160 161 func (s *Shader) convertUniforms(uniforms map[string]interface{}) []interface{} { 162 type index struct { 163 resultIndex int 164 shaderUniformIndex int 165 } 166 167 names := map[string]index{} 168 var idx int 169 for i, n := range s.uniformNames { 170 if strings.HasPrefix(n, "__") { 171 continue 172 } 173 names[n] = index{ 174 resultIndex: idx, 175 shaderUniformIndex: i, 176 } 177 idx++ 178 } 179 180 us := make([]interface{}, len(names)) 181 for name, idx := range names { 182 if v, ok := uniforms[name]; ok { 183 // TODO: Check the uniform variable types? 184 us[idx.resultIndex] = v 185 continue 186 } 187 188 t := s.uniformTypes[idx.shaderUniformIndex] 189 v := zeroUniformValue(t) 190 if v == nil { 191 panic(fmt.Sprintf("ebiten: unexpected uniform variable type: %s", t.String())) 192 } 193 us[idx.resultIndex] = v 194 } 195 196 // TODO: Panic if uniforms include an invalid name 197 198 return us 199 } 200 201 func zeroUniformValue(t shaderir.Type) interface{} { 202 switch t.Main { 203 case shaderir.Bool: 204 return false 205 case shaderir.Int: 206 return 0 207 case shaderir.Float: 208 return float32(0) 209 case shaderir.Array: 210 switch t.Sub[0].Main { 211 case shaderir.Bool: 212 return make([]bool, t.Length) 213 case shaderir.Int: 214 return make([]int, t.Length) 215 default: 216 return make([]float32, t.FloatNum()) 217 } 218 default: 219 return make([]float32, t.FloatNum()) 220 } 221 }