当前位置: 首页 > news >正文

D3D 部分功能测试结论

  写了个测试程序,方便个人在使用中查询,不当之处请指出(以下结论均为三角形数为20000,一个批次中完成渲染)

1 在三角形数量不超过20000时,DrawIndexedPrimitive和DrawIndexedPrimitiveUP效率没有明显差别,
  前提是创建buffer时没有使用D3DUSAGE_DYNAMIC,否则DrawIndexedPrimitive的性能不如DrawIndexedPrimitiveUP
2 创建buffer时,如果D3DUSAGE使用的是0,DrawIndexedPrimitive的性能不如DrawIndexedPrimitiveUP,
  而且0和D3DUSAGE_WRITEONLY在三角形数量为20000时,性能差别比较大(差不多有100帧的差距)
3 尽量不要使用DrawPrimitive和DrawPrimitiveUP,尤其是DrawPrimitiveUP,效率比起前面两个函数,差很多
4 使用DrawIndexedPrimitive和DrawIndexedPrimitiveUP两个函数测试D3DPOOL_DEFAULT,D3DPOOL_MANAGED,
  D3DPOOL_SYSTEMMEM。发现D3DPOOL_DEFAULT,D3DPOOL_MANAGED的效率差不多,D3DPOOL_DEFAULT表现稍好。
  当使用D3DPOOL_SYSTEMMEM时,效率相对前面两个参数,DrawIndexedPrimitive有明显下降。
  而DrawIndexedPrimitiveUP的速度稍有下降,而且DrawIndexedPrimitiveUP比DrawIndexedPrimitive要快

 

以下是测试程序:


ExpandedBlockStart.gif 代码
#include  < d3dx9.h >


// -----------------------------------------------------------------------------
//  Desc: 全局变量
// -----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D        =  NULL;     // Direct3D对象
LPDIRECT3DDEVICE9       g_pd3dDevice  =  NULL;     // Direct3D设备对象
LPDIRECT3DVERTEXBUFFER9 g_pVB_Index   =  NULL;     // 顶点缓冲区对象
LPDIRECT3DINDEXBUFFER9  g_pIB_Index   =  NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB        
=  NULL;     // 顶点缓冲区对象
LPDIRECT3DINDEXBUFFER9  g_pIB         =  NULL;
// -----------------------------------------------------------------------------
//  Desc: 顶点结构
// -----------------------------------------------------------------------------
struct  CUSTOMVERTEX
{
    FLOAT x, y, z;    
// 顶点位置  
    DWORD diffuse;

// public:
//     CUSTOMVERTEX():diffuse(0xffffffff){}
};
#define  D3DFVF_CUSTOMVERTEX   (D3DFVF_XYZ|D3DFVF_DIFFUSE)


const   int  g_edgeVertexCnts  =   101 ;   // 每边顶点数目
CUSTOMVERTEX  * g_pVertex_Index;      // 顶点
int   * g_pIndexes_Index;              // 索引

CUSTOMVERTEX 
* g_pVertex;            // 顶点
// -----------------------------------------------------------------------------
//  Desc: 渲染类型
// -----------------------------------------------------------------------------
enum  Draw_Prim_Type
{
    DPT_INDEX 
=   0 , // drawindexedprimitive
    DPT_INDEX_UP,  // drawindexedprimitiveup
    DPT_PRIM,      // drawprimitive
    DPT_PRIM_UP,   // drawprimitiveup
};
Draw_Prim_Type g_kDrawType 
=  DPT_INDEX;

