- 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/
我正在尝试创建一个包含 int[][] 项的数组 即 int version0Indexes[][4] = { {1,2,3,4}, {5,6,7,8} }; int version1Indexes[
我有一个整数数组: private int array[]; 如果我还有一个名为 add 的方法,那么以下有什么区别: public void add(int value) { array[va
当您尝试在 JavaScript 中将一个数组添加到另一个数组时,它会将其转换为一个字符串。通常,当以另一种语言执行此操作时,列表会合并。 JavaScript [1, 2] + [3, 4] = "
根据我正在阅读的教程,如果您想创建一个包含 5 列和 3 行的表格来表示这样的数据... 45 4 34 99 56 3 23 99 43 2 1 1 0 43 67 ...它说你可以使用下
我通常使用 python 编写脚本/程序,但最近开始使用 JavaScript 进行编程,并且在使用数组时遇到了一些问题。 在 python 中,当我创建一个数组并使用 for x in y 时,我得
我有一个这样的数组: temp = [ 'data1', ['data1_a','data1_b'], ['data2_a','data2_b','data2_c'] ]; // 我想使用 toStr
rent_property (table name) id fullName propertyName 1 A House Name1 2 B
这个问题在这里已经有了答案: 关闭13年前。 Possible Duplicate: In C arrays why is this true? a[5] == 5[a] array[index] 和
使用 Excel 2013。经过多年的寻找和适应,我的第一篇文章。 我正在尝试将当前 App 用户(即“John Smith”)与他的电子邮件地址“jsmith@work.com”进行匹配。 使用两个
当仅在一个边距上操作时,apply 似乎不会重新组装 3D 数组。考虑: arr 1),但对我来说仍然很奇怪,如果一个函数返回一个具有尺寸的对象,那么它们基本上会被忽略。 最佳答案 这是一个不太理
我有一个包含 GPS 坐标的 MySQL 数据库。这是我检索坐标的部分 PHP 代码; $sql = "SELECT lat, lon FROM gps_data"; $stmt=$db->query
我需要找到一种方法来执行这个操作,我有一个形状数组 [批量大小, 150, 1] 代表 batch_size 整数序列,每个序列有 150 个元素长,但在每个序列中都有很多添加的零,以使所有序列具有相
我必须通过 url 中的 json 获取文本。 层次结构如下: 对象>数组>对象>数组>对象。 我想用这段代码获取文本。但是我收到错误 :org.json.JSONException: No valu
enter code here- (void)viewDidLoad { NSMutableArray *imageViewArray= [[NSMutableArray alloc] init];
知道如何对二维字符串数组执行修剪操作,例如使用 Java 流 API 进行 3x3 并将其收集回相同维度的 3x3 数组? 重点是避免使用显式的 for 循环。 当前的解决方案只是简单地执行一个 fo
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我有来自 ASP.NET Web 服务的以下 XML 输出: 1710 1711 1712 1713
如果我有一个对象todo作为您状态的一部分,并且该对象包含数组列表,则列表内部有对象,在这些对象内部还有另一个数组listItems。如何更新数组 listItems 中 id 为“poi098”的对
我想将最大长度为 8 的 bool 数组打包成一个字节,通过网络发送它,然后将其解压回 bool 数组。已经在这里尝试了一些解决方案,但没有用。我正在使用单声道。 我制作了 BitArray,然后尝试
我们的数据库中有这个字段指示一周中的每一天的真/假标志,如下所示:'1111110' 我需要将此值转换为 boolean 数组。 为此,我编写了以下代码: char[] freqs = weekday
我是一名优秀的程序员,十分优秀!