Search code examples
c++shadermetal

Converting Metal Shader texture type from 2D to 2D Multisample


I am following a tutorial by 2etime on YouTube about Apple's Metal graphics API. Everything works as intended, but when I try to change the view's sample count, I get a lot of errors. The errors, though, are easily fixed. So far, I've changed the sample count in every file, but I am stuck at the shaders. To change the view's sample count, you have to change the texture sample count (at least in my case), and to change the texture sample count, you have to change the texture type to multisample. This is easily done. However, after this change, I get errors from the shaders. It looks like the fragment shader uses texture2d, and I need to change it to texture2d_ms, but when I do so, I get error saying that 'sample' function is not valid. I will post the code and the error. I've tried searching on the internet, but I can't seem to find anything.

FinalShaders.metal

#include <metal_stdlib>
#include "Shared.metal"
using namespace metal;

struct FinalRasterizerData {
    float4 position [[ position ]];
    float2 textureCoordinate;
};

vertex FinalRasterizerData final_vertex_shader(const VertexIn vIn [[ stage_in ]]) {
    FinalRasterizerData rd;
    
    rd.position = float4(vIn.position, 1.0);
    rd.textureCoordinate = vIn.textureCoordinate;
    
    return rd;
}

fragment half4 final_fragment_shader(const FinalRasterizerData rd [[ stage_in ]],
                                     texture2d_ms<float> baseTexture) {
    sampler s;
    
    float2 textureCoordinate = rd.textureCoordinate;
    textureCoordinate.y = 1 - textureCoordinate.y;
    
    float4 color = baseTexture.sample(s, textureCoordinate);
    
    return half4(color);
}

Shared.metal

#ifndef SHARED_METAL
#define SHARED_METAL

#include <metal_stdlib>
using namespace metal;

struct VertexIn {
    float3 position [[ attribute(0) ]];
    float4 color [[ attribute(1) ]];
    float2 textureCoordinate [[ attribute(2) ]];
    float3 normal [[ attribute(3) ]];
    float3 tangent [[ attribute(4) ]];
    float3 bitangent [[ attribute(5) ]];
};

struct RasterizerData{
    float4 position [[ position ]];
    float4 color;
    float2 textureCoordinate;
    float totalGameTime;
    
    float3 worldPosition;
    float3 toCameraVector;
    
    float3 surfaceNormal;
    float3 surfaceTangent;
    float3 surfaceBitangent;
};

struct ModelConstants{
    float4x4 modelMatrix;
};

struct SceneConstants{
    float totalGameTime;
    float4x4 viewMatrix;
    float4x4 skyViewMatrix;
    float4x4 projectionMatrix;
    float3 cameraPosition;
};

struct Material {
    float4 color;
    bool isLit;
    bool useBaseTexture;
    bool useNormalMapTexture;
    
    float3 ambient;
    float3 diffuse;
    float3 specular;
    float shininess;
};

struct LightData {
    float3 position;
    float3 color;
    float brightness;
    
    float ambientIntensity;
    float diffuseIntensity;
    float specularIntensity;
};

#endif

And the error:

No member named 'sample' in 'metal::texture2d_ms<float, metal::access::read, void>'

I know it clearly says that 'sample' is not existent, but I can't find any solution. When I removed it, and replaced the return with different value, I only see a lot of green/yellow lines, and no 3d objects. Thanks for any help!

(the shader the error is happening in is the fragment one in final shaders.metal)


Solution

  • Sampling in a way that sample function means doesn't make sense for multisampled textures. What you are looking for instead is reading the exact sample value at a given texcoord.

    If you look in the Metal Shader Language specification, in section 6.12.8 it describes which functions exist for 2D multisampled textures.

    Those include:

    Tv read(uint2 coord, uint sample) const
    Tv read(ushort2 coord, ushort sample) const 
    
    uint get_width() const
    uint get_height() const
    uint get_num_samples() const
    

    In read function, coord means pixel coordinates, so they would go from 0 to get_width() on X axis and from 0 to get_height() on Y axis. sample is the index of the sample, so it would be from 0 to get_num_samples()