// -----------------------------------------------------------------------------
//  Desc: 设置变换矩阵
// -----------------------------------------------------------------------------
VOID SetupMatrices()
{
    D3DXMATRIX matWorld;
    D3DXMatrixIdentity(
& matWorld);
    g_pd3dDevice
-> SetTransform(D3DTS_WORLD, & matWorld);

    D3DXMATRIX matView;
    D3DXVECTOR3 vecEyeat(
0 , 100 , - 100 );
    D3DXVECTOR3 vecLookat(
0 , 0 , 10 );
    D3DXVECTOR3 vecUp(
0 , 1 , 0 );
    D3DXMatrixLookAtLH(
& matView,  & vecEyeat,  & vecLookat,  & vecUp);
    g_pd3dDevice
-> SetTransform(D3DTS_VIEW,  & matView);

    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH(
& matProj, D3DX_PI / 2 4.0f / 3.0f 1.0f 1000.0f );
    g_pd3dDevice
-> SetTransform(D3DTS_PROJECTION,  & matProj);
}


// -----------------------------------------------------------------------------
//  Desc: 初始化Direct3D
// -----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
    
// 创建d3d对象
    g_pD3D  =  Direct3DCreate9(D3D_SDK_VERSION);

    
// 初始化设置参数
    D3DPRESENT_PARAMETERS parm;
    ZeroMemory(
& parm,  sizeof (D3DPRESENT_PARAMETERS));
    parm.BackBufferWidth 
=   800 ;
    parm.BackBufferHeight 
=   600 ;
    parm.BackBufferCount 
=   2 ;
    parm.BackBufferFormat 
=  D3DFMT_UNKNOWN;
    parm.MultiSampleType 
=  D3DMULTISAMPLE_NONE;
    parm.SwapEffect 
=  D3DSWAPEFFECT_DISCARD;
    parm.Windowed 
=   true ;
    parm.hDeviceWindow 
=  hWnd;
    parm.EnableAutoDepthStencil 
=   false ;
    parm.PresentationInterval 
=  D3DPRESENT_INTERVAL_IMMEDIATE;

    
// 创建设备
    HRESULT hr  =  g_pD3D -> CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING, 
& parm,  & g_pd3dDevice);
    
if  (FAILED(hr))
    {
        
return  S_FALSE;
    }

    
// 禁用light
    g_pd3dDevice -> SetRenderState(D3DRS_LIGHTING,  false );
    g_pd3dDevice
-> SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

    SetupMatrices();

    
return  S_OK;
}


