<commit_before>//
//  MetalRenderer.swift
//  tapmap
//
//  Created by <NAME> on 2020-05-03.
//  Copyright © 2020 Wildbrain. All rights reserved.
//

import Foundation
import Metal
import MetalKit

class MetalRenderer {
	var device: MTLDevice!
	var commandQueue: MTLCommandQueue
	var latestFrame = Date()
	var modelViewProjectionMatrix = simd_float4x4()

	// App renderers
	var regionRenderer: RegionRenderer
	var poiRenderer: PoiRenderer
	var effectRenderer: EffectRenderer
	var selectionRenderer: SelectionRenderer
	var borderRenderer: BorderRenderer
	var debugRenderer: DebugRenderer
	
	init(in view: MTKView, forWorld world: RuntimeWorld) {
		device = MTLCreateSystemDefaultDevice()
		commandQueue = device.makeCommandQueue()!
		view.device = device
		view.colorPixelFormat = .bgra8Unorm
		
		// Create renderers
		regionRenderer = RegionRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		borderRenderer = BorderRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		selectionRenderer = SelectionRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		poiRenderer = PoiRenderer(withDevice: device, pixelFormat: view.colorPixelFormat,
															withVisibleContinents: world.availableContinents,
															countries: world.availableCountries,
															provinces: world.availableProvinces)
		
		effectRenderer = EffectRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		debugRenderer = DebugRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
	}
	
	func updateProjection(viewSize: CGSize, mapSize: CGSize, centeredOn offset: CGPoint, zoomedTo zoom: Float) {
		modelViewProjectionMatrix = buildProjectionMatrix(viewSize: viewSize,
																											mapSize: mapSize,
																											centeredOn: offset,
																											zoomedTo: zoom)
	}
	
	func zoomedTo(_ zoom: Float) {
		selectionRenderer.updateStyle(zoomLevel: zoom)
		borderRenderer.updateStyle(zoomLevel: zoom)
		poiRenderer.updateZoomThreshold(viewZoom: zoom)
		poiRenderer.updateStyle(zoomLevel: zoom)
	}
	
	func prepareFrame(forWorld worldState: RuntimeWorld) {
		let available = AppDelegate.sharedUserState.availableSet
		let visible = AppDelegate.sharedUIState.visibleRegionHashes
		let renderSet = available.intersection(visible)
		let borderedContinents = worldState.allContinents.filter { visible.contains($0.key) }	// All visible continents (even if visited)

		effectRenderer.updatePrimitives()
		regionRenderer.prepareFrame(visibleSet: renderSet)
		borderRenderer.prepareFrame(visibleContinents: borderedContinents, visibleCountries: worldState.visibleCountries)
		poiRenderer.updateFades()
	}
	
	func render(forWorld worldState: RuntimeWorld, into drawable: CAMetalDrawable) {
		let renderPassDescriptor = MTLRenderPassDescriptor()
		renderPassDescriptor.colorAttachments[0].texture = drawable.texture
		renderPassDescriptor.colorAttachments[0].loadAction = .clear
		renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.5, blue: 0.7, alpha: 1.0)
		
