Android TextureView / Zeichnung / Malerei Leistung

Ich versuche, eine Zeichnung / Malerei App mit TextureView auf Android zu machen. Ich möchte eine Zeichnungsoberfläche von bis zu 4096×4096 Pixel unterstützen, was für mein minimales Zielgerät (welches ich zum Testen verwenden) vernünftig erscheint, was ein Google Nexus 7 2013 ist, der eine schöne Quad Core CPU und 2GB Speicher hat.

Einer meiner Anforderungen ist, dass meine Ansicht in einer Ansicht sein muss, die es erlaubt, gezoomt zu werden und heraus und verschoben, das ist alles benutzerdefinierte Code, den ich geschrieben habe (denke UIScrollView von iOS).

Ich habe versucht, mit einem regulären View (nicht TextureView ) mit OnDraw und Leistung war absolut schrecklich – weniger als 1 Frame eine Sekunde. Dies geschah auch, wenn ich Invalidate(rect) mit nur dem Rektum anrief, das sich geändert hat. Ich habe versucht, Hardware Beschleunigung für die Ansicht auszuschalten, aber nichts gerendert, nehme ich an, weil 4096×4096 zu groß für Software ist.

Ich habe dann versucht TextureView und Leistung ist ein wenig besser – ca. 5-10 Frames pro Sekunde (immer noch schrecklich aber besser). Der Benutzer zieht in eine Bitmap, die dann später in die Textur mit einem Hintergrund-Thread gezeichnet wird. Ich benutze Xamarin aber hoffentlich ist der Code sinnvoll für Java-Leute.

 private void RunUpdateThread() { try { TimeSpan sleep = TimeSpan.FromSeconds(1.0f / 60.0f); while (true) { lock (dirtyRect) { if (dirtyRect.Width() > 0 && dirtyRect.Height() > 0) { Canvas c = LockCanvas(dirtyRect); if (c != null) { c.DrawBitmap(bitmap, dirtyRect, dirtyRect, bufferPaint); dirtyRect.Set(0, 0, 0, 0); UnlockCanvasAndPost(c); } } } Thread.Sleep(sleep); } } catch { } } 

Wenn ich lockCanvas ändere, um null anstelle eines rektors zu übergeben, ist die Leistung bei 60 fps groß, aber der Inhalt der TextureView flackert und wird beschädigt, was enttäuschend ist. Ich hätte gedacht, dass es einfach wäre, einen OpenGL-Frame-Puffer / Render-Textur zu verwenden oder zumindest eine Möglichkeit zu haben, Inhalte zu bewahren.