// -----------------------------------------------------------------------------
//  Desc: 创建场景图形
// -----------------------------------------------------------------------------
HRESULT InitGriphics()
{
    
// --------------vertex buffer for index
     const   float  fGridLenth  =   0.10f ;
    g_pVertex_Index 
=   new  CUSTOMVERTEX[g_edgeVertexCnts  *  g_edgeVertexCnts];

    
for  ( int  j  =   0 ; j  <  g_edgeVertexCnts; j += 1 )
    {
        
for  ( int  i  =   0 ; i  <  g_edgeVertexCnts; i += 1 )
        {
            g_pVertex_Index[j 
*  g_edgeVertexCnts  +  i].x  =  fGridLenth  *  i;
            g_pVertex_Index[j 
*  g_edgeVertexCnts  +  i].y  =   0 ;
            g_pVertex_Index[j 
*  g_edgeVertexCnts  +  i].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 -  fGridLenth  *  j;
        }
    }

    HRESULT hr 
=  g_pd3dDevice -> CreateVertexBuffer(
        g_edgeVertexCnts 
*  g_edgeVertexCnts * sizeof (CUSTOMVERTEX),
        D3DUSAGE_WRITEONLY,
// 非常影响新能
        D3DFVF_CUSTOMVERTEX,
        D3DPOOL_DEFAULT,
        
& g_pVB_Index,
        NULL);
    
if  (FAILED(hr))
    {
        
return  S_FALSE;
    }

    
// 写入数据
     void   * pData;
    g_pVB_Index
-> Lock( 0 , g_edgeVertexCnts  *  g_edgeVertexCnts * sizeof (CUSTOMVERTEX), ( void ** ) & pData, D3DLOCK_DISCARD);
    memcpy(pData, g_pVertex_Index, g_edgeVertexCnts 
*  g_edgeVertexCnts * sizeof (CUSTOMVERTEX));
    g_pVB_Index
-> Unlock();


    
// --------------index buffer for index
     int  iIndexCount  =  (g_edgeVertexCnts - 1 *  (g_edgeVertexCnts - 1 *   6 ;
    g_pIndexes_Index 
=   new   int [iIndexCount];

    
for ( int  j = 0 ;j < g_edgeVertexCnts - 1 ;j += 1 )
    {
        
for ( int  i = 0 ;i < g_edgeVertexCnts - 1 ;i += 1 )
        {
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 0 = ( int )(j * g_edgeVertexCnts + i);
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 1 = ( int )(j * g_edgeVertexCnts + i + g_edgeVertexCnts + 1 );
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 2 = ( int )(j * g_edgeVertexCnts + i + g_edgeVertexCnts);
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 3 = ( int )(j * g_edgeVertexCnts + i);
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 4 = ( int )(j * g_edgeVertexCnts + i + 1 );
            g_pIndexes_Index[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 5 = ( int )(j * g_edgeVertexCnts + i + g_edgeVertexCnts + 1 );
        }
    }

    g_pd3dDevice
-> CreateIndexBuffer(iIndexCount * sizeof ( int ), 
        D3DUSAGE_WRITEONLY,
        D3DFMT_INDEX32,
        D3DPOOL_DEFAULT,
        
& g_pIB_Index,
        NULL);
    
void   * pp;
    
if (FAILED(g_pIB_Index -> Lock( 0 , iIndexCount * sizeof ( int ), ( void ** ) & pp,  0 )))
    {
        
// add code
         return  S_FALSE;
    }
    memcpy(pp, g_pIndexes_Index, iIndexCount
* sizeof ( int ));
    g_pIB_Index
-> Unlock();

    
// --------------vertex buffer
     int  prim_list_vertex_cnts  =  (g_edgeVertexCnts - 1 ) * (g_edgeVertexCnts - 1 ) * 6 ;
    g_pVertex 
=   new  CUSTOMVERTEX[prim_list_vertex_cnts];

    
for  ( int  j  =   0 ; j  <  g_edgeVertexCnts - 1 ; j += 1 )
    {
        
for  ( int  i  =   0 ; i  <  g_edgeVertexCnts - 1 ; i += 1 )
        {
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 0 ].x  =  i * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 0 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 0 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - j * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 1 ].x  =  (i + 1 ) * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 1 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 1 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - j * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 2 ].x  =  (i + 1 ) * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 2 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 2 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - (j + 1 ) * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 3 ].x  =  i * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 3 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 3 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - j * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 4 ].x  =  (i + 1 ) * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 4 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 4 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - (j + 1 ) * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 5 ].x  =  i * fGridLenth;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 5 ].y  =   0 ;
            g_pVertex[j
* (g_edgeVertexCnts - 1 ) * 6 + i * 6 + 5 ].z  =   0.5f * fGridLenth * (g_edgeVertexCnts - 1 ) - (j + 1 ) * fGridLenth;
        }
    }

    
if ( FAILED( g_pd3dDevice -> CreateVertexBuffer( 
        prim_list_vertex_cnts
* sizeof (CUSTOMVERTEX),
        D3DUSAGE_WRITEONLY, 
        D3DFVF_CUSTOMVERTEX,
        D3DPOOL_DEFAULT, 
        
& g_pVB,
        NULL ) ) )
    {
        
return  E_FAIL;
    }

    VOID
*  pVertices;
    
if ( FAILED( g_pVB -> Lock(  0 , prim_list_vertex_cnts * sizeof (CUSTOMVERTEX), ( void ** ) & pVertices,  0  ) ) )
        
return  E_FAIL;
    memcpy( pVertices, g_pVertex, prim_list_vertex_cnts
* sizeof (CUSTOMVERTEX) );
    g_pVB
-> Unlock();

    
return  S_OK;
}


// -----------------------------------------------------------------------------
//  Desc: 释放创建的对象
// -----------------------------------------------------------------------------
VOID Cleanup()
{
    
// 释放顶点缓冲区对象
     if ( g_pVB_Index  !=  NULL )        
        g_pVB_Index
-> Release();

    
// 释放Direct3D设备对象
     if ( g_pd3dDevice  !=  NULL ) 
        g_pd3dDevice
-> Release();

    
// 释放Direct3D对象
     if ( g_pD3D  !=  NULL )       
        g_pD3D
-> Release();
}


// -----------------------------------------------------------------------------
//  Desc: 渲染图形 
// -----------------------------------------------------------------------------
VOID Render()
{
    g_pd3dDevice
-> Clear( 0 , NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0 , 0 , 255 ), 1 , 0 );

    g_pd3dDevice
-> BeginScene();

    g_pd3dDevice
-> SetFVF(D3DFVF_CUSTOMVERTEX);

    
switch (g_kDrawType)
    {
    
case   DPT_INDEX:
        g_pd3dDevice
-> SetStreamSource( 0 ,g_pVB_Index, 0 , sizeof (CUSTOMVERTEX));
        g_pd3dDevice
-> SetIndices(g_pIB_Index);

        g_pd3dDevice
-> DrawIndexedPrimitive( D3DPT_TRIANGLELIST,  0 0
            g_edgeVertexCnts
* g_edgeVertexCnts,  0  ,
            (g_edgeVertexCnts
- 1 ) * (g_edgeVertexCnts - 1 *   2 );
        
break ;

    
case   DPT_INDEX_UP:
        g_pd3dDevice
-> SetStreamSource( 0 ,g_pVB_Index, 0 , sizeof (CUSTOMVERTEX));
        g_pd3dDevice
-> SetIndices(g_pIB_Index);

        g_pd3dDevice
-> DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST,  0 , g_edgeVertexCnts * g_edgeVertexCnts, 
            (g_edgeVertexCnts
- 1 ) * (g_edgeVertexCnts - 1 *   2 , g_pIndexes_Index,
            D3DFMT_INDEX32, g_pVertex_Index, 
sizeof (CUSTOMVERTEX));
        
break ;

    
case   DPT_PRIM:
        g_pd3dDevice
-> SetStreamSource( 0 ,g_pVB, 0 , sizeof (CUSTOMVERTEX));

        g_pd3dDevice
-> DrawPrimitive(D3DPT_TRIANGLELIST,  0 , (g_edgeVertexCnts - 1 ) * (g_edgeVertexCnts - 1 *   2 );
        
break ;

    
case   DPT_PRIM_UP:
        g_pd3dDevice
-> SetStreamSource( 0 ,g_pVB, 0 , sizeof (CUSTOMVERTEX));

        g_pd3dDevice
-> DrawPrimitiveUP(D3DPT_TRIANGLELIST, (g_edgeVertexCnts - 1 ) * (g_edgeVertexCnts - 1 *   2 ,
            g_pVertex, 
sizeof (CUSTOMVERTEX));
        
break ;
    }

    g_pd3dDevice
-> EndScene();

    g_pd3dDevice
-> Present(NULL,NULL,NULL,NULL);
}


// -----------------------------------------------------------------------------
//  Desc: 消息处理
// -----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    
switch ( msg )
    {
    
case  WM_DESTROY:
        Cleanup();
        PostQuitMessage( 
0  );

    
case  WM_KEYDOWN:
        {
            
switch ((WORD)wParam)
            {
            
case   ' W ' :
                g_pd3dDevice
-> SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
                
break ;
            
case   ' S ' :
                g_pd3dDevice
-> SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
                
break ;

            
case   ' 1 ' :
                g_kDrawType 
=  DPT_INDEX;
                
break ;

            
case   ' 2 ' :
                g_kDrawType 
=  DPT_INDEX_UP;
                
break ;

            
case   ' 3 ' :
                g_kDrawType 
=  DPT_PRIM;
                
break ;

            
case   ' 4 ' :
                g_kDrawType 
=  DPT_PRIM_UP;
                
break ;

            
case   ' Q ' :
                exit(
0 );
                
break ;
            }
        }

        
return   0 ;
    }

    
return  DefWindowProc( hWnd, msg, wParam, lParam );
}



// -----------------------------------------------------------------------------
//  Desc: 入口函数
// -----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{

    
// 注册窗口类
    WNDCLASSEX wc  =  {  sizeof (WNDCLASSEX), CS_CLASSDC, MsgProc,  0L 0L ,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                       L
" ClassName " , NULL };
    RegisterClassEx( 
& wc );

    RECT rc;
    rc.left 
=   0 ;
    rc.top 
=   0 ;
    rc.right 
=   800 ;
    rc.bottom 
=   600 ;
    AdjustWindowRect(
& rc,  WS_VISIBLE  |  WS_CLIPCHILDREN  |  WS_OVERLAPPEDWINDOW,  false );
    
// 创建窗口
    HWND hWnd  =  CreateWindow(  L " ClassName " , L " 纹理影射基础 " ,
                              WS_OVERLAPPEDWINDOW, 
                              
200
                              
100
                              rc.right
- rc.left, 
                              rc.bottom
- rc.top,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );

    
// 初始化Direct3D
     if ( SUCCEEDED( InitD3D( hWnd ) ) )
    {
        
// 创建场景图形
         if ( SUCCEEDED( InitGriphics() ) )
        {
            
// 显示窗口
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );

            
// 进入消息循环
            MSG msg;
            ZeroMemory( 
& msg,  sizeof (msg) );
            
while ( msg.message != WM_QUIT )
            {
                
if ( PeekMessage(  & msg, NULL,  0U 0U , PM_REMOVE ) )
                {
                    TranslateMessage( 
& msg );
                    DispatchMessage( 
& msg );
                }
                
else
                {
                    Render();  
// 渲染图形
                }
            }
        }
    }

    UnregisterClass(  L
" ClassName " , wc.hInstance );
    
