在iphone上绘制屏幕缓冲区的最快方法

时间:2010-03-07 08:37:24

标签: iphone user-interface rendering

我有一个“软件渲染器”,我从PC移植到iPhone。使用iphone上的像素缓冲区手动更新屏幕的最快方法是什么?例如在Windows中,我找到的最快的函数是SetDIBitsToDevice。

我对iphone或库不太了解,似乎有很多层和不同类型的UI元素,所以我可能需要大量的解释...

现在我只是不断更新opengl中的纹理并将其渲染到屏幕上,我非常怀疑这将是最好的方法。

更新:

我尝试过openGL屏幕大小的纹理方法:

我得到了17fps ......

我使用了512x512纹理(因为它需要是2的幂)

只是

的召唤
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,512,512,GL_RGBA,GL_UNSIGNED_BYTE, baseWindowGUI->GetBuffer());

似乎对所有减速负责。

将其评论出来,并留下我所有的软件渲染GUI代码,以及现在非更新纹理的渲染,导致60fps,30%的渲染器使用率,以及来自cpu的没有明显的峰值。

请注意,GetBuffer()只返回一个指向GUI系统软件后台缓冲区的指针,无论如何都没有重新调整缓冲区或调整缓冲区大小,它的大小和纹理格式正确,所以我相当肯定减速与软件渲染器无关,这是个好消息,看起来如果我能找到一种方法在60点更新屏幕,软件渲染应该暂时有用。

我尝试使用512,320而不是512,512进行更新纹理调用,这奇怪甚至更慢...以10fps运行,也表示渲染利用率仅为5%,并且所有时间都被浪费在调用在openGLES里面unntwiddle32bpp。

我可以将我的软件渲染更改为原生渲染为任何pixle格式,如果它会导致更直接的blit。

fyi,在2.2.1 ipod touch G2上测试(就像类固醇上的Iphone 3G一样)

更新2:

我刚刚写完了CoreAnimation / Graphics方法,它看起来不错,但是我有点担心它如何更新每一帧的屏幕,基本上抛弃旧的CGImage,创建一个全新的...查看它在下面的'someRandomFunction'中: 这是更新图像的最快方法吗?任何帮助将不胜感激。

//
//  catestAppDelegate.m
//  catest
//
//  Created by User on 3/14/10.
//  Copyright __MyCompanyName__ 2010. All rights reserved.
//




#import "catestAppDelegate.h"
#import "catestViewController.h"
#import "QuartzCore/QuartzCore.h"

const void* GetBytePointer(void* info)
{
    // this is currently only called once
    return info; // info is a pointer to the buffer
}

void ReleaseBytePointer(void*info, const void* pointer)
{
    // don't care, just using the one static buffer at the moment
}


size_t GetBytesAtPosition(void* info, void* buffer, off_t position, size_t count)
{
    // I don't think this ever gets called
    memcpy(buffer, ((char*)info) + position, count);
    return count;
}

CGDataProviderDirectCallbacks providerCallbacks =
{ 0, GetBytePointer, ReleaseBytePointer, GetBytesAtPosition, 0 };


static CGImageRef cgIm;

static CGDataProviderRef dataProvider;
unsigned char* imageData;
 const size_t imageDataSize = 320 * 480 * 4;
NSTimer *animationTimer;
NSTimeInterval animationInterval= 1.0f/60.0f;


@implementation catestAppDelegate

@synthesize window;
@synthesize viewController;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    


    [window makeKeyAndVisible];


    const size_t byteRowSize = 320 * 4;
    imageData = malloc(imageDataSize);

    for(int i=0;i<imageDataSize/4;i++)
            ((unsigned int*)imageData)[i] = 0xFFFF00FF; // just set it to some random init color, currently yellow


    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    dataProvider =
    CGDataProviderCreateDirect(imageData, imageDataSize,
                               &providerCallbacks);  // currently global

    cgIm = CGImageCreate
    (320, 480,
     8, 32, 320*4, colorSpace,
     kCGImageAlphaNone | kCGBitmapByteOrder32Little,
     dataProvider, 0, false, kCGRenderingIntentDefault);  // also global, probably doesn't need to be

    self.window.layer.contents = cgIm; // set the UIWindow's CALayer's contents to the image, yay works!

   // CGImageRelease(cgIm);  // we should do this at some stage...
   // CGDataProviderRelease(dataProvider);

    animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(someRandomFunction) userInfo:nil repeats:YES];
    // set up a timer in the attempt to update the image

}
float col = 0;

