According to Apple's Metal shading language specification, texture cubes have a read method,
read(uint2 coord, uint face, uint lod = 0) const
However, when I try to build this shader, I get a compiler error,
fragment half4 passFragment(VertexInOut inFrag [[stage_in]],
texturecube<float, access::read> tex [[ texture(0) ]])
{
float4 out = tex.read(uint2(0,0), uint(0));
return half4(out);
}
The error is,
No member named 'read' in 'metal::texturecube<float, metal::access::read>'
If I remove the access qualifier, then I get,
No member named 'read' in 'metal::texturecube<float, metal::access::sample>'
I also tried changing the type from float to int or short, but I get the same error. Frustrating that there's no header to look at...
Any ideas?
It appears that texturecube::read() is only available on macOS.
There are, in fact, headers available. Look in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/clang/3.5/include/metal.
In the metal_texture header, you'll see that the declaration of read() is inside of a preprocessor conditional (#if) and is only declared if the macro __HAVE_TEXTURE_CUBE_READ__ is defined. On macOS, that's defined in the metal_config header. On iOS, it's not defined.
Related
I use a codegen for generating declarations of Metal shaders and sometimes I do not know the exact types of objects that are passed to shaders.
E.g. I would have this declaration generated automatically:
vertex VertexOut vertexShader(constant Element *array [[buffer(0)]])
When I try to get the element's type of the array I get the error from the compiler:
using T = metal::remove_reference_t<decltype( *array )>;
T test;// <-- ERROR! "Automatic variable qualified with an address space"
Is this possible to "erase" the address space from the type?
What is the best way of getting the type of an array's element in Metal (if it's possible at all)?
As I said in the comment, I think the problem is that remove_reference does exactly what it says: it removes reference, while still leaving the type qualified. You can not declare a variable in device or constant space, so you also need to remove the address space qualifier, similar to how remove_cv_t works. I've written up a couple templates to show you what I mean:
template <typename T>
struct remove_address_space
{
typedef T type;
};
template <typename T>
struct remove_address_space<device T>
{
typedef T type;
};
template <typename T>
struct remove_address_space<constant T>
{
typedef T type;
};
and then you would use it like
using T = remove_address_space<metal::remove_reference_t<decltype( *array )>>::type;
Keep in mind that metal has a lot of address spaces, but for the purposes of writing entry points to functions, I think only the device and constant are relevant.
MTLRenderPipelineReflection contain lists of MTLArgument's that contains info about name, index, type, access, e.t.c. except any info about function constant binding.
There are MTLFunction and it's dictionary var functionConstantsDictionary: [String : MTLFunctionConstant], that keyed by function constant name(it feels excess because MTLFunctionConstant already contains name). I did not found any info about function constants that in use of argument.
As a result should be something like table [FunctionArgumentName: FunctionConstant]
Is there any info about pipeline that helps to calculate that bindings?
UPD:
fragment float4 fragment_main(VertexOut in [[stage_in]],
texture2d<float> baseColorTexture [[texture(BaseColorTexture), function_constant(hasColorTexture)]],
texture2d<float> normalTexture [[texture(NormalTexture), function_constant(hasNormalTexture)]],
texture2d<float> aoTexture [[texture(AOTexture), function_constant(hasAOTexture)]],
texturecube<float> reflectionTextureCube [[texture(ReflectionTextureCube), function_constant(hasReflectionTextureCube)]]) {
/// Shader code
}
In other words I need to access name of function constant for baseColorTexture argument of this shader function from swift code
I'm writing a shader tool app where I can create nodes and link them to generate a texture:
I used D3D12 shader reflection to get the constant buffer variables and now I'm trying to figure out how to pass/bind these vars in runtime. At the moment I have the constant buffers defined in code like this:
struct ObjectConstants
{
DirectX::XMFLOAT4X4 World{ D3DUtil::Identity4x4() };
DirectX::XMFLOAT3 Color{ 0.f, 0.f, 0.f };
};
How can I create a constant buffer, bind it and copy data from CPU to GPU in runtime?
PS: The root signature and PSO are being created in runtime.
I'm struggling with porting my OpenGL application to Metal. In my old app, I used to bind two buffers, one with vertices and respective colours and one with vertices and respective textures, and switch between the two based on some app logic. Now in Metal I've started with the Hello Triangle example where I tried running this vertex shader
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
constant AAPLVertex1 *vertices1 [[buffer(AAPLVertexInputIndexVertices1)]],
constant AAPLVertex2 *vertices2 [[buffer(AAPLVertexInputIndexVertices2)]],
constant bool &useFirstBuffer [[buffer(AAPLVertexInputIndexUseFirstBuffer)]])
{
float2 pixelSpacePosition;
if (useFirstBuffer) {
pixelSpacePosition = vertices1[vertexID].position.xy;
} else {
pixelSpacePosition = vertices2[vertexID].position.xy;
}
...
and this Objective-C code
bool useFirstBuffer = true;
[renderEncoder setVertexBytes:&useFirstBuffer
length:sizeof(bool)
atIndex:AAPLVertexInputIndexUseFirstBuffer];
[renderEncoder setVertexBytes:triangleVertices
length:sizeof(triangleVertices)
atIndex:AAPLVertexInputIndexVertices1];
(where AAPLVertexInputIndexVertices1 = 0, AAPLVertexInputIndexVertices2 = 1 and AAPLVertexInputIndexUseFirstBuffer = 3), which should result in vertices2 never getting accessed, but still I get the error: failed assertion 'Vertex Function(vertexShader): missing buffer binding at index 1 for vertices2[0].'
Everything works if I replace if (useFirstBuffer) with if (true) in the Metal code. What is wrong?
When you're hard-coding the conditional, the compiler is smart enough to eliminate the branch that references the absent buffer (via dead-code elimination), but when the conditional must be evaluated at runtime, the compiler doesn't know that the branch is never taken.
Since all declared buffer parameters must be bound, leaving the unreferenced buffer unbound trips the validation layer. You could bind a few "dummy" bytes at the Vertices2 slot (using -setVertexBytes:length:atIndex:) when not following that path to get around this. It's not important that the buffers have the same length, since, after all, the dummy buffer will never actually be accessed.
In the atIndex argument, you call the code with the values AAPLVertexInputIndexUseFirstBuffer and AAPLVertexInputIndexVertices1 but in the Metal code the values AAPLVertexInputIndexVertices1 and AAPLVertexInputIndexVertices2 appear in the buffer() spec. It looks like you need to use AAPLVertexInputIndexVertices1 instead of AAPLVertexInputIndexUseFirstBuffer in your calling code.
In most cases Im happy by the way SWIG is handling data, however Im facing an issue and cannot find an answer in the documentation.
First of all Im using SWIG with Lua and have the following structures wrapped:
typedef struct
{
%mutable;
float x,y,z;
...
...
} Vector3;
typedef struct
{
...
...
%immutable;
Vector3 gravity;
...
...
%extend
{
void SetGravity(Vector3 gravity)
{
WorldSetGravity($self,gravity);
}
};
} World;
As you can see the gravity XYZ can be affected by calling the SetGravity function, and it work great.
However, in order to be more intuitive and easier to use, I would like to give the opportunity to the user to set component (XY or Z) independently like:
world.gravity.x=-10;
But I need to call in the background SetGravity in order to be able to send the proper value to the physics engine (which is not exposed to Lua).
I would like to know if there’s a way to %extend variables which will allow me to call SetGravity when the world.gravity.xy or z is called?
Or be able to implement my own version of the wrap function for each component like: _wrap_World_gravity_set_x which will allot me to call SetGravity in the background.
Firstly it's worth noting that this problem is harder than simply making a "virtual" member variable using %extend that automatically calls an extra function when someone modifies it. This is because you want the fact that it's a member of another class to alter the behaviour.
I can see several fundamental approaches you could take to get this behaviour:
Inject some extra code in the target scripting language to hook the set
Inject some extra stuff in the SWIG interface to transparently convert the Vector3 inside World to something that still looks and feels the same, but has the behaviour you want under the hood.
Inject some extra code into the memberin typemap for Vector3 that checks the context it's being called from and modifies the behaviour accordingly.
Of these #2 is my preferred solution because #1 is language specific (and I don't know Lua well enough to do it!) and #3 feels dirty from a software engineering perspective.
To implement #2 I did the following:
%module test
%{
#include "test.h"
%}
typedef struct
{
%mutable;
float x,y,z;
} Vector3;
%nodefaultctor Vector3Gravity;
%{
// Inside C this is just a typedef!
typedef Vector3 Vector3Gravity;
// But we have magic for sets/gets now:
#define MEMBER_VAR(ct,vt,rt,n) \
SWIGINTERN void ct##_##n##_set(ct *self, const vt val) { \
self->n = val; \
/* Need to find a way to lookup world here */ \
WorldSetGravity(world, self); \
} \
SWIGINTERN vt ct##_##n##_get(const ct *self) { return self->n; }
MEMBER_VAR(Vector3Gravity, float, Vector3, x)
MEMBER_VAR(Vector3Gravity, float, Vector3, y)
MEMBER_VAR(Vector3Gravity, float, Vector3, z)
%}
// Inside SWIG Vector3Gravity is a distinct type:
typedef struct
{
%mutable;
%extend {
float x,y,z;
}
} Vector3Gravity;
%typemap(memberin,noblock=1) Vector3Gravity gravity %{
$1 = *((const Vector3*)$input);
WorldSetGravity($self, $1); // This gets expanded to automatically make this call
%}
typedef struct
{
// This is a blatant lie!
Vector3Gravity gravity;
} World;
Essentially we're lying and claiming that the gravity member of world is a "special" type, whereas really it's just a Vector3. Our special type has two distinct features. Firstly sets/gets on its members are implemented a C code by us. Secondly when we set this member we automatically make an extra call rather than just pass the values in.
There are two things possibly missing from this example that you might want:
Transparent conversion from Vector3Gravity to Vector3. (As it stands anything other than the set for gravity will refuse to accept Vector3Gravity instances). You can make that transparent by using the overload resolution mechanisms of SWIG/Lua if needed.
Inside the setters for Vector3Gravity we don't know which world this gravity belongs to.
We could solve that in several ways, the simplest being to implicitly set a static pointer every time we create a Vector3Gravity. This would make sense if there only ever is one world.
Another approach would be to use a global map of Vector3Gravity instances to worlds that gets maintained automatically.
Finally, instead of using a typedef for the Vector3Gravity type we could make it a real distinct type, with a compatible layout, but add a pointer to the World it came from. That's more work though.