return   0 ;
}

 

 

转载于:https://www.cnblogs.com/lancidie/archive/2010/10/12/1848847.html

相关文章:

  • PHP time(), microtime(),date()函数
  • 使用快照隔离snapshot isolation实例(二)
  • Powershell管理系列(十七)在PowerShell中添加Exchange管理单元
  • 数据库连接代码
  • javascript中setInterval与setTimeout中this的问题以及对于闭包的一些理解
  • 针对PostMessage和SendMessage区别的一个试验
  • 【spring框架】beans.xml规范与ClassPathXmlApplicationContext类介绍
  • It's not a Bug, It's a Feature! --POJ 1482
  • postgresql 死锁问题解决记录
  • WCF Data Services客户端访问
  • css知多少(4)——解读浏览器默认样式
  • Breakthrough—JavaScript基础
  • 辛苦几个小时,终于装完主机了
  • 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--PS2封装(十八)
  • android httpClient 支持HTTPS的2种处理方式
  • 《剑指offer》分解让复杂问题更简单
  • 4. 路由到控制器 - Laravel从零开始教程
  • 77. Combinations
  • Angular数据绑定机制
  • IDEA 插件开发入门教程
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JavaScript标准库系列——Math对象和Date对象(二)
  • php ci框架整合银盛支付
  • Vue 2.3、2.4 知识点小结
  • VuePress 静态网站生成
  • Yii源码解读-服务定位器(Service Locator)
  • Zepto.js源码学习之二
  • 程序员该如何有效的找工作?
  • 力扣(LeetCode)21
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 树莓派 - 使用须知
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​Linux·i2c驱动架构​
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • (2.2w字)前端单元测试之Jest详解篇
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • ./configure,make,make install的作用
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET delegate 委托 、 Event 事件,接口回调
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .NET处理HTTP请求
  • .NET设计模式(2):单件模式(Singleton Pattern)
  • .NET委托:一个关于C#的睡前故事
  • .sh 的运行
  • /proc/stat文件详解(翻译)
  • @Autowired和@Resource的区别
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [C/C++随笔] char与unsigned char区别
  • [C++]打开新世界的大门之C++入门
  • [github配置] 远程访问仓库以及问题解决