mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 19:28:57 +01:00
graphics: Add 'screen' filter for fast rendering (#509)
This commit is contained in:
parent
092cb2f3f6
commit
591e0ad995
@ -31,6 +31,9 @@ const (
|
|||||||
|
|
||||||
// FilterLinear represents linear filter
|
// FilterLinear represents linear filter
|
||||||
FilterLinear Filter = Filter(graphics.FilterLinear)
|
FilterLinear Filter = Filter(graphics.FilterLinear)
|
||||||
|
|
||||||
|
// filterScreen represents a special filter for screen. Inner usage only.
|
||||||
|
filterScreen Filter = Filter(graphics.FilterScreen)
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompositeMode represents Porter-Duff composition mode.
|
// CompositeMode represents Porter-Duff composition mode.
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
package ebiten
|
package ebiten
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/clock"
|
"github.com/hajimehoshi/ebiten/internal/clock"
|
||||||
"github.com/hajimehoshi/ebiten/internal/hooks"
|
"github.com/hajimehoshi/ebiten/internal/hooks"
|
||||||
"github.com/hajimehoshi/ebiten/internal/restorable"
|
"github.com/hajimehoshi/ebiten/internal/restorable"
|
||||||
@ -33,7 +31,6 @@ func newGraphicsContext(f func(*Image) error) *graphicsContext {
|
|||||||
type graphicsContext struct {
|
type graphicsContext struct {
|
||||||
f func(*Image) error
|
f func(*Image) error
|
||||||
offscreen *Image
|
offscreen *Image
|
||||||
offscreen2 *Image // TODO: better name
|
|
||||||
screen *Image
|
screen *Image
|
||||||
initialized bool
|
initialized bool
|
||||||
invalidated bool // browser only
|
invalidated bool // browser only
|
||||||
@ -54,24 +51,15 @@ func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale flo
|
|||||||
if c.offscreen != nil {
|
if c.offscreen != nil {
|
||||||
_ = c.offscreen.Dispose()
|
_ = c.offscreen.Dispose()
|
||||||
}
|
}
|
||||||
if c.offscreen2 != nil {
|
|
||||||
_ = c.offscreen2.Dispose()
|
|
||||||
}
|
|
||||||
offscreen := newVolatileImage(screenWidth, screenHeight, FilterDefault)
|
offscreen := newVolatileImage(screenWidth, screenHeight, FilterDefault)
|
||||||
|
|
||||||
intScreenScale := int(math.Ceil(screenScale))
|
w := int(float64(screenWidth) * screenScale)
|
||||||
w := screenWidth * intScreenScale
|
h := int(float64(screenHeight) * screenScale)
|
||||||
h := screenHeight * intScreenScale
|
|
||||||
offscreen2 := newVolatileImage(w, h, FilterDefault)
|
|
||||||
|
|
||||||
w = int(float64(screenWidth) * screenScale)
|
|
||||||
h = int(float64(screenHeight) * screenScale)
|
|
||||||
ox, oy := ui.ScreenOffset()
|
ox, oy := ui.ScreenOffset()
|
||||||
c.screen = newImageWithScreenFramebuffer(w, h, ox, oy)
|
c.screen = newImageWithScreenFramebuffer(w, h, ox, oy)
|
||||||
_ = c.screen.Clear()
|
_ = c.screen.Clear()
|
||||||
|
|
||||||
c.offscreen = offscreen
|
c.offscreen = offscreen
|
||||||
c.offscreen2 = offscreen2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *graphicsContext) initializeIfNeeded() error {
|
func (c *graphicsContext) initializeIfNeeded() error {
|
||||||
@ -117,8 +105,7 @@ func (c *graphicsContext) Update(afterFrameUpdate func()) error {
|
|||||||
afterFrameUpdate()
|
afterFrameUpdate()
|
||||||
}
|
}
|
||||||
if 0 < updateCount {
|
if 0 < updateCount {
|
||||||
drawWithFittingScale(c.offscreen2, c.offscreen, FilterNearest)
|
drawWithFittingScale(c.screen, c.offscreen, filterScreen)
|
||||||
drawWithFittingScale(c.screen, c.offscreen2, FilterLinear)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := restorable.ResolveStaleImages(); err != nil {
|
if err := restorable.ResolveStaleImages(); err != nil {
|
||||||
|
@ -247,12 +247,9 @@ func (c *drawImageCommand) Exec(indexOffsetInBytes int) error {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sw, sh := c.src.Size()
|
|
||||||
sw = emath.NextPowerOf2Int(sw)
|
|
||||||
sh = emath.NextPowerOf2Int(sh)
|
|
||||||
_, dh := c.dst.Size()
|
_, dh := c.dst.Size()
|
||||||
proj := f.projectionMatrix(dh)
|
proj := f.projectionMatrix(dh)
|
||||||
theOpenGLState.useProgram(proj, c.src.texture.native, sw, sh, c.color, c.filter)
|
theOpenGLState.useProgram(proj, c.src.texture.native, c.dst, c.src, c.color, c.filter)
|
||||||
// TODO: We should call glBindBuffer here?
|
// TODO: We should call glBindBuffer here?
|
||||||
// The buffer is already bound at begin() but it is counterintuitive.
|
// The buffer is already bound at begin() but it is counterintuitive.
|
||||||
opengl.GetContext().DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes)
|
opengl.GetContext().DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes)
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||||
|
emath "github.com/hajimehoshi/ebiten/internal/math"
|
||||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -110,6 +111,8 @@ type openGLState struct {
|
|||||||
// programLinear is OpenGL's program for rendering a texture with linear filter.
|
// programLinear is OpenGL's program for rendering a texture with linear filter.
|
||||||
programLinear opengl.Program
|
programLinear opengl.Program
|
||||||
|
|
||||||
|
programScreen opengl.Program
|
||||||
|
|
||||||
lastProgram opengl.Program
|
lastProgram opengl.Program
|
||||||
lastProjectionMatrix []float32
|
lastProjectionMatrix []float32
|
||||||
lastColorMatrix []float32
|
lastColorMatrix []float32
|
||||||
@ -157,6 +160,9 @@ func (s *openGLState) reset() error {
|
|||||||
if s.programLinear != zeroProgram {
|
if s.programLinear != zeroProgram {
|
||||||
opengl.GetContext().DeleteProgram(s.programLinear)
|
opengl.GetContext().DeleteProgram(s.programLinear)
|
||||||
}
|
}
|
||||||
|
if s.programScreen != zeroProgram {
|
||||||
|
opengl.GetContext().DeleteProgram(s.programScreen)
|
||||||
|
}
|
||||||
if s.arrayBuffer != zeroBuffer {
|
if s.arrayBuffer != zeroBuffer {
|
||||||
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
|
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
|
||||||
}
|
}
|
||||||
@ -182,6 +188,12 @@ func (s *openGLState) reset() error {
|
|||||||
}
|
}
|
||||||
defer opengl.GetContext().DeleteShader(shaderFragmentLinearNative)
|
defer opengl.GetContext().DeleteShader(shaderFragmentLinearNative)
|
||||||
|
|
||||||
|
shaderFragmentScreenNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentScreen))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
||||||
|
}
|
||||||
|
defer opengl.GetContext().DeleteShader(shaderFragmentScreenNative)
|
||||||
|
|
||||||
s.programNearest, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
s.programNearest, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
||||||
shaderVertexModelviewNative,
|
shaderVertexModelviewNative,
|
||||||
shaderFragmentNearestNative,
|
shaderFragmentNearestNative,
|
||||||
@ -198,6 +210,14 @@ func (s *openGLState) reset() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.programScreen, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
||||||
|
shaderVertexModelviewNative,
|
||||||
|
shaderFragmentScreenNative,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
s.arrayBuffer = theArrayBufferLayout.newArrayBuffer()
|
s.arrayBuffer = theArrayBufferLayout.newArrayBuffer()
|
||||||
|
|
||||||
indices := make([]uint16, 6*maxQuads)
|
indices := make([]uint16, 6*maxQuads)
|
||||||
@ -228,7 +248,7 @@ func areSameFloat32Array(a, b []float32) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// useProgram uses the program (programTexture).
|
// useProgram uses the program (programTexture).
|
||||||
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
|
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, dst, src *Image, colorM affine.ColorM, filter Filter) {
|
||||||
c := opengl.GetContext()
|
c := opengl.GetContext()
|
||||||
|
|
||||||
var program opengl.Program
|
var program opengl.Program
|
||||||
@ -237,6 +257,8 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
|||||||
program = s.programNearest
|
program = s.programNearest
|
||||||
case FilterLinear:
|
case FilterLinear:
|
||||||
program = s.programLinear
|
program = s.programLinear
|
||||||
|
case FilterScreen:
|
||||||
|
program = s.programScreen
|
||||||
default:
|
default:
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
@ -287,14 +309,23 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
|||||||
s.lastColorMatrixTranslation = esTranslate
|
s.lastColorMatrixTranslation = esTranslate
|
||||||
}
|
}
|
||||||
|
|
||||||
if program == s.programLinear {
|
if program == s.programLinear || program == s.programScreen {
|
||||||
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
|
sw, sh := src.Size()
|
||||||
c.UniformFloats(program, "source_size",
|
sw = emath.NextPowerOf2Int(sw)
|
||||||
[]float32{float32(sourceWidth), float32(sourceHeight)})
|
sh = emath.NextPowerOf2Int(sh)
|
||||||
s.lastSourceWidth = sourceWidth
|
|
||||||
s.lastSourceHeight = sourceHeight
|
if s.lastSourceWidth != sw || s.lastSourceHeight != sh {
|
||||||
|
c.UniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
|
||||||
|
s.lastSourceWidth = sw
|
||||||
|
s.lastSourceHeight = sh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if program == s.programScreen {
|
||||||
|
sw, _ := src.Size()
|
||||||
|
dw, _ := dst.Size()
|
||||||
|
scale := float32(dw) / float32(sw)
|
||||||
|
c.UniformFloat(program, "scale", scale)
|
||||||
|
}
|
||||||
|
|
||||||
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
|
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
|
||||||
// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
|
// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
|
||||||
|
@ -24,6 +24,7 @@ const (
|
|||||||
shaderVertexModelview shaderID = iota
|
shaderVertexModelview shaderID = iota
|
||||||
shaderFragmentNearest
|
shaderFragmentNearest
|
||||||
shaderFragmentLinear
|
shaderFragmentLinear
|
||||||
|
shaderFragmentScreen
|
||||||
)
|
)
|
||||||
|
|
||||||
func shader(id shaderID) string {
|
func shader(id shaderID) string {
|
||||||
@ -36,6 +37,8 @@ func shader(id shaderID) string {
|
|||||||
defs = append(defs, "#define FILTER_NEAREST")
|
defs = append(defs, "#define FILTER_NEAREST")
|
||||||
case shaderFragmentLinear:
|
case shaderFragmentLinear:
|
||||||
defs = append(defs, "#define FILTER_LINEAR")
|
defs = append(defs, "#define FILTER_LINEAR")
|
||||||
|
case shaderFragmentScreen:
|
||||||
|
defs = append(defs, "#define FILTER_SCREEN")
|
||||||
default:
|
default:
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
@ -73,10 +76,14 @@ uniform sampler2D texture;
|
|||||||
uniform mat4 color_matrix;
|
uniform mat4 color_matrix;
|
||||||
uniform vec4 color_matrix_translation;
|
uniform vec4 color_matrix_translation;
|
||||||
|
|
||||||
#if defined(FILTER_LINEAR)
|
#if defined(FILTER_LINEAR) || defined(FILTER_SCREEN)
|
||||||
uniform highp vec2 source_size;
|
uniform highp vec2 source_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(FILTER_SCREEN)
|
||||||
|
uniform highp float scale;
|
||||||
|
#endif
|
||||||
|
|
||||||
varying highp vec2 varying_tex_coord;
|
varying highp vec2 varying_tex_coord;
|
||||||
varying highp vec2 varying_tex_coord_min;
|
varying highp vec2 varying_tex_coord_min;
|
||||||
varying highp vec2 varying_tex_coord_max;
|
varying highp vec2 varying_tex_coord_max;
|
||||||
@ -142,6 +149,23 @@ void main(void) {
|
|||||||
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(FILTER_SCREEN)
|
||||||
|
pos = roundTexel(pos);
|
||||||
|
highp vec2 texel_size = 1.0 / source_size;
|
||||||
|
pos -= texel_size * 0.5 / scale;
|
||||||
|
|
||||||
|
highp vec2 p0 = pos;
|
||||||
|
highp vec2 p1 = pos + texel_size / scale;
|
||||||
|
vec4 c0 = texture2D(texture, p0);
|
||||||
|
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
||||||
|
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
||||||
|
vec4 c3 = texture2D(texture, p1);
|
||||||
|
// Texels must be in the source rect, so it is not necessary to check that like linear filter.
|
||||||
|
|
||||||
|
vec2 rate = min(max(1.0 - ((1.0 - fract(pos * source_size)) * scale), 0.0), 1.0);
|
||||||
|
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Un-premultiply alpha
|
// Un-premultiply alpha
|
||||||
if (0.0 < color.a) {
|
if (0.0 < color.a) {
|
||||||
color.rgb /= color.a;
|
color.rgb /= color.a;
|
||||||
|
@ -24,6 +24,7 @@ const (
|
|||||||
FilterDefault Filter = iota
|
FilterDefault Filter = iota
|
||||||
FilterNearest
|
FilterNearest
|
||||||
FilterLinear
|
FilterLinear
|
||||||
|
FilterScreen
|
||||||
)
|
)
|
||||||
|
|
||||||
// texture represents OpenGL's texture.
|
// texture represents OpenGL's texture.
|
||||||
|
@ -398,6 +398,14 @@ func (c *Context) UniformInt(p Program, location string, v int) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) UniformFloat(p Program, location string, v float32) {
|
||||||
|
_ = c.runOnContextThread(func() error {
|
||||||
|
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||||
|
gl.Uniform1f(l, v)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||||
_ = c.runOnContextThread(func() error {
|
_ = c.runOnContextThread(func() error {
|
||||||
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||||
|
@ -316,6 +316,12 @@ func (c *Context) UniformInt(p Program, location string, v int) {
|
|||||||
gl.Uniform1i(l.(*js.Object), v)
|
gl.Uniform1i(l.(*js.Object), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) UniformFloat(p Program, location string, v float32) {
|
||||||
|
gl := c.gl
|
||||||
|
l := c.locationCache.GetUniformLocation(c, p, location)
|
||||||
|
gl.Uniform1f(l.(*js.Object), v)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||||
gl := c.gl
|
gl := c.gl
|
||||||
l := c.locationCache.GetUniformLocation(c, p, location)
|
l := c.locationCache.GetUniformLocation(c, p, location)
|
||||||
|
@ -309,6 +309,11 @@ func (c *Context) UniformInt(p Program, location string, v int) {
|
|||||||
gl.Uniform1i(mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location)), v)
|
gl.Uniform1i(mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location)), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) UniformFloat(p Program, location string, v float32) {
|
||||||
|
gl := c.gl
|
||||||
|
gl.Uniform1f(mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location)), v)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||||
gl := c.gl
|
gl := c.gl
|
||||||
l := mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location))
|
l := mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location))
|
||||||
|
Loading…
Reference in New Issue
Block a user