shader.go (6652B)
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 __textureDestinationRegionOrigin vec2 54 55 // The unit is the source texture's texel. 56 var __textureDestinationRegionSize vec2 57 58 // imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture. 59 // The unit is the source texture's texel. 60 // 61 // As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. 62 func imageDstRegionOnTexture() (vec2, vec2) { 63 return __textureDestinationRegionOrigin, __textureDestinationRegionSize 64 } 65 66 // The unit is the source texture's texel. 67 var __textureSourceOffsets [%[2]d]vec2 68 69 // The unit is the source texture's texel. 70 var __textureSourceRegionOrigin vec2 71 72 // The unit is the source texture's texel. 73 var __textureSourceRegionSize vec2 74 75 // imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture. 76 // The unit is the source texture's texel. 77 // 78 // As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. 79 func imageSrcRegionOnTexture() (vec2, vec2) { 80 return __textureSourceRegionOrigin, __textureSourceRegionSize 81 } 82 `, graphics.ShaderImageNum, graphics.ShaderImageNum-1) 83 84 for i := 0; i < graphics.ShaderImageNum; i++ { 85 pos := "pos" 86 if i >= 1 { 87 // Convert the position in texture0's texels to the target texture texels. 88 pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i) 89 } 90 // __t%d is a special variable for a texture variable. 91 shaderSuffix += fmt.Sprintf(` 92 func imageSrc%[1]dUnsafeAt(pos vec2) vec4 { 93 // pos is the position in texels of the source texture (= 0th image's texture). 94 return texture2D(__t%[1]d, %[2]s) 95 } 96 97 func imageSrc%[1]dAt(pos vec2) vec4 { 98 // pos is the position in texels of the source texture (= 0th image's texture). 99 return texture2D(__t%[1]d, %[2]s) * 100 step(__textureSourceRegionOrigin.x, pos.x) * 101 (1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) * 102 step(__textureSourceRegionOrigin.y, pos.y) * 103 (1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y)) 104 } 105 `, i, pos) 106 } 107 108 shaderSuffix += ` 109 func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { 110 return mat4( 111 2/__imageDstTextureSize.x, 0, 0, 0, 112 0, 2/__imageDstTextureSize.y, 0, 0, 113 0, 0, 1, 0, 114 -1, -1, 0, 1, 115 ) * vec4(position, 0, 1), texCoord, color 116 } 117 ` 118 } 119 120 // Shader represents a compiled shader program. 121 // 122 // For the details about the shader, see https://ebiten.org/documents/shader.html. 123 type Shader struct { 124 shader *mipmap.Shader 125 uniformNames []string 126 uniformTypes []shaderir.Type 127 } 128 129 // NewShader compiles a shader program in the shading language Kage, and retruns the result. 130 // 131 // If the compilation fails, NewShader returns an error. 132 // 133 // For the details about the shader, see https://ebiten.org/documents/shader.html. 134 func NewShader(src []byte) (*Shader, error) { 135 var buf bytes.Buffer 136 buf.Write(src) 137 buf.WriteString(shaderSuffix) 138 139 fs := token.NewFileSet() 140 f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors) 141 if err != nil { 142 return nil, err 143 } 144 145 const ( 146 vert = "__vertex" 147 frag = "Fragment" 148 ) 149 s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum) 150 if err != nil { 151 return nil, err 152 } 153 154 if s.VertexFunc.Block == nil { 155 return nil, fmt.Errorf("ebiten: vertex shader entry point '%s' is missing", vert) 156 } 157 if s.FragmentFunc.Block == nil { 158 return nil, fmt.Errorf("ebiten: fragment shader entry point '%s' is missing", frag) 159 } 160 161 return &Shader{ 162 shader: mipmap.NewShader(s), 163 uniformNames: s.UniformNames, 164 uniformTypes: s.Uniforms, 165 }, nil 166 } 167 168 // Dispose disposes the shader program. 169 // After disposing, the shader is no longer available. 170 func (s *Shader) Dispose() { 171 s.shader.MarkDisposed() 172 s.shader = nil 173 } 174 175 func (s *Shader) convertUniforms(uniforms map[string]interface{}) []interface{} { 176 type index struct { 177 resultIndex int 178 shaderUniformIndex int 179 } 180 181 names := map[string]index{} 182 var idx int 183 for i, n := range s.uniformNames { 184 if strings.HasPrefix(n, "__") { 185 continue 186 } 187 names[n] = index{ 188 resultIndex: idx, 189 shaderUniformIndex: i, 190 } 191 idx++ 192 } 193 194 us := make([]interface{}, len(names)) 195 for name, idx := range names { 196 if v, ok := uniforms[name]; ok { 197 // TODO: Check the uniform variable types? 198 us[idx.resultIndex] = v 199 continue 200 } 201 202 t := s.uniformTypes[idx.shaderUniformIndex] 203 v := zeroUniformValue(t) 204 if v == nil { 205 panic(fmt.Sprintf("ebiten: unexpected uniform variable type: %s", t.String())) 206 } 207 us[idx.resultIndex] = v 208 } 209 210 // TODO: Panic if uniforms include an invalid name 211 212 return us 213 } 214 215 func zeroUniformValue(t shaderir.Type) interface{} { 216 switch t.Main { 217 case shaderir.Bool: 218 return false 219 case shaderir.Int: 220 return 0 221 case shaderir.Float: 222 return float32(0) 223 case shaderir.Array: 224 switch t.Sub[0].Main { 225 case shaderir.Bool: 226 return make([]bool, t.Length) 227 case shaderir.Int: 228 return make([]int, t.Length) 229 default: 230 return make([]float32, t.FloatNum()) 231 } 232 default: 233 return make([]float32, t.FloatNum()) 234 } 235 }