Challenge: Draw with metal-cpp

Paint brush symbol and metal logo symbol on a dark background

Metal is the foundation for accelerated graphics and compute power on Apple platforms — and if you’re familiar with C++, now’s the perfect time to explore its incredible power. For this challenge, we're inviting you to try out metal-cpp and render your own triangle, sphere, or even a mesh in Xcode.

We also welcome you to visit the Graphics & Games Study Hall during the day to collaborate on this challenge! Ask questions, connect with other developers, and share your creations.

Study Hall: Draw with metal-cpp

View now

Begin the challenge

Before you begin, you’ll want to watch “Program Metal in C++ with metal-cpp” and download the LearnMetalCPP project, which contains a series of C++ samples.

Program Metal in C++ with metal-cpp

Watch now

Download the LearnMetalCPP project

Open the project in Xcode, and choose 00-window.cpp as your base code. To render your image, you’ll need to set up a few things within your project.

First, create a MTL::RenderPipelineState object with a MTL::RenderPipelineDescriptor. To do this, you’ll need to create a function, like buildShaders(). In the code snippet below, we’ve provided the shader code needed to render a single triangle.

void Renderer::buildShaders()
{
    using NS::StringEncoding::UTF8StringEncoding;

    const char* shaderSrc = R"(
        #include <metal_stdlib>
        using namespace metal;
        struct AAPLVertex
        {
            float3 position;
            half3  color;
        };
    
        // Welcome to modify the mesh as you want
        constant AAPLVertex triangles[] = {
            { float3{ -0.8f,  0.8f, 0.0f }, half3{  1.0, 0.3f, 0.2f } },
            { float3{  0.0f, -0.8f, 0.0f }, half3{  0.8f, 1.0, 0.0f } },
            { float3{ +0.8f,  0.8f, 0.0f }, half3{  0.8f, 0.0f, 1.0 } }
        };
    
        struct v2f
        {
            float4 position [[position]];
            half3 color;
        };

        v2f vertex vertexMain( uint vertexId [[vertex_id]])
        {
            v2f o;
            o.position = float4( triangles[ vertexId ].position, 1.0 );
            o.color = half3 ( triangles[ vertexId ].color );
            return o;
        }

        half4 fragment fragmentMain( v2f in [[stage_in]] )
        {
            return half4( in.color, 1.0 );
        }
    )";
    // TODO: Create a MTL::RenderPipelineDescriptor
    // TODO: Allocate a MTL::RenderPipelineState object
}

Then, extend the Renderer::draw( MTK::View* pView) function by setting a MTL::RenderPipelineState and inserting draw calls.

void Renderer::draw(  MTK::View* pView  )
{
...
    // TODO: Set MTL::RenderPipelineState
    // TODO: Draw a single triangle or more!
...
}

After that:

  • Create the MTL::RenderPipelineDescriptor object and set up some properties.
  • Create the MTL::RenderPipelineState object.
  • Tip: Be careful with object lifecycles.

Ready to share your metal-cpp art with the community? Show us what you’ve made on Twitter with the hashtag #WWDC22Challenges, or share your work in the Graphics & Games Study Hall. And if you'd like to discuss metal-cpp and other Graphics & Games topics, join the team at events all throughout the week at WWDC22.

Read the WWDC22 Challenges Terms and Conditions