Open
Description
Description
When I scroll this swift file with 387 LOC:
Click me
import Foundation
import AVFoundation
import CoreGraphics
import VideoToolbox
import AppKit
// MARK: - Virtual Desktop Handler
class VirtualDesktopManager {
private var displayStream: CGDisplayStream?
private let queue = DispatchQueue(label: "com.videostreaming.capture")
func startCapturing(width: Int, height: Int, handler: @escaping (CGImage?) -> Void) {
print("Checking screen recording permissions...")
// Check screen recording permission
if CGPreflightScreenCaptureAccess() {
print("Screen recording permission already granted")
} else {
print("Requesting screen recording permission...")
CGRequestScreenCaptureAccess()
// Wait for permission
while !CGPreflightScreenCaptureAccess() {
Thread.sleep(forTimeInterval: 0.1)
}
print("Screen recording permission granted")
}
print("Starting screen capture...")
let displayID = CGMainDisplayID()
// Get the display bounds
let displayWidth = CGDisplayPixelsWide(displayID)
let displayHeight = CGDisplayPixelsHigh(displayID)
// Calculate scaled dimensions while maintaining aspect ratio
let scale = min(Double(width) / Double(displayWidth), Double(height) / Double(displayHeight))
let scaledWidth = Int(Double(displayWidth) * scale)
let scaledHeight = Int(Double(displayHeight) * scale)
print("Display dimensions: \(displayWidth)x\(displayHeight)")
print("Scaled dimensions: \(scaledWidth)x\(scaledHeight)")
let properties: [CFString: Any] = [
CGDisplayStream.showCursor: true,
CGDisplayStream.minimumFrameTime: 1.0/30.0
]
displayStream = CGDisplayStream(
dispatchQueueDisplay: displayID,
outputWidth: scaledWidth,
outputHeight: scaledHeight,
pixelFormat: Int32(kCVPixelFormatType_32BGRA),
properties: properties as CFDictionary,
queue: queue,
handler: { [weak self] (status, displayTime, frameSurface, error) in
guard let self = self else { return }
switch status {
case .frameComplete:
if let frameSurface = frameSurface,
let image = self.createCGImage(from: frameSurface) {
handler(image)
}
case .stopped:
print("Display stream stopped")
case .frameBlank:
print("Frame blank")
case .frameIdle:
print("Frame idle")
@unknown default:
print("Unknown frame status: \(status)")
}
}
)
if displayStream == nil {
print("Failed to create display stream")
return
}
print("Starting display stream...")
if let startError = displayStream?.start() {
print("Display stream start failed with error: \(startError)")
// Print error code
print("Error code: \(startError.rawValue)")
// Handle common error cases
switch startError.rawValue {
case 1000:
print("Permission denied or not available")
case 1001:
print("Invalid display")
case 1002:
print("Invalid parameters")
default:
print("Unknown error")
}
} else {
print("Display stream started successfully")
}
}
private func createCGImage(from surface: IOSurfaceRef) -> CGImage? {
let width = IOSurfaceGetWidth(surface)
let height = IOSurfaceGetHeight(surface)
let bytesPerRow = IOSurfaceGetBytesPerRow(surface)
let surfaceData = IOSurfaceGetBaseAddress(surface)
guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) else {
return nil
}
let context = CGContext(
data: surfaceData,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
)
return context?.makeImage()
}
func stopCapturing() {
print("Stopping screen capture...")
displayStream?.stop()
displayStream = nil
}
}
// MARK: - Video Streaming Manager
class VideoStreamingManager {
private let desktopManager = VirtualDesktopManager()
private let encoder: VideoEncoder
private let streamServer = StreamServer()
private var frameCount: Int64 = 0
private let width: Int32
private let height: Int32
private let clientAddress: String
private let clientPort: UInt16
init(width: Int32, height: Int32, clientAddress: String, clientPort: UInt16) {
self.width = width
self.height = height
self.clientAddress = clientAddress
self.clientPort = clientPort
self.encoder = VideoEncoder(width: width, height: height)
print("VideoStreamingManager initialized")
}
func startCapture() {
print("Starting video capture and streaming...")
do {
try streamServer.startServer(port: 12345)
streamServer.setClient(address: clientAddress, port: clientPort)
print("UDP server started on port 12345, sending to \(clientAddress):\(clientPort)")
desktopManager.startCapturing(width: Int(width), height: Int(height)) { [weak self] cgImage in
guard let self = self, let image = cgImage else { return }
let timestamp = CMTime(value: self.frameCount, timescale: 30)
self.frameCount += 1
if self.frameCount % 30 == 0 {
print("Processed \(self.frameCount) frames")
}
self.encoder.encode(image: image, presentationTimeStamp: timestamp) { encodedData in
if let data = encodedData {
do {
var header = PacketHeader(
frameNumber: UInt32(self.frameCount),
timestamp: UInt64(timestamp.value),
payloadSize: UInt32(data.count)
)
var packetData = Data(bytes: &header, count: MemoryLayout<PacketHeader>.size)
packetData.append(data)
try self.streamServer.send(data: packetData)
} catch {
print("Error sending frame \(self.frameCount): \(error)")
}
}
}
}
} catch {
print("Error starting capture: \(error)")
}
}
func stopCapture() {
print("Stopping video capture and streaming...")
desktopManager.stopCapturing()
streamServer.closeConnection()
}
deinit {
stopCapture()
}
}
// MARK: - Video Encoder
class VideoEncoder {
private var session: VTCompressionSession?
private let width: Int32
private let height: Int32
private let fps: Int32
init(width: Int32, height: Int32, fps: Int32 = 30) {
self.width = width
self.height = height
self.fps = fps
setupSession()
}
private func setupSession() {
var session: VTCompressionSession?
let status = VTCompressionSessionCreate(
allocator: kCFAllocatorDefault,
width: width,
height: height,
codecType: kCMVideoCodecType_H264,
encoderSpecification: nil,
imageBufferAttributes: nil,
compressedDataAllocator: nil,
outputCallback: nil,
refcon: nil,
compressionSessionOut: &session
)
guard status == noErr, let session = session else { return }
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Main_AutoLevel)
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AverageBitRate, value: NSNumber(value: 2000000))
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: NSNumber(value: fps))
VTCompressionSessionPrepareToEncodeFrames(session)
self.session = session
}
func encode(image: CGImage, presentationTimeStamp: CMTime, completion: @escaping (Data?) -> Void) {
guard let session = session else { return }
var pixelBuffer: CVPixelBuffer?
let status = CVPixelBufferCreate(
kCFAllocatorDefault,
image.width,
image.height,
kCVPixelFormatType_32BGRA,
nil,
&pixelBuffer
)
guard status == kCVReturnSuccess, let pixelBuffer = pixelBuffer else { return }
CVPixelBufferLockBaseAddress(pixelBuffer, [])
let context = CGContext(
data: CVPixelBufferGetBaseAddress(pixelBuffer),
width: image.width,
height: image.height,
bitsPerComponent: 8,
bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
)
context?.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
var flags: VTEncodeInfoFlags = []
VTCompressionSessionEncodeFrame(
session,
imageBuffer: pixelBuffer,
presentationTimeStamp: presentationTimeStamp,
duration: CMTime.invalid,
frameProperties: nil,
infoFlagsOut: &flags,
outputHandler: { status, flags, sampleBuffer in
guard let sampleBuffer = sampleBuffer else { return }
if CMSampleBufferDataIsReady(sampleBuffer) {
if let dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) {
var length = 0
var dataPointer: UnsafeMutablePointer<Int8>?
CMBlockBufferGetDataPointer(
dataBuffer,
atOffset: 0,
lengthAtOffsetOut: nil,
totalLengthOut: &length,
dataPointerOut: &dataPointer
)
if let pointer = dataPointer {
let data = Data(bytes: pointer, count: length)
completion(data)
}
}
}
}
)
}
}
// MARK: - UDP Stream Server
class StreamServer {
private var socket: Int32 = -1
private var clientAddr: sockaddr_in?
func startServer(port: UInt16) throws {
socket = Darwin.socket(AF_INET, SOCK_DGRAM, 0)
guard socket >= 0 else {
throw NSError(domain: "Socket creation failed", code: -1)
}
var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = port.bigEndian
addr.sin_addr.s_addr = INADDR_ANY.littleEndian
let bindResult = withUnsafePointer(to: &addr) { ptr in
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockPtr in
bind(socket, sockPtr, socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
guard bindResult == 0 else {
throw NSError(domain: "Bind failed", code: -2)
}
var bufferSize = Int32(65535 * 10)
setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &bufferSize, socklen_t(MemoryLayout<Int32>.size))
}
func setClient(address: String, port: UInt16) {
var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = port.bigEndian
addr.sin_addr.s_addr = inet_addr(address.cString(using: .utf8))
clientAddr = addr
}
func send(data: Data) throws {
guard let clientAddr = clientAddr else { return }
let maxChunkSize = 65507
var offset = 0
while offset < data.count {
let chunkSize = min(maxChunkSize, data.count - offset)
let chunk = data.subdata(in: offset..<(offset + chunkSize))
let sendResult = withUnsafePointer(to: clientAddr) { ptr in
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockPtr in
chunk.withUnsafeBytes { buffer in
sendto(socket, buffer.baseAddress, chunk.count, 0, sockPtr, socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
}
if sendResult < 0 {
throw NSError(domain: "Send failed", code: -3)
}
offset += chunkSize
}
}
func closeConnection() {
if socket >= 0 {
close(socket)
}
}
}
// MARK: - Packet Header Structure
struct PacketHeader {
var frameNumber: UInt32
var timestamp: UInt64
var payloadSize: UInt32
}
It's freezes on scroll. I debugged it with stevearc/profile.nvim, and here what I got:
And here is without context:
So the problem is coming from vim.treesitter.query.parse
. You can analyze by yourself it, here is traces:
With nvim-treesitter-context: https://drive.google.com/file/d/1nv_GdnRguZFtjgqA0R_hWYiX0VkiKhMn/view?usp=drive_link
Without: https://drive.google.com/file/d/1MYBKxS_SE4vHTzhwG9C4mD4TYJjo6kt8/view?usp=drive_link
You can load them at https://ui.perfetto.dev/
As one of maybe stupid suggestion - could we cache query parse if file not changed? But I absolutely don't have idea how this parse works.
Neovim version
NVIM v0.10.1
Expected behavior
Scroll should be smooth
Actual behavior
Scroll freezes nvim
Minimal config
local plugins = {
ts = "https://github.com/nvim-treesitter/nvim-treesitter",
ts_context = "https://github.com/nvim-treesitter/nvim-treesitter-context",
-- ADD ADDITIONAL PLUGINS THAT ARE _NECESSARY_ TO REPRODUCE THE ISSUE
}
for name, url in pairs(plugins) do
local install_path = "/tmp/nvim/site/" .. name
if vim.fn.isdirectory(install_path) == 0 then
vim.fn.system({ "git", "clone", "--depth=1", url, install_path })
end
vim.o.runtimepath = install_path .. "," .. vim.o.runtimepath
end
require("nvim-treesitter.configs").setup({
ensure_installed = { "swift" },
-- Autoinstall languages that are not installed
auto_install = true,
highlight = { enable = true },
indent = { enable = true },
})
-- ADD INIT.LUA SETTINGS THAT IS _NECESSARY_ FOR REPRODUCING THE ISSUE
require("treesitter-context").setup({
enable = true,
max_lines = 10,
})
Steps to reproduce
nvim --clean -u minimal.lua
- Open swift file
- Make smooth scroll(with trackpad or mouse)