LCOV - code coverage report
Current view: top level - source/lib/res/graphics - ogl_tex.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 2 340 0.6 %
Date: 2021-09-24 14:46:47 Functions: 1 34 2.9 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #include "precompiled.h"
      24             : #include "ogl_tex.h"
      25             : 
      26             : #include "lib/app_hooks.h"
      27             : #include "lib/bits.h"
      28             : #include "lib/ogl.h"
      29             : #include "lib/res/h_mgr.h"
      30             : #include "lib/sysdep/gfx.h"
      31             : #include "lib/tex/tex.h"
      32             : 
      33             : #include <cstdio>
      34             : 
      35             : //----------------------------------------------------------------------------
      36             : // OpenGL helper routines
      37             : //----------------------------------------------------------------------------
      38             : 
      39             : static bool filter_valid(GLint filter)
      40             : {
      41           0 :     switch(filter)
      42             :     {
      43             :     case GL_NEAREST:
      44             :     case GL_LINEAR:
      45             :     case GL_NEAREST_MIPMAP_NEAREST:
      46             :     case GL_LINEAR_MIPMAP_NEAREST:
      47             :     case GL_NEAREST_MIPMAP_LINEAR:
      48             :     case GL_LINEAR_MIPMAP_LINEAR:
      49             :         return true;
      50           0 :     default:
      51           0 :         return false;
      52             :     }
      53             : }
      54             : 
      55             : 
      56             : static bool wrap_valid(GLint wrap)
      57             : {
      58           0 :     switch(wrap)
      59             :     {
      60             : #if !CONFIG2_GLES
      61             :     case GL_CLAMP:
      62             :     case GL_CLAMP_TO_BORDER:
      63             : #endif
      64             :     case GL_CLAMP_TO_EDGE:
      65             :     case GL_REPEAT:
      66             :     case GL_MIRRORED_REPEAT:
      67             :         return true;
      68           0 :     default:
      69           0 :         return false;
      70             :     }
      71             : }
      72             : 
      73             : 
      74             : static bool are_mipmaps_needed(size_t width, size_t height, GLint filter)
      75             : {
      76             :     // can't upload the entire texture; we're going to skip some
      77             :     // levels until it no longer exceeds the OpenGL dimension limit.
      78           0 :     if((GLint)width > ogl_max_tex_size || (GLint)height > ogl_max_tex_size)
      79             :         return true;
      80             : 
      81           0 :     switch(filter)
      82             :     {
      83             :     case GL_NEAREST_MIPMAP_NEAREST:
      84             :     case GL_LINEAR_MIPMAP_NEAREST:
      85             :     case GL_NEAREST_MIPMAP_LINEAR:
      86             :     case GL_LINEAR_MIPMAP_LINEAR:
      87             :         return true;
      88           0 :     default:
      89           0 :         return false;
      90             :     }
      91             : }
      92             : 
      93             : 
      94             : static bool fmt_is_s3tc(GLenum fmt)
      95             : {
      96           0 :     switch(fmt)
      97             :     {
      98             :     case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
      99             :     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
     100             :     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
     101             :     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
     102             :         return true;
     103           0 :     default:
     104           0 :         return false;
     105             :     }
     106             : }
     107             : 
     108             : 
     109             : // determine OpenGL texture format, given <bpp> and Tex <flags>.
     110           0 : static GLint choose_fmt(size_t bpp, size_t flags)
     111             : {
     112           0 :     const bool alpha = (flags & TEX_ALPHA) != 0;
     113           0 :     const bool bgr   = (flags & TEX_BGR  ) != 0;
     114           0 :     const bool grey  = (flags & TEX_GREY ) != 0;
     115           0 :     const size_t dxt   = flags & TEX_DXT;
     116             : 
     117             :     // S3TC
     118           0 :     if(dxt != 0)
     119             :     {
     120           0 :         switch(dxt)
     121             :         {
     122             :         case DXT1A:
     123             :             return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
     124           0 :         case 1:
     125           0 :             return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
     126           0 :         case 3:
     127           0 :             return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
     128           0 :         case 5:
     129           0 :             return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
     130           0 :         default:
     131           0 :             DEBUG_WARN_ERR(ERR::LOGIC); // invalid DXT value
     132           0 :             return 0;
     133             :         }
     134             :     }
     135             : 
     136             :     // uncompressed
     137           0 :     switch(bpp)
     138             :     {
     139           0 :     case 8:
     140           0 :         ENSURE(grey);
     141             :         return GL_LUMINANCE;
     142             :     case 16:
     143             :         return GL_LUMINANCE_ALPHA;
     144           0 :     case 24:
     145           0 :         ENSURE(!alpha);
     146             : #if CONFIG2_GLES
     147             :         // GLES never supports BGR
     148             :         ENSURE(!bgr);
     149             :         return GL_RGB;
     150             : #else
     151           0 :         return bgr? GL_BGR : GL_RGB;
     152             : #endif
     153           0 :     case 32:
     154           0 :         ENSURE(alpha);
     155             :         // GLES can support BGRA via GL_EXT_texture_format_BGRA8888
     156             :         // (TODO: can we rely on support for that extension?)
     157           0 :         return bgr? GL_BGRA_EXT : GL_RGBA;
     158           0 :     default:
     159           0 :         DEBUG_WARN_ERR(ERR::LOGIC); // invalid bpp
     160             :         return 0;
     161             :     }
     162             : 
     163             :     UNREACHABLE;
     164             : }
     165             : 
     166             : 
     167             : //----------------------------------------------------------------------------
     168             : // quality mechanism
     169             : //----------------------------------------------------------------------------
     170             : 
     171             : static GLint default_filter = GL_LINEAR;    // one of the GL *minify* filters
     172             : static int default_q_flags = OGL_TEX_FULL_QUALITY;  // OglTexQualityFlags
     173             : 
     174             : static bool q_flags_valid(int q_flags)
     175             : {
     176           0 :     const size_t bits = OGL_TEX_FULL_QUALITY|OGL_TEX_HALF_BPP|OGL_TEX_HALF_RES;
     177             :     // unrecognized bits are set - invalid
     178           0 :     if((q_flags & ~bits) != 0)
     179             :         return false;
     180             :     // "full quality" but other reduction bits are set - invalid
     181           0 :     if(q_flags & OGL_TEX_FULL_QUALITY && q_flags & ~OGL_TEX_FULL_QUALITY)
     182             :         return false;
     183             :     return true;
     184             : }
     185             : 
     186             : 
     187             : // change default settings - these affect performance vs. quality.
     188             : // may be overridden for individual textures via parameter to
     189             : // ogl_tex_upload or ogl_tex_set_filter, respectively.
     190             : //
     191             : // pass 0 to keep the current setting; defaults and legal values are:
     192             : // - q_flags: OGL_TEX_FULL_QUALITY; combination of OglTexQualityFlags
     193             : // - filter: GL_LINEAR; any valid OpenGL minification filter
     194           0 : void ogl_tex_set_defaults(int q_flags, GLint filter)
     195             : {
     196           0 :     if(q_flags)
     197             :     {
     198           0 :         ENSURE(q_flags_valid(q_flags));
     199           0 :         default_q_flags = q_flags;
     200             :     }
     201             : 
     202           0 :     if(filter)
     203             :     {
     204           0 :         ENSURE(filter_valid(filter));
     205           0 :         default_filter = filter;
     206             :     }
     207           0 : }
     208             : 
     209             : 
     210             : // choose an internal format for <fmt> based on the given q_flags.
     211           0 : static GLint choose_int_fmt(GLenum fmt, int q_flags)
     212             : {
     213             :     // true => 4 bits per component; otherwise, 8
     214           0 :     const bool half_bpp = (q_flags & OGL_TEX_HALF_BPP) != 0;
     215             : 
     216             :     // early-out for S3TC textures: they don't need an internal format
     217             :     // (because upload is via glCompressedTexImage2DARB), but we must avoid
     218             :     // triggering the default case below. we might as well return a
     219             :     // meaningful value (i.e. int_fmt = fmt).
     220           0 :     if(fmt_is_s3tc(fmt))
     221           0 :         return fmt;
     222             : 
     223             : #if CONFIG2_GLES
     224             : 
     225             :     UNUSED2(half_bpp);
     226             : 
     227             :     // GLES only supports internal format == external format
     228             :     return fmt;
     229             : 
     230             : #else
     231             : 
     232           0 :     switch(fmt)
     233             :     {
     234             :     // 8bpp
     235           0 :     case GL_LUMINANCE:
     236           0 :         return half_bpp? GL_LUMINANCE4 : GL_LUMINANCE8;
     237           0 :     case GL_INTENSITY:
     238           0 :         return half_bpp? GL_INTENSITY4 : GL_INTENSITY8;
     239           0 :     case GL_ALPHA:
     240           0 :         return half_bpp? GL_ALPHA4 : GL_ALPHA8;
     241             : 
     242             :     // 16bpp
     243           0 :     case GL_LUMINANCE_ALPHA:
     244           0 :         return half_bpp? GL_LUMINANCE4_ALPHA4 : GL_LUMINANCE8_ALPHA8;
     245             : 
     246             :     // 24bpp
     247           0 :     case GL_RGB:
     248           0 :     case GL_BGR:    // note: BGR can't be used as internal format
     249           0 :         return half_bpp? GL_RGB4 : GL_RGB8;
     250             : 
     251             :     // 32bpp
     252           0 :     case GL_RGBA:
     253           0 :     case GL_BGRA:   // note: BGRA can't be used as internal format
     254           0 :         return half_bpp? GL_RGBA4 : GL_RGBA8;
     255             : 
     256           0 :     default:
     257           0 :         {
     258           0 :         wchar_t buf[100];
     259           0 :         swprintf_s(buf, ARRAY_SIZE(buf), L"choose_int_fmt: fmt 0x%x isn't covered! please add it", fmt);
     260           0 :         DEBUG_DISPLAY_ERROR(buf);
     261           0 :         DEBUG_WARN_ERR(ERR::LOGIC); // given fmt isn't covered! please add it.
     262             :         // fall back to a reasonable default
     263           0 :         return half_bpp? GL_RGB4 : GL_RGB8;
     264             :         }
     265             :     }
     266             : 
     267             :     UNREACHABLE;
     268             : 
     269             : #endif  // #if CONFIG2_GLES
     270             : }
     271             : 
     272             : 
     273             : //----------------------------------------------------------------------------
     274             : // texture state to allow seamless reload
     275             : //----------------------------------------------------------------------------
     276             : 
     277             : // see "Texture Parameters" in docs.
     278             : 
     279             : // all GL state tied to the texture that must be reapplied after reload.
     280             : // (this mustn't get too big, as it's stored in the already sizeable OglTex)
     281             : struct OglTexState
     282             : {
     283             :     // glTexParameter
     284             :     // note: there are more options, but they do not look to
     285             :     //       be important and will not be applied after a reload!
     286             :     //       in particular, LOD_BIAS isn't needed because that is set for
     287             :     //       the entire texturing unit via glTexEnv.
     288             :     // .. texture filter
     289             :     //    note: this is the minification filter value; magnification filter
     290             :     //          is GL_NEAREST if it's GL_NEAREST, otherwise GL_LINEAR.
     291             :     //          we don't store mag_filter explicitly because it
     292             :     //          doesn't appear useful - either apps can tolerate LINEAR, or
     293             :     //          mipmaps aren't called for and filter could be NEAREST anyway).
     294             :     GLint filter;
     295             :     // .. wrap mode
     296             :     GLint wrap_s;
     297             :     GLint wrap_t;
     298             :     // .. anisotropy
     299             :     //    note: ignored unless EXT_texture_filter_anisotropic is supported.
     300             :     GLfloat anisotropy;
     301             : };
     302             : 
     303             : 
     304             : // fill the given state object with default values.
     305             : static void state_set_to_defaults(OglTexState* ots)
     306             : {
     307           0 :     ots->filter = default_filter;
     308           0 :     ots->wrap_s = GL_REPEAT;
     309           0 :     ots->wrap_t = GL_REPEAT;
     310           0 :     ots->anisotropy = 1.0f;
     311             : }
     312             : 
     313             : 
     314             : // send all state to OpenGL (actually the currently bound texture).
     315             : // called from ogl_tex_upload.
     316           0 : static void state_latch(OglTexState* ots)
     317             : {
     318             :     // filter
     319           0 :     const GLint filter = ots->filter;
     320           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
     321           0 :     const GLint mag_filter = (filter == GL_NEAREST)? GL_NEAREST : GL_LINEAR;
     322           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
     323             : 
     324             :     // wrap
     325           0 :     const GLint wrap_s = ots->wrap_s;
     326           0 :     const GLint wrap_t = ots->wrap_t;
     327           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
     328           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
     329             :     // .. only CLAMP and REPEAT are guaranteed to be available.
     330             :     //    if we're using one of the others, we squelch the error that
     331             :     //    may have resulted if this GL implementation is old.
     332             : #if !CONFIG2_GLES
     333           0 :     if((wrap_s != GL_CLAMP && wrap_s != GL_REPEAT) || (wrap_t != GL_CLAMP && wrap_t != GL_REPEAT))
     334           0 :         ogl_SquelchError(GL_INVALID_ENUM);
     335             : #endif
     336             : 
     337             :     // anisotropy
     338           0 :     const GLfloat anisotropy = ots->anisotropy;
     339           0 :     if (anisotropy != 1.0f && ogl_tex_has_anisotropy())
     340             :     {
     341           0 :         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
     342             :     }
     343           0 : }
     344             : 
     345             : 
     346             : //----------------------------------------------------------------------------
     347             : // texture resource object
     348             : //----------------------------------------------------------------------------
     349             : 
     350             : // ideally we would split OglTex into data and state objects as in
     351             : // SndData / VSrc. this gives us the benefits of caching while still
     352             : // leaving each "instance" (state object, which owns a data reference)
     353             : // free to change its state. however, unlike in OpenAL, there is no state
     354             : // independent of the data object - all parameters are directly tied to the
     355             : // GL texture object. therefore, splitting them up is impossible.
     356             : // (we shouldn't even keep the texel data in memory since that's already
     357             : // covered by the FS cache).
     358             : //
     359             : // given that multiple "instances" share the state stored here, we conclude:
     360             : // - a refcount is necessary to prevent ogl_tex_upload from freeing
     361             : //   <t> as long as other instances are active.
     362             : // - concurrent use risks cross-talk (if the 2nd "instance" changes state and
     363             : //   the first is reloaded, its state may change to that of the 2nd)
     364             : //
     365             : // as bad as it sounds, the latter issue isn't a problem: we do not expect
     366             : // multiple instances of the same texture where someone changes its filter.
     367             : // even if it is reloaded, the differing state is not critical.
     368             : // the alternative is even worse: disabling *all* caching/reuse would
     369             : // really hurt performance and h_mgr doesn't support only disallowing
     370             : // reuse of active objects (this would break the index lookup code, since
     371             : // multiple instances may then exist).
     372             : 
     373             : // note: make sure these values fit inside OglTex.flags (only 16 bits)
     374             : enum OglTexFlags
     375             : {
     376             :     // "the texture is currently uploaded"; reset in dtor.
     377             :     OT_IS_UPLOADED = 1,
     378             : 
     379             :     // "the enclosed Tex object is valid and holds a texture";
     380             :     // reset in dtor and after ogl_tex_upload's tex_free.
     381             :     OT_TEX_VALID = 2,
     382             :     //size_t tex_valid : 1;
     383             : 
     384             :     // "reload() should automatically re-upload the texture" (because
     385             :     // it had been uploaded before the reload); never reset.
     386             :     OT_NEED_AUTO_UPLOAD = 4,
     387             : 
     388             :     // (used for validating flags)
     389             :     OT_ALL_FLAGS = OT_IS_UPLOADED|OT_TEX_VALID|OT_NEED_AUTO_UPLOAD
     390             : };
     391             : 
     392             : struct OglTex
     393             : {
     394             :     Tex t;
     395             : 
     396             :     // allocated by OglTex_reload; indicates the texture is currently uploaded.
     397             :     GLuint id;
     398             : 
     399             :     // ogl_tex_upload calls choose_fmt to determine these from <t>.
     400             :     // however, its caller may override those values via parameters.
     401             :     // note: these are stored here to allow retrieving via ogl_tex_get_format;
     402             :     // they are only used within ogl_tex_upload.
     403             :     GLenum fmt;
     404             :     GLint int_fmt;
     405             : 
     406             :     OglTexState state;
     407             : 
     408             :     // OglTexQualityFlags
     409             :     u8 q_flags;
     410             : 
     411             :     // to which Texture Mapping Unit was this bound?
     412             :     u8 tmu;
     413             : 
     414             :     u16 flags;
     415             : 
     416             :     u32 uploaded_size;
     417             : };
     418             : 
     419             : H_TYPE_DEFINE(OglTex);
     420             : 
     421           0 : static void OglTex_init(OglTex* ot, va_list args)
     422             : {
     423           0 :     Tex* wrapped_tex = va_arg(args, Tex*);
     424           0 :     if(wrapped_tex)
     425             :     {
     426           0 :         ot->t = *wrapped_tex;
     427             :         // indicate ot->t is now valid, thus skipping loading from file.
     428             :         // note: ogl_tex_wrap prevents actual reloads from happening.
     429           0 :         ot->flags |= OT_TEX_VALID;
     430             :     }
     431             : 
     432           0 :     state_set_to_defaults(&ot->state);
     433           0 :     ot->q_flags = default_q_flags;
     434           0 : }
     435             : 
     436           0 : static void OglTex_dtor(OglTex* ot)
     437             : {
     438           0 :     if(ot->flags & OT_TEX_VALID)
     439             :     {
     440           0 :         ot->t.free();
     441           0 :         ot->flags &= ~OT_TEX_VALID;
     442             :     }
     443             : 
     444             :     // note: do not check if OT_IS_UPLOADED is set, because we allocate
     445             :     // OglTex.id without necessarily having done an upload.
     446           0 :     glDeleteTextures(1, &ot->id);
     447           0 :     ot->id = 0;
     448           0 :     ot->flags &= ~OT_IS_UPLOADED;
     449           0 :     ot->uploaded_size = 0;
     450           0 : }
     451             : 
     452           0 : static Status OglTex_reload(OglTex* ot, const PIVFS& vfs, const VfsPath& pathname, Handle h)
     453             : {
     454             :     // we're reusing a freed but still in-memory OglTex object
     455           0 :     if(ot->flags & OT_IS_UPLOADED)
     456             :         return INFO::OK;
     457             : 
     458             :     // if we don't already have the texture in memory (*), load from file.
     459             :     // * this happens if the texture is "wrapped".
     460           0 :     if(!(ot->flags & OT_TEX_VALID))
     461             :     {
     462           0 :         std::shared_ptr<u8> file; size_t fileSize;
     463           0 :         RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
     464           0 :         if(ot->t.decode(file, fileSize) >= 0)
     465           0 :             ot->flags |= OT_TEX_VALID;
     466             :     }
     467             : 
     468           0 :     glGenTextures(1, &ot->id);
     469             : 
     470             :     // if it had already been uploaded before this reload,
     471             :     // re-upload it (this also does state_latch).
     472           0 :     if(ot->flags & OT_NEED_AUTO_UPLOAD)
     473           0 :         (void)ogl_tex_upload(h);
     474             : 
     475             :     return INFO::OK;
     476             : }
     477             : 
     478           0 : static Status OglTex_validate(const OglTex* ot)
     479             : {
     480           0 :     if(ot->flags & OT_TEX_VALID)
     481             :     {
     482           0 :         RETURN_STATUS_IF_ERR(ot->t.validate());
     483             : 
     484             :         // width, height
     485             :         // (note: this is done here because tex.cpp doesn't impose any
     486             :         // restrictions on dimensions, while OpenGL does).
     487           0 :         size_t w = ot->t.m_Width;
     488           0 :         size_t h = ot->t.m_Height;
     489             :         // .. == 0; texture file probably not loaded successfully.
     490           0 :         if(w == 0 || h == 0)
     491           0 :             WARN_RETURN(ERR::_11);
     492             :         // .. not power-of-2.
     493             :         //    note: we can't work around this because both NV_texture_rectangle
     494             :         //    and subtexture require work for the client (changing tex coords).
     495             :         //    TODO: ARB_texture_non_power_of_two
     496           0 :         if(!is_pow2(w) || !is_pow2(h))
     497           0 :             WARN_RETURN(ERR::_13);
     498             : 
     499             :         // no longer verify dimensions are less than ogl_max_tex_size,
     500             :         // because we just use the higher mip levels in that case.
     501             :     }
     502             : 
     503             :     // texture state
     504           0 :     if(!filter_valid(ot->state.filter))
     505           0 :         WARN_RETURN(ERR::_14);
     506           0 :     if(!wrap_valid(ot->state.wrap_s))
     507           0 :         WARN_RETURN(ERR::_15);
     508           0 :     if(!wrap_valid(ot->state.wrap_t))
     509           0 :         WARN_RETURN(ERR::_16);
     510             : 
     511             :     // misc
     512           0 :     if(!q_flags_valid(ot->q_flags))
     513           0 :         WARN_RETURN(ERR::_17);
     514           0 :     if(ot->tmu >= 128)    // unexpected that there will ever be this many
     515           0 :         WARN_RETURN(ERR::_18);
     516           0 :     if(ot->flags > OT_ALL_FLAGS)
     517           0 :         WARN_RETURN(ERR::_19);
     518             :     // .. note: don't check ot->fmt and ot->int_fmt - they aren't set
     519             :     //    until during ogl_tex_upload.
     520             : 
     521             :     return INFO::OK;
     522             : }
     523             : 
     524           0 : static Status OglTex_to_string(const OglTex* ot, wchar_t* buf)
     525             : {
     526           0 :     swprintf_s(buf, H_STRING_LEN, L"OglTex id=%u flags=%x", ot->id, (unsigned int)ot->flags);
     527           0 :     return INFO::OK;
     528             : }
     529             : 
     530             : 
     531             : // load and return a handle to the texture given in <pathname>.
     532             : // for a list of supported formats, see tex.h's tex_load.
     533           0 : Handle ogl_tex_load(const PIVFS& vfs, const VfsPath& pathname, size_t flags)
     534             : {
     535           0 :     Tex* wrapped_tex = 0;   // we're loading from file
     536           0 :     return h_alloc(H_OglTex, vfs, pathname, flags, wrapped_tex);
     537             : }
     538             : 
     539             : // make the given Tex object ready for use as an OpenGL texture
     540             : // and return a handle to it. this will be as if its contents
     541             : // had been loaded by ogl_tex_load.
     542             : //
     543             : // we need only add bookkeeping information and "wrap" it in
     544             : // a resource object (accessed via Handle), hence the name.
     545             : //
     546             : // <fn> isn't strictly needed but should describe the texture so that
     547             : // h_filename will return a meaningful comment for debug purposes.
     548             : // note: because we cannot guarantee that callers will pass distinct
     549             : // "filenames", caching is disabled for the created object. this avoids
     550             : // mistakenly reusing previous objects that share the same comment.
     551           0 : Handle ogl_tex_wrap(Tex* t, const PIVFS& vfs, const VfsPath& pathname, size_t flags)
     552             : {
     553             :     // this object may not be backed by a file ("may", because
     554             :     // someone could do tex_load and then ogl_tex_wrap).
     555             :     // if h_mgr asks for a reload, the dtor will be called but
     556             :     // we won't be able to reconstruct it. therefore, disallow reloads.
     557             :     // (they are improbable anyway since caller is supposed to pass a
     558             :     // 'descriptive comment' instead of filename, but don't rely on that)
     559             :     // also disable caching as explained above.
     560           0 :     flags |= RES_DISALLOW_RELOAD|RES_NO_CACHE;
     561           0 :     return h_alloc(H_OglTex, vfs, pathname, flags, t);
     562             : }
     563             : 
     564             : 
     565             : // free all resources associated with the texture and make further
     566             : // use of it impossible. (subject to refcount)
     567          12 : Status ogl_tex_free(Handle& ht)
     568             : {
     569          12 :     return h_free(ht, H_OglTex);
     570             : }
     571             : 
     572             : 
     573             : //----------------------------------------------------------------------------
     574             : // state setters (see "Texture Parameters" in docs)
     575             : //----------------------------------------------------------------------------
     576             : 
     577             : // we require the below functions be called before uploading; this avoids
     578             : // potentially redundant glTexParameter calls (we'd otherwise need to always
     579             : // set defaults because we don't know if an override is forthcoming).
     580             : 
     581             : // raise a debug warning if the texture has already been uploaded
     582             : // (except in the few cases where this is allowed; see below).
     583             : // this is so that you will notice incorrect usage - only one instance of a
     584             : // texture should be active at a time, because otherwise they vie for
     585             : // control of one shared OglTexState.
     586           0 : static void warn_if_uploaded(Handle ht, const OglTex* ot)
     587             : {
     588             : #ifndef NDEBUG
     589             :     // we do not require users of this module to remember if they've
     590             :     // already uploaded a texture (inconvenient). since they also can't
     591             :     // tell if the texture was newly loaded (due to h_alloc interface),
     592             :     // we have to squelch this warning in 2 cases:
     593             :     // - it's ogl_tex_loaded several times (i.e. refcount > 1) and the
     594             :     //   caller (typically a higher-level LoadTexture) is setting filter etc.
     595             :     // - caller is using our Handle as a caching mechanism, and calls
     596             :     //   ogl_tex_set_* before every use of the texture. note: this
     597             :     //   need not fall under the above check, e.g. if freed but cached.
     598             :     //   workaround is that ogl_tex_set_* won't call us if the
     599             :     //   same state values are being set (harmless anyway).
     600             :     intptr_t refs = h_get_refcnt(ht);
     601             :     if(refs > 1)
     602             :         return; // don't complain
     603             : 
     604             :     if(ot->flags & OT_IS_UPLOADED)
     605             :         DEBUG_WARN_ERR(ERR::LOGIC); // ogl_tex_set_*: texture already uploaded and shouldn't be changed
     606             : #else
     607             :     // (prevent warnings; the alternative of wrapping all call sites in
     608             :     // #ifndef is worse)
     609           0 :     UNUSED2(ht);
     610           0 :     UNUSED2(ot);
     611             : #endif
     612           0 : }
     613             : 
     614             : 
     615             : // override default filter (as set above) for this texture.
     616             : // must be called before uploading (raises a warning if called afterwards).
     617             : // filter is as defined by OpenGL; it is applied for both minification and
     618             : // magnification (for rationale and details, see OglTexState)
     619           0 : Status ogl_tex_set_filter(Handle ht, GLint filter)
     620             : {
     621           0 :     H_DEREF(ht, OglTex, ot);
     622             : 
     623           0 :     if(!filter_valid(filter))
     624           0 :         WARN_RETURN(ERR::INVALID_PARAM);
     625             : 
     626           0 :     if(ot->state.filter != filter)
     627             :     {
     628           0 :         warn_if_uploaded(ht, ot);
     629           0 :         ot->state.filter = filter;
     630             :     }
     631             :     return INFO::OK;
     632             : }
     633             : 
     634             : 
     635             : // override default wrap mode (GL_REPEAT) for this texture.
     636             : // must be called before uploading (raises a warning if called afterwards).
     637             : // wrap is as defined by OpenGL.
     638           0 : Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t)
     639             : {
     640           0 :     H_DEREF(ht, OglTex, ot);
     641             : 
     642           0 :     if(!wrap_valid(wrap_s))
     643           0 :         WARN_RETURN(ERR::INVALID_PARAM);
     644             : 
     645           0 :     if(!wrap_valid(wrap_t))
     646           0 :         WARN_RETURN(ERR::INVALID_PARAM);
     647             : 
     648           0 :     if(ot->state.wrap_s != wrap_s || ot->state.wrap_t != wrap_t)
     649             :     {
     650           0 :         warn_if_uploaded(ht, ot);
     651           0 :         ot->state.wrap_s = wrap_s;
     652           0 :         ot->state.wrap_t = wrap_t;
     653             :     }
     654             :     return INFO::OK;
     655             : }
     656             : 
     657             : 
     658             : // override default anisotropy for this texture.
     659             : // must be called before uploading (raises a warning if called afterwards).
     660           0 : Status ogl_tex_set_anisotropy(Handle ht, GLfloat anisotropy)
     661             : {
     662           0 :     H_DEREF(ht, OglTex, ot);
     663             : 
     664           0 :     if(anisotropy < 1.0f)
     665           0 :         WARN_RETURN(ERR::INVALID_PARAM);
     666             : 
     667           0 :     if(ot->state.anisotropy != anisotropy)
     668             :     {
     669           0 :         warn_if_uploaded(ht, ot);
     670           0 :         ot->state.anisotropy = anisotropy;
     671             :     }
     672             :     return INFO::OK;
     673             : }
     674             : 
     675             : 
     676             : //----------------------------------------------------------------------------
     677             : // upload
     678             : //----------------------------------------------------------------------------
     679             : 
     680             : // OpenGL has several features that are helpful for uploading but not
     681             : // available in all implementations. we check for their presence but
     682             : // provide for user override (in case they don't work on a card/driver
     683             : // combo we didn't test).
     684             : 
     685             : // tristate; -1 is undecided
     686             : static int have_auto_mipmap_gen = -1;
     687             : static int have_s3tc = -1;
     688             : static int have_anistropy = -1;
     689             : 
     690             : // override the default decision and force/disallow use of the
     691             : // given feature. should be called from ah_override_gl_upload_caps.
     692           0 : void ogl_tex_override(OglTexOverrides what, OglTexAllow allow)
     693             : {
     694           0 :     ENSURE(allow == OGL_TEX_ENABLE || allow == OGL_TEX_DISABLE);
     695           0 :     const bool enable = (allow == OGL_TEX_ENABLE);
     696             : 
     697           0 :     switch(what)
     698             :     {
     699           0 :     case OGL_TEX_S3TC:
     700           0 :         have_s3tc = enable;
     701           0 :         break;
     702           0 :     case OGL_TEX_AUTO_MIPMAP_GEN:
     703           0 :         have_auto_mipmap_gen = enable;
     704           0 :         break;
     705           0 :     case OGL_TEX_ANISOTROPY:
     706           0 :         have_anistropy = enable;
     707           0 :         break;
     708           0 :     default:
     709           0 :         DEBUG_WARN_ERR(ERR::LOGIC); // invalid <what>
     710             :         break;
     711             :     }
     712           0 : }
     713             : 
     714             : 
     715             : // detect caps (via OpenGL extension list) and give an app_hook the chance to
     716             : // override this (e.g. via list of card/driver combos on which S3TC breaks).
     717             : // called once from the first ogl_tex_upload.
     718           0 : static void detect_gl_upload_caps()
     719             : {
     720             :     // note: gfx_card will be empty if running in quickstart mode;
     721             :     // in that case, we won't be able to check for known faulty cards.
     722             : 
     723             :     // detect features, but only change the variables if they were at
     724             :     // "undecided" (if overrides were set before this, they must remain).
     725           0 :     if(have_auto_mipmap_gen == -1)
     726             :     {
     727           0 :         have_auto_mipmap_gen = ogl_HaveExtension("GL_SGIS_generate_mipmap");
     728             :     }
     729           0 :     if(have_s3tc == -1)
     730             :     {
     731             : #if CONFIG2_GLES
     732             :         // some GLES implementations have GL_EXT_texture_compression_dxt1
     733             :         // but that only supports DXT1 so we can't use it.
     734             :         have_s3tc = ogl_HaveExtensions(0, "GL_EXT_texture_compression_s3tc", NULL) == 0;
     735             : #else
     736             :         // note: we don't bother checking for GL_S3_s3tc - it is incompatible
     737             :         // and irrelevant (was never widespread).
     738           0 :         have_s3tc = ogl_HaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", NULL) == 0;
     739             : #endif
     740             :     }
     741           0 :     if(have_anistropy == -1)
     742             :     {
     743           0 :         have_anistropy = ogl_HaveExtension("GL_EXT_texture_filter_anisotropic");
     744             :     }
     745             : 
     746             :     // allow app hook to make ogl_tex_override calls
     747           0 :     if(AH_IS_DEFINED(override_gl_upload_caps))
     748             :     {
     749           0 :         ah_override_gl_upload_caps();
     750             :     }
     751             :     // no app hook defined - have our own crack at blacklisting some hardware.
     752             :     else
     753             :     {
     754           0 :         const std::wstring cardName = gfx::CardName();
     755             :         // rationale: janwas's laptop's S3 card blows up if S3TC is used
     756             :         // (oh, the irony). it'd be annoying to have to share this between all
     757             :         // projects, hence this default implementation here.
     758           0 :         if(cardName == L"S3 SuperSavage/IXC 1014")
     759           0 :             ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE);
     760             :     }
     761           0 : }
     762             : 
     763             : 
     764             : // take care of mipmaps. if they are called for by <filter>, either
     765             : // arrange for OpenGL to create them, or see to it that the Tex object
     766             : // contains them (if need be, creating them in software).
     767             : // sets *plevels_to_skip to influence upload behavior (depending on
     768             : // whether mipmaps are needed and the quality settings).
     769             : // returns 0 to indicate success; otherwise, caller must disable
     770             : // mipmapping by switching filter to e.g. GL_LINEAR.
     771           0 : static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_skip)
     772             : {
     773             :     // decisions:
     774             :     // .. does filter call for uploading mipmaps?
     775           0 :     const bool need_mipmaps = are_mipmaps_needed(t->m_Width, t->m_Height, filter);
     776             :     // .. does the image data include mipmaps? (stored as separate
     777             :     //    images after the regular texels)
     778           0 :     const bool includes_mipmaps = (t->m_Flags & TEX_MIPMAPS) != 0;
     779             :     // .. is this texture in S3TC format? (more generally, "compressed")
     780           0 :     const bool is_s3tc = (t->m_Flags & TEX_DXT) != 0;
     781             : 
     782           0 :     *plevels_to_skip = TEX_BASE_LEVEL_ONLY;
     783           0 :     if(!need_mipmaps)
     784             :         return INFO::OK;
     785             : 
     786             :     // image already contains pregenerated mipmaps; we need do nothing.
     787             :     // this is the nicest case, because they are fastest to load
     788             :     // (no extra processing needed) and typically filtered better than
     789             :     // if automatically generated.
     790           0 :     if(includes_mipmaps)
     791           0 :         *plevels_to_skip = 0;   // t contains mipmaps
     792             :     // OpenGL supports automatic generation; we need only
     793             :     // activate that and upload the base image.
     794             : #if !CONFIG2_GLES
     795           0 :     else if(have_auto_mipmap_gen)
     796             :     {
     797             :         // note: we assume GL_GENERATE_MIPMAP and GL_GENERATE_MIPMAP_SGIS
     798             :         // have the same values - it's heavily implied by the spec
     799             :         // governing 'promoted' ARB extensions and just plain makes sense.
     800           0 :         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
     801             :     }
     802             : #endif
     803             :     // image is S3TC-compressed and the previous 2 alternatives weren't
     804             :     // available; we're going to cheat and just disable mipmapping.
     805             :     // rationale: having tex_transform add mipmaps would be slow (since
     806             :     // all<->all transforms aren't implemented, it'd have to decompress
     807             :     // from S3TC first), and DDS images ought to include mipmaps!
     808           0 :     else if(is_s3tc)
     809             :         return ERR::FAIL;   // NOWARN
     810             :     // image is uncompressed and we're on an old OpenGL implementation;
     811             :     // we will generate mipmaps in software.
     812             :     else
     813             :     {
     814           0 :         RETURN_STATUS_IF_ERR(t->transform_to(t->m_Flags|TEX_MIPMAPS));
     815           0 :         *plevels_to_skip = 0;   // t contains mipmaps
     816             :     }
     817             : 
     818             :     // t contains mipmaps; we can apply our resolution reduction trick:
     819           0 :     if(*plevels_to_skip == 0)
     820             :     {
     821             :         // if OpenGL's texture dimension limit is too small, use the
     822             :         // higher mipmap levels. NB: the minimum guaranteed size is
     823             :         // far too low, and menu background textures may be large.
     824           0 :         GLint w = (GLint)t->m_Width, h = (GLint)t->m_Height;
     825           0 :         while(w > ogl_max_tex_size || h > ogl_max_tex_size)
     826             :         {
     827           0 :             (*plevels_to_skip)++;
     828           0 :             w /= 2; h /= 2; // doesn't matter if either dimension drops to 0
     829             :         }
     830             : 
     831             :         // this saves texture memory by skipping some of the lower
     832             :         // (high-resolution) mip levels.
     833             :         //
     834             :         // note: we don't just use GL_TEXTURE_BASE_LEVEL because it would
     835             :         // require uploading unused levels, which is wasteful.
     836             :         // .. can be expanded to reduce to 1/4, 1/8 by encoding factor in q_flags.
     837           0 :         if(q_flags & OGL_TEX_HALF_RES)
     838           0 :             (*plevels_to_skip)++;
     839             :     }
     840             : 
     841             :     return INFO::OK;
     842             : }
     843             : 
     844             : 
     845             : // tex_util_foreach_mipmap callbacks: upload the given level to OpenGL.
     846             : 
     847             : struct UploadParams
     848             : {
     849             :     GLenum fmt;
     850             :     GLint int_fmt;
     851             :     u32* uploaded_size;
     852             : };
     853             : 
     854           0 : static void upload_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData)
     855             : {
     856           0 :     const UploadParams* up = (const UploadParams*)cbData;
     857           0 :     glTexImage2D(GL_TEXTURE_2D, (GLint)level, up->int_fmt, (GLsizei)level_w, (GLsizei)level_h, 0, up->fmt, GL_UNSIGNED_BYTE, level_data);
     858           0 :     *up->uploaded_size += (u32)level_data_size;
     859           0 : }
     860             : 
     861           0 : static void upload_compressed_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData)
     862             : {
     863           0 :     const UploadParams* up = (const UploadParams*)cbData;
     864           0 :     pglCompressedTexImage2DARB(GL_TEXTURE_2D, (GLint)level, up->fmt, (GLsizei)level_w, (GLsizei)level_h, 0, (GLsizei)level_data_size, level_data);
     865           0 :     *up->uploaded_size += (u32)level_data_size;
     866           0 : }
     867             : 
     868             : // upload the texture in the specified (internal) format.
     869             : // split out of ogl_tex_upload because it was too big.
     870             : //
     871             : // pre: <t> is valid for OpenGL use; texture is bound.
     872           0 : static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip, u32* uploaded_size)
     873             : {
     874           0 :     const GLsizei w  = (GLsizei)t->m_Width;
     875           0 :     const GLsizei h  = (GLsizei)t->m_Height;
     876           0 :     const size_t bpp   = t->m_Bpp;
     877           0 :     const u8* data = (const u8*)t->get_data();
     878           0 :     const UploadParams up = { fmt, int_fmt, uploaded_size };
     879             : 
     880           0 :     if(t->m_Flags & TEX_DXT)
     881           0 :         tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 4, upload_compressed_level, (void*)&up);
     882             :     else
     883           0 :         tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 1, upload_level, (void*)&up);
     884           0 : }
     885             : 
     886             : 
     887             : // upload the texture to OpenGL.
     888             : // if not 0, parameters override the following:
     889             : //   fmt_ovr     : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags;
     890             : //   q_flags_ovr : global default "quality vs. performance" flags;
     891             : //   int_fmt_ovr : internal format (e.g. GL_RGB8) decided from fmt / q_flags.
     892             : //
     893             : // side effects:
     894             : // - enables texturing on TMU 0 and binds the texture to it;
     895             : // - frees the texel data! see ogl_tex_get_data.
     896           0 : Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint int_fmt_ovr)
     897             : {
     898           0 :     ONCE(detect_gl_upload_caps());
     899             : 
     900           0 :     H_DEREF(ht, OglTex, ot);
     901           0 :     Tex* t = &ot->t;
     902           0 :     ENSURE(q_flags_valid(q_flags_ovr));
     903             :     // we don't bother verifying *fmt_ovr - there are too many values
     904             : 
     905             :     // upload already happened; no work to do.
     906             :     // (this also happens if a cached texture is "loaded")
     907           0 :     if(ot->flags & OT_IS_UPLOADED)
     908             :         return INFO::OK;
     909             : 
     910           0 :     if(ot->flags & OT_TEX_VALID)
     911             :     {
     912             :         // decompress S3TC if that's not supported by OpenGL.
     913           0 :         if((t->m_Flags & TEX_DXT) && !have_s3tc)
     914           0 :             (void)t->transform_to(t->m_Flags & ~TEX_DXT);
     915             : 
     916             :         // determine fmt and int_fmt, allowing for user override.
     917           0 :         ot->fmt = choose_fmt(t->m_Bpp, t->m_Flags);
     918           0 :         if(fmt_ovr) ot->fmt = fmt_ovr;
     919           0 :         if(q_flags_ovr) ot->q_flags = q_flags_ovr;
     920           0 :         ot->int_fmt = choose_int_fmt(ot->fmt, ot->q_flags);
     921           0 :         if(int_fmt_ovr) ot->int_fmt = int_fmt_ovr;
     922             : 
     923           0 :         ot->uploaded_size = 0;
     924             : 
     925             :         // now actually send to OpenGL:
     926           0 :         ogl_WarnIfError();
     927           0 :         {
     928             :             // (note: we know ht is valid due to H_DEREF, but ogl_tex_bind can
     929             :             // fail in debug builds if OglTex.id isn't a valid texture name)
     930           0 :             RETURN_STATUS_IF_ERR(ogl_tex_bind(ht, ot->tmu));
     931           0 :             int levels_to_skip;
     932           0 :             if(get_mipmaps(t, ot->state.filter, ot->q_flags, &levels_to_skip) < 0)
     933             :                 // error => disable mipmapping
     934           0 :                 ot->state.filter = GL_LINEAR;
     935             :             // (note: if first time, applies our defaults/previous overrides;
     936             :             // otherwise, replays all state changes)
     937           0 :             state_latch(&ot->state);
     938           0 :             upload_impl(t, ot->fmt, ot->int_fmt, levels_to_skip, &ot->uploaded_size);
     939             :         }
     940           0 :         ogl_WarnIfError();
     941             : 
     942           0 :         ot->flags |= OT_IS_UPLOADED;
     943             : 
     944             :         // see rationale for <refs> at declaration of OglTex.
     945           0 :         intptr_t refs = h_get_refcnt(ht);
     946           0 :         if(refs == 1)
     947             :         {
     948             :             // note: we verify above that OT_TEX_VALID is set
     949           0 :             ot->flags &= ~OT_TEX_VALID;
     950             :         }
     951             :     }
     952             : 
     953           0 :     ot->flags |= OT_NEED_AUTO_UPLOAD;
     954             : 
     955           0 :     return INFO::OK;
     956             : }
     957             : 
     958             : 
     959             : //----------------------------------------------------------------------------
     960             : // getters
     961             : //----------------------------------------------------------------------------
     962             : 
     963             : // retrieve texture dimensions and bits per pixel.
     964             : // all params are optional and filled if non-NULL.
     965           0 : Status ogl_tex_get_size(Handle ht, size_t* w, size_t* h, size_t* bpp)
     966             : {
     967           0 :     H_DEREF(ht, OglTex, ot);
     968             : 
     969           0 :     if(w)
     970           0 :         *w = ot->t.m_Width;
     971           0 :     if(h)
     972           0 :         *h = ot->t.m_Height;
     973           0 :     if(bpp)
     974           0 :         *bpp = ot->t.m_Bpp;
     975             :     return INFO::OK;
     976             : }
     977             : 
     978             : 
     979             : // retrieve TexFlags and the corresponding OpenGL format.
     980             : // the latter is determined during ogl_tex_upload and is 0 before that.
     981             : // all params are optional and filled if non-NULL.
     982           0 : Status ogl_tex_get_format(Handle ht, size_t* flags, GLenum* fmt)
     983             : {
     984           0 :     H_DEREF(ht, OglTex, ot);
     985             : 
     986           0 :     if(flags)
     987           0 :         *flags = ot->t.m_Flags;
     988           0 :     if(fmt)
     989             :     {
     990           0 :         ENSURE(ot->flags & OT_IS_UPLOADED);
     991           0 :         *fmt = ot->fmt;
     992             :     }
     993             :     return INFO::OK;
     994             : }
     995             : 
     996             : 
     997             : // retrieve pointer to texel data.
     998             : //
     999             : // note: this memory is freed after a successful ogl_tex_upload for
    1000             : // this texture. after that, the pointer we retrieve is NULL but
    1001             : // the function doesn't fail (negative return value) by design.
    1002             : // if you still need to get at the data, add a reference before
    1003             : // uploading it or read directly from OpenGL (discouraged).
    1004           0 : Status ogl_tex_get_data(Handle ht, u8** p)
    1005             : {
    1006           0 :     H_DEREF(ht, OglTex, ot);
    1007             : 
    1008           0 :     *p = ot->t.get_data();
    1009           0 :     return INFO::OK;
    1010             : }
    1011             : 
    1012           0 : Status ogl_tex_get_uploaded_size(Handle ht, size_t* size)
    1013             : {
    1014           0 :     H_DEREF(ht, OglTex, ot);
    1015             : 
    1016           0 :     *size = ot->uploaded_size;
    1017           0 :     return INFO::OK;
    1018             : }
    1019             : 
    1020             : // retrieve color of 1x1 mipmap level
    1021           0 : extern Status ogl_tex_get_average_color(Handle ht, u32* p)
    1022             : {
    1023           0 :     H_DEREF(ht, OglTex, ot);
    1024           0 :     warn_if_uploaded(ht, ot);
    1025             : 
    1026           0 :     *p = ot->t.get_average_color();
    1027           0 :     return INFO::OK;
    1028             : }
    1029             : 
    1030             : //----------------------------------------------------------------------------
    1031             : // misc API
    1032             : //----------------------------------------------------------------------------
    1033             : 
    1034             : // bind the texture to the specified unit [number] in preparation for
    1035             : // using it in rendering. if <ht> is 0, texturing is disabled instead.
    1036             : //
    1037             : // side effects:
    1038             : // - changes the active texture unit;
    1039             : // - (if return value is 0:) texturing was enabled/disabled on that unit.
    1040             : //
    1041             : // notes:
    1042             : // - assumes multitexturing is available.
    1043             : // - not necessary before calling ogl_tex_upload!
    1044             : // - on error, the unit's texture state is unchanged; see implementation.
    1045           0 : Status ogl_tex_bind(Handle ht, size_t unit)
    1046             : {
    1047             :     // note: there are many call sites of glActiveTextureARB, so caching
    1048             :     // those and ignoring redundant sets isn't feasible.
    1049           0 :     pglActiveTextureARB((int)(GL_TEXTURE0+unit));
    1050             : 
    1051             :     // special case: disable texturing
    1052           0 :     if(ht == 0)
    1053             :     {
    1054             : #if !CONFIG2_GLES
    1055           0 :         glDisable(GL_TEXTURE_2D);
    1056             : #endif
    1057           0 :         return INFO::OK;
    1058             :     }
    1059             : 
    1060             :     // if this fails, the texture unit's state remains unchanged.
    1061             :     // we don't bother catching that and disabling texturing because a
    1062             :     // debug warning is raised anyway, and it's quite unlikely.
    1063           0 :     H_DEREF(ht, OglTex, ot);
    1064           0 :     ot->tmu = (u8)unit;
    1065             : 
    1066             :     // if 0, there's a problem in the OglTex reload/dtor logic.
    1067             :     // binding it results in whiteness, which can have many causes;
    1068             :     // we therefore complain so this one can be ruled out.
    1069           0 :     ENSURE(ot->id != 0);
    1070             : 
    1071             : #if !CONFIG2_GLES
    1072           0 :     glEnable(GL_TEXTURE_2D);
    1073             : #endif
    1074           0 :     glBindTexture(GL_TEXTURE_2D, ot->id);
    1075           0 :     return INFO::OK;
    1076             : }
    1077             : 
    1078           0 : Status ogl_tex_get_texture_id(Handle ht, GLuint* id)
    1079             : {
    1080           0 :     *id = 0;
    1081           0 :     H_DEREF(ht, OglTex, ot);
    1082           0 :     *id = ot->id;
    1083           0 :     return INFO::OK;
    1084             : }
    1085             : 
    1086             : // apply the specified transforms (as in tex_transform) to the image.
    1087             : // must be called before uploading (raises a warning if called afterwards).
    1088           0 : Status ogl_tex_transform(Handle ht, size_t transforms)
    1089             : {
    1090           0 :     H_DEREF(ht, OglTex, ot);
    1091           0 :     Status ret = ot->t.transform(transforms);
    1092           0 :     return ret;
    1093             : }
    1094             : 
    1095             : 
    1096             : // change the pixel format to that specified by <new_flags>.
    1097             : // (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags).
    1098           0 : Status ogl_tex_transform_to(Handle ht, size_t new_flags)
    1099             : {
    1100           0 :     H_DEREF(ht, OglTex, ot);
    1101           0 :     Status ret = ot->t.transform_to(new_flags);
    1102           0 :     return ret;
    1103             : }
    1104             : 
    1105             : 
    1106             : // return whether native S3TC support is available
    1107           0 : bool ogl_tex_has_s3tc()
    1108             : {
    1109             :     // ogl_tex_upload must be called before this
    1110           0 :     ENSURE(have_s3tc != -1);
    1111             : 
    1112           0 :     return (have_s3tc != 0);
    1113             : }
    1114             : 
    1115             : // return whether anisotropic filtering support is available
    1116           0 : bool ogl_tex_has_anisotropy()
    1117             : {
    1118             :     // ogl_tex_upload must be called before this
    1119           0 :     ENSURE(have_anistropy != -1);
    1120             : 
    1121           0 :     return (have_anistropy != 0);
    1122             : }

Generated by: LCOV version 1.13