简体   繁体   中英

What is the cost of having a class within a class in swift?

I am trying to create classes within a class in swift so I can more easily access variables by their category. For example my renderer has many pipelines so it might be nice to be able to go pipelines.<whateverPipelineINeed> or my builder might have various pipelines it can build so I might want to go Builder.pipelines.<whatEverPipelineINeed>() .

This has prompted me to generate code looking like the following

class Renderer: NSObject, MTKViewDelegate {
    let device:MTLDevice!
    let queue:MTLCommandQueue!
    let depth:MTLDepthStencilState!

    class Pipelines {
        static var defaultTriangles:MTLRenderPipelineState!
        static var fancyTriangles:MTLRenderPipelineState!
    }
    .......
}

or

class Builder {
    class func loadTexture(device: MTLDevice,
                           textureName: String) -> MTLTexture? {
        ...
    }

    class PipelineStates {
        class func blankTriangle(device: MTLDevice) -> MTLRenderPipelineState? {
            ...
        }

        class func fancyTriangle(device: MTLDevice) -> MTLRenderPipelineState? {
            ...
        }
    }
}

Of course there are much more than just 2 of these pipelines and I am using this pattern for other objects. Additionally there is not a pattern where = builder.() as some of the objects get initialized frequently. Additionally I do check for errors in the compilation with the optional type.

Is this an ok way to do things in swift? Is there any cost of inefficiency related to doing things like this that I should know about?

Additionally is there any better way to do this?

You can wrap it into enums

public enum Pipeline {

    public enum PipelineName: String {
        case defaultTriangles
        case fancyTriangles
    }

    case defaultTriangles(MTLRenderPipelineState)
    case fancyTriangles(MTLRenderPipelineState)

    public var pipelineState: MTLRenderPipelineState {
        switch self {
        case .defaultTriangles(let pipeline): return pipeline
        case .fancyTriangles(let pipeline): return pipeline
        }
    }

    public init?(withDevice device: MTLDevice,
                 library: MTLLibrary,
                 pipelineName: PipelineName) {
        switch pipelineName {
        case .defaultTriangles:
            let pipelineDescriptor = MTLRenderPipelineDescriptor()
            // Set descriptor properties
            guard
                let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)
                else {
                    return
            }
            self = .defaultTriangles(pipelineState)
        case .fancyTriangles: 
            let pipelineDescriptor = MTLRenderPipelineDescriptor()
            // Set descriptor properties
            guard
                let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)
                else {
                    return
            }
            self = .defaultTriangles(pipelineState)
        }
    }

}

Then initialize like so:

guard 
    let pipeline = Pipeline(withDevice: device,
                            library: library,
                            pipelineName: .defaultTriangles)
    else { return }
self.defaultPipeline = pipeline

Then later on when you need to render access the pipeline like so:

self.defaultPipeline.pipeline
/// Execute commands and set arguments / etc.

Also what would be cool to do would be to define a render pass with infix operators:

defaultTriangles |--> triangleComputationPipeline |--> fancyTriangles

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM