Thank you @MoreLightning, it is really tough to get up to date information on such subjects, and many examples are either in Obj-C or old swift versions which do not even build in the latest xcode :(
Finally after looking through many tutorials I have found exactly what I need for 2D buffers visualization - there is a technique to use compute pipeline instead of render pipeline, and use a very basic kernel function to output texture directly into the view:
		func init() {
				...
let function = library?.makeFunction(name: "compute")
device.makeComputePipelineState(function: function as! MTLFunction)
				...
		}
		func draw(in view: MTKView) {
				if let commandBuffer = commandQueue.makeCommandBuffer() {
						if let renderEncoder = commandBuffer.makeComputeCommandEncoder() {
								if let drawable = view.currentDrawable {
									renderEncoder.setComputePipelineState(pipelineState)
														
									renderEncoder.setTexture(drawable.texture, index: 0)
									renderEncoder.setTexture(colorMap, index: 1)
									var w = pipelineState.threadExecutionWidth
									var h = pipelineState.maxTotalThreadsPerThreadgroup / w
									let threadsPerGroup = MTLSizeMake(w, h, 1)
									w = Int(drawable.texture.width)
									h = Int(drawable.texture.height)
									let threadsPerGrid = MTLSizeMake(w, h, 1)
									renderEncoder.dispatchThreads(threadsPerGrid, threadsPerThreadgroup: threadsPerGroup)
									renderEncoder.endEncoding()
									commandBuffer.present(drawable)
									commandBuffer.commit()
								}
						}
				}
	}
and compute.metal:
#include <metal_stdlib>
using namespace metal;
kernel void compute(texture2d<float, access::write> output [[texture(0)]],
												 texture2d<float, access::sample> input [[texture(1)]],
												 uint2 id [[thread_position_in_grid]])
{
		uint2 index = uint2(id.x, id.y);
		float4 color = input.read(index);
		output.write(color, id);
}
Kudos to metalkit.org/2019/01/31/intro-to-metal-compute.html