-(void)someRandomFunction
{
    // update the original buffer
    for(int i=0;i<imageDataSize;i++)
        imageData[i] = (unsigned char)(int)col;

    col+=256.0f/60.0f;

    // and currently the only way I know how to apply that buffer update to the screen is to
    // create a new image and bind it to the layer...???
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    cgIm = CGImageCreate
    (320, 480,
     8, 32, 320*4, colorSpace,
     kCGImageAlphaNone | kCGBitmapByteOrder32Little,
     dataProvider, 0, false, kCGRenderingIntentDefault);

    CGColorSpaceRelease(colorSpace);

    self.window.layer.contents = cgIm;

    // and that currently works, updating the screen, but i don't know how well it runs...
}


- (void)dealloc {
    [viewController release];
    [window release];
    [super dealloc];
}


@end

5 个答案:

答案 0 :(得分:11)

App Store批准的最快的仅使用CPU的2D图形的方法是使用CGImage创建一个由缓冲区支持的CGDataProviderCreateDirect并将其分配给CALayer的{​​{1}属性。

为获得最佳效果,请使用contentskCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little位图类型和双缓冲区,以便显示永远不会处于不一致状态。

编辑:这应该比理论上绘制到OpenGL纹理更快,但是一如既往,配置文件可以肯定。

edit2:无论您使用哪种合成方法,kCGImageAlphaNone | kCGBitmapByteOrder32Little都是一个有用的类。

答案 1 :(得分:3)

最快的方法是使用IOFrameBuffer / IOSurface,它们是私有框架。

因此,OpenGL似乎是AppStore应用程序的唯一可行方式。

答案 2 :(得分:3)

只是以答案的形式将我的评论发布到@ rpetrich的答案,我会在测试中说我发现OpenGL是最快的方式。我已经实现了一个名为EEPixelViewer的简单对象(UIView子类),这个对象通用性很强,它应该适用于我认为的大多数人。

它使用OpenGL将各种格式的像素(24bpp RGB,32位RGBA和几种YpCbCr格式)尽可能高效地推送到屏幕。该解决方案几乎在每个iOS设备(包括旧设备)上实现了大多数像素格式的60fps。用法非常简单,不需要OpenGL知识:

pixelViewer.pixelFormat = kCVPixelFormatType_32RGBA;
pixelViewer.sourceImageSize = CGSizeMake(1024, 768);
EEPixelViewerPlane plane;
plane.width = 1024;
plane.height = 768;
plane.data = pixelBuffer;
plane.rowBytes = plane.width * 4;
[pixelViewer displayPixelBufferPlanes: &plane count: 1 withCompletion:nil];

对每个帧重复displayPixelBufferPlanes调用(使用glTexImage2D将像素缓冲区加载到GPU),这几乎就是它的全部内容。代码很聪明,因为它试图将GPU用于任何所需的简单处理,例如置换颜色通道,将YpCbCr转换为RGB等。

使用UIView的contentMode属性来实现扩展也有很多逻辑,所以UIViewContentModeScaleToFit / Fill等都按预期工作。

答案 3 :(得分:1)

也许您可以将软件渲染器中使用的方法抽象为GPU着色器......可能会获得更好的性能。您需要将编码的“视频”数据作为纹理发送。

答案 4 :(得分:0)

CGDataProviderglTexSubImage更快的方法是使用CVOpenGLESTextureCacheCVOpenGLESTextureCache允许您直接修改图形内存中的OpenGL纹理而无需重新上传。

我将它用于快速动画视图,你可以在这里看到:

https://github.com/justinmeiners/image-sequence-streaming

使用起来有点棘手,我在问自己关于这个主题的问题后遇到了它:How to directly update pixels - with CGImage and direct CGDataProvider