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 "GrGLUtil.h"
     10 #include "GrTypesPriv.h"
     11 #include "SkMatrix.h"
     12 #include <stdio.h>
     13 
     14 void GrGLClearErr(const GrGLInterface* gl) {
     15     while (GR_GL_NO_ERROR != gl->fFunctions.fGetError()) {}
     16 }
     17 
     18 namespace {
     19 const char *get_error_string(uint32_t err) {
     20     switch (err) {
     21     case GR_GL_NO_ERROR:
     22         return "";
     23     case GR_GL_INVALID_ENUM:
     24         return "Invalid Enum";
     25     case GR_GL_INVALID_VALUE:
     26         return "Invalid Value";
     27     case GR_GL_INVALID_OPERATION:
     28         return "Invalid Operation";
     29     case GR_GL_OUT_OF_MEMORY:
     30         return "Out of Memory";
     31     case GR_GL_CONTEXT_LOST:
     32         return "Context Lost";
     33     }
     34     return "Unknown";
     35 }
     36 }
     37 
     38 void GrGLCheckErr(const GrGLInterface* gl,
     39                   const char* location,
     40                   const char* call) {
     41     uint32_t err = GR_GL_GET_ERROR(gl);
     42     if (GR_GL_NO_ERROR != err) {
     43         SkDebugf("---- glGetError 0x%x(%s)", err, get_error_string(err));
     44         if (location) {
     45             SkDebugf(" at\n\t%s", location);
     46         }
     47         if (call) {
     48             SkDebugf("\n\t\t%s", call);
     49         }
     50         SkDebugf("\n");
     51     }
     52 }
     53 
     54 ///////////////////////////////////////////////////////////////////////////////
     55 
     56 #if GR_GL_LOG_CALLS
     57     bool gLogCallsGL = !!(GR_GL_LOG_CALLS_START);
     58 #endif
     59 
     60 #if GR_GL_CHECK_ERROR
     61     bool gCheckErrorGL = !!(GR_GL_CHECK_ERROR_START);
     62 #endif
     63 
     64 ///////////////////////////////////////////////////////////////////////////////
     65 
     66 GrGLStandard GrGLGetStandardInUseFromString(const char* versionString) {
     67     if (nullptr == versionString) {
     68         SkDebugf("nullptr GL version string.");
     69         return kNone_GrGLStandard;
     70     }
     71 
     72     int major, minor;
     73 
     74     // check for desktop
     75     int n = sscanf(versionString, "%d.%d", &major, &minor);
     76     if (2 == n) {
     77         return kGL_GrGLStandard;
     78     }
     79 
     80     // check for ES 1
     81     char profile[2];
     82     n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1, &major, &minor);
     83     if (4 == n) {
     84         // we no longer support ES1.
     85         return kNone_GrGLStandard;
     86     }
     87 
     88     // check for ES2
     89     n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
     90     if (2 == n) {
     91         return kGLES_GrGLStandard;
     92     }
     93     return kNone_GrGLStandard;
     94 }
     95 
     96 void GrGLGetDriverInfo(GrGLStandard standard,
     97                        GrGLVendor vendor,
     98                        const char* rendererString,
     99                        const char* versionString,
    100                        GrGLDriver* outDriver,
    101                        GrGLDriverVersion* outVersion) {
    102     int major, minor, rev, driverMajor, driverMinor, driverPoint;
    103 
    104     *outDriver = kUnknown_GrGLDriver;
    105     *outVersion = GR_GL_DRIVER_UNKNOWN_VER;
    106     // These null checks are for test GL contexts that return nullptr in their
    107     // glGetString implementation.
    108     if (!rendererString) {
    109         rendererString = "";
    110     }
    111     if (!versionString) {
    112         versionString = "";
    113     }
    114 
    115     static const char kChromium[] = "Chromium";
    116     char suffix[SK_ARRAY_COUNT(kChromium)];
    117     if (0 == strcmp(rendererString, kChromium) ||
    118         (3 == sscanf(versionString, "OpenGL ES %d.%d %8s", &major, &minor, suffix) &&
    119          0 == strcmp(kChromium, suffix))) {
    120         *outDriver = kChromium_GrGLDriver;
    121         return;
    122     }
    123 
    124     if (standard == kGL_GrGLStandard) {
    125         if (kNVIDIA_GrGLVendor == vendor) {
    126             *outDriver = kNVIDIA_GrGLDriver;
    127             int n = sscanf(versionString, "%d.%d.%d NVIDIA %d.%d",
    128                            &major, &minor, &rev, &driverMajor, &driverMinor);
    129             // Some older NVIDIA drivers don't report the driver version.
    130             if (5 == n) {
    131                 *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    132             }
    133             return;
    134         }
    135         int n = sscanf(versionString, "%d.%d Mesa %d.%d",
    136                        &major, &minor, &driverMajor, &driverMinor);
    137         if (4 != n) {
    138             n = sscanf(versionString, "%d.%d (Core Profile) Mesa %d.%d",
    139                        &major, &minor, &driverMajor, &driverMinor);
    140         }
    141         if (4 == n) {
    142             *outDriver = kMesa_GrGLDriver;
    143             *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    144             return;
    145         }
    146     }
    147     else {
    148         if (kNVIDIA_GrGLVendor == vendor) {
    149             *outDriver = kNVIDIA_GrGLDriver;
    150             int n = sscanf(versionString, "OpenGL ES %d.%d NVIDIA %d.%d",
    151                            &major, &minor, &driverMajor, &driverMinor);
    152             // Some older NVIDIA drivers don't report the driver version.
    153             if (4 == n) {
    154                 *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    155             }
    156             return;
    157         }
    158 
    159         int n = sscanf(versionString, "OpenGL ES %d.%d Mesa %d.%d",
    160                        &major, &minor, &driverMajor, &driverMinor);
    161         if (4 == n) {
    162             *outDriver = kMesa_GrGLDriver;
    163             *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    164             return;
    165         }
    166         if (0 == strncmp("ANGLE", rendererString, 5)) {
    167             *outDriver = kANGLE_GrGLDriver;
    168             n = sscanf(versionString, "OpenGL ES %d.%d (ANGLE %d.%d", &major, &minor, &driverMajor,
    169                                                                       &driverMinor);
    170             if (4 == n) {
    171                 *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    172             }
    173             return;
    174         }
    175     }
    176 
    177     if (kGoogle_GrGLVendor == vendor) {
    178         // Swiftshader is the only Google vendor at the moment
    179         *outDriver = kSwiftShader_GrGLDriver;
    180 
    181         // Swiftshader has a strange version string: w.x.y.z  Going to arbitrarily ignore
    182         // y and assume w,x and z are major, minor, point.
    183         // As of writing, version is 4.0.0.6
    184         int n = sscanf(versionString, "OpenGL ES %d.%d SwiftShader %d.%d.0.%d", &major, &minor,
    185                        &driverMajor, &driverMinor, &driverPoint);
    186         if (5 == n) {
    187             *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, driverPoint);
    188         }
    189         return;
    190     }
    191 
    192     if (kIntel_GrGLVendor == vendor) {
    193         // We presume we're on the Intel driver since it hasn't identified itself as Mesa.
    194         *outDriver = kIntel_GrGLDriver;
    195 
    196         //This is how the macOS version strings are structured. This might be different on different
    197         // OSes.
    198         int n = sscanf(versionString, "%d.%d INTEL-%d.%d.%d", &major, &minor, &driverMajor,
    199                        &driverMinor, &driverPoint);
    200         if (5 == n) {
    201             *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, driverPoint);
    202         }
    203     }
    204 
    205     if (kQualcomm_GrGLVendor == vendor) {
    206         *outDriver = kQualcomm_GrGLDriver;
    207         int n = sscanf(versionString, "OpenGL ES %d.%d V@%d.%d", &major, &minor, &driverMajor,
    208                        &driverMinor);
    209         if (4 == n) {
    210             *outVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0);
    211         }
    212         return;
    213     }
    214 }
    215 
    216 GrGLVersion GrGLGetVersionFromString(const char* versionString) {
    217     if (nullptr == versionString) {
    218         SkDebugf("nullptr GL version string.");
    219         return GR_GL_INVALID_VER;
    220     }
    221 
    222     int major, minor;
    223 
    224     // check for mesa
    225     int mesaMajor, mesaMinor;
    226     int n = sscanf(versionString, "%d.%d Mesa %d.%d", &major, &minor, &mesaMajor, &mesaMinor);
    227     if (4 == n) {
    228         return GR_GL_VER(major, minor);
    229     }
    230 
    231     n = sscanf(versionString, "%d.%d", &major, &minor);
    232     if (2 == n) {
    233         return GR_GL_VER(major, minor);
    234     }
    235 
    236     char profile[2];
    237     n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
    238                &major, &minor);
    239     if (4 == n) {
    240         return GR_GL_VER(major, minor);
    241     }
    242 
    243     n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
    244     if (2 == n) {
    245         return GR_GL_VER(major, minor);
    246     }
    247 
    248     return GR_GL_INVALID_VER;
    249 }
    250 
    251 GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString) {
    252     if (nullptr == versionString) {
    253         SkDebugf("nullptr GLSL version string.");
    254         return GR_GLSL_INVALID_VER;
    255     }
    256 
    257     int major, minor;
    258 
    259     int n = sscanf(versionString, "%d.%d", &major, &minor);
    260     if (2 == n) {
    261         return GR_GLSL_VER(major, minor);
    262     }
    263 
    264     n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor);
    265     if (2 == n) {
    266         return GR_GLSL_VER(major, minor);
    267     }
    268 
    269 #ifdef SK_BUILD_FOR_ANDROID
    270     // android hack until the gpu vender updates their drivers
    271     n = sscanf(versionString, "OpenGL ES GLSL %d.%d", &major, &minor);
    272     if (2 == n) {
    273         return GR_GLSL_VER(major, minor);
    274     }
    275 #endif
    276 
    277     return GR_GLSL_INVALID_VER;
    278 }
    279 
    280 GrGLVendor GrGLGetVendorFromString(const char* vendorString) {
    281     if (vendorString) {
    282         if (0 == strcmp(vendorString, "ARM")) {
    283             return kARM_GrGLVendor;
    284         }
    285         if (0 == strcmp(vendorString, "Google Inc.")) {
    286             return kGoogle_GrGLVendor;
    287         }
    288         if (0 == strcmp(vendorString, "Imagination Technologies")) {
    289             return kImagination_GrGLVendor;
    290         }
    291         if (0 == strncmp(vendorString, "Intel ", 6) || 0 == strcmp(vendorString, "Intel")) {
    292             return kIntel_GrGLVendor;
    293         }
    294         if (0 == strcmp(vendorString, "Qualcomm")) {
    295             return kQualcomm_GrGLVendor;
    296         }
    297         if (0 == strcmp(vendorString, "NVIDIA Corporation")) {
    298             return kNVIDIA_GrGLVendor;
    299         }
    300         if (0 == strcmp(vendorString, "ATI Technologies Inc.")) {
    301             return kATI_GrGLVendor;
    302         }
    303     }
    304     return kOther_GrGLVendor;
    305 }
    306 
    307 static bool is_renderer_angle(const char* rendererString) {
    308     static constexpr char kHeader[] = "ANGLE ";
    309     static constexpr size_t kHeaderLength = SK_ARRAY_COUNT(kHeader) - 1;
    310     return rendererString && 0 == strncmp(rendererString, kHeader, kHeaderLength);
    311 }
    312 
    313 GrGLRenderer GrGLGetRendererFromStrings(const char* rendererString,
    314                                         const GrGLExtensions& extensions) {
    315     if (rendererString) {
    316         static const char kTegraStr[] = "NVIDIA Tegra";
    317         if (0 == strncmp(rendererString, kTegraStr, SK_ARRAY_COUNT(kTegraStr) - 1)) {
    318             // Tegra strings are not very descriptive. We distinguish between the modern and legacy
    319             // architectures by the presence of NV_path_rendering.
    320             return extensions.has("GL_NV_path_rendering") ? kTegra_GrGLRenderer
    321                                                           : kTegra_PreK1_GrGLRenderer;
    322         }
    323         int lastDigit;
    324         int n = sscanf(rendererString, "PowerVR SGX 54%d", &lastDigit);
    325         if (1 == n && lastDigit >= 0 && lastDigit <= 9) {
    326             return kPowerVR54x_GrGLRenderer;
    327         }
    328         // certain iOS devices also use PowerVR54x GPUs
    329         static const char kAppleA4Str[] = "Apple A4";
    330         static const char kAppleA5Str[] = "Apple A5";
    331         static const char kAppleA6Str[] = "Apple A6";
    332         if (0 == strncmp(rendererString, kAppleA4Str,
    333                          SK_ARRAY_COUNT(kAppleA4Str)-1) ||
    334             0 == strncmp(rendererString, kAppleA5Str,
    335                          SK_ARRAY_COUNT(kAppleA5Str)-1) ||
    336             0 == strncmp(rendererString, kAppleA6Str,
    337                          SK_ARRAY_COUNT(kAppleA6Str)-1)) {
    338             return kPowerVR54x_GrGLRenderer;
    339         }
    340         static const char kPowerVRRogueStr[] = "PowerVR Rogue";
    341         static const char kAppleA7Str[] = "Apple A7";
    342         static const char kAppleA8Str[] = "Apple A8";
    343         if (0 == strncmp(rendererString, kPowerVRRogueStr,
    344                          SK_ARRAY_COUNT(kPowerVRRogueStr)-1) ||
    345             0 == strncmp(rendererString, kAppleA7Str,
    346                          SK_ARRAY_COUNT(kAppleA7Str)-1) ||
    347             0 == strncmp(rendererString, kAppleA8Str,
    348                          SK_ARRAY_COUNT(kAppleA8Str)-1)) {
    349             return kPowerVRRogue_GrGLRenderer;
    350         }
    351         int adrenoNumber;
    352         n = sscanf(rendererString, "Adreno (TM) %d", &adrenoNumber);
    353         if (1 == n) {
    354             if (adrenoNumber >= 300) {
    355                 if (adrenoNumber < 400) {
    356                     return kAdreno3xx_GrGLRenderer;
    357                 }
    358                 if (adrenoNumber < 500) {
    359                     return adrenoNumber >= 430
    360                             ? kAdreno430_GrGLRenderer : kAdreno4xx_other_GrGLRenderer;
    361                 }
    362                 if (adrenoNumber < 600) {
    363                     return kAdreno5xx_GrGLRenderer;
    364                 }
    365             }
    366         }
    367         if (0 == strcmp("Google SwiftShader", rendererString)) {
    368             return kGoogleSwiftShader_GrGLRenderer;
    369         }
    370 
    371         if (const char* intelString = strstr(rendererString, "Intel")) {
    372             if (0 == strcmp("Intel Iris Pro OpenGL Engine", intelString)) {
    373                 return kIntelIrisPro_GrGLRenderer;
    374             }
    375             if (strstr(intelString, "Sandybridge")) {
    376                 return kIntelSandyBridge_GrGLRenderer;
    377             }
    378             if (strstr(intelString, "Bay Trail")) {
    379                 return kIntelBayTrail_GrGLRenderer;
    380             }
    381             int intelNumber;
    382             if (sscanf(intelString, "Intel(R) Iris(TM) Graphics %d", &intelNumber) ||
    383                 sscanf(intelString, "Intel(R) Iris(TM) Pro Graphics %d", &intelNumber) ||
    384                 sscanf(intelString, "Intel(R) Iris(TM) Pro Graphics P%d", &intelNumber) ||
    385                 sscanf(intelString, "Intel(R) Iris(R) Graphics %d", &intelNumber) ||
    386                 sscanf(intelString, "Intel(R) Iris(R) Pro Graphics %d", &intelNumber) ||
    387                 sscanf(intelString, "Intel(R) Iris(R) Pro Graphics P%d", &intelNumber) ||
    388                 sscanf(intelString, "Intel(R) HD Graphics %d", &intelNumber) ||
    389                 sscanf(intelString, "Intel(R) HD Graphics P%d", &intelNumber)) {
    390 
    391                 if (intelNumber >= 4000 && intelNumber < 5000) {
    392                     return kIntel4xxx_GrGLRenderer;
    393                 }
    394                 if (intelNumber >= 6000 && intelNumber < 7000) {
    395                     return kIntel6xxx_GrGLRenderer;
    396                 }
    397                 if (intelNumber >= 2000 && intelNumber < 4000) {
    398                     return kIntelSandyBridge_GrGLRenderer;
    399                 }
    400                 if (intelNumber >= 500 && intelNumber < 600) {
    401                     return kIntelSkylake_GrGLRenderer;
    402                 }
    403             }
    404         }
    405 
    406         // The AMD string can have a somewhat arbitrary preamble (see skbug.com/7195)
    407         if (const char* amdString = strstr(rendererString, "Radeon")) {
    408             char amdGeneration, amdTier, amdRevision;
    409             n = sscanf(amdString, "Radeon (TM) R9 M%c%c%c",
    410                                        &amdGeneration, &amdTier, &amdRevision);
    411             if (3 == n) {
    412                 if ('4' == amdGeneration) {
    413                     return kAMDRadeonR9M4xx_GrGLRenderer;
    414                 }
    415             }
    416 
    417             char amd0, amd1, amd2;
    418             n = sscanf(amdString, "Radeon HD 7%c%c%c Series", &amd0, &amd1, &amd2);
    419             if (3 == n) {
    420                 return kAMDRadeonHD7xxx_GrGLRenderer;
    421             }
    422         }
    423 
    424         if (0 == strcmp("Mesa Offscreen", rendererString)) {
    425             return kOSMesa_GrGLRenderer;
    426         }
    427         if (strstr(rendererString, "llvmpipe")) {
    428             return kGalliumLLVM_GrGLRenderer;
    429         }
    430         static const char kMaliTStr[] = "Mali-T";
    431         if (0 == strncmp(rendererString, kMaliTStr, SK_ARRAY_COUNT(kMaliTStr) - 1)) {
    432             return kMaliT_GrGLRenderer;
    433         }
    434         int mali400Num;
    435         if (1 == sscanf(rendererString, "Mali-%d", &mali400Num) && mali400Num >= 400 &&
    436             mali400Num < 500) {
    437             return kMali4xx_GrGLRenderer;
    438         }
    439         if (is_renderer_angle(rendererString)) {
    440             return kANGLE_GrGLRenderer;
    441         }
    442     }
    443     return kOther_GrGLRenderer;
    444 }
    445 
    446 void GrGLGetANGLEInfoFromString(const char* rendererString, GrGLANGLEBackend* backend,
    447                                 GrGLANGLEVendor* vendor, GrGLANGLERenderer* renderer) {
    448     *backend = GrGLANGLEBackend::kUnknown;
    449     *vendor = GrGLANGLEVendor::kUnknown;
    450     *renderer = GrGLANGLERenderer::kUnknown;
    451     if (!is_renderer_angle(rendererString)) {
    452         return;
    453     }
    454     if (strstr(rendererString, "Intel")) {
    455         *vendor = GrGLANGLEVendor::kIntel;
    456 
    457         const char* modelStr;
    458         int modelNumber;
    459         if ((modelStr = strstr(rendererString, "HD Graphics")) &&
    460             (1 == sscanf(modelStr, "HD Graphics %i", &modelNumber) ||
    461              1 == sscanf(modelStr, "HD Graphics P%i", &modelNumber))) {
    462             switch (modelNumber) {
    463                 case 2000:
    464                 case 3000:
    465                     *renderer = GrGLANGLERenderer::kSandyBridge;
    466                     break;
    467                 case 4000:
    468                 case 2500:
    469                     *renderer = GrGLANGLERenderer::kIvyBridge;
    470                     break;
    471                 case 510:
    472                 case 515:
    473                 case 520:
    474                 case 530:
    475                     *renderer = GrGLANGLERenderer::kSkylake;
    476                     break;
    477             }
    478         } else if ((modelStr = strstr(rendererString, "Iris")) &&
    479                    (1 == sscanf(modelStr, "Iris(TM) Graphics %i", &modelNumber) ||
    480                     1 == sscanf(modelStr, "Iris(TM) Pro Graphics %i", &modelNumber) ||
    481                     1 == sscanf(modelStr, "Iris(TM) Pro Graphics P%i", &modelNumber))) {
    482             switch (modelNumber) {
    483                 case 540:
    484                 case 550:
    485                 case 555:
    486                 case 580:
    487                     *renderer = GrGLANGLERenderer::kSkylake;
    488                     break;
    489             }
    490         }
    491     }
    492     if (strstr(rendererString, "Direct3D11")) {
    493         *backend = GrGLANGLEBackend::kD3D11;
    494     } else if (strstr(rendererString, "Direct3D9")) {
    495         *backend = GrGLANGLEBackend::kD3D9;
    496     } else if (strstr(rendererString, "OpenGL")) {
    497         *backend = GrGLANGLEBackend::kOpenGL;
    498     }
    499 }
    500 
    501 GrGLVersion GrGLGetVersion(const GrGLInterface* gl) {
    502     const GrGLubyte* v;
    503     GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
    504     return GrGLGetVersionFromString((const char*) v);
    505 }
    506 
    507 GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface* gl) {
    508     const GrGLubyte* v;
    509     GR_GL_CALL_RET(gl, v, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
    510     return GrGLGetGLSLVersionFromString((const char*) v);
    511 }
    512 
    513 GrGLVendor GrGLGetVendor(const GrGLInterface* gl) {
    514     const GrGLubyte* v;
    515     GR_GL_CALL_RET(gl, v, GetString(GR_GL_VENDOR));
    516     return GrGLGetVendorFromString((const char*) v);
    517 }
    518 
    519 GrGLRenderer GrGLGetRenderer(const GrGLInterface* gl) {
    520     const GrGLubyte* rendererString;
    521     GR_GL_CALL_RET(gl, rendererString, GetString(GR_GL_RENDERER));
    522 
    523     return GrGLGetRendererFromStrings((const char*)rendererString, gl->fExtensions);
    524 }
    525 
    526 GrGLenum GrToGLStencilFunc(GrStencilTest test) {
    527     static const GrGLenum gTable[kGrStencilTestCount] = {
    528         GR_GL_ALWAYS,           // kAlways
    529         GR_GL_NEVER,            // kNever
    530         GR_GL_GREATER,          // kGreater
    531         GR_GL_GEQUAL,           // kGEqual
    532         GR_GL_LESS,             // kLess
    533         GR_GL_LEQUAL,           // kLEqual
    534         GR_GL_EQUAL,            // kEqual
    535         GR_GL_NOTEQUAL,         // kNotEqual
    536     };
    537     GR_STATIC_ASSERT(0 == (int)GrStencilTest::kAlways);
    538     GR_STATIC_ASSERT(1 == (int)GrStencilTest::kNever);
    539     GR_STATIC_ASSERT(2 == (int)GrStencilTest::kGreater);
    540     GR_STATIC_ASSERT(3 == (int)GrStencilTest::kGEqual);
    541     GR_STATIC_ASSERT(4 == (int)GrStencilTest::kLess);
    542     GR_STATIC_ASSERT(5 == (int)GrStencilTest::kLEqual);
    543     GR_STATIC_ASSERT(6 == (int)GrStencilTest::kEqual);
    544     GR_STATIC_ASSERT(7 == (int)GrStencilTest::kNotEqual);
    545     SkASSERT(test < (GrStencilTest)kGrStencilTestCount);
    546 
    547     return gTable[(int)test];
    548 }
    549 
    550