mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-05-18 13:47:04 +02:00
refactor: recreate texture only if device orientation is changed
This commit is contained in:
@@ -12,5 +12,5 @@ protocol RenderPass {
|
|||||||
var label: String { get }
|
var label: String { get }
|
||||||
var descriptor: MTLRenderPassDescriptor? { get set }
|
var descriptor: MTLRenderPassDescriptor? { get set }
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer)
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer)
|
||||||
func draw(into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer)
|
func draw(into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ extension Canvas {
|
|||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
self?.graphicContext.object = graphicContext
|
self?.graphicContext.object = graphicContext
|
||||||
self?.graphicContext.loadStrokes(bounds)
|
self?.graphicContext.loadStrokes(bounds)
|
||||||
context.refreshAllObjects()
|
context.refresh(canvas, mergeChanges: false)
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.state = .loaded
|
self?.state = .loaded
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,17 +60,23 @@ final class Renderer {
|
|||||||
self.viewPortRenderPass.view = canvasView
|
self.viewPortRenderPass.view = canvasView
|
||||||
}
|
}
|
||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize) {
|
func setUpdatesViewPort(_ value: Bool) {
|
||||||
if !updatesViewPort {
|
updatesViewPort = value
|
||||||
photoBackgroundRenderPass.resize(on: view, to: size, with: self)
|
}
|
||||||
strokeRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
graphicRenderPass.resize(on: view, to: size, with: self)
|
func setRedrawsGraphicRender() {
|
||||||
cacheRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
}
|
|
||||||
viewPortRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
redrawsGraphicRender = true
|
redrawsGraphicRender = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resize(on view: MTKView, to size: CGSize) {
|
||||||
|
photoBackgroundRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
strokeRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
graphicRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
cacheRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
viewPortRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
setRedrawsGraphicRender()
|
||||||
|
}
|
||||||
|
|
||||||
func draw(in view: MTKView, on canvas: Canvas) {
|
func draw(in view: MTKView, on canvas: Canvas) {
|
||||||
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
|
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
|
||||||
NSLog("[Memola] - Unable to create command buffer")
|
NSLog("[Memola] - Unable to create command buffer")
|
||||||
@@ -94,6 +100,7 @@ final class Renderer {
|
|||||||
cacheRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
cacheRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
||||||
|
|
||||||
viewPortRenderPass.descriptor = view.currentRenderPassDescriptor
|
viewPortRenderPass.descriptor = view.currentRenderPassDescriptor
|
||||||
|
viewPortRenderPass.excludesPhotoBackground = photoBackgroundRenderPass.clearsTexture
|
||||||
viewPortRenderPass.photoBackgroundTexture = photoBackgroundRenderPass.photoBackgroundTexture
|
viewPortRenderPass.photoBackgroundTexture = photoBackgroundRenderPass.photoBackgroundTexture
|
||||||
viewPortRenderPass.cacheTexture = cacheRenderPass.cacheTexture
|
viewPortRenderPass.cacheTexture = cacheRenderPass.cacheTexture
|
||||||
viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
||||||
|
|||||||
@@ -34,14 +34,15 @@ class CacheRenderPass: RenderPass {
|
|||||||
cacheTexture = Textures.createCacheTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
cacheTexture = Textures.createCacheTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let descriptor else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Copying texture
|
// MARK: - Copying texture
|
||||||
guard let graphicTexture, let cacheTexture else { return }
|
guard let graphicTexture, let cacheTexture else { return false }
|
||||||
guard let cachePipelineState else { return }
|
guard let cachePipelineState else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else {
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
computeEncoder.label = "Cache Compute Encoder"
|
computeEncoder.label = "Cache Compute Encoder"
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ class CacheRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Drawing
|
// MARK: - Drawing
|
||||||
guard let graphicPipelineState else { return }
|
guard let graphicPipelineState else { return false }
|
||||||
descriptor.colorAttachments[0].texture = cacheTexture
|
descriptor.colorAttachments[0].texture = cacheTexture
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
@@ -67,23 +68,25 @@ class CacheRenderPass: RenderPass {
|
|||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
if let element = graphicContext.currentElement {
|
if let element = graphicContext.currentElement {
|
||||||
let elementGroup = ElementGroup(element)
|
let elementGroup = ElementGroup(element)
|
||||||
|
var status: Bool?
|
||||||
switch elementGroup.type {
|
switch elementGroup.type {
|
||||||
case .stroke:
|
case .stroke:
|
||||||
canvas.setGraphicRenderType(.inProgress)
|
canvas.setGraphicRenderType(.inProgress)
|
||||||
strokeRenderPass?.elementGroup = elementGroup
|
strokeRenderPass?.elementGroup = elementGroup
|
||||||
strokeRenderPass?.graphicDescriptor = descriptor
|
strokeRenderPass?.graphicDescriptor = descriptor
|
||||||
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
||||||
strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
case .eraser:
|
case .eraser:
|
||||||
eraserRenderPass?.elementGroup = elementGroup
|
eraserRenderPass?.elementGroup = elementGroup
|
||||||
eraserRenderPass?.descriptor = descriptor
|
eraserRenderPass?.descriptor = descriptor
|
||||||
eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
case .photo:
|
case .photo:
|
||||||
photoRenderPass?.elementGroup = elementGroup
|
photoRenderPass?.elementGroup = elementGroup
|
||||||
photoRenderPass?.descriptor = descriptor
|
photoRenderPass?.descriptor = descriptor
|
||||||
photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
}
|
}
|
||||||
clearsTexture = false
|
clearsTexture = !(status ?? false)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,19 +26,20 @@ class EraserRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Generating vertices
|
// MARK: - Generating vertices
|
||||||
guard !elementGroup.isEmpty, let quadPipelineState else { return }
|
guard !elementGroup.isEmpty, let quadPipelineState else { return false }
|
||||||
let eraserStrokes = elementGroup.elements.compactMap { element -> EraserStroke? in
|
let eraserStrokes = elementGroup.elements.compactMap { element -> EraserStroke? in
|
||||||
guard case .stroke(let anyStroke) = element else { return nil }
|
guard case .stroke(let anyStroke) = element else { return nil }
|
||||||
return anyStroke.value as? EraserStroke
|
return anyStroke.value as? EraserStroke
|
||||||
}
|
}
|
||||||
let quads = eraserStrokes.flatMap { $0.quads }
|
let quads = eraserStrokes.flatMap { $0.quads }
|
||||||
guard !quads.isEmpty else { return }
|
guard !quads.isEmpty else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Quad Compute Encoder"
|
computeEncoder.label = "Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -58,10 +59,10 @@ class EraserRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Rendering eraser
|
// MARK: - Rendering eraser
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Render Encoder"
|
renderEncoder.label = "Stroke Render Encoder"
|
||||||
|
|
||||||
guard let eraserPipelineState else { return }
|
guard let eraserPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -77,5 +78,6 @@ class EraserRenderPass: RenderPass {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,16 +30,18 @@ class GraphicRenderPass: RenderPass {
|
|||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
||||||
guard size != .zero else { return }
|
guard size != .zero else { return }
|
||||||
graphicTexture = Textures.createGraphicTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
graphicTexture = Textures.createGraphicTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
clearsTexture = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
descriptor?.colorAttachments[0].texture = graphicTexture
|
descriptor?.colorAttachments[0].texture = graphicTexture
|
||||||
descriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor?.colorAttachments[0].storeAction = .store
|
descriptor?.colorAttachments[0].storeAction = .store
|
||||||
|
|
||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
if renderer.redrawsGraphicRender {
|
if renderer.redrawsGraphicRender {
|
||||||
|
clearsTexture = true
|
||||||
|
photoBackgroundRenderPass?.clearsTexture = true
|
||||||
canvas.setGraphicRenderType(.finished)
|
canvas.setGraphicRenderType(.finished)
|
||||||
var elementGroup: ElementGroup?
|
var elementGroup: ElementGroup?
|
||||||
let start = Date.now.timeIntervalSince1970 * 1000
|
let start = Date.now.timeIntervalSince1970 * 1000
|
||||||
@@ -51,7 +53,7 @@ class GraphicRenderPass: RenderPass {
|
|||||||
let _elementGroup = ElementGroup(_element)
|
let _elementGroup = ElementGroup(_element)
|
||||||
elementGroup = _elementGroup
|
elementGroup = _elementGroup
|
||||||
} else {
|
} else {
|
||||||
guard let _elementGroup = elementGroup else { return }
|
guard let _elementGroup = elementGroup else { continue }
|
||||||
if _elementGroup.isSameElement(_element) {
|
if _elementGroup.isSameElement(_element) {
|
||||||
_elementGroup.add(_element)
|
_elementGroup.add(_element)
|
||||||
} else {
|
} else {
|
||||||
@@ -75,6 +77,7 @@ class GraphicRenderPass: RenderPass {
|
|||||||
draw(for: elementGroup, into: commandBuffer, on: canvas, with: renderer)
|
draw(for: elementGroup, into: commandBuffer, on: canvas, with: renderer)
|
||||||
graphicContext.previousElement = nil
|
graphicContext.previousElement = nil
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func draw(for elementGroup: ElementGroup, into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
private func draw(for elementGroup: ElementGroup, into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
||||||
@@ -84,24 +87,33 @@ class GraphicRenderPass: RenderPass {
|
|||||||
strokeRenderPass?.elementGroup = elementGroup
|
strokeRenderPass?.elementGroup = elementGroup
|
||||||
strokeRenderPass?.graphicDescriptor = descriptor
|
strokeRenderPass?.graphicDescriptor = descriptor
|
||||||
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
||||||
strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
clearsTexture = false
|
if clearsTexture, let status {
|
||||||
|
clearsTexture = !status
|
||||||
|
}
|
||||||
case .eraser:
|
case .eraser:
|
||||||
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
eraserRenderPass?.elementGroup = elementGroup
|
eraserRenderPass?.elementGroup = elementGroup
|
||||||
eraserRenderPass?.descriptor = descriptor
|
eraserRenderPass?.descriptor = descriptor
|
||||||
eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
clearsTexture = false
|
if clearsTexture, let status {
|
||||||
|
clearsTexture = !status
|
||||||
|
}
|
||||||
case .photo:
|
case .photo:
|
||||||
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
photoRenderPass?.elementGroup = elementGroup
|
photoRenderPass?.elementGroup = elementGroup
|
||||||
photoRenderPass?.descriptor = descriptor
|
photoRenderPass?.descriptor = descriptor
|
||||||
photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let photoStatus = photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
|
|
||||||
photoBackgroundRenderPass?.elementGroup = elementGroup
|
photoBackgroundRenderPass?.elementGroup = elementGroup
|
||||||
photoBackgroundRenderPass?.clearsTexture = clearsTexture
|
let photoBackgroundStatus = photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
|
||||||
clearsTexture = false
|
if clearsTexture, let photoStatus {
|
||||||
|
clearsTexture = !photoStatus
|
||||||
|
}
|
||||||
|
if photoBackgroundRenderPass?.clearsTexture == true, let photoBackgroundStatus {
|
||||||
|
photoBackgroundRenderPass?.clearsTexture = !photoBackgroundStatus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,29 +28,30 @@ class PhotoBackgroundRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
||||||
|
guard size != .zero else { return }
|
||||||
photoBackgroundTexture = Textures.createPhotoBackgroundTexture(from: renderer, size: size, pixelFormat: renderer.pixelFormat)
|
photoBackgroundTexture = Textures.createPhotoBackgroundTexture(from: renderer, size: size, pixelFormat: renderer.pixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let elementGroup else { return }
|
guard let elementGroup else { return false }
|
||||||
guard let descriptor else { return }
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
descriptor.colorAttachments[0].texture = photoBackgroundTexture
|
descriptor.colorAttachments[0].texture = photoBackgroundTexture
|
||||||
descriptor.colorAttachments[0].storeAction = .store
|
descriptor.colorAttachments[0].storeAction = .store
|
||||||
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
|
|
||||||
guard !elementGroup.isEmpty else { return }
|
guard !elementGroup.isEmpty else { return false }
|
||||||
|
|
||||||
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
||||||
guard case .photo(let photo) = element else { return nil }
|
guard case .photo(let photo) = element else { return nil }
|
||||||
return photo
|
return photo
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Photo Background Render Encoder"
|
renderEncoder.label = "Photo Background Render Encoder"
|
||||||
|
|
||||||
guard let photoBackgroundPipelineState else { return }
|
guard let photoBackgroundPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(photoBackgroundPipelineState)
|
renderEncoder.setRenderPipelineState(photoBackgroundPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -60,5 +61,6 @@ class PhotoBackgroundRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,21 +24,22 @@ class PhotoRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
guard !elementGroup.isEmpty else { return }
|
guard !elementGroup.isEmpty else { return false }
|
||||||
|
|
||||||
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
||||||
guard case .photo(let photo) = element else { return nil }
|
guard case .photo(let photo) = element else { return nil }
|
||||||
return photo
|
return photo
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Photo Render Encoder"
|
renderEncoder.label = "Photo Render Encoder"
|
||||||
|
|
||||||
guard let photoPipelineState else { return }
|
guard let photoPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(photoPipelineState)
|
renderEncoder.setRenderPipelineState(photoPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -48,5 +49,6 @@ class PhotoRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,13 @@ class StrokeRenderPass: RenderPass {
|
|||||||
strokeTexture = Textures.createStrokeTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
strokeTexture = Textures.createStrokeTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Generating vertices
|
// MARK: - Generating vertices
|
||||||
guard !elementGroup.isEmpty, let quadPipelineState else { return }
|
guard !elementGroup.isEmpty, let quadPipelineState else { return false }
|
||||||
let penStrokes = elementGroup.elements.compactMap { element -> PenStroke? in
|
let penStrokes = elementGroup.elements.compactMap { element -> PenStroke? in
|
||||||
guard case .stroke(let anyStroke) = element else { return nil }
|
guard case .stroke(let anyStroke) = element else { return nil }
|
||||||
return anyStroke.value as? PenStroke
|
return anyStroke.value as? PenStroke
|
||||||
@@ -47,8 +48,8 @@ class StrokeRenderPass: RenderPass {
|
|||||||
let penStroke = penStrokes.first
|
let penStroke = penStrokes.first
|
||||||
let quads = penStrokes.flatMap { $0.quads }
|
let quads = penStrokes.flatMap { $0.quads }
|
||||||
let erasedQuads = Set(penStrokes.flatMap { $0.eraserStrokes }).flatMap { $0.quads }
|
let erasedQuads = Set(penStrokes.flatMap { $0.eraserStrokes }).flatMap { $0.quads }
|
||||||
guard !quads.isEmpty else { return }
|
guard !quads.isEmpty else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Quad Compute Encoder"
|
computeEncoder.label = "Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -68,16 +69,16 @@ class StrokeRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Rendering stroke
|
// MARK: - Rendering stroke
|
||||||
guard let strokeTexture else { return }
|
guard let strokeTexture else { return false }
|
||||||
descriptor.colorAttachments[0].texture = strokeTexture
|
descriptor.colorAttachments[0].texture = strokeTexture
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor.colorAttachments[0].loadAction = .clear
|
descriptor.colorAttachments[0].loadAction = .clear
|
||||||
descriptor.colorAttachments[0].storeAction = .store
|
descriptor.colorAttachments[0].storeAction = .store
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Render Encoder"
|
renderEncoder.label = "Stroke Render Encoder"
|
||||||
|
|
||||||
guard let strokePipelineState else { return }
|
guard let strokePipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(strokePipelineState)
|
renderEncoder.setRenderPipelineState(strokePipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -100,7 +101,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
|
|
||||||
// MARK: Erasing path
|
// MARK: Erasing path
|
||||||
if let eraserPipelineState = eraserRenderPass?.eraserPipelineState, !erasedQuads.isEmpty {
|
if let eraserPipelineState = eraserRenderPass?.eraserPipelineState, !erasedQuads.isEmpty {
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Erased Quad Compute Encoder"
|
computeEncoder.label = "Erased Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
descriptor.colorAttachments[0].loadAction = .load
|
descriptor.colorAttachments[0].loadAction = .load
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Eraser Render Encoder"
|
renderEncoder.label = "Stroke Eraser Render Encoder"
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
||||||
@@ -140,8 +141,8 @@ class StrokeRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Drawing on graphic texture
|
// MARK: Drawing on graphic texture
|
||||||
guard let graphicDescriptor, let graphicPipelineState else { return }
|
guard let graphicDescriptor, let graphicPipelineState else { return false }
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Graphic Render Encoder"
|
renderEncoder.label = "Stroke Graphic Render Encoder"
|
||||||
renderEncoder.setRenderPipelineState(graphicPipelineState)
|
renderEncoder.setRenderPipelineState(graphicPipelineState)
|
||||||
|
|
||||||
@@ -151,5 +152,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 11)
|
renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 11)
|
||||||
canvas.renderGraphic(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderGraphic(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
|
|
||||||
weak var view: MTKView?
|
weak var view: MTKView?
|
||||||
|
|
||||||
|
var excludesPhotoBackground: Bool = false
|
||||||
|
|
||||||
init(renderer: Renderer) {
|
init(renderer: Renderer) {
|
||||||
pointGridPipelineState = PipelineStates.createPointGridPipelineState(from: renderer)
|
pointGridPipelineState = PipelineStates.createPointGridPipelineState(from: renderer)
|
||||||
lineGridPipelineState = PipelineStates.createLineGridPipelineState(from: renderer)
|
lineGridPipelineState = PipelineStates.createLineGridPipelineState(from: renderer)
|
||||||
@@ -31,12 +33,13 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else {
|
guard let descriptor else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
renderEncoder.label = "View Port Render Encoder"
|
renderEncoder.label = "View Port Render Encoder"
|
||||||
|
|
||||||
@@ -44,36 +47,40 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case .point:
|
case .point:
|
||||||
guard let pointGridPipelineState else { return }
|
guard let pointGridPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(pointGridPipelineState)
|
renderEncoder.setRenderPipelineState(pointGridPipelineState)
|
||||||
canvas.renderPointGrid(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderPointGrid(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
case .line:
|
case .line:
|
||||||
guard let lineGridPipelineState else { return }
|
guard let lineGridPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(lineGridPipelineState)
|
renderEncoder.setRenderPipelineState(lineGridPipelineState)
|
||||||
canvas.renderLineGrid(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderLineGrid(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
if renderer.updatesViewPort {
|
if renderer.updatesViewPort {
|
||||||
guard let viewPortUpdatePipelineState else {
|
guard let viewPortUpdatePipelineState else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(viewPortUpdatePipelineState)
|
renderEncoder.setRenderPipelineState(viewPortUpdatePipelineState)
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
if !excludesPhotoBackground {
|
||||||
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
||||||
|
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
||||||
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
} else {
|
} else {
|
||||||
guard let viewPortPipelineState else {
|
guard let viewPortPipelineState else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(viewPortPipelineState)
|
renderEncoder.setRenderPipelineState(viewPortPipelineState)
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
if !excludesPhotoBackground {
|
||||||
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
||||||
|
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
||||||
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -82,9 +89,10 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
|
||||||
guard let drawable = view?.currentDrawable else {
|
guard let drawable = view?.currentDrawable else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
commandBuffer.present(drawable)
|
commandBuffer.present(drawable)
|
||||||
commandBuffer.commit()
|
commandBuffer.commit()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ extension CanvasViewController: UIScrollViewDelegate {
|
|||||||
|
|
||||||
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
||||||
canvas.setZoomScale(scrollView.zoomScale)
|
canvas.setZoomScale(scrollView.zoomScale)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
// renderer.resize(on: renderView, to: renderView.drawableSize)
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ extension CanvasViewController: UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
// renderer.resize(on: renderView, to: renderView.drawableSize)
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +302,12 @@ extension CanvasViewController {
|
|||||||
drawingView.touchCancelled()
|
drawingView.touchCancelled()
|
||||||
canvas.updateClipBounds(scrollView, on: drawingView)
|
canvas.updateClipBounds(scrollView, on: drawingView)
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.updatesViewPort = true
|
renderer.setUpdatesViewPort(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func magnificationEnded() {
|
func magnificationEnded() {
|
||||||
renderer.updatesViewPort = false
|
renderer.setUpdatesViewPort(false)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -316,12 +316,12 @@ extension CanvasViewController {
|
|||||||
guard !renderer.updatesViewPort else { return }
|
guard !renderer.updatesViewPort else { return }
|
||||||
canvas.updateClipBounds(scrollView, on: drawingView)
|
canvas.updateClipBounds(scrollView, on: drawingView)
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.updatesViewPort = true
|
renderer.setUpdatesViewPort(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draggingEnded() {
|
func draggingEnded() {
|
||||||
renderer.updatesViewPort = false
|
renderer.setUpdatesViewPort(false)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -371,7 +371,7 @@ extension CanvasViewController {
|
|||||||
|
|
||||||
func gridModeChanged(_ mode: GridMode) {
|
func gridModeChanged(_ mode: GridMode) {
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -382,8 +382,7 @@ extension CanvasViewController {
|
|||||||
guard let event = history.undo() else { return }
|
guard let event = history.undo() else { return }
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
canvas.graphicContext.undoGraphic(for: event)
|
canvas.graphicContext.undoGraphic(for: event)
|
||||||
renderer.redrawsGraphicRender = true
|
renderer.setRedrawsGraphicRender()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -392,8 +391,7 @@ extension CanvasViewController {
|
|||||||
guard let event = history.redo() else { return }
|
guard let event = history.redo() else { return }
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
canvas.graphicContext.redoGraphic(for: event)
|
canvas.graphicContext.redoGraphic(for: event)
|
||||||
renderer.redrawsGraphicRender = true
|
renderer.setRedrawsGraphicRender()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user