SkiaSharp 性能比 Processing 差?

问题描述 投票:0回答:0

编辑:F# 代码已更新,因此它仅使用

Silk.NET
SkiaSharp
NuGet 包。它再现了同样较低的性能。

Processing中,您可以在

Examples -> Demos -> Performance -> LineRendering
中找到演示在800x600窗口中渲染50,000行的性能示例。这以 60fps 的速度运行。


public void setup() {
  size(800, 600, P2D);  
}
  
public void draw() {    
  background(255);
  stroke(0, 10);
  for (int i = 0; i < 50000; i++) {
    float x0 = random(width);
    float y0 = random(height);
    float z0 = random(-100, 100);
    float x1 = random(width);
    float y1 = random(height);
    float z1 = random(-100, 100);    
    
    // purely 2D lines will trigger the GLU 
    // tessellator to add accurate line caps,
    // but performance will be substantially
    // lower.
    line(x0, y0, x1, y1); // this line is modified from the example to use a 2D line
  }
  if (frameCount % 10 == 0) println(frameRate);
}

处理可以保持 60fps 的性能,直到绘制 70,000 条线,之后才下降 60fps。

我正在使用 SkiaSharpGLFW 进行窗口化,以在 F# 中进行跨平台窗口化和图形处理。我想检查我的窗口库的性能,所以我尝试复制这个 Processing 示例。令我惊讶的是我无法达到 60fps,所以我决定放弃使用仅包含 GLFW 和 SkiaSharp 的原始 for 循环。令我惊讶的是,性能根本没有提高,所以看起来性能瓶颈在 SkiaSharp 中。

open FSharp.NativeInterop
open Silk.NET.GLFW
open SkiaSharp
#nowarn "9"

let width, height = 800, 600

let glfw = Glfw.GetApi()
glfw.Init() |> printfn "Initialized?: %A"

// Uncomment these window hints if on macOS
//glfw.WindowHint(WindowHintInt.ContextVersionMajor, 3)
//glfw.WindowHint(WindowHintInt.ContextVersionMinor, 3)
//glfw.WindowHint(WindowHintBool.OpenGLForwardCompat, true)
//glfw.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Core)

let window = glfw.CreateWindow(width, height, "Test Window", NativePtr.ofNativeInt 0n, NativePtr.ofNativeInt 0n)
printfn "Window: %A" window
glfw.MakeContextCurrent(window)
let mutable error = nativeint<byte> 1uy |> NativePtr.ofNativeInt
glfw.GetError(&error) |> printfn "Error: %A"

let grGlInterface = GRGlInterface.Create(fun name -> glfw.GetProcAddress name)

if not(grGlInterface.Validate()) then
    raise (System.Exception("Invalid GRGlInterface"))

let grContext = GRContext.CreateGl(grGlInterface)
let grGlFramebufferInfo = new GRGlFramebufferInfo(0u, SKColorType.Rgba8888.ToGlSizedFormat()) // 0x8058
let grBackendRenderTarget = new GRBackendRenderTarget(width, height, 1, 0, grGlFramebufferInfo)
let surface = SKSurface.Create(grContext, grBackendRenderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888)
let canvas = surface.Canvas
grContext.ResetContext()

let random = System.Random()

let randomFloat (maximumNumber: int) =
    (float (maximumNumber + 1)) * random.NextDouble()
    |> float32

// Setup up mutable bindings and a function to calculate the framerate
let mutable lastRenderTime = System.DateTimeOffset.Now.ToUnixTimeMilliseconds()
let mutable currentRenderTime = System.DateTimeOffset.Now.ToUnixTimeMilliseconds()
let mutable numberOfFrameRatesToAverage = 30
let mutable frameRates = Array.zeroCreate<float>(numberOfFrameRatesToAverage)
let mutable frameRateArrayIndex = 0

let calculateFrameRate () =
    lastRenderTime <- currentRenderTime
    currentRenderTime <- System.DateTimeOffset.Now.ToUnixTimeMilliseconds()
    let currentFrameRate = 1.0 / (float(currentRenderTime - lastRenderTime) / 1000.0)
    frameRates[frameRateArrayIndex] <- currentFrameRate
    frameRateArrayIndex <- (frameRateArrayIndex + 1) % numberOfFrameRatesToAverage
    (Array.sum frameRates) / (float numberOfFrameRatesToAverage)

let linePaint = new SKPaint(Color = SKColor(0uy, 0uy, 0uy, 10uy))

let frameRatePaint = new SKPaint(Color = SKColor(byte 0, byte 0, byte 0, byte 255))
frameRatePaint.TextSize <- 50.0f

while not (glfw.WindowShouldClose(window)) do
    glfw.PollEvents()
    
    canvas.Clear(SKColors.WhiteSmoke)

    for _ in 1..50_000 do
        canvas.DrawLine(
            SKPoint(randomFloat <| int width, randomFloat <| int height),
            SKPoint(randomFloat <| int width, randomFloat <| int height),
            linePaint)
    let frameRate = calculateFrameRate()

    canvas.DrawText(sprintf "%.0f" frameRate, 10.0f, 50.0f, frameRatePaint)

    canvas.Flush()
    glfw.SwapBuffers(window)
        
glfw.DestroyWindow(window)
glfw.Terminate()

如您所见,性能范围在 40-50fps 之间。我对此感到非常惊讶,而且我看不出我的代码与 Processing 似乎在做什么有什么本质上的不同。我以为可能是

sprintf
拖后腿,但我用
string
和其他各种东西代替它,这似乎并没有影响它。此外,如果我减少绘制的线条数量,性能肯定会提高,所以我不认为
sprintf
是这里的瓶颈。

这里发生了什么?仅仅是处理(以及底层 Java 和 Java 图形)比 F# 和 SkiaSharp 更快的简单情况吗?

我使用的是 F# 和 .NET 7、最新的 SkiaSharp 和 GLFW 3。对于处理,我使用的是最新版本的 Processing 4。这些测试是在配备第 12 代 i7 和 NVIDIA RTX 3050 的笔记本电脑上完成的。

graphics f# processing skiasharp skia
© www.soinside.com 2019 - 2024. All rights reserved.