This is in a compute shader, but I think it's a general hlsl thing. Here's a snippet:
Texture2D<float> Ground : register(t1);
Texture2D<float> Water : register(t2);
SamplerState LinearSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Clamp;
AddressV = Clamp;
};
float4 Get(Texture2D source, float x, float y)
{
return source.SampleLevel(LinearSampler, float2(x * dimension.z, y * dimension.w), 0);
}
[numthreads(32, 32, 1)]
void main(uint3 threadID : SV_DispatchThreadID, uint3 groupThreadID : SV_GroupThreadID, uint3 blockID : SV_GroupID)
{
float4 g = GetGround(Ground, 0, 0);
Output[threadID.xy] = g.z;
}
Any calls to the Get method give me:
error X3017: 'Get': cannot implicitly convert from 'const Texture2D' to 'Texture2D'
I assume your lack of code tags are causing the angle brackets to be omitted. The error should be error X3017: 'Get': cannot implicitly convert from 'const Texture2D<float>' to 'Texture2D<float4>' right? This makes sense since Texture2D is implicitly Texture2D<float4> (4-channel), and your global textures are Texture2D<float> (single-channel). The compiler doesn't expand types implicitly (though it will truncate with a warning). This is regardless of whether it's a Texture2D or a plain old float4. To fix your code, you should make sure your source textures are really single-channel, and then make the function argument match (i.e. Texture2D<float> source).
Related
I have fragment shader "fsh" file and I am trying to compile it, it is originally taken from Shadertoy, and it is in GLSL, I am trying to port it to METAL, and I am getting the following error:
program_source:129:12: error: program scope variable must reside in constant address space
const vec3 ro, rd;
As far as I can understand, I can not define ro and rd in global scope like this, how can I fix this ?
Thank you very much.
The code is below:
const vec3 ro, rd;
....
void main(void)
{
float t = u_time;
vec3 col = vec3(0.);
vec2 uv = gl_FragCoord.xy / iResolution.xy; // 0 <> 1
uv -= .5;
uv.x *= iResolution.x/iResolution.y;
vec2 mouse = gl_FragCoord.xy/iResolution.xy;
vec3 pos = vec3(.3, .15, 0.);
float bt = t * 5.;
float h1 = N(floor(bt));
float h2 = N(floor(bt+1.));
float bumps = mix(h1, h2, fract(bt))*.1;
bumps = bumps*bumps*bumps*CAM_SHAKE;
pos.y += bumps;
float lookatY = pos.y+bumps;
vec3 lookat = vec3(0.3, lookatY, 1.);
vec3 lookat2 = vec3(0., lookatY, .7);
lookat = mix(lookat, lookat2, sin(t*.1)*.5+.5);
uv.y += bumps*4.;
CameraSetup(uv, pos, lookat, 2., mouse.x);
t *= .03;
t += mouse.x;
// fix for GLES devices by MacroMachines
#ifdef GL_ES
const float stp = 1./8.;
#else
float stp = 1./8.;
#endif
for(float i=0.; i<1.; i+=stp) {
col += StreetLights(i, t);
}
for(float i=0.; i<1.; i+=stp) {
float n = N(i+floor(t));
col += HeadLights(i+n*stp*.7, t);
}
#ifndef GL_ES
#ifdef HIGH_QUALITY
stp = 1./32.;
#else
stp = 1./16.;
#endif
#endif
for(float i=0.; i<1.; i+=stp) {
col += EnvironmentLights(i, t);
}
col += TailLights(0., t);
col += TailLights(.5, t);
col += sat(rd.y)*vec3(.6, .5, .9);
gl_FragColor = vec4(col, 0.);
}
The equivalent declaration in Metal Shading Language (MSL) would be
constant float3 ro, rd;
However, you should also initialize these variables with values, since your shader functions will not be allowed to mutate them. Something like
constant float3 ro(0, 0, 0), rd(1, 1, 1);
A few more translation hints:
Metal doesn't have syntax for declaring uniforms. Instead, you'll need to pass such values in via a buffer in the constant or device address space. This includes things like your screen resolution and time variables.
Vector type names generally start with the name of their element type, followed by the number of elements (half2, float3). There are no explicit precision qualifiers in MSL.
Rather than writing to special values like gl_FragColor, basic fragment functions in Metal return a color value (which by convention is written to the first color attachment of the framebuffer, provided it passes the depth and stencil test).
I am using a sampler to sample from texture:
constexpr sampler cubeSampler(filter::linear, mip_filter::none);
half4 res = cubeTexture.sample(cubeSampler, texVec);
The result is of type half4 but i need to cast it to float4 in order to perform math operations. How can i perform this cast?
static_cast works, or you can use the more terse converting constructor:
float4 res_float4 = float4(res);
constexpr sampler cubeSampler(filter::linear, mip_filter::none);
half4 res = cubeTexture.sample(cubeSampler, texVec);
// cast to float4:
float4 res_float4 = static_cast<float4>(res);
As mentioned in Apple's document, texture2d of shading language could be of int type. I have tried to use texture2d of int type as parameter of shader language, but the write method of texture2d failed to work.
kernel void dummy(texture2d<int, access::write> outTexture [[ texture(0) ]],
uint2 gid [[ thread_position_in_grid ]])
{
outTexture.write( int4( 2, 4, 6, 8 ), gid );
}
However, if I replace the int with float, it worked.
kernel void dummy(texture2d<float, access::write> outTexture [[ texture(0) ]],
uint2 gid [[ thread_position_in_grid ]])
{
outTexture.write( float4( 1.0, 0, 0, 1.0 ), gid );
}
Could other types of texture2d, such texture2d of int, texture2d of short and so on, be used as shader function parameters, and how to use them? Thanks for reviewing my question.
The related host codes:
MTLTextureDescriptor *desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
desc.usage = MTLTextureUsageShaderWrite;
id<MTLTexture> texture = [device newTextureWithDescriptor:desc];
[commandEncoder setTexture:texture atIndex:0];
The code to show the output computed by GPU, w and h represents width and height of textrue, respectively.
uint8_t* imageBytes = malloc(w*h*4);
memset( imageBytes, 0, w*h*4 );
MTLRegion region = MTLRegionMake2D(0, 0, [texture width], [texture height]);
[texture getBytes:imageBytes bytesPerRow:[texture width]*4 fromRegion:region mipmapLevel:0];
for( int j = 0; j < h; j++ )
{
printf("%3d: ", j);
for( int i = 0; i < w*pixel_size; i++ )
{
printf(" %3d",imageBytes[j*w*pixel_size+i] );
}
printf("\n")
}
The problem is that the pixel format you used to create this texture (MTLPixelFormatRGBA8Unorm) is normalized, meaning that the expected pixel value range is 0.0-1.0. For normalized pixel types, the required data type for reading or writing to this texture within a Metal kernel is float or half-float.
In order to write to a texture with integers, you must select an integer pixel format. Here are all of the available formats:
https://developer.apple.com/documentation/metal/mtlpixelformat
The Metal Shading Language Guide states that:
Note: If T is int or short, the data associated with the texture must use a signed integer format. If T is uint or ushort, the data associated with the texture must use an unsigned integer format.
All you have to do is make sure the texture you write to in the API (host code) matches what you have in the kernel function. Alternatively, you can also cast the int values into float before writing to the outTexture.
Been banging my head up against the wall with this for a while. Despite the fact that I THINK I have a proper Vertex Format defined with D3D11_INPUT_ELEMENT_DESC, no matter what I do, I can't see to read my TEXCOORD1 values from this shader. To test this shader, I put random values into my second set of UV coordinates just to see if they were reaching the shader, but to my dismay, I haven't been able to find these random values anywhere. I have also watched the data go into the mapped memory directly, and I am pretty sure the random values were there when they were mapped.
Here is the Shader code:
sampler ImageSampler: register(s0);
Texture2D <float4> ImageTexture: register(t0);
Texture2D <float4> ReflectionTexture: register(t1);
//Texture2D <float4> ReflectionMap: register(t0);
struct PS_IN
{
float4 InPos: SV_POSITION;
float2 InTex: TEXCOORD;
float2 InRef: TEXCOORD1;
float4 InCol: COLOR0;
};
float4 main(PS_IN input): SV_TARGET
{
float4 res;
float4 mul;
float2 tcRef;
float4 res1 = ImageTexture.Sample(ImageSampler, input.InTex) * input.InCol;
float4 res2 = ReflectionTexture.Sample(ImageSampler, input.InRef+input.InTex);
mul.r = 0.5;
mul.g = 0.5;
mul.b = 0.5;
mul.a = 0.5;
res = res1 + res2;
res = res * mul;
res.a = res1.a;
res.r = input.InRef.x;//<-----should be filled with random stuff... not working
res.b = input.InRef.y;//<-----should be filled with random stuff... not working
return res;
}
Here is my D3D11_ELEMENT_DESC... (sorry it is in pascal, but I like pascal)
const
CanvasVertexLayout: array[0..3] of D3D11_INPUT_ELEMENT_DESC =
((SemanticName: 'POSITION';
SemanticIndex: 0;
Format: DXGI_FORMAT_R32G32_FLOAT;
InputSlot: 0;
AlignedByteOffset: 0;
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA;
InstanceDataStepRate: 0),
(SemanticName: 'TEXCOORD';
SemanticIndex: 0;
Format: DXGI_FORMAT_R32G32_FLOAT;
InputSlot: 0;
AlignedByteOffset: 8;
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA;
InstanceDataStepRate: 0),
(SemanticName: 'TEXCOORD';
SemanticIndex: 1;
Format: DXGI_FORMAT_R32G32_FLOAT;
InputSlot: 0;
AlignedByteOffset: 16;
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA;
InstanceDataStepRate: 0),
(SemanticName: 'COLOR';
SemanticIndex: 0;
Format: DXGI_FORMAT_R8G8B8A8_UNORM;
InputSlot: 0;
AlignedByteOffset: 24;
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA;
InstanceDataStepRate: 0)
);
And here's the Vertext Struct
TVertexEntry = packed record
X, Y: Single;
U, V: Single;
u2,v2:single;
Color: LongWord;
end;
Since the COLOR semantic follows the TEXTURE semantics, my best guess is that the problem is with the SHADER and not the pascal code... but since I'm new to this kind of stuff, I'm obviously lost
Any insight is appreciated.
Answering my own question. Since I'm new to Shaders in general, maybe this will help some other newbs.
I was assuming that all I needed to do was add a second set of UV coordinates to the Vertex Format and add a D3D11_INPUT_ELEMENT_DESC for it. However, there is also a vertex shader involved, more-or-less a passthrough and that vertex shader needs to be aware of the new UV coordinates and let them pass through. I was just making a 2D engine so I didn't think that I'd even have to mess with VertexShaders... go figure. So I modified the vertex shader, and this was the result:
void main(
float2 InPos: POSITION0,
float2 InTex: TEXCOORD0,
float2 InTex2: TEXCOORD1,//<--added
float4 InCol: COLOR0,
out float4 OutPos: SV_POSITION,
out float2 OutTex: TEXCOORD2,
out float2 OutTex2: TEXCOORD3,//<--added
out float4 OutCol: COLOR0)
{
OutPos = float4(InPos, 0.0, 1.0);
OutTex = InTex;
OutCol = InCol;
OutTex2 = InTex2;//<--added
}
I need to pass an array to my vertex shader as an argument in Direct3D. The signature of the shader function looks like the following:
ReturnDataType main(float3 QuadPos : POSITION0, float4 OffsetArray[4] : COLOR0)
Is the signature OK? How do I define input layout description?
Thanks in Advance.
From HLSL references page, The function arguments only support intrinsic type, and user-defined type, the user-defined type also rely on intrinsic type, which does not support native array type, vector type or struct type might be your choice.
here is an example to use struct, you can simply build up a struct and pass in it as below like VS_INPUT.
//--------------------------------------------------------------------------------------
// Input / Output structures
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 vPosition : POSITION;
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
};
struct VS_OUTPUT
{
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
float4 vPosition : SV_POSITION;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VSMain( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.vPosition = mul( Input.vPosition, g_mWorldViewProjection );
Output.vNormal = mul( Input.vNormal, (float3x3)g_mWorld );
Output.vTexcoord = Input.vTexcoord;
return Output;
}