		guard let commandBuffer = commandQueue.makeCommandBuffer() else { return }
		guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }

		let available = AppDelegate.sharedUserState.availableSet
		let visible = AppDelegate.sharedUIState.visibleRegionHashes
		let renderSet = available.intersection(visible)
		let borderedContinents = visible.intersection(worldState.allContinents.keys)	// All visible continents (even if visited)
		let borderedCountries = Set(worldState.visibleCountries.keys)
		
		borderRenderer.renderContinentBorders(borderedContinents, inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		regionRenderer.renderWorld(visibleSet: renderSet, inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		borderRenderer.renderCountryBorders(borderedCountries, inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		poiRenderer.renderWorld(visibleSet: renderSet, inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		effectRenderer.renderWorld(inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		selectionRenderer.renderSelection(inProjection: modelViewProjectionMatrix, inEncoder: commandEncoder)
		
		//		DebugRenderer.shared.renderMarkers(inProjection: modelViewProjectionMatrix)
		
		commandEncoder.endEncoding()
		commandBuffer.present(drawable)
		commandBuffer.commit()
	}
	
	func shouldIdle(appUpdated: Bool) -> Bool {
		var sleepRenderer = false
		let needsRender = appUpdated || effectRenderer.animating
		
		if needsRender {
			latestFrame = Date()
		} else if Date().timeIntervalSince(latestFrame) > 1.0 {
			sleepRenderer = true
		}
		return sleepRenderer
	}
}

extension Color {
	var vector: simd_float4 {
		return simd_float4(arrayLiteral: r, g, b, a)
	}
}<commit_msg>Break renderer into three distinct passes (geometry, markers, overlay) and encode them in parallel<commit_after>//
//  MetalRenderer.swift
//  tapmap
//
//  Created by <NAME> on 2020-05-03.
//  Copyright © 2020 Wildbrain. All rights reserved.
//

import Foundation
import Metal
import MetalKit

class MetalRenderer {
	var device: MTLDevice!
	var commandQueue: MTLCommandQueue
	var latestFrame = Date()
	var modelViewProjectionMatrix = simd_float4x4()

	// App renderers
	var regionRenderer: RegionRenderer
	var poiRenderer: PoiRenderer
	var effectRenderer: EffectRenderer
	var selectionRenderer: SelectionRenderer
	var borderRenderer: BorderRenderer
	var debugRenderer: DebugRenderer
	let encodingQueue = DispatchQueue(label: "Parallel command encoding", attributes: .concurrent)

	init(in view: MTKView, forWorld world: RuntimeWorld) {
		device = MTLCreateSystemDefaultDevice()
		commandQueue = device.makeCommandQueue()!
		view.device = device
		view.colorPixelFormat = .bgra8Unorm
		
		// Create renderers
		regionRenderer = RegionRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		borderRenderer = BorderRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		selectionRenderer = SelectionRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		poiRenderer = PoiRenderer(withDevice: device, pixelFormat: view.colorPixelFormat,
															withVisibleContinents: world.availableContinents,
															countries: world.availableCountries,
															provinces: world.availableProvinces)
		
		effectRenderer = EffectRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
		debugRenderer = DebugRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
	}
	
	func updateProjection(viewSize: CGSize, mapSize: CGSize, centeredOn offset: CGPoint, zoomedTo zoom: Float) {
		modelViewProjectionMatrix = buildProjectionMatrix(viewSize: viewSize,
																											mapSize: mapSize,
																											centeredOn: offset,
																											zoomedTo: zoom)
	}
	
	func zoomedTo(_ zoom: Float) {
		selectionRenderer.updateStyle(zoomLevel: zoom)
		borderRenderer.updateStyle(zoomLevel: zoom)
		poiRenderer.updateZoomThreshold(viewZoom: zoom)
		poiRenderer.updateStyle(zoomLevel: zoom)
	}
	
	func prepareFrame(forWorld worldState: RuntimeWorld) {
		let available = AppDelegate.sharedUserState.availableSet
		let visible = AppDelegate.sharedUIState.visibleRegionHashes
		let renderSet = available.intersection(visible)
		let borderedContinents = worldState.allContinents.filter { visible.contains($0.key) }	// All visible continents (even if visited)

		effectRenderer.updatePrimitives()
		regionRenderer.prepareFrame(visibleSet: renderSet)
		borderRenderer.prepareFrame(visibleContinents: borderedContinents, visibleCountries: worldState.visibleCountries)
		poiRenderer.updateFades()
	}
	
	func render(forWorld worldState: RuntimeWorld, into drawable: CAMetalDrawable) {
		let clearPassDescriptor = MTLRenderPassDescriptor()
		clearPassDescriptor.colorAttachments[0].texture = drawable.texture
		clearPassDescriptor.colorAttachments[0].loadAction = .clear
		clearPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.5, blue: 0.7, alpha: 1.0)
		let addPassDescriptor = clearPassDescriptor.copy() as! MTLRenderPassDescriptor
		addPassDescriptor.colorAttachments[0].loadAction = .load
				
		// Create parallel command buffers and enqueue in order
		guard let geographyBuffer = commandQueue.makeCommandBuffer() else { return }
		guard let markerBuffer = commandQueue.makeCommandBuffer() else { return }
		guard let overlayBuffer = commandQueue.makeCommandBuffer() else { return }
		
		geographyBuffer.enqueue()
		markerBuffer.enqueue()
		overlayBuffer.enqueue()
		
		// Present after the last render pass
		overlayBuffer.addScheduledHandler { buffer in
			drawable.present()
		}

		let available = AppDelegate.sharedUserState.availableSet
		let visible = AppDelegate.sharedUIState.visibleRegionHashes
		let renderSet = available.intersection(visible)
		let borderedContinents = visible.intersection(worldState.allContinents.keys)	// All visible continents (even if visited)
		let borderedCountries = Set(worldState.visibleCountries.keys)
		
		let mvpMatrix = modelViewProjectionMatrix
		
		encodingQueue.async(execute: makeRenderPass(geographyBuffer, clearPassDescriptor) { (encoder) in
			self.borderRenderer.renderContinentBorders(borderedContinents, inProjection: mvpMatrix, inEncoder: encoder)
			self.regionRenderer.renderWorld(visibleSet: renderSet, inProjection: mvpMatrix, inEncoder: encoder)
			self.borderRenderer.renderCountryBorders(borderedCountries, inProjection: mvpMatrix, inEncoder: encoder)
		})

		encodingQueue.async(execute: makeRenderPass(markerBuffer, addPassDescriptor) { (encoder) in
			self.poiRenderer.renderWorld(visibleSet: renderSet, inProjection: mvpMatrix, inEncoder: encoder)
		})
		
		encodingQueue.async(execute: makeRenderPass(overlayBuffer, addPassDescriptor) { (encoder) in
			self.effectRenderer.renderWorld(inProjection: mvpMatrix, inEncoder: encoder)
			self.selectionRenderer.renderSelection(inProjection: mvpMatrix, inEncoder: encoder)
			//		DebugRenderer.shared.renderMarkers(inProjection: modelViewProjectionMatrix)
		})
	}
	
	typealias MapRenderPass = () -> ()
	func makeRenderPass(_ buffer: MTLCommandBuffer, _ renderPass: MTLRenderPassDescriptor, render: @escaping (MTLRenderCommandEncoder) -> ()) -> MapRenderPass {
		return {
			guard let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) else { return }
			render(encoder)
			encoder.endEncoding()
			buffer.commit()
		}
	}
	
	func shouldIdle(appUpdated: Bool) -> Bool {
		var sleepRenderer = false
		let needsRender = appUpdated || effectRenderer.animating
		
		if needsRender {
			latestFrame = Date()
		} else if Date().timeIntervalSince(latestFrame) > 1.0 {
			sleepRenderer = true
		}
		return sleepRenderer
	}
}

extension Color {
	var vector: simd_float4 {
		return simd_float4(arrayLiteral: r, g, b, a)
	}
}
{'fraction_non_alphanumeric': 0.08056698673982625, 'fraction_numerical': 0.005761316872427984, 'mean_word_length': 5.1855203619909505, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7196766', 'n_tokens_mistral': 3363, 'n_tokens_neox': 2998, 'n_words': 684}