From 1d9982ee6d200f8f01e5dc7f40c9089a958a0eae Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 20 Mar 2022 02:00:44 +0900 Subject: [PATCH] internal/ui: move the dependency on graphicscommand from ebiten to ui --- internal/ui/image.go | 16 ---- internal/ui/shader.go | 218 ++++++++++++++++++++++++++++++++++++++++++ shader.go | 184 +---------------------------------- 3 files changed, 222 insertions(+), 196 deletions(-) create mode 100644 internal/ui/shader.go diff --git a/internal/ui/image.go b/internal/ui/image.go index a203c3b97..e5cf6f471 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -19,7 +19,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/mipmap" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) type Image struct { @@ -79,18 +78,3 @@ func (i *Image) SetIndependent(independent bool) { func (i *Image) SetVolatile(volatile bool) { i.mipmap.SetVolatile(volatile) } - -type Shader struct { - shader *mipmap.Shader -} - -func NewShader(program *shaderir.Program) *Shader { - return &Shader{ - shader: mipmap.NewShader(program), - } -} - -func (s *Shader) MarkDisposed() { - s.shader.MarkDisposed() - s.shader = nil -} diff --git a/internal/ui/shader.go b/internal/ui/shader.go new file mode 100644 index 000000000..2ca87bd37 --- /dev/null +++ b/internal/ui/shader.go @@ -0,0 +1,218 @@ +// Copyright 2022 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ui + +import ( + "bytes" + "fmt" + "go/parser" + "go/token" + "strings" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" + "github.com/hajimehoshi/ebiten/v2/internal/mipmap" + "github.com/hajimehoshi/ebiten/v2/internal/shader" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +var shaderSuffix string + +func init() { + shaderSuffix = ` +var __imageDstTextureSize vec2 + +// imageSrcTextureSize returns the destination image's texture size in pixels. +func imageDstTextureSize() vec2 { + return __imageDstTextureSize +} +` + + shaderSuffix += fmt.Sprintf(` +var __textureSizes [%[1]d]vec2 + +// imageSrcTextureSize returns the source image's texture size in pixels. +// As an image is a part of internal texture, the texture is usually bigger than the image. +// The texture's size is useful when you want to calculate pixels from texels. +func imageSrcTextureSize() vec2 { + return __textureSizes[0] +} + +// The unit is the source texture's texel. +var __textureDestinationRegionOrigin vec2 + +// The unit is the source texture's texel. +var __textureDestinationRegionSize vec2 + +// imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture. +// The unit is the source texture's texel. +// +// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. +func imageDstRegionOnTexture() (vec2, vec2) { + return __textureDestinationRegionOrigin, __textureDestinationRegionSize +} + +// The unit is the source texture's texel. +var __textureSourceOffsets [%[2]d]vec2 + +// The unit is the source texture's texel. +var __textureSourceRegionOrigin vec2 + +// The unit is the source texture's texel. +var __textureSourceRegionSize vec2 + +// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture. +// The unit is the source texture's texel. +// +// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. +func imageSrcRegionOnTexture() (vec2, vec2) { + return __textureSourceRegionOrigin, __textureSourceRegionSize +} +`, graphics.ShaderImageNum, graphics.ShaderImageNum-1) + + for i := 0; i < graphics.ShaderImageNum; i++ { + pos := "pos" + if i >= 1 { + // Convert the position in texture0's texels to the target texture texels. + pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i) + } + // __t%d is a special variable for a texture variable. + shaderSuffix += fmt.Sprintf(` +func imageSrc%[1]dUnsafeAt(pos vec2) vec4 { + // pos is the position in texels of the source texture (= 0th image's texture). + return texture2D(__t%[1]d, %[2]s) +} + +func imageSrc%[1]dAt(pos vec2) vec4 { + // pos is the position in texels of the source texture (= 0th image's texture). + return texture2D(__t%[1]d, %[2]s) * + step(__textureSourceRegionOrigin.x, pos.x) * + (1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) * + step(__textureSourceRegionOrigin.y, pos.y) * + (1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y)) +} +`, i, pos) + } +} + +type Shader struct { + shader *mipmap.Shader + uniformNames []string + uniformTypes []shaderir.Type +} + +func NewShader(src []byte) (*Shader, error) { + var buf bytes.Buffer + buf.Write(src) + buf.WriteString(shaderSuffix) + if graphicscommand.NeedsInvertY() { + buf.WriteString(` +func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { + return mat4( + 2/__imageDstTextureSize.x, 0, 0, 0, + 0, -2/__imageDstTextureSize.y, 0, 0, + 0, 0, 1, 0, + -1, 1, 0, 1, + ) * vec4(position, 0, 1), texCoord, color +} +`) + } else { + buf.WriteString(` +func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { + return mat4( + 2/__imageDstTextureSize.x, 0, 0, 0, + 0, 2/__imageDstTextureSize.y, 0, 0, + 0, 0, 1, 0, + -1, -1, 0, 1, + ) * vec4(position, 0, 1), texCoord, color +} +`) + } + + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors) + if err != nil { + return nil, err + } + + const ( + vert = "__vertex" + frag = "Fragment" + ) + s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum) + if err != nil { + return nil, err + } + + if s.VertexFunc.Block == nil { + return nil, fmt.Errorf("ui: vertex shader entry point '%s' is missing", vert) + } + if s.FragmentFunc.Block == nil { + return nil, fmt.Errorf("ui: fragment shader entry point '%s' is missing", frag) + } + + return &Shader{ + shader: mipmap.NewShader(s), + uniformNames: s.UniformNames, + uniformTypes: s.Uniforms, + }, nil +} + +func (s *Shader) ConvertUniforms(uniforms map[string]interface{}) [][]float32 { + type index struct { + resultIndex int + shaderUniformIndex int + } + + names := map[string]index{} + var idx int + for i, n := range s.uniformNames { + if strings.HasPrefix(n, "__") { + continue + } + names[n] = index{ + resultIndex: idx, + shaderUniformIndex: i, + } + idx++ + } + + us := make([][]float32, len(names)) + for name, idx := range names { + if v, ok := uniforms[name]; ok { + switch v := v.(type) { + case float32: + us[idx.resultIndex] = []float32{v} + case []float32: + us[idx.resultIndex] = v + default: + panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s, %T", name, v)) + } + continue + } + + t := s.uniformTypes[idx.shaderUniformIndex] + us[idx.resultIndex] = make([]float32, t.FloatNum()) + } + + // TODO: Panic if uniforms include an invalid name + + return us +} + +func (s *Shader) MarkDisposed() { + s.shader.MarkDisposed() + s.shader = nil +} diff --git a/shader.go b/shader.go index 2eef81b5e..1a96c90c7 100644 --- a/shader.go +++ b/shader.go @@ -15,105 +15,14 @@ package ebiten import ( - "bytes" - "fmt" - "go/parser" - "go/token" - "strings" - - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" - "github.com/hajimehoshi/ebiten/v2/internal/shader" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) -var shaderSuffix string - -func init() { - shaderSuffix = ` -var __imageDstTextureSize vec2 - -// imageSrcTextureSize returns the destination image's texture size in pixels. -func imageDstTextureSize() vec2 { - return __imageDstTextureSize -} -` - - shaderSuffix += fmt.Sprintf(` -var __textureSizes [%[1]d]vec2 - -// imageSrcTextureSize returns the source image's texture size in pixels. -// As an image is a part of internal texture, the texture is usually bigger than the image. -// The texture's size is useful when you want to calculate pixels from texels. -func imageSrcTextureSize() vec2 { - return __textureSizes[0] -} - -// The unit is the source texture's texel. -var __textureDestinationRegionOrigin vec2 - -// The unit is the source texture's texel. -var __textureDestinationRegionSize vec2 - -// imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture. -// The unit is the source texture's texel. -// -// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. -func imageDstRegionOnTexture() (vec2, vec2) { - return __textureDestinationRegionOrigin, __textureDestinationRegionSize -} - -// The unit is the source texture's texel. -var __textureSourceOffsets [%[2]d]vec2 - -// The unit is the source texture's texel. -var __textureSourceRegionOrigin vec2 - -// The unit is the source texture's texel. -var __textureSourceRegionSize vec2 - -// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture. -// The unit is the source texture's texel. -// -// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture. -func imageSrcRegionOnTexture() (vec2, vec2) { - return __textureSourceRegionOrigin, __textureSourceRegionSize -} -`, graphics.ShaderImageNum, graphics.ShaderImageNum-1) - - for i := 0; i < graphics.ShaderImageNum; i++ { - pos := "pos" - if i >= 1 { - // Convert the position in texture0's texels to the target texture texels. - pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i) - } - // __t%d is a special variable for a texture variable. - shaderSuffix += fmt.Sprintf(` -func imageSrc%[1]dUnsafeAt(pos vec2) vec4 { - // pos is the position in texels of the source texture (= 0th image's texture). - return texture2D(__t%[1]d, %[2]s) -} - -func imageSrc%[1]dAt(pos vec2) vec4 { - // pos is the position in texels of the source texture (= 0th image's texture). - return texture2D(__t%[1]d, %[2]s) * - step(__textureSourceRegionOrigin.x, pos.x) * - (1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) * - step(__textureSourceRegionOrigin.y, pos.y) * - (1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y)) -} -`, i, pos) - } -} - // Shader represents a compiled shader program. // // For the details about the shader, see https://ebiten.org/documents/shader.html. type Shader struct { - shader *ui.Shader - uniformNames []string - uniformTypes []shaderir.Type + shader *ui.Shader } // NewShader compiles a shader program in the shading language Kage, and retruns the result. @@ -122,59 +31,12 @@ type Shader struct { // // For the details about the shader, see https://ebiten.org/documents/shader.html. func NewShader(src []byte) (*Shader, error) { - var buf bytes.Buffer - buf.Write(src) - buf.WriteString(shaderSuffix) - if graphicscommand.NeedsInvertY() { - buf.WriteString(` -func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { - return mat4( - 2/__imageDstTextureSize.x, 0, 0, 0, - 0, -2/__imageDstTextureSize.y, 0, 0, - 0, 0, 1, 0, - -1, 1, 0, 1, - ) * vec4(position, 0, 1), texCoord, color -} -`) - } else { - buf.WriteString(` -func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { - return mat4( - 2/__imageDstTextureSize.x, 0, 0, 0, - 0, 2/__imageDstTextureSize.y, 0, 0, - 0, 0, 1, 0, - -1, -1, 0, 1, - ) * vec4(position, 0, 1), texCoord, color -} -`) - } - - fs := token.NewFileSet() - f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors) + s, err := ui.NewShader(src) if err != nil { return nil, err } - - const ( - vert = "__vertex" - frag = "Fragment" - ) - s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum) - if err != nil { - return nil, err - } - - if s.VertexFunc.Block == nil { - return nil, fmt.Errorf("ebiten: vertex shader entry point '%s' is missing", vert) - } - if s.FragmentFunc.Block == nil { - return nil, fmt.Errorf("ebiten: fragment shader entry point '%s' is missing", frag) - } - return &Shader{ - shader: ui.NewShader(s), - uniformNames: s.UniformNames, - uniformTypes: s.Uniforms, + shader: s, }, nil } @@ -186,43 +48,5 @@ func (s *Shader) Dispose() { } func (s *Shader) convertUniforms(uniforms map[string]interface{}) [][]float32 { - type index struct { - resultIndex int - shaderUniformIndex int - } - - names := map[string]index{} - var idx int - for i, n := range s.uniformNames { - if strings.HasPrefix(n, "__") { - continue - } - names[n] = index{ - resultIndex: idx, - shaderUniformIndex: i, - } - idx++ - } - - us := make([][]float32, len(names)) - for name, idx := range names { - if v, ok := uniforms[name]; ok { - switch v := v.(type) { - case float32: - us[idx.resultIndex] = []float32{v} - case []float32: - us[idx.resultIndex] = v - default: - panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s, %T", name, v)) - } - continue - } - - t := s.uniformTypes[idx.shaderUniformIndex] - us[idx.resultIndex] = make([]float32, t.FloatNum()) - } - - // TODO: Panic if uniforms include an invalid name - - return us + return s.shader.ConvertUniforms(uniforms) }