- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
对于一个客户项目,我需要一个可以在旧硬件上运行的简单 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
) 违反了所有这些原则,使用立即模式您所能做的就是将命令放入显示列表中,并希望驱动程序能够处理点2和3。
另请注意,由于我们谈论的硬件可以追溯到 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/
有人可以解释预定义谓词forall如何在列表中找到最小值吗? 最佳答案 对于列表L,您可以使用: member(Min,L), forall(member(N,L), N>=Min). 但是,尽管这是
编辑:澄清一下,我正在搜索的对象数组确实已按搜索变量的字母数字顺序进行了预排序。 我做了一个二分搜索函数并将它嵌套在另一个函数中。出于某种原因,每次我使用二进制搜索都无法找到相关的字符数组。 基本上,
是否可以阻止用户(甚至是管理员)终止我的程序? 或者万一被杀死,它会迅速恢复自身? 更新:澄清一下:我正在编写一个监控程序,类似于家长控制,它记录用户对 PC 的操作。你可以通过查看我最近的其他问题来
我有一个 for 循环,我希望它永远递增。 我的代码: for a in (0...Float::INFINITY).step(2) puts a end 输出: 0.0 2.0 4.0 Et
我很困惑。我有一个运行Ubuntu 14.04的VM。我在这里遵循了以下程序:http://clang.llvm.org/docs/LibASTMatchersTutorial.html,现在正在运行
这是我的代码 #include #include #include #include #include #include #include #include #include usi
我有一个程序会或多或少地通过标准输入使用 COPY FROM 将大量数据复制到 Postgres 9 中。 这目前工作正常,但我正在缓冲数据 block ,然后分批运行 COPY FROM 操作。 我
我想我不小心在某个地方安装了 Foreverjs 并启动了它。每次我杀死这个进程时,另一个进程就会取代它的位置 ] 1 我不知道永远在哪里(或者这实际上是导致它的原因),因为我在本地安装了它。 最佳答
我得到了一个 forever: command not found 当我使用 forever 命令作为 cronjob 运行 nodejs 进程时出现错误(在亚马逊 ec2 机器中):我正在使用的 b
我创建了一些容器,它们还没有准备好使用,总是“重新启动”状态: docker ps CONTAINER ID IMAGE COMMAND
我试图永远重复一个 IO 操作,但是将一个执行的结果输入到下一个执行中。像这样的东西: -- poorly named iterateM :: Monad m => (a -> m a) -> a -
这里的代码样式问题。 我看着this问题,它询问.NET CLR是否真的总是初始化字段值。 (答案是肯定的。)但令我感到惊讶的是,我不确定执行此操作始终是个好主意。我的想法是,如果我看到这样的声明:
美好的一天,我对永久启动\停止脚本有一些问题。 中央操作系统 6.2 内核 2.6.32-220.el6.x86_64 node.js v0.6.19 npm v 1.1.24 永远@0.9.2 我创
我在让管道与 paramiko 一起工作时遇到问题。 这个有效: ssh = paramiko.SSHClient() [...] stdin, stdout, stderr = ssh.exec_c
我希望守护我的 Node.js 应用程序。 Upstart 和永远有什么区别?另外,还有其他我可能想要考虑的软件包吗? 最佳答案 正如评论中指出的,upstart将用于启动 forever脚本,因为
我有以下查询,其中包含在 5 秒内返回数据的选择查询。但是当我在前面添加创建物化 View 命令时,查询需要创建物化 View 。 最佳答案 当您创建物化 View 时,实际上是创建了 Oracle
当我今天访问我的项目的 Google Cloud 控制台并单击“计算引擎”或“云存储”时,它只会永远显示“正在加载”。几天前,我能够看到我的虚拟机和存储桶。有没有办法让控制台再次工作? 谢谢, 麦克风
我编写了一个函数,它当前显示 1000 以下的所有质数。 我可以继续增大 1000 以生成更多数字,但我不知道如何让它在运行后一直持续下去。 func generatePrimes() { l
这是由 another question 触发的. 具体来说,我有一个进程中的 COM 类,它在 CLSID registry 中定义。因为有 ThreadingModel of Both . 我们的
我正在试用新的 React Hooks的 useEffect API,它似乎永远在无限循环中运行!我只希望 useEffect 中的回调运行一次。这是我的引用代码: 单击“运行代码片段”以查看“运行
我是一名优秀的程序员,十分优秀!