{"id":18088339,"url":"https://github.com/yukiny0811/easymetalshader","last_synced_at":"2025-04-13T03:13:30.362Z","repository":{"id":216326216,"uuid":"738616256","full_name":"yukiny0811/EasyMetalShader","owner":"yukiny0811","description":"Metal Shader! Easy!","archived":false,"fork":false,"pushed_at":"2025-02-06T13:41:03.000Z","size":149,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T03:13:22.159Z","etag":null,"topics":["compute-shader","creative-coding","generative-art","gpgpu","ios","macos","metal","shader","swift","swiftui"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yukiny0811.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-03T16:32:43.000Z","updated_at":"2025-04-09T10:28:23.000Z","dependencies_parsed_at":"2024-07-31T21:56:37.245Z","dependency_job_id":null,"html_url":"https://github.com/yukiny0811/EasyMetalShader","commit_stats":{"total_commits":64,"total_committers":1,"mean_commits":64.0,"dds":0.0,"last_synced_commit":"074036bc6150d9112311a651611ff6d0df9b1e7e"},"previous_names":["yukiny0811/easymetalshader"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukiny0811%2FEasyMetalShader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukiny0811%2FEasyMetalShader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukiny0811%2FEasyMetalShader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukiny0811%2FEasyMetalShader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yukiny0811","download_url":"https://codeload.github.com/yukiny0811/EasyMetalShader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248657923,"owners_count":21140846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compute-shader","creative-coding","generative-art","gpgpu","ios","macos","metal","shader","swift","swiftui"],"created_at":"2024-10-31T17:12:49.313Z","updated_at":"2025-04-13T03:13:30.317Z","avatar_url":"https://github.com/yukiny0811.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# EasyMetalShader\n\n[![Release](https://img.shields.io/github/v/release/yukiny0811/EasyMetalShader)](https://github.com/yukiny0811/EasyMetalShader/releases/latest)\n[![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fyukiny0811%2FEasyMetalShader%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/yukiny0811/EasyMetalShader)\n[![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fyukiny0811%2FEasyMetalShader%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/yukiny0811/EasyMetalShader)\n[![License](https://img.shields.io/github/license/yukiny0811/EasyMetalShader)](https://github.com/yukiny0811/EasyMetalShader/blob/main/LICENSE)\n\nEasily create pipelines for Metal Shader.\n\n## Q\u0026A (AI Documentation)\n\nhttps://chatgpt.com/g/g-67a4a543c1048191b24ee44bd805f64a-easymetalshader-library-q-a\n\n## Usage\n\n### Create Compute Function\n\nYou can easily send any variables to the shader, simply by defining it in your class.\n\n\"gid\" is pre-defined for [[thread_position_in_grid]]\n\n```.swift\nimport EasyMetalShader\n\n@EMComputeShader\nclass MyCompute {\n    \n    var intensity: Float = 3\n    var tex: MTLTexture?\n    \n    var impl: String {\n        \"float2 floatGid = float2(gid.x, gid.y);\"\n        \"float2 center = float2(tex.get_width() / 2, tex.get_height() / 2);\"\n        \"float dist = distance(center, floatGid);\"\n        \"float color = intensity / dist;\"\n        \"tex.write(float4(color, color, color, 1), gid);\"\n    }\n    \n    var customMetalCode: String {\n        \"\"\n    }\n}\n```\n\n### Create Render Function\n\nSame as the compute function, you can send variables to both vertex and fragment shader.\n\n\"vertexInput\" is pre-defined in vertex function as VertexInput, which stands for [[ stage_in ]].\n\n\"c0\" is pre-defined in fragment function as float4, which stands for [[ color(0) ]].\n\n\"rd\" is pre-defined for both vertex and fragment function, which stands for RasterizerData.\n\n```.metal\n//VertexInput\nstruct VertexInput {\n    float4 input0 [[ attribute(0) ]]; // this is usually for position\n    float4 input1 [[ attribute(1) ]]; // this is usually for color\n    float4 input2 [[ attribute(2) ]]; // use other inputs to pass values to vertex shader\n    float4 input3 [[ attribute(3) ]];\n    float4 input4 [[ attribute(4) ]];\n    float4 input5 [[ attribute(5) ]];\n    float4 input6 [[ attribute(6) ]];\n    float4 input7 [[ attribute(7) ]];\n    float4 input8 [[ attribute(8) ]];\n    float4 input9 [[ attribute(9) ]];\n};\n\n//RasterizerData\nstruct RasterizerData {\n    float4 position [[ position ]];\n    float4 color;\n    \n    // use this for defining size of MTLPrimitiveType.point\n    // this has no effect when you are using MTLPrimitiveType other than .point\n    float size [[ point_size ]];\n\n    // use this for passing some variables from vertex shader to fragment shader\n    float4 temp1;\n    float4 temp2;\n    float4 temp3;\n    float4 temp4;\n    float4 temp5;\n    float4 temp6;\n    float4 temp7;\n    float4 temp8;\n    float4 temp9;\n    \n    // these variables are flat (no interpolation)\n    float4 flat1 [[ flat ]];\n    float4 flat2 [[ flat ]];\n    float4 flat3 [[ flat ]];\n    float4 flat4 [[ flat ]];\n    float4 flat5 [[ flat ]];\n    float4 flat6 [[ flat ]];\n    float4 flat7 [[ flat ]];\n    float4 flat8 [[ flat ]];\n    float4 flat9 [[ flat ]];\n};\n```\n\n```.swift\n@EMRenderShader\nclass MyRender {\n    \n    var vertImpl: String {\n        \"rd.size = 10;\"\n        \"rd.position = vertexInput.input0;\"\n        \"rd.color = vertexInput.input1;\"\n    }\n    \n    var fragImpl: String {\n        \"return rd.color + c0;\"\n    }\n    \n    var customMetalCode: String {\n        \"\"\n    }\n}\n```\n\n### Create pipeline and renderer\n\ndate and mousePos is defined in ShaderRenderer\n\n```.swift\nimport EasyMetalShader\n\nclass MyRenderer: ShaderRenderer {\n    \n    var particles: [VertexInput] = {\n        var inputs: [VertexInput] = []\n        for _ in 0...1000 {\n            var input = VertexInput()\n            input.input0 = .init(Float.random(in: -1...1), Float.random(in: -1...1), 0, 1)\n            input.input1 = .init(Float.random(in: 0.3...1), 0.3, 0.3, 1)\n            inputs.append(input)\n        }\n        return inputs\n    }()\n    \n    let compute = MyCompute()\n    let render = MyRender(targetPixelFormat: .bgra8Unorm)\n    \n    override func draw(view: MTKView, drawable: CAMetalDrawable) {\n        let dispatch = EMMetalDispatch()\n        dispatch.compute { [self] encoder in\n            compute.intensity = abs(sin(Float(Date().timeIntervalSince(date)))) * 100\n            compute.tex = drawable.texture\n            compute.dispatch(encoder, textureSizeReference: drawable.texture)\n        }\n        dispatch.render(renderTargetTexture: drawable.texture, needsClear: false) { [self] encoder in\n            render.dispatch(encoder, textureSizeReference: drawable.texture, primitiveType: .point, vertices: particles)\n        }\n        dispatch.present(drawable: drawable)\n        dispatch.commit()\n    }\n}\n```\n\n### Show in SwiftUI View\n\n```.swift\nimport SwiftUI\nimport EasyMetalShader\n\nstruct ContentView: View {\n    let renderer = MyRenderer()\n    var body: some View {\n        EasyShaderView(renderer: renderer)\n    }\n}\n```\n\n## Notes\n\n### Manual Dispatch\n\nYou can manually dispatch compute or render functions outside of MTKView.\n\n```.swift\nlet tex = EMMetalTexture.create(width: 100, height: 100, pixelFormat: .bgra8Unorm, label: \"tex\")\nlet dispatch = EMMetalDispatch()\ndispatch.compute { encoder in\n    compute.tex = tex\n    compute.intensity = 50\n    compute.dispatch(encoder, textureSizeReference: tex)\n}\ndispatch.render(renderTargetTexture: tex, needsClear: false) { encoder in\n    render.dispatch(encoder, textureSizeReference: tex, primitiveType: .point, vertices: [simd_float4(0.2, 0.2, 0, 1.0)])\n}\ndispatch.commit()\n```\n\n### Ignoring variables from being sent to the shader\n```.swift\n@EMComputeShader\nclass MyCompute {\n    \n    @EMIgnore\n    var someVariable = 3\n}\n```\n\n### Custom Constructor\n\n```.swift\n@EMComputeShader\nclass MyCompute {\n    \n    init(a: Float) {\n        setup() // you need this!\n    }\n}\n\n@EMRenderShader\nclass MyRender {\n    \n    init(a: Float) {\n        setup(targetPixelFormat: .bgra8unorm) // you need this!\n    }\n}\n```\n\n### Supported Data Types\n\n- Int32\n- Float\n- Double\n- Bool\n- simd_int2\n- simd_int3\n- simd_int4\n- simd_float2\n- simd_float3\n- simd_float4\n- simd_float2x2\n- simd_float3x3\n- simd_float4x4\n- simd_double2\n- simd_double3\n- simd_double4\n- simd_double2x2\n- simd_double3x3\n- simd_double4x4\n- MTLTexture (2d)\n- MTLTexture (3d)\n- IntBuffer (array)\n- Int2Buffer (array)\n- Int3Buffer (array)\n- Int4Buffer (array)\n- BoolBuffer (array)\n- FloatBuffer (array)\n- Float2Buffer (array)\n- Float3Buffer (array)\n- Float4Buffer (array)\n\n### Supported Platforms\n- macOS 11.0~\n- iOS 14.0~\n- iOS Simulator (render shader only works on physical devices)\n\n### Custom Pipelines\n\n```.swift\nlet dispatch = EMMetalDispatch()\ndispatch.custom { commandBuffer in\n    // do something with commandBuffer\n}\ndispatch.commit()\n```\n\n### Custom Metal Functions\n\nimplement customMetalCode property to add your original metal codes.\n\n```.swift\nvar customMetalCode: String {\n    \"inline float myFunc() {\"\n    \"return 1.0;\"\n    \"}\"\n}\n```\n\n### Compute Shader for 3D Textures\n\n```.swift\n@EMComputeShader3D\nclass MyCompute3D {\n    \n    @EMTextureArgument(.read, .type3D)\n    var tex: MTLTexture?\n    \n    var impl: String {\n        \"float4 c = tex.read(gid);\"\n    }\n    \n    var customMetalCode: String {\n        \"\"\n    }\n}\n```\n\n## Sample Code\n\n\n```.swift\n// compute shader\n@EMComputeShader\nclass MyCompute {\n    \n    var intensity: Float = 3\n    var tex: MTLTexture?\n    \n    var impl: String {\n        \"float2 floatGid = float2(gid.x, gid.y);\"\n        \"float2 center = float2(tex.get_width() / 2, tex.get_height() / 2);\"\n        \"float dist = distance(center, floatGid);\"\n        \"float color = intensity / dist;\"\n        \"tex.write(float4(color, color, color, 1), gid);\"\n    }\n    \n    var customMetalCode: String {\n        \"\"\n    }\n}\n\n// vertex/fragment shader\n@EMRenderShader\nclass MyRender {\n    \n    var vertImpl: String {\n        \"rd.size = 10;\"\n        \"rd.position = vertexInput.input0;\"\n        \"rd.color = vertexInput.input1;\"\n    }\n    \n    var fragImpl: String {\n        \"return rd.color + c0;\"\n    }\n    \n    var customMetalCode: String {\n        \"\"\n    }\n}\n```\n\n```.swift\n// renderer\nclass MyRenderer: ShaderRenderer {\n    \n    var particles: [VertexInput] = {\n        var inputs: [VertexInput] = []\n        for _ in 0...1000 {\n            var input = VertexInput()\n            input.input0 = .init(Float.random(in: -1...1), Float.random(in: -1...1), 0, 1)\n            input.input1 = .init(Float.random(in: 0.3...1), 0.3, 0.3, 1)\n            inputs.append(input)\n        }\n        return inputs\n    }()\n    \n    let compute = MyCompute()\n    let render = MyRender(targetPixelFormat: .bgra8Unorm)\n    \n    override func draw(view: MTKView, drawable: CAMetalDrawable) {\n        let dispatch = EMMetalDispatch()\n        dispatch.compute { [self] encoder in\n            compute.intensity = abs(sin(Float(Date().timeIntervalSince(date)))) * 100\n            compute.tex = drawable.texture\n            compute.dispatch(encoder, textureSizeReference: drawable.texture)\n        }\n        dispatch.render(renderTargetTexture: drawable.texture, needsClear: false) { [self] encoder in\n            render.dispatch(encoder, textureSizeReference: drawable.texture, primitiveType: .point, vertices: particles)\n        }\n        dispatch.present(drawable: drawable)\n        dispatch.commit()\n    }\n}\n```\n```.swift\nstruct ContentView: View {\n    let renderer = MyRenderer()\n    var body: some View {\n        EasyShaderView(renderer: renderer)\n    }\n}\n```\n\n![outoutout](https://github.com/yukiny0811/EasyMetalShader/assets/28947703/27c53c0b-941a-4e63-8f2d-ca913029ed81)\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukiny0811%2Feasymetalshader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyukiny0811%2Feasymetalshader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukiny0811%2Feasymetalshader/lists"}