graphics: Separate GLSL programs for filters (#461)

This commit is contained in:
Hajime Hoshi 2017-12-21 00:24:27 +09:00
parent a8914ff245
commit 8e72906f3d
2 changed files with 113 additions and 73 deletions

View File

@ -114,14 +114,16 @@ type openGLState struct {
// elementArrayBuffer is OpenGL's element array buffer (indices data).
elementArrayBuffer opengl.Buffer
// programTexture is OpenGL's program for rendering a texture.
programTexture opengl.Program
// programNearest is OpenGL's program for rendering a texture with nearest filter.
programNearest opengl.Program
// programLinear is OpenGL's program for rendering a texture with linear filter.
programLinear opengl.Program
lastProgram opengl.Program
lastProjectionMatrix []float32
lastColorMatrix []float32
lastColorMatrixTranslation []float32
lastFilterType Filter
lastSourceWidth int
lastSourceHeight int
}
@ -153,15 +155,17 @@ func (s *openGLState) reset() error {
s.lastProjectionMatrix = nil
s.lastColorMatrix = nil
s.lastColorMatrixTranslation = nil
s.lastFilterType = FilterNone
s.lastSourceWidth = 0
s.lastSourceHeight = 0
// When context lost happens, deleting programs or buffers is not necessary.
// However, it is not assumed that reset is called only when context lost happens.
// Let's delete them explicitly.
if s.programTexture != zeroProgram {
opengl.GetContext().DeleteProgram(s.programTexture)
if s.programNearest != zeroProgram {
opengl.GetContext().DeleteProgram(s.programNearest)
}
if s.programLinear != zeroProgram {
opengl.GetContext().DeleteProgram(s.programLinear)
}
if s.arrayBuffer != zeroBuffer {
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
@ -176,15 +180,29 @@ func (s *openGLState) reset() error {
}
defer opengl.GetContext().DeleteShader(shaderVertexModelviewNative)
shaderFragmentTextureNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentTexture))
shaderFragmentNearestNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentNearest))
if err != nil {
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
}
defer opengl.GetContext().DeleteShader(shaderFragmentTextureNative)
defer opengl.GetContext().DeleteShader(shaderFragmentNearestNative)
s.programTexture, err = opengl.GetContext().NewProgram([]opengl.Shader{
shaderFragmentLinearNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentLinear))
if err != nil {
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
}
defer opengl.GetContext().DeleteShader(shaderFragmentLinearNative)
s.programNearest, err = opengl.GetContext().NewProgram([]opengl.Shader{
shaderVertexModelviewNative,
shaderFragmentTextureNative,
shaderFragmentNearestNative,
})
if err != nil {
return err
}
s.programLinear, err = opengl.GetContext().NewProgram([]opengl.Shader{
shaderVertexModelviewNative,
shaderFragmentLinearNative,
})
if err != nil {
return err
@ -222,7 +240,16 @@ func areSameFloat32Array(a, b []float32) bool {
// useProgram uses the program (programTexture).
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
c := opengl.GetContext()
program := s.programTexture
var program opengl.Program
switch filter {
case FilterNearest:
program = s.programNearest
case FilterLinear:
program = s.programLinear
default:
panic("not reached")
}
if s.lastProgram != program {
c.UseProgram(program)
@ -231,7 +258,7 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
}
theArrayBufferLayout.enable(program)
s.lastProgram = s.programTexture
s.lastProgram = program
s.lastProjectionMatrix = nil
s.lastColorMatrix = nil
s.lastColorMatrixTranslation = nil
@ -279,16 +306,13 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
copy(s.lastColorMatrixTranslation, colorMatrixTranslation)
}
if s.lastFilterType != filter {
c.UniformInt(program, "filter_type", int(filter))
s.lastFilterType = filter
}
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
c.UniformFloats(program, "source_size",
[]float32{float32(sourceWidth), float32(sourceHeight)})
s.lastSourceWidth = sourceWidth
s.lastSourceHeight = sourceHeight
if program == s.programLinear {
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
c.UniformFloats(program, "source_size",
[]float32{float32(sourceWidth), float32(sourceHeight)})
s.lastSourceWidth = sourceWidth
s.lastSourceHeight = sourceHeight
}
}
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture

View File