Gibt es noch andere Möglichkeiten, alles in rohem OpenGL in Android für Hochleistungs-Zeichnung und Malerei auf einer Oberfläche zu tun, die zwischen den Anrufen von Anrufen bewahrt wird?

  • Android Paint Strich-Positionierung
  • Präge Kanten einer Bildform, die die Tiefe in Android zeigt
  • Wie man Frame-Koordinaten auf Overlay in Vision abbildet
  • Canvas.clipPath (Pfad) nicht beschnitten wie erwartet
  • Wie zeichne ungefüllte Figuren auf Android?
  • Android: Wie bekomme ich eine benutzerdefinierte Ansicht, um teilweise neu zu zeichnen?
  • Tipps zur Hochleistungszeichnung in Android
  • Kann ich außerhalb der Grenzen eines Android Canvas ziehen
  • 2 Solutions collect form web for “Android TextureView / Zeichnung / Malerei Leistung”

    Zuerst einmal, wenn du verstehen willst, was unter der Kapuze los ist, musst du das Android Graphics Architecture Dokument lesen. Es ist lange, aber wenn Sie aufrichtig das "Warum" verstehen wollen, ist es der Ort, um zu beginnen.

    Über TextureView

    TextureView funktioniert wie folgt: Es hat eine Oberfläche, die eine Schlange von Puffern mit einem Produzenten-Verbraucher-Beziehung ist. Wenn du Software (Canvas) Rendering benutzt, sperrst du die Oberfläche, die dir einen Puffer gibt Du ziehst es an; Dann entriegelst du die Oberfläche, die den Puffer an den Verbraucher sendet. Der Verbraucher ist in diesem Fall im selben Prozess und heißt SurfaceTexture oder (intern, treffender) GLConsumer. Es konvertiert den Puffer in eine OpenGL ES-Textur, die dann in die View übertragen wird.

    Wenn Sie die Hardwarebeschleunigung deaktivieren, ist GLES deaktiviert und TextureView kann nichts machen. Deshalb hast du nichts, wenn du Hardwarebeschleunigung ausgeschaltet hast. Die Dokumentation ist sehr spezifisch: "TextureView kann nur in einem beschleunigten Hardware-Fenster verwendet werden. Bei der gerenderten Software wird TextureView nichts zeichnen."

    Wenn Sie einen dirty rect angeben, wird der Software-Renderer den vorherigen Inhalt in den Frame einfangen, nachdem das Rendering abgeschlossen ist. Ich glaube nicht, dass es einen Clip regelt, also wenn du drawColor () nennst, wirst du den ganzen Bildschirm füllen und dann die Pixel überschrieben. Wenn Sie derzeit nicht einen Clip regeln, können Sie sehen, einige Leistung profitieren von dies zu tun. (Ich habe den Code aber nicht überprüft.)

    Der dirty rect ist ein in-out Parameter. Sie übergeben das Recht, das Sie wollen, wenn Sie lockCanvas() anrufen, und das System darf es ändern, bevor der Anruf zurückkehrt. (In der Praxis wäre der einzige Grund, weshalb es wäre, wenn es keinen vorherigen Rahmen gäbe oder die Oberfläche verkleinert wäre. In diesem Fall würde es ihn erweitern, um den gesamten Bildschirm zu decken. Ich denke, das wäre besser gehandhabt mit einem direkteren "Ich lehne dein richtiges" Signal ab.) Du musst jedes Pixel in der rechten Seite aktualisieren. Sie sind nicht berechtigt, das Rektal zu ändern, was Sie in Ihrer Probe zu tun versuchen – was auch immer in den schmutzigen lockCanvas() ist, nachdem lockCanvas() ist, was Sie lockCanvas() müssen.

    Ich vermute, dass die verschmutzte, korrekte Handhabung die Quelle deines Flackerns ist. Leider ist das ein einfacher Fehler zu machen, da das Verhalten der lockCanvas() dirtyRect arg nur in der Surface-Klasse selbst dokumentiert ist.

    Oberflächen und Pufferung

    Alle Flächen sind doppelt oder dreifach gepuffert. Es gibt keinen Weg um diese – Sie können nicht lesen und schreiben gleichzeitig und nicht reißen. Wenn du einen einzigen Puffer wünschst, den du ändern kannst und drückst, wenn gewünscht, muss dieser Puffer gesperrt, kopiert und freigeschaltet werden, was Stände in der Kompositionspipeline erzeugt. Für den besten Durchsatz und die Latenzzeit sind die Pufferpuffer besser.

    Wenn du das Lock-Copy-Unlock-Verhalten willst, kannst du das selbst schreiben (oder eine Bibliothek finden, die es macht), und es wird so effizient sein wie es wäre, wenn das System es für dich getan hat (vorausgesetzt, du bist gut mit Blitzschleifen). Zeichnen Sie auf eine Off-Screen-Leinwand und blit die Bitmap oder ein OpenGL ES FBO und blit den Puffer. Ein Beispiel dafür finden Sie in der " Record GL App " -Aktivität von Grafika, die einen Modus hat, der einmal ausgeschaltet ist und dann zweimal blendet (einmal für die Anzeige, einmal für die Aufnahme von Video).

    Mehr Geschwindigkeit und so

    Es gibt zwei grundlegende Möglichkeiten, um Pixel auf Android zu zeichnen: mit Canvas oder mit OpenGL. Canvas-Rendering zu einer Oberfläche oder Bitmap wird immer in Software durchgeführt, während OpenGL-Rendering mit der GPU durchgeführt wird. Die einzige Ausnahme ist, dass Sie beim Rendern einer benutzerdefinierten Ansicht die Hardwarebeschleunigung verwenden können, aber dies gilt nicht, wenn Sie die Oberfläche einer SurfaceView oder TextureView rendern.

    Eine Zeichnung oder Malerei App kann entweder an die Zeichnung Befehle erinnern, oder einfach nur werfen Pixel an einem Puffer und verwenden, dass als Speicher. Das erstere erlaubt tieferes "rückgängig", das letztere ist viel einfacher und hat immer bessere Leistung als die Menge an Sachen, um zu wachsen. Es klingt wie du willst das letztere machen, so dass Blasen von Off-Screen sinnvoll ist.

    Die meisten mobilen Geräte haben eine Hardware-Begrenzung von 4096×4096 oder kleiner für GLES-Texturen, so dass Sie nicht in der Lage, eine einzige Textur für etwas größer zu verwenden. Sie können den Größengrenzwert (GL_MAX_TEXTURE_SIZE) abfragen, aber Sie können mit einem internen Puffer, der so groß ist, wie Sie wollen, besser sein, und machen Sie einfach den Teil, der auf dem Bildschirm passt. Ich weiß nicht, was die Skia (Canvas) Beschränkung ist offhand, aber ich glaube, Sie können viel größere Bitmaps erstellen.

    Abhängig von Ihren Bedürfnissen kann eine SurfaceView einer TextureView vorzuziehen sein, da sie den Zwischen-GLES-Texturschritt vermeidet. Alles, was du auf die Oberfläche ziehst, geht direkt zum Systemkomponisten (SurfaceFlinger). Der Nachteil dieses Ansatzes ist, dass, weil die Oberfläche der Verbraucher ist nicht in-Prozess, gibt es keine Möglichkeit für das View-System, um die Ausgabe zu behandeln, so dass die Oberfläche ist eine unabhängige Schicht. (Für ein Zeichnungsprogramm könnte dies von Vorteil sein – das Bild, das gezeichnet wird, ist auf einer Ebene, Ihre Benutzeroberfläche ist auf einer separaten Ebene oben.)

    FWIW, ich habe den Code nicht angesehen, aber Dan Sandlers Marker App könnte einen Blick wert sein (Quellcode hier ).

    Update: Die Korruption wurde als Bug identifiziert und in 'L' behoben .

    UPDATE Ich habe TextureView gegraben und nun eine OpenGL-Ansicht verwenden, wo ich glTexSubImage2D anrufe, um geänderte Stücke einer Render-Textur zu aktualisieren.

    ALTE ANTWORT Ich landete TextureView in einem 4×4 Raster. Abhängig von den verschmutzten Rechten jedes Bildes, erfrue ich die entsprechenden TextureView Ansichten. Jede Ansicht, die nicht aktualisiert wird, dass Rahmen Ich rufe Invalidate auf.

    Einige Geräte wie das Moto G-Telefon haben ein Problem, bei dem die Doppelpufferung für einen Rahmen beschädigt ist. Sie können das reparieren, indem Sie lockCanvas zweimal aufrufen, wenn die übergeordnete Ansicht es ist onLayout aufgerufen wird.

     private void InvalidateRect(int l, int t, int r, int b) { dirtyRect.Set(l, t, r, b); foreach (DrawSubView v in drawViews) { if (Rect.Intersects(dirtyRect, v.Clip)) { v.RedrawAsync(); } else { v.Invalidate(); } } Invalidate(); } protected override void OnLayout(bool changed, int l, int t, int r, int b) { for (int i = 0; i < ChildCount; i++) { View v = GetChildAt(i); v.Layout(v.Left, v.Top, v.Right, v.Bottom); DrawSubView sv = v as DrawSubView; if (sv != null) { sv.RedrawAsync(); // why are we re-drawing you ask? because of double buffering bugs in Android :) PostDelayed(() => sv.RedrawAsync(), 50); } } } 
    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.