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) SetVsyncEnabled(enabled bool)
SetFullscreen(fullscreen bool) SetFullscreen(fullscreen bool)
FramebufferYDirection() YDirection FramebufferYDirection() YDirection
NDCYDirection() YDirection
NeedsRestoring() bool NeedsRestoring() bool
NeedsClearingScreen() bool NeedsClearingScreen() bool
IsGL() bool IsGL() bool

View File

@ -72,11 +72,13 @@ vertex VertexOut VertexShader(
const device VertexIn* vertices [[buffer(0)]], const device VertexIn* vertices [[buffer(0)]],
constant float2& viewport_size [[buffer(1)]] 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( float4x4 projectionMatrix = float4x4(
float4(2.0 / viewport_size.x, 0, 0, 0), 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(0, 0, 1, 0),
float4(-1, -1, 0, 1) float4(-1, 1, 0, 1)
); );
VertexIn in = vertices[vid]; VertexIn in = vertices[vid];
@ -829,14 +831,12 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion graph
g.rce.SetRenderPipelineState(rps) 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() w, h := dst.internalSize()
g.rce.SetViewport(mtl.Viewport{ g.rce.SetViewport(mtl.Viewport{
OriginX: 0, OriginX: 0,
OriginY: float64(h), OriginY: 0,
Width: float64(w), Width: float64(w),
Height: -float64(h), Height: float64(h),
ZNear: -1, ZNear: -1,
ZFar: 1, ZFar: 1,
}) })
@ -1051,6 +1051,10 @@ func (g *Graphics) FramebufferYDirection() graphicsdriver.YDirection {
return graphicsdriver.Downward return graphicsdriver.Downward
} }
func (g *Graphics) NDCYDirection() graphicsdriver.YDirection {
return graphicsdriver.Upward
}
func (g *Graphics) NeedsRestoring() bool { func (g *Graphics) NeedsRestoring() bool {
return false return false
} }

View File

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

View File

@ -67,3 +67,7 @@ const (
WindowResizingModeOnlyFullscreenEnabled WindowResizingModeOnlyFullscreenEnabled
WindowResizingModeEnabled 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/mipmap"
"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"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
var shaderSuffix string var shaderSuffix string
@ -105,17 +106,6 @@ func imageSrc%[1]dAt(pos vec2) vec4 {
} }
`, i, pos) `, 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. // Shader represents a compiled shader program.
@ -136,6 +126,29 @@ func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer var buf bytes.Buffer
buf.Write(src) buf.Write(src)
buf.WriteString(shaderSuffix) 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() fs := token.NewFileSet()
f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors) f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)