I've got a question about constant buffers in Metal.
Let's assume, that I've got something like:
...list of includes goes here...
using namespace metal;
struct ConstantBuffer {
float ANY_VALUE;
};
struct VS_INPUTS {
float4 i_pos_ms [ [ attribute ( 0 ) ] ] ;
} ;
struct V2P_STRUCT {
float4 v_pos_out [ [ position ] ] ;
} ;
float3 CalcSomething() {
return float3(ANY_VALUE, ANY_VALUE, ANY_VALUE); // !!!!!!!!
}
vertex V2P_STRUCT VertexFunc(VS_INPUTS vs_inputs [ [ stage_in ] ] ,
constant ConstantBuffer& cb [ [ buffer (1) ] ] )
{
V2P_STRUCT vs_outputs;
vs_outputs.v_pos_out.xyz = CalcSomething();
vs_outputs.v_pos_out.w = cb.ANY_VALUE; // that's OK
return vs_outputs;
}
Is it possible to call CalcSomething() without passing ANY_VALUE as input argument?
For example in DX11 or in OpenGL you create constant buffer, which can be accessed from every place in shader code.
I think about copying content of "cb" to temporary global object but I have no idea how to do it (because of constant address space).
Another idea is to somehow declare "cb" in global scope (but unfortunately [[buffer]] is designed only for arguments). Is there any trick for that?
Solution to my issue:
#include <metal_stdlib>
#include <metal_graphics>
#include <metal_texture>
#include <metal_matrix>
#include <metal_math>
#include <metal_geometric>
#include <metal_common>
using namespace metal;
constant float MyVariable = 4;
struct ConstantBuffer
{
float ANY_VALUE;
};
struct VS_INPUTS {
float4 i_pos_ms [ [ attribute ( 0 ) ] ] ;
};
struct V2P_STRUCT {
float4 v_pos_out [ [ position ] ] ;
};
struct VertexShader
{
thread VS_INPUTS& vs_inputs;
thread texture2d<float> img;
constant ConstantBuffer& cb;
VertexShader(thread VS_INPUTS& inputs, constant ConstantBuffer& b, thread texture2d<float>& texture)
: cb(b)
, vs_inputs(inputs)
, img(texture)
{}
float3 CalcSomething() {
return float3(cb.ANY_VALUE, cb.ANY_VALUE, cb.ANY_VALUE); // !!!!!!!!
}
V2P_STRUCT majn()
{
V2P_STRUCT vs_outputs;
vs_outputs.v_pos_out.xyz = CalcSomething();
vs_outputs.v_pos_out.w = cb.ANY_VALUE * vs_inputs.i_pos_ms.x * MyVariable; // that's OK
return vs_outputs;
}
};
vertex V2P_STRUCT VertexFunc(VS_INPUTS vs_inputs [ [ stage_in ] ] ,
constant ConstantBuffer& cb [ [ buffer (1) ] ] ,
texture2d<float> img [[ texture(0) ]]
)
{
VertexShader vs(vs_inputs, cb, img);
return vs.majn();
}
I create one struct which contains my whole original shader. Arguments are passed as references to the constructor. Any function can read from constant buffer without receiving tons of arguments.
To fix problem with ANY_VALUE which is now part of cb I use macro:
#define ANY_VALUE cb.ANY_VALUE.
There are many questions here. I think it would be best if you provided us a problem to solve, instead of trying to shoehorn concepts from other platforms into metal. For now, here are some ideas.
Is it possible to call CalcSomething() without passing ANY_VALUE as input argument?
struct ConstantBuffer {
const float ANY_VALUE;
};
constant const ConstantBuffer constantBuffer = {1};
static float3 CalcSomething() {
return float3(constantBuffer.ANY_VALUE);
}
Are you sure CalcSomething shouldn't be a method?
struct ConstantBuffer {
ConstantBuffer(const float value): value(value) {}
float3 calculateSomething() const {
return float3(value);
}
const float value;
};
vertex V2P_STRUCT VertexFunc(
constant const ConstantBuffer& _constantBuffer [[buffer(1)]]
) {
// Metal can't currently deal with methods without this.
const auto constantBuffer = _constantBuffer;
Another idea is to somehow declare "cb" in global scope (but unfortunately [[buffer]] is designed only for arguments). Is there any trick for that?
The "trick", in my mind, is to create the buffer in Swift, not the Metal shading language.
Related
I have the shader below where I define a sampler (constexpr sampler textureSampler (mag_filter::linear,min_filter::linear);).
using namespace metal;
struct ProjectedVertex {
'float4 position [[position]];
'float2 textureCoord;
};
fragment float4 fragmentShader(const ProjectedVertex in [[stage_in]],
const texture2d<float> colorTexture [[texture(0)]],
constant float4 &opacity [[buffer(1)]]){
constexpr sampler textureSampler (mag_filter::linear,min_filter::linear);
const float4 colorSample = colorTexture.sample(textureSampler, in.textureCoord);
return colorSample*opacity[0];
}
Now I would like to avoid to hardly define this sampler inside my shader code. I found MTLSamplerState But I don't know how to use it
To create a sampler, first create a MTLSamplerDescriptor object and configure the descriptor’s properties. Then call the newSamplerStateWithDescriptor: method on the MTLDevice object that will use this sampler. After you create the sampler, you can release the descriptor or reconfigure its properties to create other samplers.
// Create default sampler state
MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new];
samplerDesc.rAddressMode = MTLSamplerAddressModeRepeat;
samplerDesc.sAddressMode = MTLSamplerAddressModeRepeat;
samplerDesc.tAddressMode = MTLSamplerAddressModeRepeat;
samplerDesc.minFilter = MTLSamplerMinMagFilterLinear;
samplerDesc.magFilter = MTLSamplerMinMagFilterLinear;
samplerDesc.mipFilter = MTLSamplerMipFilterNotMipmapped;
id<MTLSamplerState> ss = [device newSamplerStateWithDescriptor:samplerDesc];
Sets a sampler state for the fragment function:
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor: passDescriptor];
...
[encoder setFragmentSamplerState: ss atIndex:0];
Accessing from the shader:
fragment float4 albedoMainFragment(ImageColor in [[stage_in]],
texture2d<float> diffuseTexture [[texture(0)]],
sampler smp [[sampler(0)]]) {
float4 color = diffuseTexture.sample(smp, in.texCoord);
return color;
}
How to create SamplerState
First, declare MTLSamplerDescriptor and configure some properties such as addressModes, magFilter, minFilter.
Second, call makeSamplerState method from MTLDevice. Most cases default device.
That
You can use the below code. I hope it helps.
private static func buildSamplerState() -> MTLSamplerState? {
let descriptor = MTLSamplerDescriptor()
descriptor.sAddressMode = .repeat // .clampToEdge, .mirrorRepeat, .clampToZero
descriptor.tAddressMode = .repeat // .clampToEdge, .mirrorRepeat, .clampToZero
descriptor.magFilter = .linear // .nearest
descriptor.minFilter = .linear // .nearest
let samplerState = MTLCreateSystemDefaultDevice()?.makeSamplerState(descriptor: descriptor)
return samplerState
}
How to use it
...
let samplerState = buildSamplerState()
...
// call `makeRenderCommandEncoder` to create commandEncoder from commandBuffer
let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
commandEncoder.setFragmentSamplerState(samplerState, index: 0)
in your fragment shader
fragment float4 exampleShader(VertexIO inputFragment [[stage_in]],
sampler textureSampler [[sampler(0)]],
texture2d<float> inputTexture [[texture(0)]])
{
float2 position = inputFragment.textureCoord;
// ...
return inputTexture.sample(textureSampler, position);
}
I have this Metal shader :
struct InVertex {
packed_float3 pos;
packed_uchar4 color;
};
vertex ProjectedVertex vertexShader(const device InVertex *vertexArray [[buffer(0)]],
const unsigned int vertexId [[vertex_id]]){
InVertex in = vertexArray[vertexId];
....
}
However I would like to make the buffer declaration "dynamic". IE I would like that my shader is able to handle buffer type like for exemple
struct InVertex1 {
packed_float3 pos;
packed_uchar4 color;
};
struct InVertex2 {
float4 pos;
flat4 color;
};
struct InVertex3 {
float4 pos;
float4 tangent;
float4 color;
};
etc..
so I would like something like :
vertex ProjectedVertex vertexShader(const device ???? *vertexArray [[buffer(0)]],
const unsigned int vertexId [[vertex_id]]
const device int vertexType [[buffer(1)]] ){
if vertexType = InVertex1Type {
... handle InVertex1 type ...
}
else if vertexType = InVertex2Type {
... handle InVertex2 type ...
}
else if vertexType = InVertex3Type {
... handle InVertex3 type ...
}
}
The Metal programming language is a C++14-based Specification with extensions and
restrictions. Taking this into account you can do the following.
First create a header file called ShaderTypes.h:
// Header containing types and enum constants shared between Metal shaders and Swift/ObjC source
#ifndef ShaderTypes_h
#define ShaderTypes_h
#ifndef __METAL_VERSION__
/// 96-bit 3 component float vector type
typedef struct __attribute__ ((packed)) packed_float3 {
float x;
float y;
float z;
} packed_float3;
#endif
typedef struct
{
packed_float3 pos;
packed_uchar4 color;
} InVertex1;
typedef struct
{
vector_float4 pos;
vector_float4 color;
} InVertex2;
typedef struct
{
vector_float4 pos;
vector_float4 tangent;
vector_float4 color;
} InVertex3;
enum VertexType {InVertex1Type = 0, InVertex2Type = 1, InVertex3Type = 2};
typedef struct
{
InVertex1 InVertex1;
InVertex2 InVertex2;
InVertex3 InVertex3;
VertexType vertexType;
} dynamicStruct;
#endif /* ShaderTypes_h */
In your render class add the following:
// Include header shared between C code here, which executes Metal API commands, and .metal files
#import "ShaderTypes.h"
id <MTLBuffer> _dynamicBuffer;
// Create your dynamic buffer.
void InitBuffer(id<MTLDevice> device)
{
_dynamicBuffer = [device newBufferWithLength:sizeof(dynamicStruct) options:MTLResourceStorageModeShared];
}
// Update your dynamic buffer.
void UpdateBuffer()
{
dynamicStruct* ds = (dynamicStruct*)_dynamicBuffer.contents;
ds->InVertex1.color = {0, 0, 0, 0};
ds->InVertex2.pos = {0, 1, 1, 1};
ds->InVertex3.tangent = {1, 1, 1, 1};
// Select specific struct
ds->vertexType = VertexType::InVertex2Type;
}
- (void)drawInMTKView:(nonnull MTKView *)view
{
...
// Pass your dynamic buffer to the shader.
[renderEncoder setVertexBuffer:_dynamicBuffer offset:0 atIndex:0];
...
}
And finally in your shader file (.metal):
// Including header shared between this Metal shader code and Swift/C code executing Metal API commands
#import "ShaderTypes.h"
vertex ProjectedVertex vertexShader(constant dynamicStruct & dynamicStruct[[ buffer(0) ]],
const unsigned int vertexId [[vertex_id]])
{
InVertex1 v1;
InVertex2 v2;
InVertex3 v3;
if(dynamicStruct.vertexType == VertexType::InVertex1Type)
{
v1 = dynamicStruct.InVertex1;
}
else if(dynamicStruct.vertexType == VertexType::InVertex2Type)
{
v2 = dynamicStruct.InVertex2;
}
else if(dynamicStruct.vertexType == VertexType::InVertex3Type)
{
v3 = dynamicStruct.InVertex3;
}
....
}
I want to do some custom work in custom shader, but get a internal error when run it. compile seems ok, but run will get the error message:
error building shaders : Error Domain=AGXMetalG4P Code=1 "Compiler encountered an internal error" UserInfo={NSLocalizedDescription=Compiler encountered an internal error}
[SCNKit ERROR] display link thread seems stuck
SCNProgram *program = [[SCNProgram alloc] init];
program.fragmentFunctionName = #"myVertex";
program.vertexFunctionName = #"myFragment";
mySceneNode.geometry.program = program
and the shader:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct MyNodeBuffer {
float4x4 modelTransform;
float4x4 modelViewTransform;
float4x4 normalTransform;
float4x4 modelViewProjectionTransform;
};
typedef struct {
float3 position [[ attribute(SCNVertexSemanticPosition) ]];
} MyVertexInput;
struct SimpleVertex
{
float4 position [[position]];
};
vertex SimpleVertex myVertex(MyVertexInput in [[ stage_in ]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]])
{
SimpleVertex vert;
vert.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
vert.position = float4(in.position,0);
return vert;
}
fragment half4 myFragment(SimpleVertex in [[stage_in]])
{
half4 color;
color = half4(0.0 ,1.0 ,0.0, 1.0);
return color;
}
Suppose I have the following uniform buffer in my shader:
typedef struct
{
matrix_float4x4 modelview_projection_matrix;
float someValue;
} uniforms_t;
How do I get someValue's location in C++ or Objective-C? I want to do something like this:
void Shader::SetFloat( const char* name, float value )
where name would be 'someValue'.
I came up with a solution by examining BGFX's source code:
NSError* error = NULL;
MTLRenderPipelineReflection* reflectionObj;
MTLPipelineOption option = MTLPipelineOptionBufferTypeInfo | MTLPipelineOptionArgumentInfo;
id <MTLRenderPipelineState> pso = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor options:option reflection:&reflectionObj error:&error];
for (MTLArgument *arg in reflectionObj.vertexArguments)
{
NSLog(#"Found arg: %#\n", arg.name);
if (arg.bufferDataType == MTLDataTypeStruct)
{
for( MTLStructMember* uniform in arg.bufferStructType.members )
{
NSLog(#"uniform: %# type:%lu, location: %lu", uniform.name, (unsigned long)uniform.dataType, (unsigned long)uniform.offset);
}
}
}
Have a look at the Specifying Resources for a Render Command Encoder section of Apple's Metal Programming Guide.
As a very basic explanation...
Declare your uniforms_t structure (which will typically be a single structure containing all the uniforms for a particular shader function) as an argument of a Metal shader function, and associate it with a specific buffer index (eg. [[ buffer(0) ]]) as part of your shader function declaration.
From your app code, copy the contents of your uniforms_t structure into a MTLBuffer, at some offset.
From your app code, invoke the MTLRenderCommandEncoder setVertexBuffer:offset:atIndex: or setFragmentBuffer:offset:atIndex: methods to associate the contents of the MTLBuffer (at the offset where you copied your uniforms_t structure) with the buffer index you declared in your shader function. This basically tells the shader function which MTLBuffer to look in (and where in that buffer) for the value of that function argument.
I'm trying to load in meshes into DirectX 10. I've created a bunch of classes that handle it and allow me to call in a mesh with only a single line of code in my main game class.
How ever, when I run the program this is what renders:
In the debug output window the following errors keep appearing:
D3D10: ERROR: ID3D10Device::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. The reason is that Semantic 'TEXCOORD' is defined for mismatched hardware registers between the output stage and input stage. [ EXECUTION ERROR #343: DEVICE_SHADER_LINKAGE_REGISTERINDEX ]
D3D10: ERROR: ID3D10Device::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. The reason is that the input stage requires Semantic/Index (POSITION,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND ]
The thing is, I've no idea how to fix this. The code I'm using does work and I've simply brought all of that code into a new project of mine. There are no build errors and this only appears when the game is running
The .fx file is as follows:
float4x4 matWorld;
float4x4 matView;
float4x4 matProjection;
struct VS_INPUT
{
float4 Pos:POSITION;
float2 TexCoord:TEXCOORD;
};
struct PS_INPUT
{
float4 Pos:SV_POSITION;
float2 TexCoord:TEXCOORD;
};
Texture2D diffuseTexture;
SamplerState diffuseSampler
{
Filter = MIN_MAG_MIP_POINT;
AddressU = WRAP;
AddressV = WRAP;
};
//
// Vertex Shader
//
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output=(PS_INPUT)0;
float4x4 viewProjection=mul(matView,matProjection);
float4x4 worldViewProjection=mul(matWorld,viewProjection);
output.Pos=mul(input.Pos,worldViewProjection);
output.TexCoord=input.TexCoord;
return output;
}
//
// Pixel Shader
//
float4 PS(PS_INPUT input ) : SV_Target
{
return diffuseTexture.Sample(diffuseSampler,input.TexCoord);
//return float4(1.0f,1.0f,1.0f,1.0f);
}
RasterizerState NoCulling
{
FILLMODE=SOLID;
CULLMODE=NONE;
};
technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
SetRasterizerState(NoCulling);
}
}
In my game, the .fx file and model are called and set as follows:
Loading in shader file
//Set the shader flags - BMD
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
ID3D10Blob * pErrorBuffer=NULL;
if( FAILED( D3DX10CreateEffectFromFile( TEXT("TransformedTexture.fx" ), NULL, NULL, "fx_4_0", dwShaderFlags, 0, md3dDevice, NULL, NULL, &m_pEffect, &pErrorBuffer, NULL ) ) )
{
char * pErrorStr = ( char* )pErrorBuffer->GetBufferPointer();
//If the creation of the Effect fails then a message box will be shown
MessageBoxA( NULL, pErrorStr, "Error", MB_OK );
return false;
}
//Get the technique called Render from the effect, we need this for rendering later on
m_pTechnique=m_pEffect->GetTechniqueByName("Render");
//Number of elements in the layout
UINT numElements = TexturedLitVertex::layoutSize;
//Get the Pass description, we need this to bind the vertex to the pipeline
D3D10_PASS_DESC PassDesc;
m_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
//Create Input layout to describe the incoming buffer to the input assembler
if (FAILED(md3dDevice->CreateInputLayout( TexturedLitVertex::layout, numElements,PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &m_pVertexLayout ) ) )
{
return false;
}
model loading:
m_pTestRenderable=new CRenderable();
//m_pTestRenderable->create<TexturedVertex>(md3dDevice,8,6,vertices,indices);
m_pModelLoader = new CModelLoader();
m_pTestRenderable = m_pModelLoader->loadModelFromFile( md3dDevice,"armoredrecon.fbx" );
m_pGameObjectTest = new CGameObject();
m_pGameObjectTest->setRenderable( m_pTestRenderable );
// Set primitive topology, how are we going to interpet the vertices in the vertex buffer
md3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
if ( FAILED( D3DX10CreateShaderResourceViewFromFile( md3dDevice, TEXT( "armoredrecon_diff.png" ), NULL, NULL, &m_pTextureShaderResource, NULL ) ) )
{
MessageBox( NULL, TEXT( "Can't load Texture" ), TEXT( "Error" ), MB_OK );
return false;
}
m_pDiffuseTextureVariable = m_pEffect->GetVariableByName( "diffuseTexture" )->AsShaderResource();
m_pDiffuseTextureVariable->SetResource( m_pTextureShaderResource );
Finally, the draw function code:
//All drawing will occur between the clear and present
m_pViewMatrixVariable->SetMatrix( ( float* )m_matView );
m_pWorldMatrixVariable->SetMatrix( ( float* )m_pGameObjectTest->getWorld() );
//Get the stride(size) of the a vertex, we need this to tell the pipeline the size of one vertex
UINT stride = m_pTestRenderable->getStride();
//The offset from start of the buffer to where our vertices are located
UINT offset = m_pTestRenderable->getOffset();
ID3D10Buffer * pVB=m_pTestRenderable->getVB();
//Bind the vertex buffer to input assembler stage -
md3dDevice->IASetVertexBuffers( 0, 1, &pVB, &stride, &offset );
md3dDevice->IASetIndexBuffer( m_pTestRenderable->getIB(), DXGI_FORMAT_R32_UINT, 0 );
//Get the Description of the technique, we need this in order to loop through each pass in the technique
D3D10_TECHNIQUE_DESC techDesc;
m_pTechnique->GetDesc( &techDesc );
//Loop through the passes in the technique
for( UINT p = 0; p < techDesc.Passes; ++p )
{
//Get a pass at current index and apply it
m_pTechnique->GetPassByIndex( p )->Apply( 0 );
//Draw call
md3dDevice->DrawIndexed(m_pTestRenderable->getNumOfIndices(),0,0);
//m_pD3D10Device->Draw(m_pTestRenderable->getNumOfVerts(),0);
}
Is there anything I've clearly done wrong or are missing? Spent 2 weeks trying to workout what on earth I've done wrong to no avail.
Any insight a fresh pair eyes could give on this would be great.