diff --git a/src/renderer/gl33/gl33.c b/src/renderer/gl33/gl33.c index 5cc601ce..e5129493 100644 --- a/src/renderer/gl33/gl33.c +++ b/src/renderer/gl33/gl33.c @@ -1091,6 +1091,7 @@ RendererBackend _r_backend_gl33 = { .custom = &(GLBackendData) { .vtable = { .texture_type_info = gl33_texture_type_info, + .texture_format_caps = gl33_texture_format_caps, .init_context = gl33_init_context, } }, diff --git a/src/renderer/gl33/texture.c b/src/renderer/gl33/texture.c index d2a7010b..f6780746 100644 --- a/src/renderer/gl33/texture.c +++ b/src/renderer/gl33/texture.c @@ -14,7 +14,21 @@ #include "gl33.h" #include "../glcommon/debug.h" -static GLuint r_filter_to_gl_filter(TextureFilterMode mode) { +static GLenum linear_to_nearest(GLenum filter) { + switch(filter) { + case GL_LINEAR: + return GL_NEAREST; + + case GL_LINEAR_MIPMAP_LINEAR: + case GL_LINEAR_MIPMAP_NEAREST: + return GL_NEAREST_MIPMAP_NEAREST; + + default: + return GL_NONE; + } +} + +static GLuint r_filter_to_gl_filter(TextureFilterMode mode, GLenum for_format) { static GLuint map[] = { [TEX_FILTER_LINEAR] = GL_LINEAR, [TEX_FILTER_LINEAR_MIPMAP_NEAREST] = GL_LINEAR_MIPMAP_NEAREST, @@ -25,7 +39,35 @@ static GLuint r_filter_to_gl_filter(TextureFilterMode mode) { }; assert((uint)mode < sizeof(map)/sizeof(*map)); - return map[mode]; + GLenum filter = map[mode]; + GLenum alt_filter = linear_to_nearest(filter); + + if(alt_filter != GL_NONE && !(GLVT.texture_format_caps(for_format) & GLTEX_FILTERABLE)) { + filter = alt_filter; + } + + return filter; +} + +GLTexFormatCapabilities gl33_texture_format_caps(GLenum internal_fmt) { + GLTexFormatCapabilities caps = 0; + + GLenum base_fmt = glcommon_texture_base_format(internal_fmt); + + switch(base_fmt) { + case GL_RED: + case GL_RG: + case GL_RGB: + case GL_RGBA: + caps |= GLTEX_COLOR_RENDERABLE | GLTEX_FILTERABLE; + break; + + case GL_DEPTH_COMPONENT: + caps |= GLTEX_DEPTH_RENDERABLE | GLTEX_FILTERABLE; + break; + } + + return caps; } static GLuint r_wrap_to_gl_wrap(TextureWrapMode mode) { @@ -197,10 +239,12 @@ Texture* gl33_texture_create(const TextureParams *params) { gl33_bind_texture(tex, false); gl33_sync_texunit(tex->binding_unit, false, true); + tex->type_info = GLVT.texture_type_info(p->type); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, r_wrap_to_gl_wrap(p->wrap.s)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, r_wrap_to_gl_wrap(p->wrap.t)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_filter_to_gl_filter(p->filter.min)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_filter_to_gl_filter(p->filter.mag)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_filter_to_gl_filter(p->filter.min, tex->type_info->internal_fmt)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_filter_to_gl_filter(p->filter.mag, tex->type_info->internal_fmt)); if(!glext.version.is_es || GLES_ATLEAST(3, 0)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p->mipmaps - 1); @@ -210,8 +254,6 @@ Texture* gl33_texture_create(const TextureParams *params) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, p->anisotropy); } - tex->type_info = GLVT.texture_type_info(p->type); - if(p->stream && glext.pixel_buffer_object) { glGenBuffers(1, &tex->pbo); } @@ -245,14 +287,14 @@ void gl33_texture_set_filter(Texture *tex, TextureFilterMode fmin, TextureFilter gl33_bind_texture(tex, false); gl33_sync_texunit(tex->binding_unit, false, true); tex->params.filter.min = fmin; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_filter_to_gl_filter(fmin)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_filter_to_gl_filter(fmin, tex->type_info->internal_fmt)); } if(tex->params.filter.mag != fmag) { gl33_bind_texture(tex, false); gl33_sync_texunit(tex->binding_unit, false, true); tex->params.filter.mag = fmag; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_filter_to_gl_filter(fmag)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_filter_to_gl_filter(fmag, tex->type_info->internal_fmt)); } } diff --git a/src/renderer/gl33/texture.h b/src/renderer/gl33/texture.h index 7b65441f..bfcf8c15 100644 --- a/src/renderer/gl33/texture.h +++ b/src/renderer/gl33/texture.h @@ -44,5 +44,6 @@ void gl33_texture_clear(Texture *tex, const Color *clr); void gl33_texture_destroy(Texture *tex); GLTextureTypeInfo* gl33_texture_type_info(TextureType type); +GLTexFormatCapabilities gl33_texture_format_caps(GLenum internal_fmt); #endif // IGUARD_renderer_gl33_texture_h diff --git a/src/renderer/glcommon/opengl.c b/src/renderer/glcommon/opengl.c index a48a3990..a06caf01 100644 --- a/src/renderer/glcommon/opengl.c +++ b/src/renderer/glcommon/opengl.c @@ -502,7 +502,7 @@ void glcommon_check_extensions(void) { glext.version.is_es = strstartswith(glv, "OpenGL ES"); if(glext.version.is_es) { - glext.version.is_ANGLE = GL_ANGLE_translated_shader_source; + glext.version.is_ANGLE = strstr(glv, "(ANGLE "); } log_info("OpenGL version: %s", glv); diff --git a/src/renderer/glcommon/texture.c b/src/renderer/glcommon/texture.c index bea44919..baaae30b 100644 --- a/src/renderer/glcommon/texture.c +++ b/src/renderer/glcommon/texture.c @@ -82,3 +82,90 @@ GLTextureFormatTuple* glcommon_find_best_pixformat(TextureType textype, PixmapFo GLTextureFormatTuple *formats = GLVT.texture_type_info(textype)->external_formats; return glcommon_find_best_pixformat_internal(formats, pxfmt); } + +GLenum glcommon_texture_base_format(GLenum internal_fmt) { + switch(internal_fmt) { + // NOTE: semi-generated code + case GL_COMPRESSED_RED: return GL_RED; + case GL_COMPRESSED_RED_RGTC1: return GL_RED; + case GL_COMPRESSED_RG: return GL_RG; + case GL_COMPRESSED_RGB: return GL_RGB; + case GL_COMPRESSED_RGBA: return GL_RGBA; + case GL_COMPRESSED_RG_RGTC2: return GL_RG; + case GL_COMPRESSED_SIGNED_RED_RGTC1: return GL_RED; + case GL_COMPRESSED_SIGNED_RG_RGTC2: return GL_RG; + case GL_COMPRESSED_SRGB: return GL_RGB; + case GL_COMPRESSED_SRGB_ALPHA: return GL_RGBA; + case GL_DEPTH24_STENCIL8: return GL_DEPTH_STENCIL; + case GL_DEPTH32F_STENCIL8: return GL_DEPTH_STENCIL; + case GL_DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT; + case GL_R11F_G11F_B10F: return GL_RGB; + case GL_R16: return GL_RED; + case GL_R16F: return GL_RED; + case GL_R16I: return GL_RED; + case GL_R16UI: return GL_RED; + case GL_R16_SNORM: return GL_RED; + case GL_R32F: return GL_RED; + case GL_R32I: return GL_RED; + case GL_R32UI: return GL_RED; + case GL_R3_G3_B2: return GL_RGB; + case GL_R8: return GL_RED; + case GL_R8I: return GL_RED; + case GL_R8UI: return GL_RED; + case GL_R8_SNORM: return GL_RED; + case GL_RG16: return GL_RG; + case GL_RG16F: return GL_RG; + case GL_RG16I: return GL_RG; + case GL_RG16UI: return GL_RG; + case GL_RG16_SNORM: return GL_RG; + case GL_RG32F: return GL_RG; + case GL_RG32I: return GL_RG; + case GL_RG32UI: return GL_RG; + case GL_RG8: return GL_RG; + case GL_RG8I: return GL_RG; + case GL_RG8UI: return GL_RG; + case GL_RG8_SNORM: return GL_RG; + case GL_RGB10: return GL_RGB; + case GL_RGB10_A2: return GL_RGBA; + case GL_RGB10_A2UI: return GL_RGBA; + case GL_RGB12: return GL_RGB; + case GL_RGB16: return GL_RGB; + case GL_RGB16F: return GL_RGB; + case GL_RGB16I: return GL_RGB; + case GL_RGB16UI: return GL_RGB; + case GL_RGB16_SNORM: return GL_RGB; + case GL_RGB32F: return GL_RGB; + case GL_RGB32I: return GL_RGB; + case GL_RGB32UI: return GL_RGB; + case GL_RGB4: return GL_RGB; + case GL_RGB5: return GL_RGB; + case GL_RGB5_A1: return GL_RGBA; + case GL_RGB8: return GL_RGB; + case GL_RGB8I: return GL_RGB; + case GL_RGB8UI: return GL_RGB; + case GL_RGB8_SNORM: return GL_RGB; + case GL_RGB9_E5: return GL_RGB; + case GL_RGBA12: return GL_RGBA; + case GL_RGBA16: return GL_RGBA; + case GL_RGBA16F: return GL_RGBA; + case GL_RGBA16I: return GL_RGBA; + case GL_RGBA16UI: return GL_RGBA; + case GL_RGBA16_SNORM: return GL_RGBA; + case GL_RGBA2: return GL_RGBA; + case GL_RGBA32F: return GL_RGBA; + case GL_RGBA32I: return GL_RGBA; + case GL_RGBA32UI: return GL_RGBA; + case GL_RGBA4: return GL_RGBA; + case GL_RGBA8: return GL_RGBA; + case GL_RGBA8I: return GL_RGBA; + case GL_RGBA8UI: return GL_RGBA; + case GL_RGBA8_SNORM: return GL_RGBA; + case GL_SRGB8: return GL_RGB; + case GL_SRGB8_ALPHA8: return GL_RGBA; + } + + UNREACHABLE; +} diff --git a/src/renderer/glcommon/texture.h b/src/renderer/glcommon/texture.h index 5e94fb55..7731afa9 100644 --- a/src/renderer/glcommon/texture.h +++ b/src/renderer/glcommon/texture.h @@ -28,5 +28,6 @@ typedef struct GLTextureTypeInfo { } GLTextureTypeInfo; GLTextureFormatTuple* glcommon_find_best_pixformat(TextureType textype, PixmapFormat pxfmt); +GLenum glcommon_texture_base_format(GLenum internal_fmt); #endif // IGUARD_renderer_glcommon_texture_h diff --git a/src/renderer/glcommon/vtable.h b/src/renderer/glcommon/vtable.h index 645cdf80..0e1c2705 100644 --- a/src/renderer/glcommon/vtable.h +++ b/src/renderer/glcommon/vtable.h @@ -15,8 +15,15 @@ #include "../common/backend.h" #include "texture.h" +typedef enum GLTexFormatCapabilities { + GLTEX_COLOR_RENDERABLE, + GLTEX_DEPTH_RENDERABLE, + GLTEX_FILTERABLE, +} GLTexFormatCapabilities; + typedef struct GLVTable { GLTextureTypeInfo* (*texture_type_info)(TextureType type); + GLTexFormatCapabilities (*texture_format_caps)(GLenum internal_fmt); void (*init_context)(SDL_Window *window); } GLVTable; diff --git a/src/renderer/gles20/gles20.c b/src/renderer/gles20/gles20.c index 2f39eb7e..fbb65d43 100644 --- a/src/renderer/gles20/gles20.c +++ b/src/renderer/gles20/gles20.c @@ -74,6 +74,7 @@ RendererBackend _r_backend_gles20 = { .custom = &(GLBackendData) { .vtable = { .texture_type_info = gles_texture_type_info, + .texture_format_caps = gles_texture_format_caps, .init_context = gles20_init_context, } }, diff --git a/src/renderer/gles30/gles30.c b/src/renderer/gles30/gles30.c index 984d2905..4d0e2eb5 100644 --- a/src/renderer/gles30/gles30.c +++ b/src/renderer/gles30/gles30.c @@ -25,6 +25,7 @@ RendererBackend _r_backend_gles30 = { .custom = &(GLBackendData) { .vtable = { .texture_type_info = gles_texture_type_info, + .texture_format_caps = gles_texture_format_caps, .init_context = gles_init_context, } }, diff --git a/src/renderer/glescommon/texture.c b/src/renderer/glescommon/texture.c index fcc67996..dba9451a 100644 --- a/src/renderer/glescommon/texture.c +++ b/src/renderer/glescommon/texture.c @@ -50,9 +50,9 @@ static GLTextureTypeInfo gles_texformats[] = { [TEX_TYPE_RGBA_32_FLOAT] = MAKEFMT(GL_RGBA8, FMT_RGBA8), // WARNING: ANGLE bug(?): texture binding fails if a sized format is used here. - [TEX_TYPE_DEPTH_8] = MAKEFMT(GL_DEPTH_COMPONENT, FMT_DEPTH), - [TEX_TYPE_DEPTH_16] = MAKEFMT(GL_DEPTH_COMPONENT, FMT_DEPTH), - [TEX_TYPE_DEPTH_32_FLOAT] = MAKEFMT(GL_DEPTH_COMPONENT, FMT_DEPTH), + [TEX_TYPE_DEPTH_8] = MAKEFMT(GL_DEPTH_COMPONENT16, FMT_DEPTH), + [TEX_TYPE_DEPTH_16] = MAKEFMT(GL_DEPTH_COMPONENT16, FMT_DEPTH), + [TEX_TYPE_DEPTH_32_FLOAT] = MAKEFMT(GL_DEPTH_COMPONENT16, FMT_DEPTH), }; static inline void set_format(TextureType t, GLenum internal, GLTextureFormatTuple external) { @@ -63,11 +63,19 @@ static inline void set_format(TextureType t, GLenum internal, GLTextureFormatTup void gles_init_texformats_table(void) { bool is_gles3 = GLES_ATLEAST(3, 0); + bool is_angle = glext.version.is_ANGLE; bool have_rg = glext.texture_rg; bool have_16bit = glext.texture_norm16; bool have_renderable_float = glext.color_buffer_float; - bool have_float16 = glext.texture_half_float_linear && have_renderable_float; - bool have_float32 = glext.texture_float_linear && have_renderable_float && glext.float_blend; + bool have_float16 = /* glext.texture_half_float_linear && */ have_renderable_float; + bool have_float32 = /* glext.texture_float_linear && */ have_renderable_float && glext.float_blend; + + // XXX: Workaround for an ANGLE bug + if(is_angle) { + set_format(TEX_TYPE_DEPTH_8, GL_DEPTH_COMPONENT, FMTSTRUCT(FMT_DEPTH)); + set_format(TEX_TYPE_DEPTH_16, GL_DEPTH_COMPONENT, FMTSTRUCT(FMT_DEPTH)); + set_format(TEX_TYPE_DEPTH_32_FLOAT, GL_DEPTH_COMPONENT, FMTSTRUCT(FMT_DEPTH)); + } // WARNING: GL_RGB* is generally not color-renderable, except for GL_RGB8 // So use GL_RGBA instead, where appropriate. @@ -123,42 +131,7 @@ void gles_init_texformats_table(void) { gles_texformats[i].external_formats[0] = gles_texformats[i].primary_external_format; if(!is_gles3) { - switch(gles_texformats[i].internal_fmt) { - case GL_R8: - case GL_R16: - case GL_R16F: - case GL_R32F: - gles_texformats[i].internal_fmt = GL_RED; - break; - - case GL_RG8: - case GL_RG16: - case GL_RG16F: - case GL_RG32F: - gles_texformats[i].internal_fmt = GL_RG; - break; - - case GL_RGB8: - case GL_RGB16: - case GL_RGB16F: - case GL_RGB32F: - gles_texformats[i].internal_fmt = GL_RGB; - break; - - case GL_RGBA8: - case GL_RGBA16: - case GL_RGBA16F: - case GL_RGBA32F: - gles_texformats[i].internal_fmt = GL_RGBA; - break; - - case GL_DEPTH_COMPONENT: - break; - - default: - UNREACHABLE; - } - + gles_texformats[i].internal_fmt = glcommon_texture_base_format(gles_texformats[i].internal_fmt); assert(gles_texformats[i].internal_fmt == gles_texformats[i].primary_external_format.gl_fmt); } } @@ -168,3 +141,75 @@ GLTextureTypeInfo* gles_texture_type_info(TextureType type) { assert((uint)type < sizeof(gles_texformats)/sizeof(*gles_texformats)); return gles_texformats + type; } + +GLTexFormatCapabilities gles_texture_format_caps(GLenum internal_fmt) { + GLTexFormatCapabilities caps = 0; + + /* + * Some formats we don't care about were omitted here + */ + + switch(internal_fmt) { + case GL_R8: + case GL_RG8: + case GL_RGB8: + case GL_RGBA8: + caps |= GLTEX_COLOR_RENDERABLE | GLTEX_FILTERABLE; + break; + + case GL_R16: + case GL_RG16: + case GL_RGBA16: + if(glext.texture_norm16) { + caps |= GLTEX_COLOR_RENDERABLE | GLTEX_FILTERABLE; + } + break; + + case GL_RGB16: + if(glext.texture_norm16) { + caps |= GLTEX_FILTERABLE; + } + break; + + case GL_R16F: + case GL_RG16F: + case GL_RGBA16F: + if(glext.color_buffer_float) { + caps |= GLTEX_COLOR_RENDERABLE; + } + // fallthrough + case GL_RGB16F: + if(glext.texture_half_float_linear) { + caps |= GLTEX_FILTERABLE; + } + break; + + case GL_R32F: + case GL_RG32F: + case GL_RGB32F: + case GL_RGBA32F: + if(glext.float_blend) { + if(glext.color_buffer_float && internal_fmt != GL_RGB32F) { + caps |= GLTEX_COLOR_RENDERABLE; + } + + if(glext.texture_float_linear) { + caps |= GLTEX_FILTERABLE; + } + } + break; + + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + // FIXME: Is there an extension that makes it filterable? + // It seems to work on Mesa and ANGLE at least. + caps |= GLTEX_DEPTH_RENDERABLE; + break; + + case GL_DEPTH_COMPONENT32F: + caps |= GLTEX_DEPTH_RENDERABLE | GLTEX_FILTERABLE; + break; + } + + return caps; +} diff --git a/src/renderer/glescommon/texture.h b/src/renderer/glescommon/texture.h index 0afd0188..2afb74de 100644 --- a/src/renderer/glescommon/texture.h +++ b/src/renderer/glescommon/texture.h @@ -12,8 +12,10 @@ #include "taisei.h" #include "../glcommon/texture.h" +#include "../glcommon/vtable.h" void gles_init_texformats_table(void); GLTextureTypeInfo* gles_texture_type_info(TextureType type); +GLTexFormatCapabilities gles_texture_format_caps(GLenum internal_fmt); #endif // IGUARD_renderer_glescommon_texture_h