mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
parent
6c4260d0e1
commit
c2c3579cde
@ -107,6 +107,11 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
type programKey struct {
|
||||
filter graphics.Filter
|
||||
address graphics.Address
|
||||
}
|
||||
|
||||
// openGLState is a state for
|
||||
type openGLState struct {
|
||||
// arrayBuffer is OpenGL's array buffer (vertices data).
|
||||
@ -115,8 +120,8 @@ type openGLState struct {
|
||||
// elementArrayBuffer is OpenGL's element array buffer (indices data).
|
||||
elementArrayBuffer buffer
|
||||
|
||||
// program is OpenGL's program for rendering a texture.
|
||||
program program
|
||||
// programs is OpenGL's program for rendering a texture.
|
||||
programs map[programKey]program
|
||||
|
||||
lastProgram program
|
||||
lastViewportWidth int
|
||||
@ -161,8 +166,13 @@ func (s *openGLState) reset(context *context) error {
|
||||
// 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.program != zeroProgram {
|
||||
context.deleteProgram(s.program)
|
||||
if s.programs == nil {
|
||||
s.programs = map[programKey]program{}
|
||||
} else {
|
||||
for k, p := range s.programs {
|
||||
context.deleteProgram(p)
|
||||
delete(s.programs, k)
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers (at least Chrome), buffers are already detached from the context
|
||||
@ -176,25 +186,40 @@ func (s *openGLState) reset(context *context) error {
|
||||
}
|
||||
}
|
||||
|
||||
shaderVertexModelviewNative, err := context.newShader(vertexShader, shaderStr(shaderVertexModelview))
|
||||
shaderVertexModelviewNative, err := context.newShader(vertexShader, vertexShaderStr())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
||||
}
|
||||
defer context.deleteShader(shaderVertexModelviewNative)
|
||||
|
||||
shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, shaderStr(shaderFragmentColorMatrix))
|
||||
for _, a := range []graphics.Address{
|
||||
graphics.AddressClampToZero,
|
||||
graphics.AddressRepeat,
|
||||
} {
|
||||
for _, f := range []graphics.Filter{
|
||||
graphics.FilterNearest,
|
||||
graphics.FilterLinear,
|
||||
graphics.FilterScreen,
|
||||
} {
|
||||
shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, fragmentShaderStr(f, a))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
||||
}
|
||||
defer context.deleteShader(shaderFragmentColorMatrixNative)
|
||||
|
||||
s.program, err = context.newProgram([]shader{
|
||||
program, err := context.newProgram([]shader{
|
||||
shaderVertexModelviewNative,
|
||||
shaderFragmentColorMatrixNative,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.programs[programKey{
|
||||
filter: f,
|
||||
address: a,
|
||||
}] = program
|
||||
}
|
||||
}
|
||||
|
||||
s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)
|
||||
|
||||
@ -238,7 +263,10 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
|
||||
|
||||
d.context.blendFunc(mode)
|
||||
|
||||
program := d.state.program
|
||||
program := d.state.programs[programKey{
|
||||
filter: filter,
|
||||
address: address,
|
||||
}]
|
||||
if d.state.lastProgram != program {
|
||||
d.context.useProgram(program)
|
||||
if d.state.lastProgram != zeroProgram {
|
||||
@ -291,15 +319,6 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
|
||||
d.state.lastSourceHeight = sh
|
||||
}
|
||||
|
||||
if d.state.lastFilter == nil || *d.state.lastFilter != filter {
|
||||
d.context.uniformInt(program, "filter_type", int(filter))
|
||||
d.state.lastFilter = &filter
|
||||
}
|
||||
if d.state.lastAddress == nil || *d.state.lastAddress != address {
|
||||
d.context.uniformInt(program, "address", int(address))
|
||||
d.state.lastAddress = &address
|
||||
}
|
||||
|
||||
if filter == graphics.FilterScreen {
|
||||
scale := float32(dstW) / float32(srcW)
|
||||
d.context.uniformFloat(program, "scale", scale)
|
||||
|
@ -22,13 +22,6 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
)
|
||||
|
||||
type shaderID int
|
||||
|
||||
const (
|
||||
shaderVertexModelview shaderID = iota
|
||||
shaderFragmentColorMatrix
|
||||
)
|
||||
|
||||
// glslReservedKeywords is a set of reserved keywords that cannot be used as an indentifier on some environments.
|
||||
// See https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf.
|
||||
var glslReservedKeywords = map[string]struct{}{
|
||||
@ -62,26 +55,43 @@ func checkGLSL(src string) {
|
||||
}
|
||||
}
|
||||
|
||||
func shaderStr(id shaderID) string {
|
||||
src := ""
|
||||
switch id {
|
||||
case shaderVertexModelview:
|
||||
src = shaderStrVertex
|
||||
case shaderFragmentColorMatrix:
|
||||
func vertexShaderStr() string {
|
||||
src := shaderStrVertex
|
||||
checkGLSL(src)
|
||||
return src
|
||||
}
|
||||
|
||||
func fragmentShaderStr(filter graphics.Filter, address graphics.Address) string {
|
||||
replaces := map[string]string{
|
||||
"{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest),
|
||||
"{{.FilterLinear}}": fmt.Sprintf("%d", graphics.FilterLinear),
|
||||
"{{.FilterScreen}}": fmt.Sprintf("%d", graphics.FilterScreen),
|
||||
"{{.AddressClampToZero}}": fmt.Sprintf("%d", graphics.AddressClampToZero),
|
||||
"{{.AddressRepeat}}": fmt.Sprintf("%d", graphics.AddressRepeat),
|
||||
}
|
||||
src = shaderStrFragment
|
||||
src := shaderStrFragment
|
||||
for k, v := range replaces {
|
||||
src = strings.Replace(src, k, v, -1)
|
||||
}
|
||||
|
||||
var defs []string
|
||||
switch filter {
|
||||
case graphics.FilterNearest:
|
||||
defs = append(defs, "#define FILTER_NEAREST")
|
||||
case graphics.FilterLinear:
|
||||
defs = append(defs, "#define FILTER_LINEAR")
|
||||
case graphics.FilterScreen:
|
||||
defs = append(defs, "#define FILTER_SCREEN")
|
||||
default:
|
||||
panic(fmt.Sprintf("opengl: invalid shader id: %d", id))
|
||||
panic(fmt.Sprintf("opengl: invalid filter: %d", filter))
|
||||
}
|
||||
switch address {
|
||||
case graphics.AddressClampToZero:
|
||||
defs = append(defs, "#define ADDRESS_CLAMP_TO_ZERO")
|
||||
case graphics.AddressRepeat:
|
||||
defs = append(defs, "#define ADDRESS_REPEAT")
|
||||
default:
|
||||
panic(fmt.Sprintf("opengl: invalid address: %d", address))
|
||||
}
|
||||
|
||||
src = strings.Replace(src, "{{.Definitions}}", strings.Join(defs, "\n"), -1)
|
||||
|
||||
checkGLSL(src)
|
||||
return src
|
||||
@ -121,19 +131,13 @@ precision mediump float;
|
||||
#define highp
|
||||
#endif
|
||||
|
||||
#define FILTER_NEAREST ({{.FilterNearest}})
|
||||
#define FILTER_LINEAR ({{.FilterLinear}})
|
||||
#define FILTER_SCREEN ({{.FilterScreen}})
|
||||
#define ADDRESS_CLAMP_TO_ZERO ({{.AddressClampToZero}})
|
||||
#define ADDRESS_REPEAT ({{.AddressRepeat}})
|
||||
{{.Definitions}}
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform mat4 color_matrix_body;
|
||||
uniform vec4 color_matrix_translation;
|
||||
|
||||
uniform int filter_type;
|
||||
uniform highp vec2 source_size;
|
||||
uniform int address;
|
||||
|
||||
#if defined(FILTER_SCREEN)
|
||||
uniform highp float scale;
|
||||
@ -164,17 +168,16 @@ highp float floorMod(highp float x, highp float y) {
|
||||
return x - y * floor(x/y);
|
||||
}
|
||||
|
||||
highp vec2 adjustTexelByAddress(highp vec2 p, highp vec4 tex_region, int address) {
|
||||
if (address == ADDRESS_CLAMP_TO_ZERO) {
|
||||
highp vec2 adjustTexelByAddress(highp vec2 p, highp vec4 tex_region) {
|
||||
#if defined(ADDRESS_CLAMP_TO_ZERO)
|
||||
return p;
|
||||
}
|
||||
if (address == ADDRESS_REPEAT) {
|
||||
#endif
|
||||
|
||||
#if defined(ADDRESS_REPEAT)
|
||||
highp vec2 o = vec2(tex_region[0], tex_region[1]);
|
||||
highp vec2 size = vec2(tex_region[2] - tex_region[0], tex_region[3] - tex_region[1]);
|
||||
return vec2(floorMod((p.x - o.x), size.x) + o.x, floorMod((p.y - o.y), size.y) + o.y);
|
||||
}
|
||||
// Not reached.
|
||||
return vec2(0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
@ -183,8 +186,8 @@ void main(void) {
|
||||
|
||||
vec4 color;
|
||||
|
||||
if (filter_type == FILTER_NEAREST) {
|
||||
pos = adjustTexelByAddress(pos, varying_tex_region, address);
|
||||
#if defined(FILTER_NEAREST)
|
||||
pos = adjustTexelByAddress(pos, varying_tex_region);
|
||||
color = texture2D(texture, pos);
|
||||
if (pos.x < varying_tex_region[0] ||
|
||||
pos.y < varying_tex_region[1] ||
|
||||
@ -192,13 +195,15 @@ void main(void) {
|
||||
(varying_tex_region[3] - texel_size.y / 512.0) <= pos.y) {
|
||||
color = vec4(0, 0, 0, 0);
|
||||
}
|
||||
} else if (filter_type == FILTER_LINEAR) {
|
||||
#endif
|
||||
|
||||
#if defined(FILTER_LINEAR)
|
||||
highp vec2 p0 = pos - texel_size / 2.0;
|
||||
highp vec2 p1 = pos + texel_size / 2.0;
|
||||
|
||||
p1 = adjustTexel(p0, p1);
|
||||
p0 = adjustTexelByAddress(p0, varying_tex_region, address);
|
||||
p1 = adjustTexelByAddress(p1, varying_tex_region, address);
|
||||
p0 = adjustTexelByAddress(p0, varying_tex_region);
|
||||
p1 = adjustTexelByAddress(p1, varying_tex_region);
|
||||
|
||||
vec4 c0 = texture2D(texture, p0);
|
||||
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
||||
@ -223,10 +228,15 @@ void main(void) {
|
||||
|
||||
vec2 rate = fract(p0 * source_size);
|
||||
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||
} else if (filter_type == FILTER_SCREEN) {
|
||||
#endif
|
||||
|
||||
#if defined(FILTER_SCREEN)
|
||||
highp vec2 p0 = pos - texel_size / 2.0 / scale;
|
||||
highp vec2 p1 = pos + texel_size / 2.0 / scale;
|
||||
|
||||
// Prevent this variable from being optimized out.
|
||||
p0 += varying_tex_region.xy - varying_tex_region.xy;
|
||||
|
||||
p1 = adjustTexel(p0, p1);
|
||||
|
||||
vec4 c0 = texture2D(texture, p0);
|
||||
@ -238,10 +248,7 @@ void main(void) {
|
||||
vec2 rateCenter = vec2(1.0, 1.0) - texel_size / 2.0 / scale;
|
||||
vec2 rate = clamp(((fract(p0 * source_size) - rateCenter) * scale) + rateCenter, 0.0, 1.0);
|
||||
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||
} else {
|
||||
// Not reached.
|
||||
discard;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Un-premultiply alpha
|
||||
if (0.0 < color.a) {
|
||||
|
Loading…
Reference in New Issue
Block a user