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