gpt4 book ai didi

delphi - 对于小数组来说,glDrawArrays 永远不会比 glBegin/glEnd 更快吗?

转载 作者:行者123 更新时间:2023-12-03 15:58:11 27 4
gpt4 key购买 nike

对于一个客户项目,我需要一个可以在旧硬件上运行的简单 Sprite block 传送器。 OpenGL 1.1 似乎是一个简单的答案,因为我有可以重用的旧代码。

无论如何,一切都很好,但令我大吃一惊的是,事实证明,对于位图传输移动 Sprite (因此在正交投影中渲染纹理四边形),glBegin/glTexCoord2/glVertex2/glEnd 模式始终与 glDrawArrays 一样快。这是在新旧硬件上进行了测试,我有点困惑,因为我期望得到不同的结果!

但要注意的是,两种模式肯定都足够快以满足要求(所以这确实是好奇的讨论而不是严肃的工作讨论!),但是客户端的演示允许泵送 Sprite 数达到 10000 或更多,然后我们注意到启用 gldrawarrays 选项通常具有相同的速度,并且在某些机器上速度是 glbegin/glend 的一半。

下面是渲染的代码片段。请注意,对于此演示,顶点和纹理数组是彼此相邻的全局变量。

for index:=0 to (sprite_list.count-1) do begin
s:=sprite_list[index];
s.update;
glBindTexture(GL_TEXTURE_2D,s.sprite_id);
glColor4b(127,127,127,s.ialpha);
if immediate then begin
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2i(coords[0].x,coords[0].y);
glTexCoord2f(0,1); glVertex2i(coords[1].x,coords[1].y);
glTexCoord2f(1,1); glVertex2i(coords[2].x,coords[2].y);
glTexCoord2f(1,0); glVertex2i(coords[3].x,coords[3].y);
glEnd();
end else
glDrawArrays(GL_QUADS, 0, 4);

编辑:这是delphi单元的代码。使用表单创建一个新项目,在其上添加timer1(启用,间隔= 1)和timer2(禁用,间隔= 1)对象,用此替换单元代码,插入表单事件:doubleclick/keydown/resize/destroy。请注意,这是在旧版本的 delphi 中编译的,因此一些 opengl 头文件被添加到单元的开头。另外,按向左/向右可更改 Sprite 数量,按空格可在 glDrawArrays 和 glBegin/glEnd 之间切换。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;

type
TForm1 = class(TForm)
Timer1: TTimer;
Timer2: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormDblClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

uses
opengl;

const
GL_BGRA_EXT = $80E1;
GL_VERTEX_ARRAY = $8074;
GL_TEXTURE_COORD_ARRAY = $8078;

type
PGLvoid = Pointer;

procedure glDeleteTextures(n: GLsizei; textures: pGLuint);stdcall;external opengl32;
procedure glGenTextures(n: GLsizei; textures: pGLuint);stdcall;external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint);stdcall;external opengl32;
procedure glEnableClientState(state: GLenum);stdcall;external opengl32;
procedure glDisableClientState(state: GLenum);stdcall;external opengl32;
procedure glTexCoordPointer(size: GLint; _type: GLenum; stride: GLsizei; const _pointer: PGLvoid);stdcall;external opengl32;
procedure glVertexPointer(size: GLint; _type: GLenum; stride: GLsizei; const _pointer: PGLvoid);stdcall;external opengl32;
procedure glDrawArrays(mode: GLenum; first: GLint; count: GLsizei);stdcall;external opengl32;

type
tgeo_point=record
x,y:longint;
end;

var
gl_Texture_Coordinates:array [0..7] of single=(0,0,0,1,1,1,1,0);
coords:array [0..3] of tgeo_point;
immediate:boolean=false;

type
tsprite=class
private
ix,iy:longint;
ix_dir,iy_dir:longint;
ialpha:longint;
public
constructor create;
destructor Destroy;override;

procedure update(w,h:longint);
end;

var
gl_dc:hdc;
gl_pixel_format:longint;
gl_context:longint;
gl_sprite_id:cardinal;
sprite:array [0..1023] of dword;
sprite_width:longint=32;
sprite_height:longint=32;
sprite_list:tlist;
times:array [0..10] of longint=(0,0,0,0,0,0,0,0,0,0,0);

procedure gl_init;
var
p,p2:tpixelformatdescriptor;
begin
gl_dc:=getdc(form1.handle);

zeromemory(@p,sizeof(p));
p.nSize:=sizeof(p);
p.nVersion:=1;
p.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
p.iPixelType:=PFD_TYPE_RGBA;
p.cColorBits:=32;
p.iLayerType:=PFD_MAIN_PLANE;

