谁能提供一个 DirectX 窗口的例子? ( 积分: 200 )

  • 主题发起人 主题发起人 soFTangeL
  • 开始时间 开始时间
S

soFTangeL

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟初学DirectX,有一些小问题还望各位能够不吝赐教。
我想使用一下窗口模式,但在网上搜索的全是全屏独占(DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN)模式:
FDD.SetCooperativeLevel(h_Wnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN);
我把独占模式改为普通窗口(DDSCL_NORMAL)模式之后:
FDD.SetCooperativeLevel(h_Wnd, DDSCL_NORMAL);
在创建FDD.CreateSurface(ddsd, FDDSPrimary, nil);时出错。

FillChar(ddsd, SizeOf(ddsd), 0);
ddsd.dwSize := SizeOf(ddsd);
ddsd.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount := 1;
hRet := FDD.CreateSurface(ddsd, FDDSPrimary, nil); //此句不能正常执行
if hRet <> DD_OK then
begin
ErrorOut(hRet, 'CreateSurface');
Exit;
end;

哪位朋友能提示一下是怎么回事?最好能给小弟发一份源码(myzz007@163.com),具体内容可以是在一个窗体上有一个image,然后利用DirectX对该图像进行简单的操作(比如变色或者翻转等等)。要求使用SDK,不要用DelphiX。谢谢!
 
小弟初学DirectX,有一些小问题还望各位能够不吝赐教。
我想使用一下窗口模式,但在网上搜索的全是全屏独占(DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN)模式:
FDD.SetCooperativeLevel(h_Wnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN);
我把独占模式改为普通窗口(DDSCL_NORMAL)模式之后:
FDD.SetCooperativeLevel(h_Wnd, DDSCL_NORMAL);
在创建FDD.CreateSurface(ddsd, FDDSPrimary, nil);时出错。

FillChar(ddsd, SizeOf(ddsd), 0);
ddsd.dwSize := SizeOf(ddsd);
ddsd.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount := 1;
hRet := FDD.CreateSurface(ddsd, FDDSPrimary, nil); //此句不能正常执行
if hRet <> DD_OK then
begin
ErrorOut(hRet, 'CreateSurface');
Exit;
end;

哪位朋友能提示一下是怎么回事?最好能给小弟发一份源码(myzz007@163.com),具体内容可以是在一个窗体上有一个image,然后利用DirectX对该图像进行简单的操作(比如变色或者翻转等等)。要求使用SDK,不要用DelphiX。谢谢!
 
1.不再需要 DDSCAPS_FLIP 模式,因窗口肯定不会使用 Flip 。
2.需要一个DirectX窗口解切器,把它关联到窗口,这样就不会出错了。
3.其实网上源代码多得翻天,可惜我自己写的例子代码全删掉了,你自己去搜一搜吧。
 
