Hi all,
apologies in advance if this is completely obvious to people, but I've spent two days trying to understand what's going on with no luck. If anyone has any weblinks which'd help explain this, then that'd be very much appreciated.
I'm modelling a 2D grid (of water) as an array of 4-bit numbers (bits 0..3 for 1st droplet, bits 4..7 for the 2nd droplet) within an array of 8 bit numbers where the location in the array is defined as '(y * totalWidth) + x'. I was hoping to be able to:
1. pass this memory chunk across to the graphics card for rendering directly
2. use a pointlist and the SV_VERTEXID functionality inside an expanding vertex shader to take in 32bits and output 8 vertices each
3. Expand these by the geometry shader into 2 triangles per original point (if the appropriate bits are set in the input data, otherwise output nothing for that input part of the point)
The HLSL code that I've written is:
// The data structure used to define the water points
struct WaterDropletsInputType
{
uint bitMask : BITMASK;
uint vertexID : SV_VertexID;
};
// Data structure used to communicate between the vertex shader and the geometry shader
struct WaterDropletExpandedType
{
int2 position : SV_POSITION;
// The following 2 values are expanded out of the bit mask
// arguably this bit is unnecessary as we could simply pass
// it through in a single value, but it's not costing us much
// to split here
uint movement;
uint colour;
};
// Data structure which is passed out from the vertex shader and into the geometry shader
// prior to expansion into the various billboards
struct WaterDropletsVertexOutputType
{
float2 offset : OFFSET;
WaterDropletExpandedType droplets[8] : DROPLETS;
};
// This is what will get passed into the pixel shader from the geometry shader
struct WaterDropletPixelInputType
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
WaterDropletExpandedType PixelDetailsFromMask(int vertexNumber, uint mask)
{
WaterDropletExpandedType output;
uint uVertexNumber = vertexNumber;
output.position = int2(uVertexNumber % (30 * 8), uVertexNumber / (30 * 8));
output.colour = mask & 3;
output.movement = (mask >> 2) & 3;
return output;
}
WaterDropletsVertexOutputType WaterVertexShader(WaterDropletsInputType input)
{
uint scaledVertexCount = 8 * input.vertexID;
WaterDropletsVertexOutputType output;
output.offset.xy = 0;
output.droplets[0] = PixelDetailsFromMask(scaledVertexCount + 0, input.bitMask % 16);
output.droplets[1] = PixelDetailsFromMask(scaledVertexCount + 1, (input.bitMask >> 4) & 15);
output.droplets[2] = PixelDetailsFromMask(scaledVertexCount + 2, (input.bitMask >> 8) & 15);
output.droplets[3] = PixelDetailsFromMask(scaledVertexCount + 3, (input.bitMask >> 12) & 15);
output.droplets[4] = PixelDetailsFromMask(scaledVertexCount + 4, (input.bitMask >> 16) & 15);
output.droplets[5] = PixelDetailsFromMask(scaledVertexCount + 5, (input.bitMask >> 20) & 15);
output.droplets[6] = PixelDetailsFromMask(scaledVertexCount + 6, (input.bitMask >> 24) & 15);
output.droplets[7] = PixelDetailsFromMask(scaledVertexCount + 7, (input.bitMask >> 28) & 15);
return output;
}
[maxvertexcount(8*2*3*2)]
void WaterGeometryShader(point WaterDropletsVertexOutputType inputArray [1], inout TriangleStream<WaterDropletPixelInputType> outputStream)
{
WaterDropletsVertexOutputType input = inputArray[0];
for (int i = 0; i < 8;i++)
{
// Do nothing here
if (input.droplets[i].colour == 0)
continue;
// These values are in pixels
float pixelsX = input.offset.x + (input.droplets[i].position.x * 8);
float pixelsY = input.offset.y + (input.droplets[i].position.y * 4);
float4 color;
color.rgb = 0;
color.a = 1;
switch (input.droplets[i].colour)
{
case 1:
color.r = 1;
break;
case 2:
color.b = 1;
break;
case 3:
color.rb = 1;
break;
}
WaterDropletPixelInputType pTL, pTR, pBL, pBR;
pTL.position.wz = 0;
pTR.position.wz = 0;
pBL.position.wz = 0;
pBR.position.wz = 0;
pTL.color = color;
pTR.color = color;
pBL.color = color;
pBR.color = color;
pTL.position.x = pixelsX;
pTL.position.y = pixelsY;
pTR.position.x = pixelsX + 8;
pTR.position.y = pixelsY;
pBL.position.x = pixelsX;
pBL.position.y = pixelsY + 4;
pBR.position.x = pixelsX + 8;
pBR.position.y = pixelsY + 4;
// this must be configurable (scale to -1,1)
pTL.position.x /= 640.0;
pTR.position.x /= 640.0;
pBL.position.x /= 640.0;
pBR.position.x /= 640.0;
pTL.position.x -= 1.0;
pTR.position.x -= 1.0;
pBL.position.x -= 1.0;
pBR.position.x -= 1.0;
// e.g. pixelsY = 64 (on screen of 1024 high) should go to 0.875
pTL.position.y /= 512.0;
pTR.position.y /= 512.0;
pBL.position.y /= 512.0;
pBR.position.y /= 512.0;
pTL.position.y = 1.0 - pTL.position.y;
pTR.position.y = 1.0 - pTR.position.y;
pBL.position.y = 1.0 - pBL.position.y;
pBR.position.y = 1.0 - pBR.position.y;
// output the triangles (both ways round to avoid any culling issues)
outputStream.Append(pTL);
outputStream.Append(pBR);
outputStream.Append(pTR);
outputStream.RestartStrip();
outputStream.Append(pTL);
outputStream.Append(pBL);
outputStream.Append(pBR);
outputStream.RestartStrip();
outputStream.Append(pTL);
outputStream.Append(pTR);
outputStream.Append(pBR);
outputStream.RestartStrip();
outputStream.Append(pTL);
outputStream.Append(pBR);
outputStream.Append(pBL);
outputStream.RestartStrip();
}
}
float4 WaterPixelShader(WaterDropletPixelInputType input) : SV_TARGET
{
return input.color;
}
When I look in VS2015's graphics analyzer, it shows me the geometry shader's output, but no reference to the pixel shader (see attached screen shot):
[attachment=30124:screenShot1.png]
Looking at the rasterizer state, I have:
So I'm utterly perplexed as to what's going on here.
I'm using the DX toolkit in the project as well, and that's working. I've removed the spritebatch calls as well to see if that is the issue, but no combination seems to work.
Can anyone give me some hints / tips on what might be the issue here? Or alternatively, suggest places to learn about the debugging tools available to solve this sort of problem.
Thanks in advance
Steve
PS Yes, the constants of 30, 50 and the screen size etc. will be moved to a constant buffer, but one step at a time :-)