mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
internal/ui: reland the screen shader in Kage
This change relads a part of the change to use the screen shader instead of FilterScreen, but with the issue on iOS fixed. Let's remove FilterScreen later. Updates #2282
This commit is contained in:
parent
62127e432e
commit
bd43b42ee5
@ -1407,7 +1407,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
|
||||
|
||||
} else {
|
||||
if evenOdd {
|
||||
s, err := shader.pipelineState(mode, prepareStencil)
|
||||
s, err := shader.pipelineState(mode, prepareStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1415,7 +1415,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
|
||||
return err
|
||||
}
|
||||
|
||||
s, err = shader.pipelineState(mode, drawWithStencil)
|
||||
s, err = shader.pipelineState(mode, drawWithStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1423,7 +1423,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s, err := shader.pipelineState(mode, noStencil)
|
||||
s, err := shader.pipelineState(mode, noStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1866,6 +1866,7 @@ const (
|
||||
type pipelineStateKey struct {
|
||||
compositeMode graphicsdriver.CompositeMode
|
||||
stencilMode stencilMode
|
||||
screen bool
|
||||
}
|
||||
|
||||
type Shader struct {
|
||||
@ -1902,16 +1903,17 @@ func (s *Shader) disposeImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shader) pipelineState(compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode) (*_ID3D12PipelineState, error) {
|
||||
func (s *Shader) pipelineState(compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (*_ID3D12PipelineState, error) {
|
||||
key := pipelineStateKey{
|
||||
compositeMode: compositeMode,
|
||||
stencilMode: stencilMode,
|
||||
screen: screen,
|
||||
}
|
||||
if state, ok := s.pipelineStates[key]; ok {
|
||||
return state, nil
|
||||
}
|
||||
|
||||
state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, compositeMode, stencilMode, false)
|
||||
state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, compositeMode, stencilMode, screen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -924,7 +924,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
noStencil,
|
||||
} {
|
||||
var err error
|
||||
rpss[stencil], err = g.shaders[shaderID].RenderPipelineState(g.view.getMTLDevice(), mode, stencil)
|
||||
rpss[stencil], err = g.shaders[shaderID].RenderPipelineState(g.view, mode, stencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
type shaderRpsKey struct {
|
||||
compositeMode graphicsdriver.CompositeMode
|
||||
stencilMode stencilMode
|
||||
screen bool
|
||||
}
|
||||
|
||||
type Shader struct {
|
||||
@ -85,11 +86,13 @@ func (s *Shader) init(device mtl.Device) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode) (mtl.RenderPipelineState, error) {
|
||||
if rps, ok := s.rpss[shaderRpsKey{
|
||||
func (s *Shader) RenderPipelineState(view view, compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (mtl.RenderPipelineState, error) {
|
||||
key := shaderRpsKey{
|
||||
compositeMode: compositeMode,
|
||||
stencilMode: stencilMode,
|
||||
}]; ok {
|
||||
screen: screen,
|
||||
}
|
||||
if rps, ok := s.rpss[key]; ok {
|
||||
return rps, nil
|
||||
}
|
||||
|
||||
@ -102,7 +105,11 @@ func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode graphicsdr
|
||||
}
|
||||
|
||||
// TODO: For the precise pixel format, whether the render target is the screen or not must be considered.
|
||||
rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm
|
||||
pix := mtl.PixelFormatRGBA8UNorm
|
||||
if screen {
|
||||
pix = view.colorPixelFormat()
|
||||
}
|
||||
rpld.ColorAttachments[0].PixelFormat = pix
|
||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||
|
||||
src, dst := compositeMode.Operations()
|
||||
@ -116,14 +123,11 @@ func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode graphicsdr
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
}
|
||||
|
||||
rps, err := device.MakeRenderPipelineState(rpld)
|
||||
rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
if err != nil {
|
||||
return mtl.RenderPipelineState{}, err
|
||||
}
|
||||
|
||||
s.rpss[shaderRpsKey{
|
||||
compositeMode: compositeMode,
|
||||
stencilMode: stencilMode,
|
||||
}] = rps
|
||||
s.rpss[key] = rps
|
||||
return rps, nil
|
||||
}
|
||||
|
@ -30,6 +30,40 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/hooks"
|
||||
)
|
||||
|
||||
const screenShader = `package main
|
||||
|
||||
var Scale vec2
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
sourceSize := imageSrcTextureSize()
|
||||
// texelSize is one pixel size in texel sizes.
|
||||
texelSize := 1 / sourceSize
|
||||
halfScaledTexelSize := texelSize / 2 / Scale
|
||||
|
||||
// Shift 1/512 [texel] to avoid the tie-breaking issue.
|
||||
// As all the vertex positions are aligned to 1/16 [pixel], this shiting should work in most cases.
|
||||
pos := texCoord
|
||||
p0 := pos - halfScaledTexelSize + (texelSize / 512)
|
||||
p1 := pos + halfScaledTexelSize + (texelSize / 512)
|
||||
|
||||
// Texels must be in the source rect, so it is not necessary to check.
|
||||
c0 := imageSrc0UnsafeAt(p0)
|
||||
c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y))
|
||||
c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y))
|
||||
c3 := imageSrc0UnsafeAt(p1)
|
||||
|
||||
// p is the p1 value in one pixel assuming that the pixel's upper-left is (0, 0) and the lower-right is (1, 1).
|
||||
p := fract(p1 * sourceSize)
|
||||
|
||||
// rate indicates how much the 4 colors are mixed. rate is in between [0, 1].
|
||||
//
|
||||
// 0 <= p <= 1/Scale: The rate is in between [0, 1]
|
||||
// 1/Scale < p: Don't care. Adjacent colors (e.g. c0 vs c1 in an X direction) should be the same.
|
||||
rate := clamp(p*Scale, 0, 1)
|
||||
return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y)
|
||||
}
|
||||
`
|
||||
|
||||
type Game interface {
|
||||
NewOffscreenImage(width, height int) *Image
|
||||
Layout(outsideWidth, outsideHeight int) (int, int)
|
||||
@ -49,6 +83,8 @@ type context struct {
|
||||
outsideWidth float64
|
||||
outsideHeight float64
|
||||
|
||||
screenShader *Shader
|
||||
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
@ -108,6 +144,15 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a shader for the screen if necessary.
|
||||
if c.screenShader == nil {
|
||||
ir, err := graphics.CompileShader([]byte(screenShader))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.screenShader = NewShader(ir)
|
||||
}
|
||||
|
||||
// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
|
||||
if w, h := c.layoutGame(outsideWidth, outsideHeight, deviceScaleFactor); w == 0 || h == 0 {
|
||||
return nil
|
||||
@ -186,15 +231,16 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics) {
|
||||
gty += offsetY
|
||||
|
||||
var filter graphicsdriver.Filter
|
||||
var screenFilter bool
|
||||
switch {
|
||||
case !theGlobalState.isScreenFilterEnabled():
|
||||
filter = graphicsdriver.FilterNearest
|
||||
case math.Floor(s) == s:
|
||||
filter = graphicsdriver.FilterNearest
|
||||
case s > 1:
|
||||
filter = graphicsdriver.FilterScreen
|
||||
screenFilter = true
|
||||
default:
|
||||
// FilterScreen works with >=1 scale, but does not well with <1 scale.
|
||||
// screenShader works with >=1 scale, but does not well with <1 scale.
|
||||
// Use regular FilterLinear instead so far (#669).
|
||||
filter = graphicsdriver.FilterLinear
|
||||
}
|
||||
@ -213,7 +259,21 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics) {
|
||||
is := graphics.QuadIndices()
|
||||
|
||||
srcs := [graphics.ShaderImageCount]*Image{c.offscreen}
|
||||
c.screen.DrawTriangles(srcs, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, nil, nil, false, true)
|
||||
|
||||
var shader *Shader
|
||||
var uniforms [][]float32
|
||||
if screenFilter {
|
||||
shader = c.screenShader
|
||||
dstWidth, dstHeight := c.screen.width, c.screen.height
|
||||
srcWidth, srcHeight := c.offscreen.width, c.offscreen.height
|
||||
uniforms = shader.ConvertUniforms(map[string]interface{}{
|
||||
"Scale": []float32{
|
||||
float32(dstWidth) / float32(srcWidth),
|
||||
float32(dstHeight) / float32(srcHeight),
|
||||
},
|
||||
})
|
||||
}
|
||||
c.screen.DrawTriangles(srcs, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, shader, uniforms, false, true)
|
||||
}
|
||||
|
||||
func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int) {
|
||||
|
Loading…
Reference in New Issue
Block a user