Home | History | Annotate | Download | only in gl
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 
      9 #include "gl/GrGLInterface.h"
     10 #include "gl/GrGLExtensions.h"
     11 #include "gl/GrGLUtil.h"
     12 
     13 #include <stdio.h>
     14 
     15 #if GR_GL_PER_GL_FUNC_CALLBACK
     16 namespace {
     17 void GrGLDefaultInterfaceCallback(const GrGLInterface*) {}
     18 }
     19 #endif
     20 
     21 const GrGLInterface* GrGLInterfaceAddTestDebugMarker(const GrGLInterface* interface,
     22                                                      GrGLInsertEventMarkerProc insertEventMarkerFn,
     23                                                      GrGLPushGroupMarkerProc pushGroupMarkerFn,
     24                                                      GrGLPopGroupMarkerProc popGroupMarkerFn) {
     25     GrGLInterface* newInterface = GrGLInterface::NewClone(interface);
     26 
     27     if (!newInterface->fExtensions.has("GL_EXT_debug_marker")) {
     28         newInterface->fExtensions.add("GL_EXT_debug_marker");
     29     }
     30 
     31     newInterface->fFunctions.fInsertEventMarker = insertEventMarkerFn;
     32     newInterface->fFunctions.fPushGroupMarker = pushGroupMarkerFn;
     33     newInterface->fFunctions.fPopGroupMarker = popGroupMarkerFn;
     34 
     35     return newInterface;
     36 }
     37 
     38 const GrGLInterface* GrGLInterfaceRemoveNVPR(const GrGLInterface* interface) {
     39     GrGLInterface* newInterface = GrGLInterface::NewClone(interface);
     40 
     41     newInterface->fExtensions.remove("GL_NV_path_rendering");
     42     newInterface->fFunctions.fPathCommands = NULL;
     43     newInterface->fFunctions.fPathCoords = NULL;
     44     newInterface->fFunctions.fPathParameteri = NULL;
     45     newInterface->fFunctions.fPathParameterf = NULL;
     46     newInterface->fFunctions.fGenPaths = NULL;
     47     newInterface->fFunctions.fDeletePaths = NULL;
     48     newInterface->fFunctions.fIsPath = NULL;
     49     newInterface->fFunctions.fPathStencilFunc = NULL;
     50     newInterface->fFunctions.fStencilFillPath = NULL;
     51     newInterface->fFunctions.fStencilStrokePath = NULL;
     52     newInterface->fFunctions.fStencilFillPathInstanced = NULL;
     53     newInterface->fFunctions.fStencilStrokePathInstanced = NULL;
     54     newInterface->fFunctions.fPathTexGen = NULL;
     55     newInterface->fFunctions.fCoverFillPath = NULL;
     56     newInterface->fFunctions.fCoverStrokePath = NULL;
     57     newInterface->fFunctions.fCoverFillPathInstanced = NULL;
     58     newInterface->fFunctions.fCoverStrokePathInstanced = NULL;
     59     newInterface->fFunctions.fStencilThenCoverFillPath = NULL;
     60     newInterface->fFunctions.fStencilThenCoverStrokePath = NULL;
     61     newInterface->fFunctions.fStencilThenCoverFillPathInstanced = NULL;
     62     newInterface->fFunctions.fStencilThenCoverStrokePathInstanced = NULL;
     63     newInterface->fFunctions.fProgramPathFragmentInputGen = NULL;
     64     newInterface->fFunctions.fPathMemoryGlyphIndexArray = NULL;
     65     return newInterface;
     66 }
     67 
     68 GrGLInterface::GrGLInterface() {
     69     fStandard = kNone_GrGLStandard;
     70 
     71 #if GR_GL_PER_GL_FUNC_CALLBACK
     72     fCallback = GrGLDefaultInterfaceCallback;
     73     fCallbackData = 0;
     74 #endif
     75 }
     76 
     77 GrGLInterface* GrGLInterface::NewClone(const GrGLInterface* interface) {
     78     SkASSERT(interface);
     79 
     80     GrGLInterface* clone = SkNEW(GrGLInterface);
     81     clone->fStandard = interface->fStandard;
     82     clone->fExtensions = interface->fExtensions;
     83     clone->fFunctions = interface->fFunctions;
     84 #if GR_GL_PER_GL_FUNC_CALLBACK
     85     clone->fCallback = interface->fCallback;
     86     clone->fCallbackData = interface->fCallbackData;
     87 #endif
     88     return clone;
     89 }
     90 
     91 #ifdef SK_DEBUG
     92     static int kIsDebug = 1;
     93 #else
     94     static int kIsDebug = 0;
     95 #endif
     96 
     97 #define RETURN_FALSE_INTERFACE                                                                   \
     98     if (kIsDebug) { SkDebugf("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); } \
     99     return false;
    100 
    101 bool GrGLInterface::validate() const {
    102 
    103     if (kNone_GrGLStandard == fStandard) {
    104         RETURN_FALSE_INTERFACE
    105     }
    106 
    107     if (!fExtensions.isInitialized()) {
    108         RETURN_FALSE_INTERFACE
    109     }
    110 
    111     // functions that are always required
    112     if (NULL == fFunctions.fActiveTexture ||
    113         NULL == fFunctions.fAttachShader ||
    114         NULL == fFunctions.fBindAttribLocation ||
    115         NULL == fFunctions.fBindBuffer ||
    116         NULL == fFunctions.fBindTexture ||
    117         NULL == fFunctions.fBlendFunc ||
    118         NULL == fFunctions.fBlendColor ||      // -> GL >= 1.4, ES >= 2.0 or extension
    119         NULL == fFunctions.fBufferData ||
    120         NULL == fFunctions.fBufferSubData ||
    121         NULL == fFunctions.fClear ||
    122         NULL == fFunctions.fClearColor ||
    123         NULL == fFunctions.fClearStencil ||
    124         NULL == fFunctions.fColorMask ||
    125         NULL == fFunctions.fCompileShader ||
    126         NULL == fFunctions.fCopyTexSubImage2D ||
    127         NULL == fFunctions.fCreateProgram ||
    128         NULL == fFunctions.fCreateShader ||
    129         NULL == fFunctions.fCullFace ||
    130         NULL == fFunctions.fDeleteBuffers ||
    131         NULL == fFunctions.fDeleteProgram ||
    132         NULL == fFunctions.fDeleteShader ||
    133         NULL == fFunctions.fDeleteTextures ||
    134         NULL == fFunctions.fDepthMask ||
    135         NULL == fFunctions.fDisable ||
    136         NULL == fFunctions.fDisableVertexAttribArray ||
    137         NULL == fFunctions.fDrawArrays ||
    138         NULL == fFunctions.fDrawElements ||
    139         NULL == fFunctions.fEnable ||
    140         NULL == fFunctions.fEnableVertexAttribArray ||
    141         NULL == fFunctions.fFrontFace ||
    142         NULL == fFunctions.fGenBuffers ||
    143         NULL == fFunctions.fGenTextures ||
    144         NULL == fFunctions.fGetBufferParameteriv ||
    145         NULL == fFunctions.fGenerateMipmap ||
    146         NULL == fFunctions.fGetError ||
    147         NULL == fFunctions.fGetIntegerv ||
    148         NULL == fFunctions.fGetProgramInfoLog ||
    149         NULL == fFunctions.fGetProgramiv ||
    150         NULL == fFunctions.fGetShaderInfoLog ||
    151         NULL == fFunctions.fGetShaderiv ||
    152         NULL == fFunctions.fGetString ||
    153         NULL == fFunctions.fGetUniformLocation ||
    154         NULL == fFunctions.fLinkProgram ||
    155         NULL == fFunctions.fLineWidth ||
    156         NULL == fFunctions.fPixelStorei ||
    157         NULL == fFunctions.fReadPixels ||
    158         NULL == fFunctions.fScissor ||
    159         NULL == fFunctions.fShaderSource ||
    160         NULL == fFunctions.fStencilFunc ||
    161         NULL == fFunctions.fStencilMask ||
    162         NULL == fFunctions.fStencilOp ||
    163         NULL == fFunctions.fTexImage2D ||
    164         NULL == fFunctions.fTexParameteri ||
    165         NULL == fFunctions.fTexParameteriv ||
    166         NULL == fFunctions.fTexSubImage2D ||
    167         NULL == fFunctions.fUniform1f ||
    168         NULL == fFunctions.fUniform1i ||
    169         NULL == fFunctions.fUniform1fv ||
    170         NULL == fFunctions.fUniform1iv ||
    171         NULL == fFunctions.fUniform2f ||
    172         NULL == fFunctions.fUniform2i ||
    173         NULL == fFunctions.fUniform2fv ||
    174         NULL == fFunctions.fUniform2iv ||
    175         NULL == fFunctions.fUniform3f ||
    176         NULL == fFunctions.fUniform3i ||
    177         NULL == fFunctions.fUniform3fv ||
    178         NULL == fFunctions.fUniform3iv ||
    179         NULL == fFunctions.fUniform4f ||
    180         NULL == fFunctions.fUniform4i ||
    181         NULL == fFunctions.fUniform4fv ||
    182         NULL == fFunctions.fUniform4iv ||
    183         NULL == fFunctions.fUniformMatrix2fv ||
    184         NULL == fFunctions.fUniformMatrix3fv ||
    185         NULL == fFunctions.fUniformMatrix4fv ||
    186         NULL == fFunctions.fUseProgram ||
    187         NULL == fFunctions.fVertexAttrib4fv ||
    188         NULL == fFunctions.fVertexAttribPointer ||
    189         NULL == fFunctions.fViewport ||
    190         NULL == fFunctions.fBindFramebuffer ||
    191         NULL == fFunctions.fBindRenderbuffer ||
    192         NULL == fFunctions.fCheckFramebufferStatus ||
    193         NULL == fFunctions.fDeleteFramebuffers ||
    194         NULL == fFunctions.fDeleteRenderbuffers ||
    195         NULL == fFunctions.fFinish ||
    196         NULL == fFunctions.fFlush ||
    197         NULL == fFunctions.fFramebufferRenderbuffer ||
    198         NULL == fFunctions.fFramebufferTexture2D ||
    199         NULL == fFunctions.fGetFramebufferAttachmentParameteriv ||
    200         NULL == fFunctions.fGetRenderbufferParameteriv ||
    201         NULL == fFunctions.fGenFramebuffers ||
    202         NULL == fFunctions.fGenRenderbuffers ||
    203         NULL == fFunctions.fRenderbufferStorage) {
    204         RETURN_FALSE_INTERFACE
    205     }
    206 
    207     GrGLVersion glVer = GrGLGetVersion(this);
    208     if (GR_GL_INVALID_VER == glVer) {
    209         RETURN_FALSE_INTERFACE
    210     }
    211 
    212     // Now check that baseline ES/Desktop fns not covered above are present
    213     // and that we have fn pointers for any advertised fExtensions that we will
    214     // try to use.
    215 
    216     // these functions are part of ES2, we assume they are available
    217     // On the desktop we assume they are available if the extension
    218     // is present or GL version is high enough.
    219     if (kGLES_GrGLStandard == fStandard) {
    220         if (NULL == fFunctions.fStencilFuncSeparate ||
    221             NULL == fFunctions.fStencilMaskSeparate ||
    222             NULL == fFunctions.fStencilOpSeparate) {
    223             RETURN_FALSE_INTERFACE
    224         }
    225     } else if (kGL_GrGLStandard == fStandard) {
    226 
    227         if (glVer >= GR_GL_VER(2,0)) {
    228             if (NULL == fFunctions.fStencilFuncSeparate ||
    229                 NULL == fFunctions.fStencilMaskSeparate ||
    230                 NULL == fFunctions.fStencilOpSeparate) {
    231                 RETURN_FALSE_INTERFACE
    232             }
    233         }
    234         if (glVer >= GR_GL_VER(3,0) && NULL == fFunctions.fBindFragDataLocation) {
    235             RETURN_FALSE_INTERFACE
    236         }
    237         if (glVer >= GR_GL_VER(2,0) || fExtensions.has("GL_ARB_draw_buffers")) {
    238             if (NULL == fFunctions.fDrawBuffers) {
    239                 RETURN_FALSE_INTERFACE
    240             }
    241         }
    242 
    243         if (glVer >= GR_GL_VER(1,5) || fExtensions.has("GL_ARB_occlusion_query")) {
    244             if (NULL == fFunctions.fGenQueries ||
    245                 NULL == fFunctions.fDeleteQueries ||
    246                 NULL == fFunctions.fBeginQuery ||
    247                 NULL == fFunctions.fEndQuery ||
    248                 NULL == fFunctions.fGetQueryiv ||
    249                 NULL == fFunctions.fGetQueryObjectiv ||
    250                 NULL == fFunctions.fGetQueryObjectuiv) {
    251                 RETURN_FALSE_INTERFACE
    252             }
    253         }
    254         if (glVer >= GR_GL_VER(3,3) ||
    255             fExtensions.has("GL_ARB_timer_query") ||
    256             fExtensions.has("GL_EXT_timer_query")) {
    257             if (NULL == fFunctions.fGetQueryObjecti64v ||
    258                 NULL == fFunctions.fGetQueryObjectui64v) {
    259                 RETURN_FALSE_INTERFACE
    260             }
    261         }
    262         if (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_timer_query")) {
    263             if (NULL == fFunctions.fQueryCounter) {
    264                 RETURN_FALSE_INTERFACE
    265             }
    266         }
    267     }
    268 
    269     // optional function on desktop before 1.3
    270     if (kGL_GrGLStandard != fStandard ||
    271         (glVer >= GR_GL_VER(1,3)) ||
    272         fExtensions.has("GL_ARB_texture_compression")) {
    273         if (NULL == fFunctions.fCompressedTexImage2D
    274 #if 0
    275             || NULL == fFunctions.fCompressedTexSubImage2D
    276 #endif
    277             ) {
    278             RETURN_FALSE_INTERFACE
    279         }
    280     }
    281 
    282     // part of desktop GL, but not ES
    283     if (kGL_GrGLStandard == fStandard &&
    284         (NULL == fFunctions.fGetTexLevelParameteriv ||
    285          NULL == fFunctions.fDrawBuffer ||
    286          NULL == fFunctions.fReadBuffer)) {
    287         RETURN_FALSE_INTERFACE
    288     }
    289 
    290     // GL_EXT_texture_storage is part of desktop 4.2
    291     // There is a desktop ARB extension and an ES+desktop EXT extension
    292     if (kGL_GrGLStandard == fStandard) {
    293         if (glVer >= GR_GL_VER(4,2) ||
    294             fExtensions.has("GL_ARB_texture_storage") ||
    295             fExtensions.has("GL_EXT_texture_storage")) {
    296             if (NULL == fFunctions.fTexStorage2D) {
    297                 RETURN_FALSE_INTERFACE
    298             }
    299         }
    300     } else if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_texture_storage")) {
    301         if (NULL == fFunctions.fTexStorage2D) {
    302             RETURN_FALSE_INTERFACE
    303         }
    304     }
    305 
    306     if (fExtensions.has("GL_EXT_discard_framebuffer")) {
    307 // FIXME: Remove this once Chromium is updated to provide this function
    308 #if 0
    309         if (NULL == fFunctions.fDiscardFramebuffer) {
    310             RETURN_FALSE_INTERFACE
    311         }
    312 #endif
    313     }
    314 
    315     // FBO MSAA
    316     if (kGL_GrGLStandard == fStandard) {
    317         // GL 3.0 and the ARB extension have multisample + blit
    318         if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_ARB_framebuffer_object")) {
    319             if (NULL == fFunctions.fRenderbufferStorageMultisample ||
    320                 NULL == fFunctions.fBlitFramebuffer) {
    321                 RETURN_FALSE_INTERFACE
    322             }
    323         } else {
    324             if (fExtensions.has("GL_EXT_framebuffer_blit") &&
    325                 NULL == fFunctions.fBlitFramebuffer) {
    326                 RETURN_FALSE_INTERFACE
    327             }
    328             if (fExtensions.has("GL_EXT_framebuffer_multisample") &&
    329                 NULL == fFunctions.fRenderbufferStorageMultisample) {
    330                 RETURN_FALSE_INTERFACE
    331             }
    332         }
    333     } else {
    334         if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_CHROMIUM_framebuffer_multisample")) {
    335             if (NULL == fFunctions.fRenderbufferStorageMultisample ||
    336                 NULL == fFunctions.fBlitFramebuffer) {
    337                 RETURN_FALSE_INTERFACE
    338             }
    339         }
    340         if (fExtensions.has("GL_APPLE_framebuffer_multisample")) {
    341             if (NULL == fFunctions.fRenderbufferStorageMultisampleES2APPLE ||
    342                 NULL == fFunctions.fResolveMultisampleFramebuffer) {
    343                 RETURN_FALSE_INTERFACE
    344             }
    345         }
    346         if (fExtensions.has("GL_IMG_multisampled_render_to_texture") ||
    347             fExtensions.has("GL_EXT_multisampled_render_to_texture")) {
    348             if (NULL == fFunctions.fRenderbufferStorageMultisampleES2EXT ||
    349                 NULL == fFunctions.fFramebufferTexture2DMultisample) {
    350                 RETURN_FALSE_INTERFACE
    351             }
    352         }
    353     }
    354 
    355     // On ES buffer mapping is an extension. On Desktop
    356     // buffer mapping was part of original VBO extension
    357     // which we require.
    358     if (kGL_GrGLStandard == fStandard || fExtensions.has("GL_OES_mapbuffer")) {
    359         if (NULL == fFunctions.fMapBuffer ||
    360             NULL == fFunctions.fUnmapBuffer) {
    361             RETURN_FALSE_INTERFACE
    362         }
    363     }
    364 
    365     // Dual source blending
    366     if (kGL_GrGLStandard == fStandard &&
    367         (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_blend_func_extended"))) {
    368         if (NULL == fFunctions.fBindFragDataLocationIndexed) {
    369             RETURN_FALSE_INTERFACE
    370         }
    371     }
    372 
    373     // glGetStringi was added in version 3.0 of both desktop and ES.
    374     if (glVer >= GR_GL_VER(3, 0)) {
    375         if (NULL == fFunctions.fGetStringi) {
    376             RETURN_FALSE_INTERFACE
    377         }
    378     }
    379 
    380     if (kGL_GrGLStandard == fStandard) {
    381         if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_ARB_vertex_array_object")) {
    382             if (NULL == fFunctions.fBindVertexArray ||
    383                 NULL == fFunctions.fDeleteVertexArrays ||
    384                 NULL == fFunctions.fGenVertexArrays) {
    385                 RETURN_FALSE_INTERFACE
    386             }
    387         }
    388     } else {
    389         if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_OES_vertex_array_object")) {
    390             if (NULL == fFunctions.fBindVertexArray ||
    391                 NULL == fFunctions.fDeleteVertexArrays ||
    392                 NULL == fFunctions.fGenVertexArrays) {
    393                 RETURN_FALSE_INTERFACE
    394             }
    395         }
    396     }
    397 
    398     if (fExtensions.has("GL_EXT_debug_marker")) {
    399         if (NULL == fFunctions.fInsertEventMarker ||
    400             NULL == fFunctions.fPushGroupMarker ||
    401             NULL == fFunctions.fPopGroupMarker) {
    402             RETURN_FALSE_INTERFACE
    403         }
    404     }
    405 
    406     if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,3)) ||
    407         fExtensions.has("GL_ARB_invalidate_subdata")) {
    408         if (NULL == fFunctions.fInvalidateBufferData ||
    409             NULL == fFunctions.fInvalidateBufferSubData ||
    410             NULL == fFunctions.fInvalidateFramebuffer ||
    411             NULL == fFunctions.fInvalidateSubFramebuffer ||
    412             NULL == fFunctions.fInvalidateTexImage ||
    413             NULL == fFunctions.fInvalidateTexSubImage) {
    414             RETURN_FALSE_INTERFACE;
    415         }
    416     } else if (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0)) {
    417         // ES 3.0 adds the framebuffer functions but not the others.
    418         if (NULL == fFunctions.fInvalidateFramebuffer ||
    419             NULL == fFunctions.fInvalidateSubFramebuffer) {
    420             RETURN_FALSE_INTERFACE;
    421         }
    422     }
    423 
    424     if (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_CHROMIUM_map_sub")) {
    425         if (NULL == fFunctions.fMapBufferSubData ||
    426             NULL == fFunctions.fMapTexSubImage2D ||
    427             NULL == fFunctions.fUnmapBufferSubData ||
    428             NULL == fFunctions.fUnmapTexSubImage2D) {
    429             RETURN_FALSE_INTERFACE;
    430         }
    431     }
    432 
    433     // These functions are added to the 3.0 version of both GLES and GL.
    434     if (glVer >= GR_GL_VER(3,0) ||
    435         (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_EXT_map_buffer_range")) ||
    436         (kGL_GrGLStandard == fStandard && fExtensions.has("GL_ARB_map_buffer_range"))) {
    437         if (NULL == fFunctions.fMapBufferRange ||
    438             NULL == fFunctions.fFlushMappedBufferRange) {
    439             RETURN_FALSE_INTERFACE;
    440         }
    441     }
    442 
    443     if ((kGL_GrGLStandard == fStandard && fExtensions.has("GL_EXT_direct_state_access")) ||
    444         (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_NV_path_rendering"))) {
    445         if (NULL == fFunctions.fMatrixLoadf ||
    446             NULL == fFunctions.fMatrixLoadIdentity) {
    447             RETURN_FALSE_INTERFACE
    448         }
    449     }
    450 
    451     if ((kGL_GrGLStandard == fStandard &&
    452          (glVer >= GR_GL_VER(4,3) || fExtensions.has("GL_ARB_program_interface_query"))) ||
    453         (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,1))) {
    454         if (NULL == fFunctions.fGetProgramResourceLocation) {
    455             RETURN_FALSE_INTERFACE
    456         }
    457     }
    458 
    459     if (fExtensions.has("GL_NV_path_rendering")) {
    460         if (NULL == fFunctions.fPathCommands ||
    461             NULL == fFunctions.fPathCoords ||
    462             NULL == fFunctions.fPathParameteri ||
    463             NULL == fFunctions.fPathParameterf ||
    464             NULL == fFunctions.fGenPaths ||
    465             NULL == fFunctions.fDeletePaths ||
    466             NULL == fFunctions.fIsPath ||
    467             NULL == fFunctions.fPathStencilFunc ||
    468             NULL == fFunctions.fStencilFillPath ||
    469             NULL == fFunctions.fStencilStrokePath ||
    470             NULL == fFunctions.fStencilFillPathInstanced ||
    471             NULL == fFunctions.fStencilStrokePathInstanced ||
    472             NULL == fFunctions.fCoverFillPath ||
    473             NULL == fFunctions.fCoverStrokePath ||
    474             NULL == fFunctions.fCoverFillPathInstanced ||
    475             NULL == fFunctions.fCoverStrokePathInstanced) {
    476             RETURN_FALSE_INTERFACE
    477         }
    478         if (kGL_GrGLStandard == fStandard) {
    479             // Some methods only exist on desktop
    480             if (NULL == fFunctions.fPathTexGen) {
    481                 RETURN_FALSE_INTERFACE
    482             }
    483         } else {
    484             // All additions through v1.3 exist on GLES
    485             if (NULL == fFunctions.fStencilThenCoverFillPath ||
    486                 NULL == fFunctions.fStencilThenCoverStrokePath ||
    487                 NULL == fFunctions.fStencilThenCoverFillPathInstanced ||
    488                 NULL == fFunctions.fStencilThenCoverStrokePathInstanced ||
    489                 NULL == fFunctions.fProgramPathFragmentInputGen ||
    490                 NULL == fFunctions.fPathMemoryGlyphIndexArray) {
    491                 RETURN_FALSE_INTERFACE
    492             }
    493         }
    494     }
    495 
    496     return true;
    497 }
    498