From 566957dc1cb471a33912aac3846d4a0af0ebbe4a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 27 Feb 2022 01:49:56 +0900 Subject: [PATCH] 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 --- internal/graphicsdriver/graphics.go | 1 + .../graphicsdriver/metal/graphics_darwin.go | 16 +++++---- internal/graphicsdriver/opengl/graphics.go | 4 +++ internal/ui/ui.go | 4 +++ shader.go | 35 +++++++++++++------ 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index 5a444fb71..ef0364ba8 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -55,6 +55,7 @@ type Graphics interface { SetVsyncEnabled(enabled bool) SetFullscreen(fullscreen bool) FramebufferYDirection() YDirection + NDCYDirection() YDirection NeedsRestoring() bool NeedsClearingScreen() bool IsGL() bool diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 2516b4872..6a53eddd2 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -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 } diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index fba774fdd..593d50264 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -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() } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index c5951de97..a6004c64f 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -67,3 +67,7 @@ const ( WindowResizingModeOnlyFullscreenEnabled WindowResizingModeEnabled ) + +func NeedsInvertY() bool { + return graphics().FramebufferYDirection() != graphics().NDCYDirection() +} diff --git a/shader.go b/shader.go index f9eb7e0b1..c7574d58a 100644 --- a/shader.go +++ b/shader.go @@ -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)