@ -14,19 +14,34 @@
package graphics
import (
"strings"
)
type shaderID int
const (
shaderVertexModelview shaderID = iota
shaderFragmentTexture
shaderFragmentNearest
shaderFragmentLinear
)
func shader(id shaderID) string {
return shaders[id]
if id == shaderVertexModelview {
return shaderStrVertex
}
defs := []string{}
switch id {
case shaderFragmentNearest:
defs = append(defs, "#define FILTER_NEAREST")
case shaderFragmentLinear:
defs = append(defs, "#define FILTER_LINEAR")
}
return strings.Replace(shaderStrFragment, "{{Definitions}}", strings.Join(defs, "\n"), -1)
}
var shaders = map[shaderID]string{
shaderVertexModelview: `
const (
shaderStrVertex = `
uniform mat4 projection_matrix;
attribute vec2 vertex;
attribute vec4 tex_coord;
@ -50,8 +65,8 @@ void main(void) {
);
gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
}
`,
shaderFragmentTexture: `
`
shaderStrFragment = `
#if defined(GL_ES)
precision mediump float;
#else
@ -60,11 +75,15 @@ precision mediump float;
#define highp
#endif
{{Definitions}}
uniform sampler2D texture;
uniform mat4 color_matrix;
uniform vec4 color_matrix_translation;
#if defined(FILTER_LINEAR)
uniform highp vec2 source_size;
uniform int filter_type;
#endif
varying highp vec2 varying_tex_coord;
varying highp vec2 varying_tex_coord_min;
@ -80,51 +99,48 @@ highp vec2 roundTexel(highp vec2 p) {
}
void main(void) {
vec4 color = vec4(0, 0, 0, 0);
highp vec2 pos = roundTexel(varying_tex_coord);
if (filter_type == 1) {
// Nearest neighbor
if (varying_tex_coord_min.x <= pos.x &&
varying_tex_coord_min.y <= pos.y &&
pos.x < varying_tex_coord_max.x &&
pos.y < varying_tex_coord_max.y) {
color = texture2D(texture, pos);
}
} else if (filter_type == 2) {
// Bi-linear
highp vec2 texel_size = 1.0 / source_size;
pos -= texel_size * 0.5;
highp vec2 p0 = pos;
highp vec2 p1 = pos + texel_size;
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);
if (p0.x < varying_tex_coord_min.x) {
c0 = vec4(0, 0, 0, 0);
c2 = vec4(0, 0, 0, 0);
}
if (p0.y < varying_tex_coord_min.y) {
c0 = vec4(0, 0, 0, 0);
c1 = vec4(0, 0, 0, 0);
}
if (varying_tex_coord_max.x <= p1.x) {
c1 = vec4(0, 0, 0, 0);
c3 = vec4(0, 0, 0, 0);
}
if (varying_tex_coord_max.y <= p1.y) {
c2 = vec4(0, 0, 0, 0);
c3 = vec4(0, 0, 0, 0);
}
vec2 rate = fract(pos * source_size);
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
} else {
// Error
color = vec4(1, 0, 0, 1);
#if defined(FILTER_NEAREST)
vec4 color = texture2D(texture, pos);
if (pos.x < varying_tex_coord_min.x ||
pos.y < varying_tex_coord_min.y ||
varying_tex_coord_max.x <= pos.x ||
varying_tex_coord_max.y <= pos.y) {
color = vec4(0, 0, 0, 0);
}
#endif
#if defined(FILTER_LINEAR)
highp vec2 texel_size = 1.0 / source_size;
pos -= texel_size * 0.5;
highp vec2 p0 = pos;
highp vec2 p1 = pos + texel_size;
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);
if (p0.x < varying_tex_coord_min.x) {
c0 = vec4(0, 0, 0, 0);
c2 = vec4(0, 0, 0, 0);
}
if (p0.y < varying_tex_coord_min.y) {
c0 = vec4(0, 0, 0, 0);
c1 = vec4(0, 0, 0, 0);
}
if (varying_tex_coord_max.x <= p1.x) {
c1 = vec4(0, 0, 0, 0);
c3 = vec4(0, 0, 0, 0);
}
if (varying_tex_coord_max.y <= p1.y) {
c2 = vec4(0, 0, 0, 0);
c3 = vec4(0, 0, 0, 0);
}
vec2 rate = fract(pos * source_size);
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
#endif
// Un-premultiply alpha
if (0.0 < color.a) {
@ -138,5 +154,5 @@ void main(void) {
gl_FragColor = color;
}
`,
}
`
)