shader: Reland: Add a predefined uniform variable: __viewportSize

This commit is contained in:
Hajime Hoshi 2020-06-05 03:00:43 +09:00
parent dde7d00231
commit ba36d5a8e9
8 changed files with 57 additions and 13 deletions

View File

@ -29,17 +29,20 @@ const (
const shaderSrc = `package main const shaderSrc = `package main
// __viewportSize is a predefined uniform variable.
// TODO: Hide this by a function.
func Vertex(position vec2, texCoord vec2, color vec4) vec4 { func Vertex(position vec2, texCoord vec2, color vec4) vec4 {
return mat4( return mat4(
2.0/640, 0, 0, 0, 2.0/__viewportSize.x, 0, 0, 0,
0, 2.0/480, 0, 0, 0, 2.0/__viewportSize.y, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
-1, -1, 0, 1, -1, -1, 0, 1,
) * vec4(position, 0, 1) ) * vec4(position, 0, 1)
} }
func Fragment(position vec4) vec4 { func Fragment(position vec4) vec4 {
return vec4(position.x/640, position.y/480, 0, 1) return vec4(position.x/__viewportSize.x, position.y/__viewportSize.y, 0, 1)
}` }`
type Game struct { type Game struct {

View File

@ -384,6 +384,10 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
} }
} }
// The first uniform variable is Internal_ViewportSize.
// The actual value is set at graphicscommand package.
us = append([]interface{}{[]float32{0, 0}}, us...)
var bx0, by0, bx1, by1 float32 var bx0, by0, bx1, by1 float32
if firstImage != nil { if firstImage != nil {
b := firstImage.Bounds() b := firstImage.Bounds()

View File

@ -453,6 +453,13 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
us[i] = v us[i] = v
} }
} }
// The last uniform variables are added at /shader.go and represents a viewport size.
w, h := c.dst.InternalSize()
viewport := us[0].([]float32)
viewport[0] = float32(w)
viewport[1] = float32(h)
return theGraphicsDriver.DrawShader(c.dst.image.ID(), c.shader.shader.ID(), c.nindices, indexOffset, c.mode, us) return theGraphicsDriver.DrawShader(c.dst.image.ID(), c.shader.shader.ID(), c.nindices, indexOffset, c.mode, us)
} }
return theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address) return theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address)

View File

@ -94,7 +94,7 @@ func TestShader(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
us := []interface{}{ us := []interface{}{
[]float32{w, h}, []float32{0, 0},
} }
dst.DrawTriangles(nil, vs, is, nil, driver.CompositeModeSourceOver, 0, 0, s, us) dst.DrawTriangles(nil, vs, is, nil, driver.CompositeModeSourceOver, 0, 0, s, us)

View File

@ -36,7 +36,7 @@ func TestShader(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
us := []interface{}{ us := []interface{}{
0: []float32{1, 1}, []float32{0, 0},
} }
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us) img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us)
@ -73,7 +73,7 @@ func TestShaderChain(t *testing.T) {
s := NewShader(&ir) s := NewShader(&ir)
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
us := []interface{}{ us := []interface{}{
[]float32{1, 1}, []float32{0, 0},
imgs[i], imgs[i],
} }
imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us) imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us)
@ -113,7 +113,7 @@ func TestShaderMultipleSources(t *testing.T) {
ir := etesting.ShaderProgramImages(3) ir := etesting.ShaderProgramImages(3)
s := NewShader(&ir) s := NewShader(&ir)
us := []interface{}{ us := []interface{}{
[]float32{1, 1}, []float32{0, 0},
srcs[0], srcs[0],
srcs[1], srcs[1],
[]float32{0, 0, 1, 1}, []float32{0, 0, 1, 1},
@ -150,7 +150,7 @@ func TestShaderDispose(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
us := []interface{}{ us := []interface{}{
[]float32{1, 1}, []float32{0, 0},
} }
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us) img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us)

View File

@ -136,6 +136,26 @@ func (cs *compileState) parse(f *ast.File) {
cs.parseDecl(&cs.global, d) cs.parseDecl(&cs.global, d)
} }
} }
// Sort the uniform variable so that special variable starting with __ should come first.
var unames []string
var utypes []shaderir.Type
for i, u := range cs.uniforms {
if strings.HasPrefix(u, "__") {
unames = append(unames, u)
utypes = append(utypes, cs.ir.Uniforms[i])
}
}
for i, u := range cs.uniforms {
if !strings.HasPrefix(u, "__") {
unames = append(unames, u)
utypes = append(utypes, cs.ir.Uniforms[i])
}
}
cs.uniforms = unames
cs.ir.Uniforms = utypes
// Parse functions.
for _, d := range f.Decls { for _, d := range f.Decls {
if _, ok := d.(*ast.FuncDecl); ok { if _, ok := d.(*ast.FuncDecl); ok {
cs.parseDecl(&cs.global, d) cs.parseDecl(&cs.global, d)
@ -175,10 +195,12 @@ func (cs *compileState) parseDecl(b *block, d ast.Decl) {
vs := cs.parseVariable(b, s) vs := cs.parseVariable(b, s)
if b == &cs.global { if b == &cs.global {
for i, v := range vs { for i, v := range vs {
if !strings.HasPrefix(v.name, "__") {
if v.name[0] < 'A' || 'Z' < v.name[0] { if v.name[0] < 'A' || 'Z' < v.name[0] {
cs.addError(s.Names[i].Pos(), fmt.Sprintf("global variables must be exposed: %s", v.name)) cs.addError(s.Names[i].Pos(), fmt.Sprintf("global variables must be exposed: %s", v.name))
} }
// TODO: Check init }
// TODO: Check rhs
cs.uniforms = append(cs.uniforms, v.name) cs.uniforms = append(cs.uniforms, v.name)
cs.ir.Uniforms = append(cs.ir.Uniforms, v.typ.ir) cs.ir.Uniforms = append(cs.ir.Uniforms, v.typ.ir)
} }

View File

@ -192,7 +192,7 @@ var (
func defaultProgram() shaderir.Program { func defaultProgram() shaderir.Program {
return shaderir.Program{ return shaderir.Program{
Uniforms: []shaderir.Type{ Uniforms: []shaderir.Type{
{Main: shaderir.Vec2}, {Main: shaderir.Vec2}, // Viewport size. This must be the first uniform variable, and the values are set at graphicscommand driver.
}, },
Attributes: []shaderir.Type{ Attributes: []shaderir.Type{
{Main: shaderir.Vec2}, // Local var (0) in the vertex shader {Main: shaderir.Vec2}, // Local var (0) in the vertex shader

View File

@ -15,6 +15,7 @@
package ebiten package ebiten
import ( import (
"bytes"
"go/parser" "go/parser"
"go/token" "go/token"
@ -22,13 +23,20 @@ import (
"github.com/hajimehoshi/ebiten/internal/shader" "github.com/hajimehoshi/ebiten/internal/shader"
) )
const shaderSuffix = `
var __viewportSize vec2`
type Shader struct { type Shader struct {
shader *buffered.Shader shader *buffered.Shader
} }
func NewShader(src []byte) (*Shader, error) { func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer
buf.Write(src)
buf.WriteString(shaderSuffix)
fs := token.NewFileSet() fs := token.NewFileSet()
f, err := parser.ParseFile(fs, "", src, parser.AllErrors) f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)
if err != nil { if err != nil {
return nil, err return nil, err
} }