代码之家  ›  专栏  ›  技术社区  ›  matt

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

  •  10
  • matt  · 技术社区  · 15 年前

    我有一个“软件渲染器”,我正在从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());
    

    似乎对所有的减速都有很大的责任。

    把它注释掉,然后把所有的图形用户界面代码和现在未更新的纹理的渲染留在我的软件中,结果是60帧每秒,30%的渲染器使用率,CPU没有显著的峰值。

    注意,getbuffer()只是返回一个指向GUI系统的软件backbuffer的指针,缓冲区不需要重新编译或调整大小,它的大小和格式适合纹理,所以我可以肯定,速度的减慢与软件渲染器无关,这是个好消息,如果我能找到一种方法在60时更新屏幕,软件渲染暂时可以工作。

    我尝试用512320而不是512512进行更新纹理调用,这是奇怪的,甚至更慢…它以10fps的速度运行,并且说渲染利用率只有5%,所有的时间都浪费在调用OpenGles中的untWiddle32bpp上。

    我可以将我的软件渲染改为本地渲染为任何像素格式,如果它会导致更直接的blit。

    仅供参考,在2.2.1 iPod touch G2上测试(就像在类固醇上测试iPhone 3G一样)

    更新2:

    我刚写完coreanimation/graphics方法,看起来不错,但我有点担心它如何更新屏幕的每一帧,基本上放弃了旧的cgimage,创建了一个全新的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 回复  |  直到 8 年前
        1
  •  11
  •   rpetrich    15 年前

    最快的应用商店批准的方法做CPU只有二维图形是创建一个 CGImage 由缓冲区支持,使用 CGDataProviderCreateDirect 把它分配给 CALayer contents 属性。

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

    编辑:理论上,这应该比绘制OpenGL纹理快,但和往常一样,轮廓要确定。

    编辑2: CADisplayLink 是一个有用的类,无论您使用哪种合成方法。

        2
  •  3
  •   kennytm    15 年前

    最快的方法是使用ioframebuffer/iosurface,这是私有框架。

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

        3
  •  3
  •   ldoogy    8 年前

    只需将我的评论以答案的形式发布到@rpetrich的答案中,我会说在我的测试中,我发现OpenGL是最快的方法。我实现了一个简单的对象(uiview子类),名为 EEPixelViewer 一般来说,这样做足以让我认为对大多数人都有效。

    它使用OpenGL将各种格式(24bpp-rgb、32位rgb a和几种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 /按预期完成所有工作。

        4
  •  1
  •   kineticfocus    13 年前

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

        5
  •  0
  •   Community CDub    8 年前

    比两者都快的方法 CGDataProvider glTexSubImage 是使用 CVOpenGLESTextureCache . 这个 cvOpenGlestextureCache(cvOpenGlestextureCache) 允许您直接修改图形内存中的OpenGL纹理,而无需重新上载。

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

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

    使用起来有点困难,我是在问了自己关于这个主题的问题之后发现的: How to directly update pixels - with CGImage and direct CGDataProvider