mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
all: add a compiler directive kage:unit
This change adds a new compiler directive 'kage:unit' to Kage. This takes one of these two values: 'pixel' and 'texel'. The default value is 'texel'. With the pixel-unit mode, all the built-in functions treats pixels instead of texels, and the texCoord argument of Fragment is in pixels. This simplifies shader programs as programs no longer have the notion of texels. With the texel-unit mode, the behavior is the same as the current behavior. Closes #1431
This commit is contained in:
parent
b5ca404c42
commit
49582519c1
@ -14,17 +14,17 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
center := ScreenSize / 2
|
||||
// Convert a pixel to a texel by dividing by the texture size.
|
||||
// TODO: This is confusing. Add a function to treat pixels (#1431).
|
||||
amount := (center - Cursor) / 10 / imageSrcTextureSize()
|
||||
_, dstSize := imageDstRegionOnTexture()
|
||||
center := dstSize / 2
|
||||
amount := (center - Cursor) / 10
|
||||
var clr vec3
|
||||
clr.r = imageSrc2At(texCoord + amount).r
|
||||
clr.g = imageSrc2UnsafeAt(texCoord).g
|
||||
|
@ -14,16 +14,17 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
dstOrigin, dstSize := imageDstRegionOnTexture()
|
||||
pos := (position.xy/imageDstTextureSize() - dstOrigin) / dstSize
|
||||
pos += Cursor / ScreenSize / 4
|
||||
pos := (position.xy - dstOrigin) / dstSize
|
||||
pos += Cursor / dstSize / 4
|
||||
clr := 0.0
|
||||
clr += sin(pos.x*cos(Time/15)*80) + cos(pos.y*cos(Time/15)*10)
|
||||
clr += sin(pos.y*sin(Time/10)*40) + cos(pos.x*sin(Time/25)*40)
|
||||
|
@ -14,11 +14,12 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
// Triangle wave to go 0-->1-->0...
|
||||
|
@ -14,16 +14,16 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
srcOrigin, srcSize := imageSrcRegionOnTexture()
|
||||
pos := (texCoord - srcOrigin) / srcSize
|
||||
pos *= ScreenSize
|
||||
dstOrigin, _ := imageDstRegionOnTexture()
|
||||
pos := position.xy - dstOrigin
|
||||
|
||||
lightpos := vec3(Cursor, 50)
|
||||
lightdir := normalize(lightpos - vec3(pos, 0))
|
||||
|
@ -146,9 +146,8 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
op := &ebiten.DrawRectShaderOptions{}
|
||||
op.Uniforms = map[string]any{
|
||||
"Time": float32(g.time) / 60,
|
||||
"Cursor": []float32{float32(cx), float32(cy)},
|
||||
"ScreenSize": []float32{float32(w), float32(h)},
|
||||
"Time": float32(g.time) / 60,
|
||||
"Cursor": []float32{float32(cx), float32(cy)},
|
||||
}
|
||||
op.Images[0] = gopherImage
|
||||
op.Images[1] = normalImage
|
||||
|
@ -14,16 +14,16 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
srcOrigin, srcSize := imageSrcRegionOnTexture()
|
||||
pos := (texCoord - srcOrigin) / srcSize
|
||||
pos *= ScreenSize
|
||||
dstOrigin, _ := imageDstRegionOnTexture()
|
||||
pos := position.xy - dstOrigin
|
||||
|
||||
dir := normalize(pos - Cursor)
|
||||
clr := imageSrc2UnsafeAt(texCoord)
|
||||
@ -33,8 +33,7 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
sum := clr
|
||||
for i := 0; i < len(samples); i++ {
|
||||
pos := texCoord + dir*samples[i]/imageSrcTextureSize()
|
||||
sum += imageSrc2At(pos)
|
||||
sum += imageSrc2At(texCoord + dir*samples[i])
|
||||
}
|
||||
sum /= 10 + 1
|
||||
|
||||
|
@ -14,10 +14,12 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
pos := position.xy / imageDstTextureSize()
|
||||
pos := position.xy
|
||||
origin, size := imageDstRegionOnTexture()
|
||||
pos -= origin
|
||||
pos /= size
|
||||
|
@ -14,31 +14,28 @@
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
var ScreenSize vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
srcOrigin, srcSize := imageSrcRegionOnTexture()
|
||||
pos := (texCoord - srcOrigin) / srcSize
|
||||
pos *= ScreenSize
|
||||
dstOrigin, dstSize := imageDstRegionOnTexture()
|
||||
pos := position.xy - dstOrigin
|
||||
|
||||
border := ScreenSize.y*0.6 + 4*cos(Time*3+pos.y/10)
|
||||
border := dstSize.y*0.6 + 4*cos(Time*3+pos.y/10)
|
||||
if pos.y < border {
|
||||
return imageSrc2UnsafeAt(texCoord)
|
||||
}
|
||||
|
||||
// Convert a pixel to a texel by dividing by the texture size.
|
||||
// TODO: This is confusing. Add a function to treat pixels (#1431).
|
||||
srcTexSize := imageSrcTextureSize()
|
||||
xoffset := (4 / srcTexSize.x) * cos(Time*3+pos.y/10)
|
||||
yoffset := (20 / srcTexSize.y) * (1 + cos(Time*3+pos.y/40))
|
||||
bordertex := border / srcTexSize.y
|
||||
xoffset := 4 * cos(Time*3+pos.y/10)
|
||||
yoffset := 20 * (1 + cos(Time*3+pos.y/40))
|
||||
srcOrigin, _ := imageSrcRegionOnTexture()
|
||||
clr := imageSrc2At(vec2(
|
||||
texCoord.x+xoffset,
|
||||
-(texCoord.y+yoffset-srcOrigin.y)+bordertex*2+srcOrigin.y,
|
||||
-(texCoord.y+yoffset-srcOrigin.y)+border*2+srcOrigin.y,
|
||||
)).rgb
|
||||
|
||||
overlay := vec3(0.5, 1, 1)
|
||||
|
20
gameforui.go
20
gameforui.go
@ -24,23 +24,19 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
)
|
||||
|
||||
const screenShaderSrc = `package main
|
||||
const screenShaderSrc = `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
// TODO: Calculate the scale in the shader after pixels become the main unit in shaders (#1431)
|
||||
_, dr := imageDstRegionOnTexture()
|
||||
_, sr := imageSrcRegionOnTexture()
|
||||
scale := (imageDstTextureSize() * dr) / (imageSrcTextureSize() * sr)
|
||||
scale := dr/sr
|
||||
|
||||
sourceSize := imageSrcTextureSize()
|
||||
// texelSize is one pixel size in texel sizes.
|
||||
texelSize := 1 / sourceSize
|
||||
halfScaledTexelSize := texelSize / 2 / scale
|
||||
|
||||
// Shift 1/512 [texel] to avoid the tie-breaking issue.
|
||||
// Shift 1/512 [pixel] to avoid the tie-breaking issue.
|
||||
pos := texCoord
|
||||
p0 := pos - halfScaledTexelSize + (texelSize / 512)
|
||||
p1 := pos + halfScaledTexelSize + (texelSize / 512)
|
||||
p0 := pos - 1/2.0 + 1/512.0
|
||||
p1 := pos + 1/2.0 + 1/512.0
|
||||
|
||||
// Texels must be in the source rect, so it is not necessary to check.
|
||||
c0 := imageSrc0UnsafeAt(p0)
|
||||
@ -49,7 +45,7 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
c3 := imageSrc0UnsafeAt(p1)
|
||||
|
||||
// p is the p1 value in one pixel assuming that the pixel's upper-left is (0, 0) and the lower-right is (1, 1).
|
||||
p := fract(p1 * sourceSize)
|
||||
p := fract(p1)
|
||||
|
||||
// rate indicates how much the 4 colors are mixed. rate is in between [0, 1].
|
||||
//
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/packing"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/restorable"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -455,14 +456,20 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
ox, oy, _, _ := srcs[0].regionWithPadding()
|
||||
ps := srcs[0].paddingSize()
|
||||
oxf, oyf = float32(ox+ps), float32(oy+ps)
|
||||
sw, sh := srcs[0].backend.restorable.InternalSize()
|
||||
swf, shf := float32(sw), float32(sh)
|
||||
n := len(vertices)
|
||||
for i := 0; i < n; i += graphics.VertexFloatCount {
|
||||
vertices[i] += dx
|
||||
vertices[i+1] += dy
|
||||
vertices[i+2] = (vertices[i+2] + oxf) / swf
|
||||
vertices[i+3] = (vertices[i+3] + oyf) / shf
|
||||
vertices[i+2] += oxf
|
||||
vertices[i+3] += oyf
|
||||
}
|
||||
if shader.unit() == shaderir.Texel {
|
||||
sw, sh := srcs[0].backend.restorable.InternalSize()
|
||||
swf, shf := float32(sw), float32(sh)
|
||||
for i := 0; i < n; i += graphics.VertexFloatCount {
|
||||
vertices[i+2] /= swf
|
||||
vertices[i+3] /= shf
|
||||
}
|
||||
}
|
||||
// srcRegion can be deliberately empty when this is not needed in order to avoid unexpected
|
||||
// performance issue (#1293).
|
||||
|
@ -36,6 +36,10 @@ func NewShader(ir *shaderir.Program) *Shader {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Shader) unit() shaderir.Unit {
|
||||
return s.shader.Unit()
|
||||
}
|
||||
|
||||
// MarkDisposed marks the shader as disposed. The actual operation is deferred.
|
||||
// MarkDisposed can be called from finalizers.
|
||||
//
|
||||
|
@ -52,7 +52,9 @@ var (
|
||||
shadersM sync.Mutex
|
||||
)
|
||||
|
||||
var tmpl = template.Must(template.New("tmpl").Parse(`package main
|
||||
var tmpl = template.Must(template.New("tmpl").Parse(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
{{if .UseColorM}}
|
||||
var ColorMBody mat4
|
||||
@ -76,12 +78,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
clr := imageSrc0At(adjustTexelForAddressRepeat(texCoord))
|
||||
{{end}}
|
||||
{{else if eq .Filter .FilterLinear}}
|
||||
sourceSize := imageSrcTextureSize()
|
||||
texelSize := 1 / sourceSize
|
||||
|
||||
// Shift 1/512 [texel] to avoid the tie-breaking issue (#1212).
|
||||
p0 := texCoord - texelSize/2 + texelSize/512
|
||||
p1 := texCoord + texelSize/2 + texelSize/512
|
||||
// Shift 1/512 [pixel] to avoid the tie-breaking issue (#1212).
|
||||
p0 := texCoord - 1/2.0 + 1/512.0
|
||||
p1 := texCoord + 1/2.0 + 1/512.0
|
||||
|
||||
{{if eq .Address .AddressRepeat}}
|
||||
p0 = adjustTexelForAddressRepeat(p0)
|
||||
@ -100,7 +99,7 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
c3 := imageSrc0At(p1)
|
||||
{{end}}
|
||||
|
||||
rate := fract(p0 * sourceSize)
|
||||
rate := fract(p0)
|
||||
clr := mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y)
|
||||
{{end}}
|
||||
|
||||
|
@ -22,10 +22,8 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
var shaderSuffix string
|
||||
|
||||
func init() {
|
||||
shaderSuffix = `
|
||||
func shaderSuffix(unit shaderir.Unit) (string, error) {
|
||||
shaderSuffix := `
|
||||
var __imageDstTextureSize vec2
|
||||
|
||||
// imageSrcTextureSize returns the destination image's texture size in pixels.
|
||||
@ -39,36 +37,36 @@ 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.
|
||||
// The texture's size is useful when you want to calculate pixels from texels in the texel mode.
|
||||
func imageSrcTextureSize() vec2 {
|
||||
return __textureSizes[0]
|
||||
}
|
||||
|
||||
// The unit is the source texture's texel.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __textureDestinationRegionOrigin vec2
|
||||
|
||||
// The unit is the source texture's texel.
|
||||
// The unit is the source texture's pixel or 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.
|
||||
// The unit is the source texture's pixel or 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.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __textureSourceOffsets [%[2]d]vec2
|
||||
|
||||
// The unit is the source texture's texel.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __textureSourceRegionOrigin vec2
|
||||
|
||||
// The unit is the source texture's texel.
|
||||
// The unit is the source texture's pixel or 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.
|
||||
// The unit is the source texture's pixel or 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) {
|
||||
@ -79,18 +77,25 @@ func imageSrcRegionOnTexture() (vec2, vec2) {
|
||||
for i := 0; i < ShaderImageCount; 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)
|
||||
// Convert the position in texture0's positions to the target texture positions.
|
||||
switch unit {
|
||||
case shaderir.Pixel:
|
||||
pos = fmt.Sprintf("pos + __textureSourceOffsets[%d]", i-1)
|
||||
case shaderir.Texel:
|
||||
pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i)
|
||||
default:
|
||||
return "", fmt.Errorf("graphics: unexpected unit: %d", unit)
|
||||
}
|
||||
}
|
||||
// __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).
|
||||
// pos is the position in positions 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).
|
||||
// pos is the position of the source texture (= 0th image's texture).
|
||||
// If pos is in the region, the result is (1, 1). Otherwise, either element is 0.
|
||||
in := step(__textureSourceRegionOrigin, pos) - step(__textureSourceRegionOrigin + __textureSourceRegionSize, pos)
|
||||
return texture2D(__t%[1]d, %[2]s) * in.x * in.y
|
||||
@ -105,12 +110,22 @@ func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
|
||||
return __projectionMatrix * vec4(position, 0, 1), texCoord, color
|
||||
}
|
||||
`
|
||||
return shaderSuffix, nil
|
||||
}
|
||||
|
||||
func CompileShader(src []byte) (*shaderir.Program, error) {
|
||||
unit, err := shader.ParseCompilerDirectives(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
suffix, err := shaderSuffix(unit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(src)
|
||||
buf.WriteString(shaderSuffix)
|
||||
buf.WriteString(suffix)
|
||||
|
||||
const (
|
||||
vert = "__vertex"
|
||||
|
@ -599,16 +599,23 @@ func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shade
|
||||
}
|
||||
idx += len(srcs) * 2
|
||||
|
||||
if shader.unit() == shaderir.Texel {
|
||||
dstRegion.X /= float32(dw)
|
||||
dstRegion.Y /= float32(dh)
|
||||
dstRegion.Width /= float32(dw)
|
||||
dstRegion.Height /= float32(dh)
|
||||
}
|
||||
|
||||
// Set the destination region.
|
||||
uniforms[idx+0] = math.Float32bits(dstRegion.X / float32(dw))
|
||||
uniforms[idx+1] = math.Float32bits(dstRegion.Y / float32(dh))
|
||||
uniforms[idx+0] = math.Float32bits(dstRegion.X)
|
||||
uniforms[idx+1] = math.Float32bits(dstRegion.Y)
|
||||
idx += 2
|
||||
|
||||
uniforms[idx+0] = math.Float32bits(dstRegion.Width / float32(dw))
|
||||
uniforms[idx+1] = math.Float32bits(dstRegion.Height / float32(dh))
|
||||
uniforms[idx+0] = math.Float32bits(dstRegion.Width)
|
||||
uniforms[idx+1] = math.Float32bits(dstRegion.Height)
|
||||
idx += 2
|
||||
|
||||
if srcs[0] != nil {
|
||||
if shader.unit() == shaderir.Texel && srcs[0] != nil {
|
||||
w, h := srcs[0].InternalSize()
|
||||
srcRegion.X /= float32(w)
|
||||
srcRegion.Y /= float32(h)
|
||||
|
@ -41,14 +41,12 @@ func TestMain(m *testing.M) {
|
||||
etesting.MainWithRunLoop(m)
|
||||
}
|
||||
|
||||
func quadVertices(srcImage *graphicscommand.Image, w, h float32) []float32 {
|
||||
sw, sh := srcImage.InternalSize()
|
||||
swf, shf := float32(sw), float32(sh)
|
||||
func quadVertices(w, h float32) []float32 {
|
||||
return []float32{
|
||||
0, 0, 0, 0, 1, 1, 1, 1,
|
||||
w, 0, w / swf, 0, 1, 1, 1, 1,
|
||||
0, w, 0, h / shf, 1, 1, 1, 1,
|
||||
w, h, w / swf, h / shf, 1, 1, 1, 1,
|
||||
w, 0, w, 0, 1, 1, 1, 1,
|
||||
0, w, 0, h, 1, 1, 1, 1,
|
||||
w, h, w, h, 1, 1, 1, 1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +55,7 @@ func TestClear(t *testing.T) {
|
||||
src := graphicscommand.NewImage(w/2, h/2, false)
|
||||
dst := graphicscommand.NewImage(w, h, false)
|
||||
|
||||
vs := quadVertices(src, w/2, h/2)
|
||||
vs := quadVertices(w/2, h/2)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -88,7 +86,7 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) {
|
||||
clr := graphicscommand.NewImage(w, h, false)
|
||||
src := graphicscommand.NewImage(w/2, h/2, false)
|
||||
dst := graphicscommand.NewImage(w, h, false)
|
||||
vs := quadVertices(src, w/2, h/2)
|
||||
vs := quadVertices(w/2, h/2)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -107,7 +105,7 @@ func TestShader(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
clr := graphicscommand.NewImage(w, h, false)
|
||||
dst := graphicscommand.NewImage(w, h, false)
|
||||
vs := quadVertices(clr, w, h)
|
||||
vs := quadVertices(w, h)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
|
@ -42,3 +42,7 @@ func (s *Shader) Dispose() {
|
||||
}
|
||||
theCommandQueue.Enqueue(c)
|
||||
}
|
||||
|
||||
func (s *Shader) unit() shaderir.Unit {
|
||||
return s.ir.Unit
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ func (i *Image) Extend(width, height int) *Image {
|
||||
srcs := [graphics.ShaderImageCount]*Image{i}
|
||||
var offsets [graphics.ShaderImageCount - 1][2]float32
|
||||
sw, sh := i.image.InternalSize()
|
||||
vs := quadVertices(i, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1)
|
||||
vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -191,22 +191,12 @@ func (i *Image) Extend(width, height int) *Image {
|
||||
}
|
||||
|
||||
// quadVertices returns vertices to render a quad. These values are passed to graphicscommand.Image.
|
||||
func quadVertices(src *Image, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 {
|
||||
if src == nil {
|
||||
return []float32{
|
||||
dx0, dy0, 0, 0, cr, cg, cb, ca,
|
||||
dx1, dy0, 0, 0, cr, cg, cb, ca,
|
||||
dx0, dy1, 0, 0, cr, cg, cb, ca,
|
||||
dx1, dy1, 0, 0, cr, cg, cb, ca,
|
||||
}
|
||||
}
|
||||
sw, sh := src.InternalSize()
|
||||
swf, shf := float32(sw), float32(sh)
|
||||
func quadVertices(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 {
|
||||
return []float32{
|
||||
dx0, dy0, sx0 / swf, sy0 / shf, cr, cg, cb, ca,
|
||||
dx1, dy0, sx1 / swf, sy0 / shf, cr, cg, cb, ca,
|
||||
dx0, dy1, sx0 / swf, sy1 / shf, cr, cg, cb, ca,
|
||||
dx1, dy1, sx1 / swf, sy1 / shf, cr, cg, cb, ca,
|
||||
dx0, dy0, sx0, sy0, cr, cg, cb, ca,
|
||||
dx1, dy0, sx1, sy0, cr, cg, cb, ca,
|
||||
dx0, dy1, sx0, sy1, cr, cg, cb, ca,
|
||||
dx1, dy1, sx1, sy1, cr, cg, cb, ca,
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +204,7 @@ func clearImage(i *graphicscommand.Image) {
|
||||
// This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some
|
||||
// devices.
|
||||
dw, dh := i.InternalSize()
|
||||
vs := quadVertices(nil, 0, 0, float32(dw), float32(dh), 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
vs := quadVertices(0, 0, float32(dw), float32(dh), 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
var offsets [graphics.ShaderImageCount - 1][2]float32
|
||||
dstRegion := graphicsdriver.Region{
|
||||
|
@ -99,7 +99,7 @@ func TestRestoreWithoutDraw(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func quadVertices(srcImage *restorable.Image, sw, sh, x, y int) []float32 {
|
||||
func quadVertices(sw, sh, x, y int) []float32 {
|
||||
dx0 := float32(x)
|
||||
dy0 := float32(y)
|
||||
dx1 := float32(x + sw)
|
||||
@ -108,17 +108,11 @@ func quadVertices(srcImage *restorable.Image, sw, sh, x, y int) []float32 {
|
||||
sy0 := float32(0)
|
||||
sx1 := float32(sw)
|
||||
sy1 := float32(sh)
|
||||
iswf := float32(1)
|
||||
ishf := float32(1)
|
||||
if srcImage != nil {
|
||||
isw, ish := srcImage.InternalSize()
|
||||
iswf, ishf = float32(isw), float32(ish)
|
||||
}
|
||||
return []float32{
|
||||
dx0, dy0, sx0 / iswf, sy0 / ishf, 1, 1, 1, 1,
|
||||
dx1, dy0, sx1 / iswf, sy0 / ishf, 1, 1, 1, 1,
|
||||
dx0, dy1, sx0 / iswf, sy1 / ishf, 1, 1, 1, 1,
|
||||
dx1, dy1, sx1 / iswf, sy1 / ishf, 1, 1, 1, 1,
|
||||
dx0, dy0, sx0, sy0, 1, 1, 1, 1,
|
||||
dx1, dy0, sx1, sy0, 1, 1, 1, 1,
|
||||
dx0, dy1, sx0, sy1, 1, 1, 1, 1,
|
||||
dx1, dy1, sx1, sy1, 1, 1, 1, 1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +131,7 @@ func TestRestoreChain(t *testing.T) {
|
||||
clr := color.RGBA{A: 0xff}
|
||||
imgs[0].WritePixels([]byte{clr.R, clr.G, clr.B, clr.A}, 0, 0, 1, 1)
|
||||
for i := 0; i < num-1; i++ {
|
||||
vs := quadVertices(imgs[i], 1, 1, 0, 0)
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -193,10 +187,10 @@ func TestRestoreChain2(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
imgs[8].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[7]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[7], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
imgs[9].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[8]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[8], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
imgs[8].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[7]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
imgs[9].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[8]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
for i := 0; i < 7; i++ {
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
}
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
@ -242,10 +236,10 @@ func TestRestoreOverrideSource(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
img2.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img2.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -330,23 +324,23 @@ func TestRestoreComplexGraph(t *testing.T) {
|
||||
Height: h,
|
||||
}
|
||||
var offsets [graphics.ShaderImageCount - 1][2]float32
|
||||
vs := quadVertices(img0, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img1, w, h, 1, 0)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img1, w, h, 1, 0)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img2, w, h, 2, 0)
|
||||
vs = quadVertices(w, h, 2, 0)
|
||||
img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img3, w, h, 0, 0)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img5.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img3, w, h, 0, 0)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img4, w, h, 1, 0)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img4}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img2, w, h, 0, 0)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
vs = quadVertices(img3, w, h, 2, 0)
|
||||
vs = quadVertices(w, h, 2, 0)
|
||||
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -445,8 +439,8 @@ func TestRestoreRecursive(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -541,7 +535,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
|
||||
img1 := restorable.NewImage(2, 1, restorable.ImageTypeRegular)
|
||||
defer img1.Dispose()
|
||||
|
||||
vs := quadVertices(img0, 1, 1, 0, 0)
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -592,8 +586,8 @@ func TestDispose(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, 1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, 1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
|
||||
img1.Dispose()
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
@ -699,7 +693,7 @@ func TestWritePixelsOnly(t *testing.T) {
|
||||
img0.WritePixels([]byte{1, 2, 3, 4}, i%w, i/w, 1, 1)
|
||||
}
|
||||
|
||||
vs := quadVertices(img0, 1, 1, 0, 0)
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -758,7 +752,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
||||
pix[i] = 0xff
|
||||
}
|
||||
src.WritePixels(pix, 0, 0, w, h)
|
||||
vs := quadVertices(src, 1, 1, 0, 0)
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -787,7 +781,7 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) {
|
||||
src := restorable.NewImage(w, h, restorable.ImageTypeRegular)
|
||||
dst := restorable.NewImage(w, h, restorable.ImageTypeRegular)
|
||||
|
||||
vs := quadVertices(src, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -811,7 +805,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
|
||||
}
|
||||
src.WritePixels(pix, 0, 0, w, h)
|
||||
|
||||
vs := quadVertices(src, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -910,7 +904,7 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
|
||||
src.WritePixels(pix, 0, 0, w, h)
|
||||
|
||||
orig := restorable.NewImage(w, h, restorable.ImageTypeRegular)
|
||||
vs := quadVertices(src, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -964,7 +958,7 @@ func TestMutateSlices(t *testing.T) {
|
||||
}
|
||||
src.WritePixels(pix, 0, 0, w, h)
|
||||
|
||||
vs := quadVertices(src, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := make([]uint16, len(graphics.QuadIndices()))
|
||||
copy(is, graphics.QuadIndices())
|
||||
dr := graphicsdriver.Region{
|
||||
@ -1162,7 +1156,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
|
||||
|
||||
src.WritePixels([]byte{0x80, 0x80, 0x80, 0x80}, 0, 0, 1, 1)
|
||||
|
||||
vs := quadVertices(src, w, h, 0, 0)
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -1191,7 +1185,7 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) {
|
||||
dst.WritePixels([]byte{0x40, 0x40, 0x40, 0x40}, 0, 0, 1, 1)
|
||||
|
||||
// Call DrawTriangles at a different region second.
|
||||
vs := quadVertices(src, 1, 1, 1, 0)
|
||||
vs := quadVertices(1, 1, 1, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 1,
|
||||
|
@ -50,6 +50,10 @@ func (s *Shader) restore() {
|
||||
s.shader = graphicscommand.NewShader(s.ir)
|
||||
}
|
||||
|
||||
func (s *Shader) Unit() shaderir.Unit {
|
||||
return s.ir.Unit
|
||||
}
|
||||
|
||||
var (
|
||||
NearestFilterShader *Shader
|
||||
LinearFilterShader *Shader
|
||||
@ -76,7 +80,9 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
wg.Go(func() error {
|
||||
ir, err := graphics.CompileShader([]byte(`package main
|
||||
ir, err := graphics.CompileShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return vec4(0)
|
||||
|
@ -64,7 +64,7 @@ func TestShader(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -99,7 +99,7 @@ func TestShaderChain(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
}
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
|
||||
@ -137,7 +137,7 @@ func TestShaderMultipleSources(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
|
||||
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||
clearImage(srcs[0], 1, 1)
|
||||
@ -178,7 +178,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
|
||||
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||
clearImage(srcs[0], 3, 1)
|
||||
@ -208,7 +208,7 @@ func TestShaderDispose(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
|
||||
// Dispose the shader. This should invalidate all the images using this shader i.e., all the images become
|
||||
// stale.
|
||||
|
@ -15,11 +15,14 @@
|
||||
package shader
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
gconstant "go/constant"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
@ -49,6 +52,7 @@ type compileState struct {
|
||||
|
||||
vertexEntry string
|
||||
fragmentEntry string
|
||||
unit shaderir.Unit
|
||||
|
||||
ir shaderir.Program
|
||||
|
||||
@ -182,6 +186,11 @@ func (p *ParseError) Error() string {
|
||||
}
|
||||
|
||||
func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*shaderir.Program, error) {
|
||||
unit, err := ParseCompilerDirectives(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fs := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fs, "", src, parser.AllErrors)
|
||||
if err != nil {
|
||||
@ -192,6 +201,7 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*
|
||||
fs: fs,
|
||||
vertexEntry: vertexEntry,
|
||||
fragmentEntry: fragmentEntry,
|
||||
unit: unit,
|
||||
}
|
||||
s.global.ir = &shaderir.Block{}
|
||||
s.parse(f)
|
||||
@ -209,12 +219,45 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*
|
||||
return &s.ir, nil
|
||||
}
|
||||
|
||||
func ParseCompilerDirectives(src []byte) (shaderir.Unit, error) {
|
||||
// TODO: Change the unit to pixels in v3 (#2645).
|
||||
unit := shaderir.Texel
|
||||
|
||||
reUnit := regexp.MustCompile(`^//kage:unit\s+(.+)$`)
|
||||
var unitParsed bool
|
||||
|
||||
buf := bytes.NewBuffer(src)
|
||||
s := bufio.NewScanner(buf)
|
||||
for s.Scan() {
|
||||
m := reUnit.FindStringSubmatch(s.Text())
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
if unitParsed {
|
||||
return 0, fmt.Errorf("shader: at most one //kage:unit can exist in a shader")
|
||||
}
|
||||
switch m[1] {
|
||||
case "pixel":
|
||||
unit = shaderir.Pixel
|
||||
case "texel":
|
||||
unit = shaderir.Texel
|
||||
default:
|
||||
return 0, fmt.Errorf("shader: invalid value for //kage:unit: %s", m[1])
|
||||
}
|
||||
unitParsed = true
|
||||
}
|
||||
|
||||
return unit, nil
|
||||
}
|
||||
|
||||
func (s *compileState) addError(pos token.Pos, str string) {
|
||||
p := s.fs.Position(pos)
|
||||
s.errs = append(s.errs, fmt.Sprintf("%s: %s", p, str))
|
||||
}
|
||||
|
||||
func (cs *compileState) parse(f *ast.File) {
|
||||
cs.ir.Unit = cs.unit
|
||||
|
||||
// Parse GenDecl for global variables, and then parse functions.
|
||||
for _, d := range f.Decls {
|
||||
if _, ok := d.(*ast.FuncDecl); !ok {
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shader"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl"
|
||||
@ -52,8 +53,9 @@ func hlslNormalize(str string) string {
|
||||
}
|
||||
|
||||
func metalNormalize(str string) string {
|
||||
if strings.HasPrefix(str, msl.Prelude) {
|
||||
str = str[len(msl.Prelude):]
|
||||
prelude := msl.Prelude(shaderir.Texel)
|
||||
if strings.HasPrefix(str, prelude) {
|
||||
str = str[len(prelude):]
|
||||
}
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
@ -3036,3 +3036,89 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompilerDirective(t *testing.T) {
|
||||
cases := []struct {
|
||||
src string
|
||||
unit shaderir.Unit
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
src: `package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
unit: shaderir.Texel,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
src: `//kage:unit texel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
unit: shaderir.Texel,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
src: `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
unit: shaderir.Pixel,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
src: `//kage:unit foo
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
src: `//kage:unit pixel
|
||||
//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
src: `//kage:unit pixel
|
||||
//kage:unit texel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return position
|
||||
}`,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
ir, err := compileToIR([]byte(c.src))
|
||||
if err == nil && c.err {
|
||||
t.Errorf("Compile(%q) must return an error but does not", c.src)
|
||||
} else if err != nil && !c.err {
|
||||
t.Errorf("Compile(%q) must not return nil but returned %v", c.src, err)
|
||||
}
|
||||
if err != nil || c.err {
|
||||
continue
|
||||
}
|
||||
if got, want := ir.Unit, c.unit; got != want {
|
||||
t.Errorf("Compile(%q).Unit: got: %d, want: %d", c.src, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ type compileContext struct {
|
||||
version GLSLVersion
|
||||
structNames map[string]string
|
||||
structTypes []shaderir.Type
|
||||
unit shaderir.Unit
|
||||
}
|
||||
|
||||
func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) string {
|
||||
@ -120,6 +121,7 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
||||
c := &compileContext{
|
||||
version: version,
|
||||
structNames: map[string]string{},
|
||||
unit: p.Unit,
|
||||
}
|
||||
|
||||
// Vertex func
|
||||
@ -513,8 +515,12 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
for _, exp := range e.Exprs[1:] {
|
||||
args = append(args, expr(&exp))
|
||||
}
|
||||
f := expr(&e.Exprs[0])
|
||||
if f == "texelFetch" {
|
||||
return fmt.Sprintf("%s(%s, ivec2(%s), 0)", f, args[0], args[1])
|
||||
}
|
||||
// Using parentheses at the callee is illegal.
|
||||
return fmt.Sprintf("%s(%s)", expr(&e.Exprs[0]), strings.Join(args, ", "))
|
||||
return fmt.Sprintf("%s(%s)", f, strings.Join(args, ", "))
|
||||
case shaderir.FieldSelector:
|
||||
return fmt.Sprintf("(%s).%s", expr(&e.Exprs[0]), expr(&e.Exprs[1]))
|
||||
case shaderir.Index:
|
||||
|
@ -122,6 +122,9 @@ func (c *compileContext) builtinFuncString(f shaderir.BuiltinFunc) string {
|
||||
case shaderir.Dfdy:
|
||||
return "dFdy"
|
||||
case shaderir.Texture2DF:
|
||||
if c.unit == shaderir.Pixel {
|
||||
return "texelFetch"
|
||||
}
|
||||
return "texture"
|
||||
default:
|
||||
return string(f)
|
||||
|
@ -31,6 +31,7 @@ const (
|
||||
type compileContext struct {
|
||||
structNames map[string]string
|
||||
structTypes []shaderir.Type
|
||||
unit shaderir.Unit
|
||||
}
|
||||
|
||||
func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) string {
|
||||
@ -87,7 +88,9 @@ float4x4 float4x4FromScalar(float x) {
|
||||
func Compile(p *shaderir.Program) (vertexShader, pixelShader string, offsets []int) {
|
||||
offsets = calculateMemoryOffsets(p.Uniforms)
|
||||
|
||||
c := &compileContext{}
|
||||
c := &compileContext{
|
||||
unit: p.Unit,
|
||||
}
|
||||
|
||||
var lines []string
|
||||
lines = append(lines, strings.Split(Prelude, "\n")...)
|
||||
@ -117,7 +120,9 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string, offsets []i
|
||||
for i := 0; i < p.TextureCount; i++ {
|
||||
lines = append(lines, fmt.Sprintf("Texture2D T%[1]d : register(t%[1]d);", i))
|
||||
}
|
||||
lines = append(lines, "SamplerState samp : register(s0);")
|
||||
if c.unit == shaderir.Texel {
|
||||
lines = append(lines, "SamplerState samp : register(s0);")
|
||||
}
|
||||
}
|
||||
|
||||
vslines := make([]string, len(lines))
|
||||
@ -472,7 +477,14 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
return fmt.Sprintf("float4x4FromScalar(%s)", args[0])
|
||||
}
|
||||
case shaderir.Texture2DF:
|
||||
return fmt.Sprintf("%s.Sample(samp, %s)", args[0], strings.Join(args[1:], ", "))
|
||||
switch c.unit {
|
||||
case shaderir.Pixel:
|
||||
return fmt.Sprintf("%s.Load(int3(%s, 0))", args[0], strings.Join(args[1:], ", "))
|
||||
case shaderir.Texel:
|
||||
return fmt.Sprintf("%s.Sample(samp, %s)", args[0], strings.Join(args[1:], ", "))
|
||||
default:
|
||||
panic(fmt.Sprintf("hlsl: unexpected unit: %d", p.Unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", expr(&e.Exprs[0]), strings.Join(args, ", "))
|
||||
|
@ -163,14 +163,17 @@ func TestOutput(t *testing.T) {
|
||||
Metal string
|
||||
}{
|
||||
{
|
||||
Name: "Empty",
|
||||
Program: shaderir.Program{},
|
||||
GlslVS: glsl.VertexPrelude(glsl.GLSLVersionDefault),
|
||||
GlslFS: glsl.FragmentPrelude(glsl.GLSLVersionDefault),
|
||||
Name: "Empty",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
},
|
||||
GlslVS: glsl.VertexPrelude(glsl.GLSLVersionDefault),
|
||||
GlslFS: glsl.FragmentPrelude(glsl.GLSLVersionDefault),
|
||||
},
|
||||
{
|
||||
Name: "Uniform",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Uniforms: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
},
|
||||
@ -183,6 +186,7 @@ uniform float U0;`,
|
||||
{
|
||||
Name: "UniformStruct",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Uniforms: []shaderir.Type{
|
||||
{
|
||||
Main: shaderir.Struct,
|
||||
@ -208,6 +212,7 @@ uniform S0 U0;`,
|
||||
{
|
||||
Name: "Vars",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Uniforms: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
},
|
||||
@ -229,6 +234,7 @@ in vec3 V0;`,
|
||||
{
|
||||
Name: "Func",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -249,6 +255,7 @@ void F0(void) {
|
||||
{
|
||||
Name: "FuncParams",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -277,6 +284,7 @@ void F0(in float l0, in vec2 l1, in vec4 l2, out mat4 l3) {
|
||||
{
|
||||
Name: "FuncReturn",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -310,6 +318,7 @@ float F0(in float l0) {
|
||||
{
|
||||
Name: "FuncLocals",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -344,6 +353,7 @@ void F0(in float l0, out float l1) {
|
||||
{
|
||||
Name: "FuncBlocks",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -398,6 +408,7 @@ void F0(in float l0, out float l1) {
|
||||
{
|
||||
Name: "Add",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -439,6 +450,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
{
|
||||
Name: "Selection",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -481,6 +493,7 @@ void F0(in bool l0, in float l1, in float l2, out float l3) {
|
||||
{
|
||||
Name: "Call",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -529,6 +542,7 @@ void F0(in float l0, in float l1, out vec2 l2) {
|
||||
{
|
||||
Name: "BuiltinFunc",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -570,6 +584,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
{
|
||||
Name: "FieldSelector",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -609,6 +624,7 @@ void F0(in vec4 l0, out vec2 l1) {
|
||||
{
|
||||
Name: "If",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -673,6 +689,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
{
|
||||
Name: "For",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -728,6 +745,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
{
|
||||
Name: "For2",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -783,7 +801,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
l2 = l4;
|
||||
}
|
||||
}`,
|
||||
Metal: msl.Prelude + `
|
||||
Metal: msl.Prelude(shaderir.Pixel) + `
|
||||
|
||||
void F0(float l0, float l1, thread float& l2);
|
||||
|
||||
@ -797,6 +815,7 @@ void F0(float l0, float l1, thread float& l2) {
|
||||
{
|
||||
Name: "For3",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Funcs: []shaderir.Func{
|
||||
{
|
||||
Index: 0,
|
||||
@ -879,7 +898,7 @@ void F0(in float l0, in float l1, out float l2) {
|
||||
l2 = l5;
|
||||
}
|
||||
}`,
|
||||
Metal: msl.Prelude + `
|
||||
Metal: msl.Prelude(shaderir.Pixel) + `
|
||||
|
||||
void F0(float l0, float l1, thread float& l2);
|
||||
|
||||
@ -897,6 +916,7 @@ void F0(float l0, float l1, thread float& l2) {
|
||||
{
|
||||
Name: "VertexFunc",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Uniforms: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
},
|
||||
@ -949,6 +969,7 @@ in vec2 V1;`,
|
||||
{
|
||||
Name: "FragmentFunc",
|
||||
Program: shaderir.Program{
|
||||
Unit: shaderir.Pixel,
|
||||
Uniforms: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
},
|
||||
|
@ -47,16 +47,22 @@ func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) strin
|
||||
return n
|
||||
}
|
||||
|
||||
const Prelude = `#include <metal_stdlib>
|
||||
func Prelude(unit shaderir.Unit) string {
|
||||
str := `#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
constexpr sampler texture_sampler{filter::nearest};
|
||||
|
||||
template<typename T, typename U>
|
||||
T mod(T x, U y) {
|
||||
return x - y * floor(x/y);
|
||||
}`
|
||||
if unit == shaderir.Texel {
|
||||
str += `
|
||||
|
||||
constexpr sampler texture_sampler{filter::nearest};`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func Compile(p *shaderir.Program, vertex, fragment string) (shader string) {
|
||||
c := &compileContext{
|
||||
@ -64,7 +70,7 @@ func Compile(p *shaderir.Program, vertex, fragment string) (shader string) {
|
||||
}
|
||||
|
||||
var lines []string
|
||||
lines = append(lines, strings.Split(Prelude, "\n")...)
|
||||
lines = append(lines, strings.Split(Prelude(p.Unit), "\n")...)
|
||||
lines = append(lines, "", "{{.Structs}}")
|
||||
|
||||
if len(p.Attributes) > 0 {
|
||||
@ -396,7 +402,14 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
args = append(args, expr(&exp))
|
||||
}
|
||||
if callee.Type == shaderir.BuiltinFuncExpr && callee.BuiltinFunc == shaderir.Texture2DF {
|
||||
return fmt.Sprintf("%s.sample(texture_sampler, %s)", args[0], strings.Join(args[1:], ", "))
|
||||
switch p.Unit {
|
||||
case shaderir.Texel:
|
||||
return fmt.Sprintf("%s.sample(texture_sampler, %s)", args[0], strings.Join(args[1:], ", "))
|
||||
case shaderir.Pixel:
|
||||
return fmt.Sprintf("%s.read(static_cast<uint2>(%s))", args[0], strings.Join(args[1:], ", "))
|
||||
default:
|
||||
panic(fmt.Sprintf("msl: unexpected unit: %d", p.Unit))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", expr(&callee), strings.Join(args, ", "))
|
||||
case shaderir.FieldSelector:
|
||||
|
@ -22,6 +22,13 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Unit int
|
||||
|
||||
const (
|
||||
Texel Unit = iota
|
||||
Pixel
|
||||
)
|
||||
|
||||
type Program struct {
|
||||
UniformNames []string
|
||||
Uniforms []Type
|
||||
@ -31,6 +38,7 @@ type Program struct {
|
||||
Funcs []Func
|
||||
VertexFunc VertexFunc
|
||||
FragmentFunc FragmentFunc
|
||||
Unit Unit
|
||||
|
||||
reachableUniforms []bool
|
||||
uniformUint32Counts []int
|
||||
|
@ -24,7 +24,9 @@ import (
|
||||
|
||||
// ShaderProgramFill returns a shader source to fill the frambuffer.
|
||||
func ShaderProgramFill(r, g, b, a byte) *shaderir.Program {
|
||||
ir, err := graphics.CompileShader([]byte(fmt.Sprintf(`package main
|
||||
ir, err := graphics.CompileShader([]byte(fmt.Sprintf(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return vec4(%0.9f, %0.9f, %0.9f, %0.9f)
|
||||
@ -47,7 +49,9 @@ func ShaderProgramImages(numImages int) *shaderir.Program {
|
||||
exprs = append(exprs, fmt.Sprintf("imageSrc%dUnsafeAt(texCoord)", i))
|
||||
}
|
||||
|
||||
ir, err := graphics.CompileShader([]byte(fmt.Sprintf(`package main
|
||||
ir, err := graphics.CompileShader([]byte(fmt.Sprintf(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return %s
|
||||
|
189
shader_test.go
189
shader_test.go
@ -28,7 +28,9 @@ func TestShaderFill(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return vec4(1, 0, 0, 1)
|
||||
@ -58,7 +60,9 @@ func TestShaderFillWithDrawImage(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return vec4(1, 0, 0, 1)
|
||||
@ -93,7 +97,9 @@ func TestShaderWithDrawImageDoesNotWreckTextureUnits(t *testing.T) {
|
||||
rect := image.Rectangle{Max: image.Point{X: w, Y: h}}
|
||||
|
||||
dst := ebiten.NewImageWithOptions(rect, &ebiten.NewImageOptions{Unmanaged: true})
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return imageSrc0At(texCoord)
|
||||
@ -161,7 +167,9 @@ func TestShaderFillWithDrawTriangles(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return vec4(1, 0, 0, 1)
|
||||
@ -236,7 +244,9 @@ func TestShaderFunction(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func clr(red float) (float, float, float, float) {
|
||||
return red, 0, 0, 1
|
||||
@ -267,7 +277,9 @@ func TestShaderUninitializedUniformVariables(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var U vec4
|
||||
|
||||
@ -296,7 +308,9 @@ func TestShaderMatrix(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
var a, b mat4
|
||||
@ -334,7 +348,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
func TestShaderSubImage(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
r := imageSrc0At(texCoord).r
|
||||
@ -460,7 +476,9 @@ func TestShaderDerivatives(t *testing.T) {
|
||||
|
||||
const w, h = 16, 16
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
p := imageSrc0At(texCoord)
|
||||
@ -515,7 +533,9 @@ func TestShaderDerivatives2(t *testing.T) {
|
||||
|
||||
const w, h = 16, 16
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
// This function uses dfdx and then should not be in GLSL's vertex shader (#1701).
|
||||
func Foo(p vec4) vec4 {
|
||||
@ -578,7 +598,9 @@ func TestShaderUniformFirstElement(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
Name: "float array",
|
||||
Shader: `package main
|
||||
Shader: `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var C [2]float
|
||||
|
||||
@ -591,7 +613,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
},
|
||||
{
|
||||
Name: "float one-element array",
|
||||
Shader: `package main
|
||||
Shader: `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var C [1]float
|
||||
|
||||
@ -604,7 +628,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
},
|
||||
{
|
||||
Name: "matrix array",
|
||||
Shader: `package main
|
||||
Shader: `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var C [2]mat2
|
||||
|
||||
@ -646,7 +672,9 @@ func TestShaderFuncMod(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
r := mod(-0.25, 1.0)
|
||||
@ -680,7 +708,9 @@ func TestShaderMatrixInitialize(t *testing.T) {
|
||||
src.Fill(color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0xff})
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return mat4(2) * imageSrc0At(texCoord);
|
||||
@ -710,7 +740,9 @@ func TestShaderModVectorAndFloat(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
r := mod(vec3(0.25, 0.5, 0.75), 0.5)
|
||||
@ -741,7 +773,9 @@ func TestShaderTextureAt(t *testing.T) {
|
||||
src.Fill(color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0xff})
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func textureAt(uv vec2) vec4 {
|
||||
return imageSrc0UnsafeAt(uv)
|
||||
@ -777,7 +811,9 @@ func TestShaderAtan2(t *testing.T) {
|
||||
src.Fill(color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0xff})
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
y := vec4(1, 1, 1, 1)
|
||||
@ -809,7 +845,9 @@ func TestShaderUniformMatrix2(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat2 mat2
|
||||
var F float
|
||||
@ -847,7 +885,9 @@ func TestShaderUniformMatrix2Array(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat2 [2]mat2
|
||||
var F float
|
||||
@ -887,7 +927,9 @@ func TestShaderUniformMatrix3(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat3 mat3
|
||||
var F float
|
||||
@ -926,7 +968,9 @@ func TestShaderUniformMatrix3Array(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat3 [2]mat3
|
||||
var F float
|
||||
@ -968,7 +1012,9 @@ func TestShaderUniformMatrix4(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat4 mat4
|
||||
var F float
|
||||
@ -1008,7 +1054,9 @@ func TestShaderUniformMatrix4Array(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var Mat4 [2]mat4
|
||||
var F float
|
||||
@ -1051,7 +1099,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
func TestShaderOptionsNegativeBounds(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
r := imageSrc0At(texCoord).r
|
||||
@ -1179,7 +1229,9 @@ func TestShaderVectorEqual(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
a := vec3(1)
|
||||
@ -1227,7 +1279,9 @@ func TestShaderDiscard(t *testing.T) {
|
||||
}
|
||||
src.WritePixels(pix)
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
p := imageSrc0At(texCoord)
|
||||
@ -1272,7 +1326,9 @@ func TestShaderDrawRect(t *testing.T) {
|
||||
dst := ebiten.NewImage(dstW, dstH)
|
||||
src := ebiten.NewImage(srcW, srcH)
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
// Adjust texCoord into [0, 1].
|
||||
@ -1319,7 +1375,9 @@ func TestShaderDrawRectColorScale(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
dst := ebiten.NewImage(w, h)
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
s, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
return color
|
||||
@ -1349,7 +1407,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
|
||||
func TestShaderUniformInt(t *testing.T) {
|
||||
const ints = `package main
|
||||
const ints = `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var U0 int
|
||||
var U1 int
|
||||
@ -1361,7 +1421,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
`
|
||||
|
||||
const intArray = `package main
|
||||
const intArray = `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var U [4]int
|
||||
|
||||
@ -1370,7 +1432,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
`
|
||||
|
||||
const intVec = `package main
|
||||
const intVec = `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var U0 ivec4
|
||||
var U1 [2]ivec3
|
||||
@ -1545,7 +1609,9 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
|
||||
// Issue #2463
|
||||
func TestShaderUniformVec3Array(t *testing.T) {
|
||||
const shader = `package main
|
||||
const shader = `//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
var U [4]vec3
|
||||
|
||||
@ -1616,7 +1682,9 @@ return vec4(b)/255`,
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
shader := fmt.Sprintf(`package main
|
||||
shader := fmt.Sprintf(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
%s
|
||||
@ -1640,3 +1708,56 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShaderTexelAndPixel(t *testing.T) {
|
||||
const dstW, dstH = 13, 17
|
||||
const srcW, srcH = 19, 23
|
||||
dstTexel := ebiten.NewImage(dstW, dstH)
|
||||
dstPixel := ebiten.NewImage(dstW, dstH)
|
||||
src := ebiten.NewImage(srcW, srcH)
|
||||
|
||||
shaderTexel, err := ebiten.NewShader([]byte(fmt.Sprintf(`//kage:unit texel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
orig, size := imageSrcRegionOnTexture()
|
||||
pos := (texCoord - orig) / size
|
||||
pos *= vec2(%d, %d)
|
||||
pos /= 255
|
||||
return vec4(pos.x, pos.y, 0, 1)
|
||||
}
|
||||
`, srcW, srcH)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shaderPixel, err := ebiten.NewShader([]byte(`//kage:unit pixel
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
orig, _ := imageSrcRegionOnTexture()
|
||||
pos := texCoord - orig
|
||||
pos /= 255
|
||||
return vec4(pos.x, pos.y, 0, 1)
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
op := &ebiten.DrawRectShaderOptions{}
|
||||
op.Images[0] = src
|
||||
dstTexel.DrawRectShader(src.Bounds().Dx(), src.Bounds().Dy(), shaderTexel, op)
|
||||
dstPixel.DrawRectShader(src.Bounds().Dx(), src.Bounds().Dy(), shaderPixel, op)
|
||||
|
||||
for j := 0; j < dstH; j++ {
|
||||
for i := 0; i < dstW; i++ {
|
||||
c0 := dstTexel.At(i, j).(color.RGBA)
|
||||
c1 := dstPixel.At(i, j).(color.RGBA)
|
||||
if !sameColors(c0, c1, 1) {
|
||||
t.Errorf("dstTexel.At(%d, %d) %v != dstPixel.At(%d, %d) %v", i, j, c0, i, j, c1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user