网上源代码的确“多得翻天”,但很可惜全是DDSCL_FULLSCREEN。[:(]
另外你说的“DirectX窗口解切器”是。。。?
 
老大,你的DirectX头文件用的是什么版本?难道没有Clipper?因我很久没留意这东西
了,很简单的,不是CreateClipper就是BuildClipper之类的函数。你可以找到一个专
门倒腾Delphi下使用DirectX的网站: http://clootie.narod.ru/delphi/ ,祝好运!
 
谢谢小雨哥。
窗口模式问题已经部分解决。已经成功建立了窗口。我现在准备做一个窗口水波特效。但是发现了一些问题:由于网上大部分是全屏独占模式,因此可以使用缓冲分页。但窗口模式是不允许这么做的。窗口模式大部分是用LoadBitmap装载一个图片,然后对图片进行切割装载。这么一来就产生了矛盾:水波特效显然是没办法载入所有帧的集合的,但窗口模式计算水波却无法在程序中进行缓冲交换分页。怎么处理这个矛盾?
 
窗口模式也可以创建缓冲分页,如果要对缓冲区进行特效处理,那么最好是在创建
Surface 时指定 SYSTEM_MEMORY,这个标志用于在系统内存中创建表面,而不是在
显存中。

窗口模式和全屏模式我觉得在使用上几乎没有不同,在我的试验中,全屏模式的
Flip 还不如用 Blt 快。
 
窗口模式也可以创建缓冲分页?窗口模式和全屏模式最大的区别之一应该就是不能分页吧。 我所指的分页是创建 primary surface 和 Back buffers来互相 Flipping Surfaces 。不知 savetime 所指的分页是什么意思?

看一下我的代码:
// Setting the cooperate level
hRet := FDD.SetCooperativeLevel(Handle, DDSCL_NORMAL); //窗口模式
if hRet <> DD_OK then
begin
ErrorOut(hRet, 'SetCooperativeLevel');
Exit;
end;

// Create the primary surface
FillChar(ddsd, SizeOf(ddsd), 0);
ddsd.dwSize := SizeOf(ddsd);
ddsd.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;
//能使用下面的语句创建一个带缓存的主页面?我这里怎么通不过啊。请问 savetime 是如何创建缓冲页面的?
// ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or DDSCAPS_COMPLEX;
// ddsd.dwBackBufferCount := 1;
hRet := FDD.CreateSurface(ddsd, FDDSPrimary, nil);
if hRet <> DD_OK then
begin
ErrorOut(hRet, 'CreateSurface');
Exit;
end;
 
对不起,可能是我对讨论的题目理解错误,窗口模式的 flip 分页确实没有。
我学 DirectX 10天左右,不妨一起研究。

小雨哥给的链接中,有一套 DirectDraw 的例子,其中有个是切换全屏和窗口,可以看看。

flip,按文档说明,是说可以通过显存指针切换(而不是内容拷贝)进行快速刷新的方法。
可是 flip 方法在我的电脑上试验,反而不如 blt 快。一个原因是:可能我的显卡驱动
未实现指针移动式的 flip。另外,我认为 flip 模式不适应特效处理。

flip 要求 PrimarySurface 和 BufferSurface 都在显存中,如果所有表面都只是从文件
中读取位图,再 blt,那确实很快,因为复制操作都在显存中,由显卡驱动完成了。而如
果要对表面做特效处理时,访问显存的内容则是很慢的,所以,我将所有表面从显存中移
向系统内存中,速度提升很多。

这是我写的一个游戏类中的一段选择全屏和窗口的代码,有点乱,没时间改它。其中还留
着从 flip 转到 blt 的痕迹:

if FFullScreen then
begin
CheckResult(FDirectDraw.SetCooperativeLevel(FMainWindow, DDSCL_EXCLUSIVE or
DDSCL_FULLSCREEN), ' SetCooperativeLevel');

CheckResult(FDirectDraw.SetDisplayMode(FWidth, FHeight, 8, 0, 0),
'SetDisplayMode');

FillChar(DDSD, SizeOf(DDSD), #0);
DDSD.dwSize := SizeOf(DDSD);
DDSD.dwFlags := DDSD_CAPS{ or DDSD_BACKBUFFERCOUNT};
// DirectX 例子中的写法
// DDSD.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or
// DDSCAPS_COMPLEX or DDSCAPS_3DDEVICE;
DDSD.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE {or DDSCAPS_FLIP or DDSCAPS_COMPLEX};
// DDSD.dwBackBufferCount := 1;
CheckResult(FDirectDraw.CreateSurface(DDSD, FPrimarySurface, nil),
'CreateSurface: FPrimarySurface');

// FillChar(DDSCaps, SizeOf(DDSCaps), #0);
// DDSCaps.dwCaps := DDSCAPS_BACKBUFFER;
// CheckResult(FPrimarySurface.GetAttachedSurface(DDSCaps, FBufferSurface),
// 'FPrimarySurface.GetAttachedSurface');
DDSD.dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;
DDSD.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY; //or DDSCAPS_3DDEVICE;
DDSD.dwHeight := FHeight;
DDSD.dwWidth := FWidth;
CheckResult(FDirectDraw.CreateSurface(DDSD, FBufferSurface, nil),
'CreateSurface(CreateSurface(FBufferSurface in Window Mode)');
end
// 窗口模式
else begin
CheckResult(FDirectDraw.SetCooperativeLevel(FMainWindow, DDSCL_NORMAL),
'SetCooperativeLevel(FMainWindow, DDSCL_NORMAL)');

// TODO: 修正
CheckResult(FDirectDraw.SetDisplayMode(1024, 768, 8, 0, 0),
'SetDisplayMode');

FillChar(DDSD, SizeOf(DDSD), #0);
DDSD.dwSize := SizeOf(DDSD);
DDSD.dwFlags := DDSD_CAPS;
DDSD.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;
CheckResult(FDirectDraw.CreateSurface(DDSD, FPrimarySurface, nil),
'CreateSurface(FPrimarySurface in Window Mode)');

DDSD.dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;
DDSD.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY; //or DDSCAPS_3DDEVICE;
DDSD.dwHeight := FHeight;
DDSD.dwWidth := FWidth;
CheckResult(FDirectDraw.CreateSurface(DDSD, FBufferSurface, nil),
'CreateSurface(CreateSurface(FBufferSurface in Window Mode)');

CheckResult(FDirectDraw.CreateClipper(0, FClipper, nil),
'CreateClipper in Window Mode');
CheckResult(FClipper.SetHWnd(0, FMainWindow), 'FClipper.SetHWnd');
CheckResult(FPrimarySurface.SetClipper(FClipper), 'FPrimarySurface.SetClipper');
FClipper := nil;
end;
end;


在 TGame.Flip 中的代码是这样的:

Result := FPrimarySurface.Blt(@FClientRect, FBufferSurface, nil, DDBLT_WAIT, nil);

if Result = DDERR_SURFACELOST then
begin
// 重新生成主表面和缓冲表面
if FDirectDraw.RestoreAllSurfaces <> DD_OK then Exit;
// 重新载入所有表面图像(由用户处理)
if not ReloadAllSurfaceImages then Exit;
end;

我用 API 重写的传2客户端在下面,要安装传奇才能看到效果
http://bbs.gameres.com/showthread.asp?page=end&threadid=21344
 
谢谢 小雨哥 及 savetime 的帮助。问题已初步解决。不过现在有发现一个新问题:CPU占用率过高,基本上一打开程序就100%了。我是用
Application.OnIdle := FormOnIdle;
procedure TfrmMain.FormOnIdle(Sender: TObject; var Done: Boolean);
begin
//限制帧数,防止程序过多的消耗系统资源。
if FActive and (GetTickCount - LastDrawTime > 100) then
begin
UpdateFrame;
LastDrawTime := GetTickCount;
end;
Done := not FActive;
end;
这样来不断刷新窗口的。刷新完毕后强迫Done为False,继续刷新。结果就造成CPU占用过高。不知楼上几位老大是如何处理的?
 
嗯,我的也是占100%,我原以为游戏都是如此。
可是我在刷新的过程中加了这行代码, CPU 占用率就只有10%了。而且一点也不影响效果。

// TickCount 是 FPS
if TickCount > 100 then Sleep(1);
 
Sleep(1); //添加了这个似乎还不行。
我上面的语句该怎样改进?不用OnIdle么?
procedure TfrmMain.FormOnIdle(Sender: TObject; var Done: Boolean);
begin
//限制帧数,防止程序过多的消耗系统资源。
if FActive and (GetTickCount - LastDrawTime > 100) then
begin
// TickCount 是 FPS
Sleep(1); //添加了这个似乎还不行。:(
UpdateFrame;
LastDrawTime := GetTickCount;
end;

Done := not FActive;
end;
 
TickCount 不是 FPS,我的代码改得乱七八糟,我随手抓了个值就以为是 FPS。
我现在取 FPS 的方法是这样的:

// 显示 FPS
if FIsShowFPS then
begin
TickCount := GetTickCount - FStartTick;
if TickCount < 1000 then
begin
Inc(FFrameCount);
if TickCount > 0 then
begin
FPS := FFrameCount * 1000 div TickCount;
S := Format('FPS: %d', [FPS]);
DrawSurfaceText(BufferSurface, S, 10, 10, FDefaultFont, RGB(255, 192, 64));
if FPS > 100 then Sleep(1);
end;
end
else begin
FFrameCount := 0;
FStartTick := GetTickCount;
end;
end;
 
想方便一些的话用DELPHIX
 
用 DelphiX 学不到东西,而且我看 DelphiX 实现效率也不高
 
那是什么时候通知更新的呢?我想OnIdle不是一个很好的办法。原因见下:
Application.OnIdle := FormOnIdle;
procedure TfrmMain.FormOnIdle(Sender: TObject; var Done: Boolean);
begin
//这里面即使什么都不做,只要有下面一句,那么程序的CPU就是100%。因为这有一钟死循环味道。如果没有这一句,那么就会偶尔的刷新的一下。想问一下老大,您是用什么事件或者方法去通知要 UpdateFrame 呢?
Done := not FActive;
end;
 
我想应该是在没有消息的情况下持续更新的,因为对一个重杂的场景来说,不可能像
一般的 Windows 程序那样来判断哪些矩形区域需要重绘。
在VCL 中是 TApplication.OnIdle,用 API 则是在 PeekMessage 为否时处理。
一般的应用程序中CPU占用少是因为调用了 GetMessage 或 WaitMessage ,这两个 API
都会在没有消息时出让 CPU 。我们在需要持续刷新的情况下,可以使用 Sleep 出让部
分 CPU 占用。你在 FormOnIdle 中加上 Sleep 就应可看到效果了。
 
谢谢各位的帮助。:)
 
后退
顶部