internal/graphicsdriver/metal: invert Y at the vertex shader

Instead of using a negative height in the viewport, invert the Y direction
at the vertex shader. This is a little more readable as a negative height
is hacky.

This is a preparation for DirectX 12. DirectX 12's coodination system
is very similar to Metal, but doesn't treat a negative height in its
viewport unfortunately.

Updates #1007
This commit is contained in:
Hajime Hoshi 2022-02-27 01:49:56 +09:00
parent ea35296be7
commit 566957dc1c
5 changed files with 43 additions and 17 deletions

View File

@ -55,6 +55,7 @@ type Graphics interface {
SetVsyncEnabled(enabled bool)
SetFullscreen(fullscreen bool)
FramebufferYDirection() YDirection
NDCYDirection() YDirection
NeedsRestoring() bool
NeedsClearingScreen() bool
IsGL() bool

View File

@ -72,11 +72,13 @@ vertex VertexOut VertexShader(
const device VertexIn* vertices [[buffer(0)]],
constant float2& viewport_size [[buffer(1)]]
) {
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
// match. Then, the Y direction must be inverted.
float4x4 projectionMatrix = float4x4(
float4(2.0 / viewport_size.x, 0, 0, 0),
float4(0, 2.0 / viewport_size.y, 0, 0),
float4(0, -2.0 / viewport_size.y, 0, 0),
float4(0, 0, 1, 0),
float4(-1, -1, 0, 1)
float4(-1, 1, 0, 1)
);
VertexIn in = vertices[vid];
@ -829,14 +831,12 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion graph
g.rce.SetRenderPipelineState(rps)
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
// match. Then, the Y direction must be inverted.
w, h := dst.internalSize()
g.rce.SetViewport(mtl.Viewport{
OriginX: 0,
OriginY: float64(h),
OriginY: 0,
Width: float64(w),
Height: -float64(h),
Height: float64(h),
ZNear: -1,
ZFar: 1,
})
@ -1051,6 +1051,10 @@ func (g *Graphics) FramebufferYDirection() graphicsdriver.YDirection {
return graphicsdriver.Downward
}
func (g *Graphics) NDCYDirection() graphicsdriver.YDirection {
return graphicsdriver.Upward
}
func (g *Graphics) NeedsRestoring() bool {
return false
}

View File

@ -385,6 +385,10 @@ func (g *Graphics) FramebufferYDirection() graphicsdriver.YDirection {
return graphicsdriver.Upward
}
func (g *Graphics) NDCYDirection() graphicsdriver.YDirection {
return graphicsdriver.Upward
}
func (g *Graphics) NeedsRestoring() bool {
return g.context.needsRestoring()
}

View File

@ -67,3 +67,7 @@ const (
WindowResizingModeOnlyFullscreenEnabled
WindowResizingModeEnabled
)
func NeedsInvertY() bool {
return graphics().FramebufferYDirection() != graphics().NDCYDirection()
}

View File

@ -26,6 +26,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
"github.com/hajimehoshi/ebiten/v2/internal/shader"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
var shaderSuffix string
@ -105,17 +106,6 @@ func imageSrc%[1]dAt(pos vec2) vec4 {
}
`, i, pos)
}
shaderSuffix += `
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
return mat4(
2/__imageDstTextureSize.x, 0, 0, 0,
0, 2/__imageDstTextureSize.y, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1,
) * vec4(position, 0, 1), texCoord, color
}
`
}
// Shader represents a compiled shader program.
@ -136,6 +126,29 @@ func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer
buf.Write(src)
buf.WriteString(shaderSuffix)
if ui.NeedsInvertY() {
buf.WriteString(`
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
return mat4(
2/__imageDstTextureSize.x, 0, 0, 0,
0, -2/__imageDstTextureSize.y, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1,
) * vec4(position, 0, 1), texCoord, color
}
`)
} else {
buf.WriteString(`
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
return mat4(
2/__imageDstTextureSize.x, 0, 0, 0,
0, 2/__imageDstTextureSize.y, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1,
) * vec4(position, 0, 1), texCoord, color
}
`)
}
fs := token.NewFileSet()
f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)