zoukankan      html  css  js  c++  java
  • 测试VGA12H直接写屏速度

    File:      VGASpeed.txt
    Name:      测试VGA12H模式的速度
    Author:    zyl910
    Blog:      http://blog.csdn.net/zyl910/
    Version:   V1.0
    Updata:    2006-11-14

    下载(注意修改下载后的扩展名)


    简介
    ~~~~
      DOS下直接写屏的代码写过不少了,但一直没有想过一个问题——操作VGA的速度有多块。因此,我写了个小程序来测试VGA的速度。
      图形模式:VGA 12h,640*480*16色。
      三个测试项目:
        1.读测试。使用VGA读模式0,逐扫描行逐位平面的将显存数据复制到系统内存。
        2.写测试。使用VGA写模式0,逐扫描行逐位平面的将系统内存数据复制到显存。
        3.在等待垂直回扫情况下的写测试。
      四种访问方式:用C语言远指针、movsb、movsw、movsd

    测试结果
    ~~~~~~~~

    CPU   : AMD Athlon XP 1700+(实际频率:1463 MHz (11 x 133))
    内存  : DDR266 256MB
    显卡  : NVIDIA GeForce2 MX/MX 400(AGP 4X)
    显存带宽: 125MHz * 128bit = 2000MB/s
    操作系统: Windows XP SP2
    [FPS]
    R_C    :         11.7646
    W_C    :         51.5834
    R_BYTE :         12.0000
    W_BYTE :         86.4751
    R_WORD :         23.6298
    W_WORD :        124.8862
    R_DWORD:         44.7459
    W_DWORD:        156.8619
    WaitW_B:         60.0298
    WaitW_W:         59.9293
    WaitW_D:         59.9293
    124.8862 /  86.4751 = 144.42%
    156.8619 / 124.8862 = 125.60%

    CPU   : AMD Athlon XP 1700+(实际频率:1463 MHz (11 x 133))
    内存  : DDR266 256MB
    显卡  : NVIDIA GeForce2 MX/MX 400(AGP 4X)
    显存带宽: 125MHz * 128bit = 2000MB/s
    操作系统: Windows 98SE
    [FPS]
    R_C    :         11.6641
    W_C    :         60.7337
    R_BYTE :         11.9657
    W_BYTE :         98.8431
    R_WORD :         23.4287
    W_WORD :        173.9558
    R_DWORD:         44.4442
    W_DWORD:        267.9724
    WaitW_B:         59.9293
    WaitW_W:         59.9293
    WaitW_D:         60.0298
    173.9558 /  98.8431 = 175.99%
    267.9724 / 173.9558 = 154.05%


    CPU   : AMD Athlon XP 1700+(实际频率:1463 MHz (11 x 133))
    内存  : DDR266 256MB
    显卡  : NVIDIA GeForce2 MX/MX 400(AGP 4X)
    显存带宽: 125MHz * 128bit = 2000MB/s
    操作系统: DOS实模式
    [FPS]
    R_C    :         11.7646
    W_C    :         61.2365
    R_BYTE :         12.0663
    W_BYTE :        107.9934
    R_WORD :         23.6298
    W_WORD :        190.6475
    R_DWORD:         44.9470
    W_DWORD:        279.1337
    WaitW_B:         60.0298
    WaitW_W:         59.9293
    WaitW_D:         60.0298
    190.6475 / 107.9934 = 176.54%
    279.1337 / 190.6475 = 146.41%

    CPU   : Intel Celeron 2.53GHz
    内存  : Dual DDR333 512MB
    显卡  : NVIDIA RIVA TNT2 Model 64(AGP 4X)
    显存带宽: 110MHz * 64bit = 880MB/s
    操作系统: Windows XP SP2
    [FPS]
    R_C    :          8.4464
    W_C    :         37.5061
    R_BYTE :          8.4000
    W_BYTE :         46.1536
    R_WORD :         16.2895
    W_WORD :         64.1525
    R_DWORD:         31.1713
    W_DWORD:         78.9337
    WaitW_B:         29.8641
    WaitW_W:         59.3260
    WaitW_D:         59.5271
    64.1525 / 46.1536 = 138.99%
    78.9337 / 64.1525 = 123.04%

    CPU   : Intel Celeron, 1800 MHz (18 x 100)
    内存  : DDR266 256MB
    显卡  : nVIDIA GeForce4 MX 440(AGP 8X)
    显存带宽: 405MHz * 64bit = 3240MB/s
    操作系统: Windows XP SP2
    [FPS]
    R_C    :          7.7425
    W_C    :         33.1823
    R_BYTE :          7.8000
    W_BYTE :         42.9359
    R_WORD :         15.0829
    W_WORD :         56.5105
    R_DWORD:         28.8586
    W_DWORD:         68.5768
    WaitW_B:         28.6575
    WaitW_W:         49.2707
    WaitW_D:         55.2033
    56.5105 / 42.9359 = 131.62%
    68.5768 / 56.5105 = 121.35%

    分析
    ~~~~

    一、刷新率是60帧
      “在等待垂直回扫情况下的写测试”的测试结果都差不多,这表示VGA12H模式下的刷新率为60帧。


    二、读的速度比写的慢的多
      可能是因为在设计VGA硬件时,考虑到显存一般是用来输出的,所以专门为写操作优化的。
      由于读速度过慢,所以VGA的一些需要读显存的硬件加速特性——循环移位、位图合并模式——并不能提高性能,反而有可能拖后腿。


    三、movsw/movsd对性能的提升没有想象中得那么高
      对于读测试,movsw/movsd能使性能翻倍。
      可对于写测试,movsw/movsd虽然能提高性能,但并没翻倍。
      但是我们为了追求速度,还是坚持使用movsd方式吧。
      注意写模式1只支持movsb。


    四、为什么速度这么慢
      VGA12H模式的分辨率是640*480*16色,所以每帧图像大小为:640*480*4/8 = 153600(Byte)
      AGP总线的频率为66Mhz,如果每个时钟复制一个字节,那么理论上的帧率为:66Mhz * 1BytePerHz / 153600 = 429.6875fps
      而实际的movsb的测试结果是100帧左右,只有理论值的1/4。若再考虑AGP 4X使频率提高4倍,那么差距更大。
      可能是因为:当显卡将帧数据发送到显示器时,不可访问主表面的显存。但是VGA12H下只有一个主表面(VGA显存是256KB),放不下离屏表面,无法利用双缓冲加速。


    测试代码
    ~~~~~~~~

     

    /*
    File:      VGASpeed.c
    Name:      测试VGA12H模式的速度
    Author:    zyl910
    Blog:      http://blog.csdn.net/zyl910/
    Version:   V1.0
    Updata:    2006-11-14
    */
    #include <stdio.h>
    #include <conio.h>
    #include <mem.h>
    #include <dos.h>

    typedef unsigned char BYTE;
    typedef unsigned int  WORD;
    typedef unsigned long DWORD;
    typedef void far* LPVOID;

    #define SCR_W 640
    #define SCR_H 480
    #define SCR_PLANES 4
    #define SCANSIZE_DIB ((SCR_W)/2)
    #define SCANSIZE_VGA ((SCR_W)/8)
    #define SEG_VIDEO 0xA000
    #define WaitVR() while(!(inportb(0x3da)&0x08))
    static volatile DWORD far* const pbiosclock = MK_FP(0x0040, 0x6C);
    #define BIOSCLOCK_F ((double)18.2)
    void repmovsb(LPVOID lpD, LPVOID lpS, WORD cBytes)
    {
     _asm{
      push ds
      push es
      mov cx, cBytes
      les di, lpD
      lds si, lpS
      rep movsb
      pop es
      pop ds;
     }
    }
    void repmovsw(LPVOID lpD, LPVOID lpS, WORD cWords)
    {
     _asm{
      push ds
      push es
      mov cx, cWords
      les di, lpD
      lds si, lpS
      rep movsw
      pop es
      pop ds;
     }
    }
    void repmovsd(LPVOID lpD, LPVOID lpS, WORD cDWords)
    {
     _asm{
      push ds
      push es
      mov cx, cDWords
      les di, lpD
      lds si, lpS
      db 0x66; rep movsw; /* rep movsd */
      pop es
      pop ds
     }
    }
    int main(void)
    {
     BYTE byVGA[SCR_PLANES][SCANSIZE_VGA];
     DWORD cntF;
     int iX, iY;
     BYTE iP;
     WORD pscan;
     BYTE far *pbyV;
     BYTE *pbyM;
     BYTE bymask;
     DWORD tmrold, tmrcur, tmrover;
     double fpsR_C, fpsR_BYTE, fpsR_WORD, fpsR_DWORD;
     double fpsW_C, fpsW_BYTE, fpsW_WORD, fpsW_DWORD;
     double fpsWaitW_BYTE, fpsWaitW_WORD, fpsWaitW_DWORD;
     /* init VGA 12h: 640*480*4bit */
     _asm{
      mov ax, 0x0012;
      int 0x10;
      cld;
     }
     printf("Testing...");
     /* R:C */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3CE; /* gc[4]:Read Map Select */
          mov al, 4;
          out dx, al;
          inc dx;
          mov al, iP;
          out dx, al;
         }
         pbyM = byVGA[iP];
         pbyV = MK_FP(SEG_VIDEO, pscan);
         for(iX=0; iX<SCANSIZE_VGA; iX++)
         {
          *pbyM++ = *pbyV++;
         }
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsR_C = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* W:C */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         pbyM = byVGA[iP];
         pbyV = MK_FP(SEG_VIDEO, pscan);
         for(iX=0; iX<SCANSIZE_VGA; iX++)
         {
          *pbyV++ = *pbyM++;
         }
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsW_C = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* R:Byte */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3CE; /* gc[4]:Read Map Select */
          mov al, 4;
          out dx, al;
          inc dx;
          mov al, iP;
          out dx, al;
         }
         repmovsb(byVGA[iP], MK_FP(SEG_VIDEO, pscan), SCANSIZE_VGA);
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsR_BYTE = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* W:BYTE */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsb(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsW_BYTE = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* R:Word */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3CE; /* gc[4]:Read Map Select */
          mov al, 4;
          out dx, al;
          inc dx;
          mov al, iP;
          out dx, al;
         }
         repmovsw(byVGA[iP], MK_FP(SEG_VIDEO, pscan), SCANSIZE_VGA/2);
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsR_WORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* W:WORD */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsw(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA/2);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsW_WORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* R:DWord */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3CE; /* gc[4]:Read Map Select */
          mov al, 4;
          out dx, al;
          inc dx;
          mov al, iP;
          out dx, al;
         }
         repmovsd(byVGA[iP], MK_FP(SEG_VIDEO, pscan), SCANSIZE_VGA/4);
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsR_DWORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* W:DWORD */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsd(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA/4);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsW_DWORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* WaitW:BYTE */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       WaitVR();
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsb(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsWaitW_BYTE = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* WaitW:WORD */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       WaitVR();
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsw(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA/2);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsWaitW_WORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* WaitW:DWORD */
     do{
      cntF = 0;
      tmrold = *pbiosclock;
      tmrover = tmrold + (DWORD)(BIOSCLOCK_F * 10); /* 10s */
      do{
       memset(byVGA[0], -(1&(((int)cntF)>>0)), SCANSIZE_VGA);
       memset(byVGA[1], -(1&(((int)cntF)>>1)), SCANSIZE_VGA);
       memset(byVGA[2], -(1&(((int)cntF)>>2)), SCANSIZE_VGA);
       memset(byVGA[3], -(1&(((int)cntF)>>3)), SCANSIZE_VGA);
       pscan = 0;
       WaitVR();
       for(iY=0; iY<SCR_H; iY++)
       {
        bymask = 1;
        for(iP=0; iP<SCR_PLANES; iP++)
        {
         _asm{
          mov dx, 0x3C4; /* sc[2]:Map Mask */
          mov al, 2;
          out dx, al;
          inc dx;
          mov al, bymask;
          out dx, al;
         }
         repmovsd(MK_FP(SEG_VIDEO, pscan), byVGA[iP], SCANSIZE_VGA/4);
         bymask <<= 1;
        }
        pscan += SCANSIZE_VGA;
       }
       cntF++;
       tmrcur = *pbiosclock;
      }while((tmrcur<tmrover)&&(tmrcur>=tmrold));
      if (tmrcur < tmrold) continue;
     }while(0);
     fpsWaitW_DWORD = cntF / ((tmrcur-tmrold)/BIOSCLOCK_F);
     /* Exit VGA */
     _asm{
      mov ax, 0x0003;
      int 0x10;
     }
     /* out */
     printf("[FPS]/n");
     printf("R_C    :%16.4f/n", fpsR_C);
     printf("W_C    :%16.4f/n", fpsW_C);
     printf("R_BYTE :%16.4f/n", fpsR_BYTE);
     printf("W_BYTE :%16.4f/n", fpsW_BYTE);
     printf("R_WORD :%16.4f/n", fpsR_WORD);
     printf("W_WORD :%16.4f/n", fpsW_WORD);
     printf("R_DWORD:%16.4f/n", fpsR_DWORD);
     printf("W_DWORD:%16.4f/n", fpsW_DWORD);
     printf("WaitW_B:%16.4f/n", fpsWaitW_BYTE);
     printf("WaitW_W:%16.4f/n", fpsWaitW_WORD);
     printf("WaitW_D:%16.4f/n", fpsWaitW_DWORD);
     return 0;
    }

  • 相关阅读:
    移动开发 Native APP、Hybrid APP和Web APP介绍
    urllib与urllib2的学习总结(python2.7.X)
    fiddler及postman讲解
    接口测试基础
    UiAutomator2.0 和1.x 的区别
    adb shell am instrument 命令详解
    GT问题记录
    HDU 2492 Ping pong (树状数组)
    CF 567C Geometric Progression
    CF 545E Paths and Trees
  • 原文地址:https://www.cnblogs.com/zyl910/p/2186632.html
Copyright © 2011-2022 走看看