import MetalKit final class TextureModel { private struct VertexTexture { var position: simd_float3 var texture: simd_float2 } // texture private let samplerState: MTLSamplerState? private var texture: MTLTexture? private let pipelineTexture: MTLRenderPipelineState? private let vertexBufferTexture: MTLBuffer? // 1320 - 308 = 330 - 77 private let verticesTexture: [VertexTexture] = [ VertexTexture(position: simd_float3(-1, 0.1, 0), texture: simd_float2(0, 1)), VertexTexture(position: simd_float3(-1, -0.1, 0), texture: simd_float2(0, 0)), VertexTexture(position: simd_float3(1, -0.1, 0), texture: simd_float2(1, 0)), VertexTexture(position: simd_float3(1, 0.1, 0), texture: simd_float2(1, 1)), ] private let indicesBufferTexture: MTLBuffer? private let indicesTexture: [UInt16] = [ 0, 1, 2, 2, 3, 0, ] init?(device: MTLDevice, library: MTLLibrary, pixelFormat: MTLPixelFormat) { guard let url = Bundle.main.url(forResource: "texture", withExtension: "png"), let vertexShader = library.makeFunction(name: "vertex_shader_texture"), let fragmentShader = library.makeFunction(name: "fragment_shader_texture") else { print("url or vertexShader or fragmentShader is nil") return nil } let textureLoader = MTKTextureLoader(device: device) let options: [MTKTextureLoader.Option: Any] = [MTKTextureLoader.Option.origin: MTKTextureLoader.Origin.bottomLeft.rawValue] do { texture = try textureLoader.newTexture(URL: url, options: options) } catch { print(#function, error) return nil } let descriptorTexture = MTLRenderPipelineDescriptor() descriptorTexture.colorAttachments[0].pixelFormat = pixelFormat descriptorTexture.vertexFunction = vertexShader descriptorTexture.fragmentFunction = fragmentShader let vertexDescriptorTexture = MTLVertexDescriptor() // position vertexDescriptorTexture.attributes[0].format = .float3 vertexDescriptorTexture.attributes[0].offset = 0 vertexDescriptorTexture.attributes[0].bufferIndex = 0 // texture vertexDescriptorTexture.attributes[1].format = .float2 vertexDescriptorTexture.attributes[1].offset = MemoryLayout<simd_float3>.size// + MemoryLayout<simd_float4>.stride vertexDescriptorTexture.attributes[1].bufferIndex = 0 vertexDescriptorTexture.layouts[0].stride = MemoryLayout<VertexTexture>.stride descriptorTexture.vertexDescriptor = vertexDescriptorTexture do { pipelineTexture = try device.makeRenderPipelineState(descriptor: descriptorTexture) } catch { print(#function, error) return nil } let descriptor = MTLSamplerDescriptor() descriptor.minFilter = .linear descriptor.magFilter = .linear samplerState = device.makeSamplerState(descriptor: descriptor) vertexBufferTexture = device.makeBuffer(bytes: verticesTexture, length: verticesTexture.count * MemoryLayout<VertexTexture>.stride, options: []) indicesBufferTexture = device.makeBuffer(bytes: indicesTexture, length: indicesTexture.count * MemoryLayout<UInt16>.stride, options: []) } func updateTexture(_ texture: MTLTexture) { self.texture = texture } func setupRenderCommandEncoder(parallelRenderCommandEncoder: MTLParallelRenderCommandEncoder) { guard let pipeline = pipelineTexture, let renderCommandEncoder = parallelRenderCommandEncoder.makeRenderCommandEncoder(), let vertexBufferTexture = vertexBufferTexture, let indicesBufferTexture = indicesBufferTexture else { return } renderCommandEncoder.setRenderPipelineState(pipeline) renderCommandEncoder.setVertexBuffer(vertexBufferTexture, offset: 0, index: 0) renderCommandEncoder.setFragmentSamplerState(samplerState, index: 0) renderCommandEncoder.setFragmentTexture(texture, index: 0) renderCommandEncoder.drawIndexedPrimitives(type: .triangle, indexCount: indicesTexture.count, indexType: .uint16, indexBuffer: indicesBufferTexture, indexBufferOffset: 0) renderCommandEncoder.endEncoding() } }