gl_pixel_format:=choosepixelformat(gl_dc,@p);
if gl_pixel_format=0 then
showmessage('error');
if not setpixelformat(gl_dc,gl_pixel_format,@p) then
showmessage('error');
describepixelformat(gl_dc,gl_pixel_format,sizeof(p2),p2);
if ((p.dwFlags and p2.dwFlags)<>p.dwFlags) or
(p.iPixelType<>p2.iPixelType) or
(p.cColorBits<>p2.cColorBits) or
(p.iLayerType<>p2.iLayerType) then
showmessage('errrrror');

gl_context:=wglcreatecontext(gl_dc);
if gl_context=0 then
showmessage('error');
if not wglmakecurrent(gl_dc,gl_context) then
showmessage('error');

glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

glViewport(0,0,form1.clientwidth,form1.clientheight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,form1.clientwidth,0,form1.clientheight,-1,1);
glMatrixMode(GL_MODELVIEW);

glColor4f(1,1,1,1);

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, @coords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,gl_float,0,@gl_Texture_Coordinates);

glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(gl_dc);
end;

procedure gl_un_init;
begin
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
wgldeletecontext(gl_context);
releasedc(form1.handle,gl_dc);
end;

procedure gl_resize;
begin
glViewport(0,0,form1.clientwidth,form1.clientheight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,form1.clientwidth,0,form1.clientheight,-1,1);
glMatrixMode(GL_MODELVIEW);
end;

function make_color(a,r,g,b:longint):cardinal;
begin
result:=(a and 255) shl 24 or
(r and 255) shl 16 or
(g and 255) shl 8 or
(b and 255);
end;

procedure sprite_init;
var
x,y:longint;
begin
for x:=0 to (sprite_width-1) do
for y:=0 to (sprite_height-1) do
sprite[y*(sprite_width)+x]:=
make_color((x div 2+1)*(y div 2+1)-1,$ff,$ff,$ff);

glgentextures(1,@gl_sprite_id);
glBindTexture(GL_TEXTURE_2D,gl_sprite_id);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_nearest);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_nearest);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_clamp);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_clamp);

glTexImage2D(GL_TEXTURE_2D,0,4,sprite_width,sprite_height,0,GL_BGRA_EXT,
GL_UNSIGNED_BYTE,@sprite);
end;

procedure sprite_un_init;
begin
gldeletetextures(1,@gl_sprite_id);
end;

constructor tsprite.create;
begin
inherited create;

ix:=random(form1.clientwidth);
iy:=random(form1.clientheight);

if random(2)=1 then
ix_dir:=1
else
ix_dir:=-1;

if random(2)=1 then
iy_dir:=1
else
iy_dir:=-1;

ialpha:=random(128);
end;

destructor tsprite.Destroy;
begin
inherited destroy;
end;

procedure tsprite.update(w,h:longint);
begin
if ix_dir=-1 then begin
dec(ix);
if ix<0 then begin
ix:=0;
ix_dir:=1;
end;
end else begin
inc(ix);
if ix>=w then begin
ix:=w;
ix_dir:=-1;
end;
end;

if iy_dir=-1 then begin
dec(iy);
if iy<0 then begin
iy:=0;
iy_dir:=1;
end;
end else begin
inc(iy);
if iy>=h then begin
iy:=h;
iy_dir:=-1;
end;
end;

coords[0].x:=ix;
coords[0].y:=iy;
coords[1].x:=ix;
coords[1].y:=iy+sprite_height;
coords[2].x:=ix+sprite_width;
coords[2].y:=iy+sprite_height;
coords[3].x:=ix+sprite_height;
coords[3].y:=iy;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
index:longint;
begin
for index:=0 to (sprite_list.count-1) do
tsprite(sprite_list[index]).free;
sprite_list.free;
sprite_un_init;
gl_un_init;
end;

// --nVidia video card memory
//const
// GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX=$9048;
// GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX=$9049;

procedure TForm1.FormDblClick(Sender: TObject);
var
a,b:longint;
begin
// glGetIntegerv(GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX,@a);
// glGetIntegerv(GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX,@b);
showmessage(
glgetstring(GL_VENDOR)+#13#10+
glgetstring(GL_RENDERER)+#13#10+
glgetstring(GL_VERSION)
+#13#10+'Memory: '+inttostr(b)+'/'+inttostr(a)
// +#13#10+glgetstring(GL_EXTENSIONS)
);
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
index:longint;
begin
case key of
vk_space:immediate:=not immediate;
vk_escape:form1.close;
vk_left:if sprite_list.count>0 then
for index:=(sprite_list.count-1) downto (sprite_list.count-100) do begin
tsprite(sprite_list[index]).free;
sprite_list.delete(index);
end;
vk_right:for index:=1 to 100 do sprite_list.add(tsprite.create);
end;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
gl_resize;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
timer1.enabled:=false;
timer2.enabled:=true;

gl_init;
sprite_init;
sprite_list:=tlist.create;
end;

procedure TForm1.Timer2Timer(Sender: TObject);
var
index,w,h,elapsed:longint;
s:tsprite;
ss:string;
begin
glClear(GL_COLOR_BUFFER_BIT);

w:=form1.clientwidth;
h:=form1.clientheight;
glBindTexture(GL_TEXTURE_2D,gl_sprite_id);
for index:=0 to (sprite_list.count-1) do begin
s:=sprite_list[index];
s.update(w,h);
glColor4b(127,127,127,s.ialpha);
if immediate then begin
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2i(coords[0].x,coords[0].y);
glTexCoord2f(0,1); glVertex2i(coords[1].x,coords[1].y);
glTexCoord2f(1,1); glVertex2i(coords[2].x,coords[2].y);
glTexCoord2f(1,0); glVertex2i(coords[3].x,coords[3].y);
glEnd();
end else
glDrawArrays(GL_QUADS, 0, 4);
end;
glBindTexture(GL_TEXTURE_2D,0);

SwapBuffers(gl_dc);

for index:=10 downto 1 do
times[index]:=times[index-1];
times[0]:=gettickcount;
elapsed:=times[0]-times[10];
if elapsed=0 then elapsed:=1;

if immediate then
ss:='glBegin/glEnd '
else
ss:='glDrawArrays ';
form1.caption:=ss+'Sprites: '+inttostr(sprite_list.count)+' / FPS: '+inttostr(10*1000 div elapsed);
end;

end.

edit2:向大家深表歉意,我忘记提及,在这种情况下,每个 Sprite 的单个纹理对于最终结果至关重要,即使我通过删除它来将渲染循环集中在 glBegin/glEnd 上来简化代码glDrawArrays。因疏忽造成误导,敬请谅解!

最佳答案

我实际上在 13 年前就开始使用 OpenGL,即使在那时我也可以告诉你,如果你的应用程序结构正确,顶点数组通常会更快。

那时我们还没有顶点缓冲区对象,但我们有交错顶点数组(我的字面意思是 glInterleavedArrays (...) )和 Compiled Vertex Arrays 。 NVIDIA 后来创建了一个扩展 ( Vertex Array Ranges ),允许将顶点数据存储在虚拟内存中(该虚拟内存的地址范围旨在允许高效的 DMA 传输)。

AMD当时仍被称为ATI,也有自己的扩展,可以提高顶点数组性能(通过在服务器端存储顶点数据),称为Vertex Array Object 。不要将 AMD 的扩展与现代 OpenGL 中我们所说的 VAO 混淆。事实上,AMD 的扩展为顶点缓冲区对象奠定了基础,只是不幸的是,它与完全不相关的东西共享了它的名字。

现在,我刚才讨论的所有内容实际上都描绘了 OpenGL 中顶点数组的演变。特别是,他们表明趋势是(1)将顶点数据存储在用户定义的内存组织中(2)存储在服务器(GPU)内存中以最大程度地提高重用和(3),并尽可能减少 API 调用。立即模式 (glBegin/glEnd) 违反了所有这些原则,使用立即模式您所能做的就是将命令放入显示列表中,并希望驱动程序能够处理点23


更新:

另请注意,由于我们谈论的硬件可以追溯到 OpenGL 1.1 时代,因此图形硬件并不总是处理顶点变换。长期以来,“GPU”的祖先仅在 CPU 上处理加速光栅化和顶点变换。在我们拥有可以实现整个图形管道的 GPU 之前,顶点和光栅化之间的分离意味着将顶点传递到管道的效率有多高实际上并不重要,因为某些工作是在 CPU 上完成的。一旦硬件 T&L 出现,服务器端顶点就变得至关重要。

这就是你问题的症结所在。您的软件的设置方式并未使其受益于顶点变换的硬件加速。您在 CPU 上完成所有转换,并在需要更改时随时向 GPU 发送新数据。现代应用程序使用顶点着色器和静态顶点数据来执行相同的操作,同时最大限度地减少每帧需要发送到 GPU 的数据量。 在现代软件中,大多数变换可以通过更新一个或两个用于顶点着色器的 4x4 矩阵来完成。

最重要的是,除非您的软件受顶点限制,否则提高顶点效率不会明显提高性能。 “Sprite blitting”对我来说听起来更像是一个片段绑定(bind)场景,特别是如果 Sprite 是 alpha 混合的。

关于delphi - 对于小数组来说,glDrawArrays 永远不会比 glBegin/glEnd 更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20707723/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com