zoukankan      html  css  js  c++  java
  • DDraw笔记双缓冲和后备缓冲

    双缓冲和后备缓冲

    前面的例子都是,直接在主表面上绘东西。对于动画,直接在主表面上绘,会产生很严重的闪烁。解决的办法是采用双缓冲或后备缓冲。

    双缓冲

    双缓冲:在离屏缓冲中绘制图像,然后将其拷贝到显示表面。

     

    见下面代码,先把数据放到double_buffer,最后再拷贝到主表面上。

    下面Sleep(300);睡眠了0.3秒这么长的时间是为了更为明显的看到画面的变化。

    代码下载

     

    int Game_Main()
    {
    if (window_closed)
    return 0;

    if (KEYDOWN(VK_ESCAPE))
    {
    PostMessage(main_window_handle,WM_CLOSE,
    0,0);
    return 0;
    window_closed
    = 1;
    }

    memset((
    void *)double_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);

    for (int index = 0; index < 5000; index++)
    {
    int x = rand() % SCREEN_WIDTH;
    int y = rand() % SCREEN_HEIGHT;
    UCHAR col
    = rand() % 256;

    // 先把颜色在于缓冲区中
    double_buffer[x + y * SCREEN_WIDTH] = col;
    }

    DDRAW_INIT_STRUCT(ddsd);

    lpddsprimary
    ->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);

    // 主表面显存指针
    UCHAR * primary_buffer = (UCHAR *)ddsd.lpSurface;

    // 再把缓冲区的内容拷贝到主表面。
    if (ddsd.lPitch == SCREEN_WIDTH)
    {
    memcpy((
    void *)primary_buffer, (void *)double_buffer, SCREEN_WIDTH * SCREEN_HEIGHT);
    }
    else
    {
    UCHAR
    *dest_ptr = primary_buffer;
    UCHAR
    *src_ptr = double_buffer;

    // 一行一行的拷贝
    for (int y=0; y < SCREEN_HEIGHT; y++)
    {
    memcpy((
    void *)dest_ptr, (void *)src_ptr, SCREEN_WIDTH);
    dest_ptr
    += ddsd.lPitch;
    src_ptr
    += SCREEN_WIDTH;
    }
    }

    if (FAILED(lpddsprimary->Unlock(NULL)))
    return 0;

    Sleep(
    300);

    return 1;
    }

    后备缓冲

    后备缓冲也是离屏表面的一种。它是一些用在动画链中的表面,它们具有和主表面相同的尺寸和色深。当创建主表面的时候也创建它们。它和主表面进行页面切换,这比双缓冲方案下所需做的内存拷贝要快得多。

     

    创建一个关联后备缓冲的主表面步骤(复杂表面): 

    1. 把DDSD_BACKBUFFERCOUNT加到dwFlagss标志字段,把DDSURFACEDESC2 结构的dwBackBufferCount字段设为有效。(它可以设置后备缓冲的数目)
    2. 把DDSCAPS_COMPLEX | DDSCAPS_FLIP加到DDSURFACEDESC2 结构的ddsCaps.dwCaps
    3. 创建主表面。
    4. 得到后备缓冲有再个方法。设置DDSCAPS2 ddscaps.dwCaps = DDSCAPS_BACKBUFFER; 或 ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

    然后调用 GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)得到后备缓冲。

    见下面代码:

    DDRAW_INIT_STRUCT(ddsd);
    ddsd.dwFlags
    = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // 增加DDSD_BACKBUFFERCOUNT 表明dwBackBufferCount有效
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; // 多了 DDSCAPS_COMPLEX | DDSCAPS_FLIP
    ddsd.dwBackBufferCount = 1; // 后备缓冲的个数


    if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
    return 0;

    // 请求一个后备缓冲
    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

    // 得到后备缓冲
    if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
    return 0;

    以上代码。Lpddsprimary指向主表面,lpddsback指向后备缓冲表面。

    对后备缓冲表面进行存取,也需要对其加锁和解锁。在对后备缓冲表面绘图后,把怎么把所之图显示到主表面呢。这就要用到页面切换了(而且这个切换是由硬件完成的)。

     

    用主表面调用下面的函数来切换。(注意在页面切换前主表面或后备缓冲表面都必须被解锁)

    HRESULT Flip(LPDIRECTDRAWSURFACE7 lpDDSurfaceTargetOverride ,DWORD dwFlags);

    第一个参数:用来覆盖切换链,实现切换到另外一个表面,不是切换到同主表面想关联的后备缓冲,一般为NULL。

    第二个参数:控制标志。如下:

    DDFLIP_INTERVAL2

    2次垂直逆程后切换

    DDFLIP_INTERVAL3

    3次垂直逆程后切换

    DDFLIP_INTERVAL4

    4次垂直逆程后切换

    这些标志 在DDCAPS结构中设置了DDCAPS2_FLIPINTERVAL才能起作用。默认值是1.

    这些标志 表明在两个切换页之间等待多少垂直逆程,只有在指定的垂直逆程数目达到了,DDraw才为切换的每一页返回DERR_WASSTILLDRAWING

    DDFLIP_NOVSYNC:执行物理切换时尽量靠近下一条扫描线。

    DDFLIP_WAIT:强迫硬件不 在出现问题时立即返回,而是等待直到页面切换能够进行为止。一般就用这个标志比较多。

    代码下载

    int MainGame()
    {
    if (window_closed)
    return 0;

    if (KEYDOWN(VK_ESCAPE))
    {
    PostMessage(main_window_handle, WM_CLOSE,
    0, 0);
    window_closed
    = 1;
    }

    DDRAW_INIT_STRUCT(ddsd);
    lpddsback
    ->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);

    UCHAR
    *back_buffer = (UCHAR *)ddsd.lpSurface;

    // 清除后备缓冲数据
    if (ddsd.lPitch == SCREEN_WIDTH)
    memset(back_buffer,
    0, SCREEN_WIDTH * SCREEN_HEIGHT);
    else
    {
    UCHAR
    *dest_ptr = back_buffer;

    for (int y=0; y<SCREEN_HEIGHT; y++)
    {
    memset(dest_ptr,
    0, SCREEN_WIDTH);
    dest_ptr
    +=ddsd.lPitch;
    }
    }

    for (int index=0; index < 5000; index++)
    {
    int x = rand() % SCREEN_WIDTH;
    int y = rand() % SCREEN_HEIGHT;
    UCHAR col
    = rand() % 256;
    back_buffer[x
    + y * ddsd.lPitch] = col;
    }

    if (FAILED(lpddsback->Unlock(NULL)))
    return 0;

    // 切换主表面,是主表面调用Flip,另外要注意的是调用Flip之前,要先解锁。
    while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));

    Sleep(
    500);

    return 1
    }

     

    2011.06.07 fangyukuan

    转载请保留下面链接

    http://www.cnblogs.com/fangyukuan/archive/2011/06/07/2074713.html 



  • 相关阅读:
    T-sql for xml path使用
    解决 SQL Server2012附加出错的问题
    安装应用程序 报“ 997 重叠 I/O 操作在进行中”错解决办法
    使用QQ互联登录应用
    monogdb windows环境下 安装及使用简单示例
    idle-实现清屏
    colorscheme-如何vim颜色风格
    android-从官网下拉源码(ubuntu)
    hq-源码编译
    文件目录进入终端
  • 原文地址:https://www.cnblogs.com/fangyukuan/p/2074713.html
Copyright © 2011-2022 走看看