Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add barycentrics ordering check onto existing barycentrics test #4635

Merged
merged 8 commits into from
Dec 15, 2022
62 changes: 61 additions & 1 deletion tools/clang/test/HLSL/ShaderOpArith.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1498,7 +1498,7 @@
{ { 1.0f, -1.0f , 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -1.0f, -1.0f , 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
</Resource>
<Resource Name="RTarget" Dimension="TEXTURE2D" Width="1280" Height="2400" Format="R32G32B32A32_FLOAT" Flags="ALLOW_RENDER_TARGET" InitialResourceState="COPY_DEST" ReadBack="true" />
<Resource Name="RTarget" Dimension="TEXTURE2D" Width="1280" Height="1280" Format="R32G32B32A32_FLOAT" Flags="ALLOW_RENDER_TARGET" InitialResourceState="COPY_DEST" ReadBack="true" />
<DescriptorHeap Name="RtvHeap" NumDescriptors="1" Type="RTV">
<Descriptor Name="RTarget" Kind="RTV"/>
</DescriptorHeap>
Expand Down Expand Up @@ -1539,6 +1539,66 @@
</Shader>
</ShaderOp>


<ShaderOp Name="BarycentricsCheckOrder" PS="PS61" VS="VS61">
<RootSignature>RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)</RootSignature>
<Resource Name="VBuffer" Dimension="BUFFER" Width="1024" Flags="ALLOW_UNORDERED_ACCESS" InitialResourceState="COPY_DEST" Init="ByName" ReadBack="true">
</Resource>
<Resource Name="RTarget" Dimension="TEXTURE2D" Width="64" Height="64" Format="R32G32B32A32_FLOAT" Flags="ALLOW_RENDER_TARGET" InitialResourceState="COPY_DEST" ReadBack="true" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can make the render targets the same size, you can use different entry point targets to make one ubershader and prevent duplicating the shader code above with slight modifications.


<DescriptorHeap Name="RtvHeap" NumDescriptors="1" Type="RTV">
<Descriptor Name="RTarget" Kind="RTV"/>
</DescriptorHeap>

<InputElements>
<InputElement SemanticName="POSITION" Format="R32G32B32_FLOAT" AlignedByteOffset="0" />
<InputElement SemanticName="COLOR" Format="R32G32B32A32_FLOAT" AlignedByteOffset="12" />
</InputElements>
<RenderTargets>
<RenderTarget Name="RTarget" />
</RenderTargets>

<Shader Name="PS61" Target="ps_6_1" EntryPoint="PSMain" Text="@MAIN"/>
<Shader Name="VS61" Target="vs_6_1" EntryPoint="VSMain" Text="@MAIN"/>


<Shader Name="MAIN" Target="vs_6_1" EntryPoint="VSMain">
<![CDATA[
struct PSInput {
float4 position : SV_POSITION;
nointerpolation float4 color : COLOR;
uint svid : SVertexID;
};

PSInput VSMain(float4 position : POSITION, float4 color : COLOR, uint svid : SV_VertexID) {
PSInput result;
result.position = position;
result.color = color;
result.svid = svid;

return result;
}

float4 PSMain(PSInput input) : SV_Target {
// check to make sure that the pixel shader will see the barycentric weight associated
// with the first vertex in the x component of the SV_Barycentric vector, and likewise for subsequent vertices

if (0 == GetAttributeAtVertex(input.svid, 0) &&
1 == GetAttributeAtVertex(input.svid, 1) &&
2 == GetAttributeAtVertex(input.svid, 2))
{
// special value of all 1's in a float4 will be returned if the ordering is maintained for this pixel.
return float4(1.0, 1.0, 1.0, 1.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative way to do this (without the if) is:

return float4(float3(GetAttributeAtVertex(input.svid, 0),
                     GetAttributeAtVertex(input.svid, 1),
                     GetAttributeAtVertex(input.svid, 2)) * 0.5, 1.0)
```
then change the expected result to (0, 0.5, 1.0, 1.0) which makes out-of-order failure case reveal the ordering used in the result.

}

// special value of all 0's in a float 4 will be returned otherwise
return float4(0.0, 0.0, 0.0, 0.0);
}

]]>
</Shader>
</ShaderOp>

<ShaderOp Name="ComputeRawBufferLdSt32Bit" CS="CS">
<RootSignature>RootFlags(0), SRV(t0), SRV(t1), UAV(u0), UAV(u1), DescriptorTable(SRV(t2,numDescriptors=2), UAV(u2,numDescriptors=2))</RootSignature>
<Resource Name="SRVBuffer0" Dimension="BUFFER" Width="40" InitialResourceState="COPY_DEST" Init="ByName" Format="R32_TYPELESS"/>
Expand Down
168 changes: 125 additions & 43 deletions tools/clang/unittests/HLSL/ExecutionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9001,53 +9001,135 @@ TEST_F(ExecutionTest, BarycentricsTest) {
return;
}

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "Barycentrics", nullptr);
MappedData data;
D3D12_RESOURCE_DESC &D = test->ShaderOp->GetResourceByName("RTarget")->Desc;
UINT width = (UINT)D.Width;
UINT height = D.Height;
UINT pixelSize = GetByteSizeForFormat(D.Format);
DXASSERT_NOMSG(pStream != nullptr);
std::shared_ptr<st::ShaderOpSet> ShaderOpSet =
std::make_shared<st::ShaderOpSet>();
st::ParseShaderOpSetFromStream(pStream, ShaderOpSet.get());

test->Test->GetReadBackData("RTarget", &data);
//const uint8_t *pPixels = (uint8_t *)data.data();
const float *pPixels = (float *)data.data();
// Get the vertex of barycentric coordinate using VBuffer
MappedData triangleData;
test->Test->GetReadBackData("VBuffer", &triangleData);
const float *pTriangleData = (float*)triangleData.data();
// get the size of the input data
unsigned triangleVertexSizeInFloat = 0;
for (auto element : test->ShaderOp->InputElements)
triangleVertexSizeInFloat += GetByteSizeForFormat(element.Format) / 4;

XMFLOAT2 p0(pTriangleData[0], pTriangleData[1]);
XMFLOAT2 p1(pTriangleData[triangleVertexSizeInFloat], pTriangleData[triangleVertexSizeInFloat + 1]);
XMFLOAT2 p2(pTriangleData[triangleVertexSizeInFloat * 2], pTriangleData[triangleVertexSizeInFloat * 2 + 1]);

XMFLOAT3 barycentricWeights[4] = {
XMFLOAT3(0.3333f, 0.3333f, 0.3333f),
XMFLOAT3(0.5f, 0.25f, 0.25f),
XMFLOAT3(0.25f, 0.5f, 0.25f),
XMFLOAT3(0.25f, 0.25f, 0.50f)
};
{
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTestAfterParse(pDevice, m_support, "Barycentrics", nullptr, ShaderOpSet);
MappedData data;
D3D12_RESOURCE_DESC &D = test->ShaderOp->GetResourceByName("RTarget")->Desc;
UINT width = (UINT)D.Width;
UINT height = D.Height;
UINT pixelSize = GetByteSizeForFormat(D.Format);

test->Test->GetReadBackData("RTarget", &data);
//const uint8_t *pPixels = (uint8_t *)data.data();
bob80905 marked this conversation as resolved.
Show resolved Hide resolved
const float *pPixels = (float *)data.data();
// Get the vertex of barycentric coordinate using VBuffer
MappedData triangleData;
test->Test->GetReadBackData("VBuffer", &triangleData);
const float *pTriangleData = (float*)triangleData.data();
// get the size of the input data
unsigned triangleVertexSizeInFloat = 0;
for (auto element : test->ShaderOp->InputElements)
triangleVertexSizeInFloat += GetByteSizeForFormat(element.Format) / 4;

XMFLOAT2 p0(pTriangleData[0], pTriangleData[1]);
XMFLOAT2 p1(pTriangleData[triangleVertexSizeInFloat], pTriangleData[triangleVertexSizeInFloat + 1]);
XMFLOAT2 p2(pTriangleData[triangleVertexSizeInFloat * 2], pTriangleData[triangleVertexSizeInFloat * 2 + 1]);

XMFLOAT3 barycentricWeights[4] = {
XMFLOAT3(0.3333f, 0.3333f, 0.3333f),
XMFLOAT3(0.5f, 0.25f, 0.25f),
XMFLOAT3(0.25f, 0.5f, 0.25f),
XMFLOAT3(0.25f, 0.25f, 0.50f)
};

float tolerance = 0.001f;
for (unsigned i = 0; i < sizeof(barycentricWeights) / sizeof(XMFLOAT3); ++i) {
float w0 = barycentricWeights[i].x;
float w1 = barycentricWeights[i].y;
float w2 = barycentricWeights[i].z;
float x1 = w0 * p0.x + w1 * p1.x + w2 * p2.x;
float y1 = w0 * p0.y + w1 * p1.y + w2 * p2.y;
// map from x1 y1 to rtv pixels
int pixelX = (int)((x1 + 1) * (width - 1) / 2);
int pixelY = (int)((1 - y1) * (height - 1) / 2);
int offset = pixelSize * (pixelX + pixelY * width) / sizeof(pPixels[0]);
LogCommentFmt(L"location %u %u, value %f, %f, %f", pixelX, pixelY, pPixels[offset], pPixels[offset + 1], pPixels[offset + 2]);
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset], w0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 1], w1, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 2], w2, tolerance));
float tolerance = 0.001f;
for (unsigned i = 0; i < sizeof(barycentricWeights) / sizeof(XMFLOAT3); ++i) {
float w0 = barycentricWeights[i].x;
float w1 = barycentricWeights[i].y;
float w2 = barycentricWeights[i].z;
float x1 = w0 * p0.x + w1 * p1.x + w2 * p2.x;
float y1 = w0 * p0.y + w1 * p1.y + w2 * p2.y;
// map from x1 y1 to rtv pixels
int pixelX = (int)((x1 + 1) * (width - 1) / 2);
int pixelY = (int)((1 - y1) * (height - 1) / 2);
int offset = pixelSize * (pixelX + pixelY * width) / sizeof(pPixels[0]);
LogCommentFmt(L"location %u %u, value %f, %f, %f", pixelX, pixelY, pPixels[offset], pPixels[offset + 1], pPixels[offset + 2]);
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset], w0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 1], w1, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 2], w2, tolerance));
}
}

// Now test that barycentric ordering is consistent
LogCommentFmt(L"Now testing that the barycentric ordering constraint is upheld for each pixel...");
for(int test_iteration = 0; test_iteration < 3; test_iteration++)
{
auto ResourceCallbackFn =
[&](LPCSTR Name, std::vector<BYTE> &Data, st::ShaderOp *pShaderOp) {
std::vector<float> bary = {0.0f, 1.0f , 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f , 0.0f, 0.0f, 0.0f, 1.0f, 1.0f};
const int barysize = 21;

UNREFERENCED_PARAMETER(pShaderOp);
VERIFY_IS_TRUE(0 == _stricmp(Name, "VBuffer"));
size_t size = sizeof(float)*barysize;
Data.resize(size);
float *vb = (float *)Data.data();
for (size_t i = 0; i < barysize; ++i) {
float *p = &vb[i];
float tempfloat = bary[(i+(7*test_iteration))%barysize];
*p = tempfloat;
}
};

std::shared_ptr<ShaderOpTestResult> test2 = RunShaderOpTestAfterParse(pDevice, m_support, "BarycentricsCheckOrder", ResourceCallbackFn, ShaderOpSet);
MappedData data;
D3D12_RESOURCE_DESC &D = test2->ShaderOp->GetResourceByName("RTarget")->Desc;
UINT width = (UINT)D.Width;
UINT height = D.Height;
UINT pixelSize = GetByteSizeForFormat(D.Format);
test2->Test->GetReadBackData("RTarget", &data);
//const uint8_t *pPixels = (uint8_t *)data.data();
bob80905 marked this conversation as resolved.
Show resolved Hide resolved
const float *pPixels = (float *)data.data();
// Get the vertex of barycentric coordinate using VBuffer
MappedData triangleData;
test2->Test->GetReadBackData("VBuffer", &triangleData);
const float *pTriangleData = (float*)triangleData.data();

// get the size of the input data
unsigned triangleVertexSizeInFloat = 0;
for (auto element : test2->ShaderOp->InputElements)
triangleVertexSizeInFloat += GetByteSizeForFormat(element.Format) / 4;
XMFLOAT2 p0(pTriangleData[0], pTriangleData[1]);
XMFLOAT2 p1(pTriangleData[triangleVertexSizeInFloat], pTriangleData[triangleVertexSizeInFloat + 1]);
XMFLOAT2 p2(pTriangleData[triangleVertexSizeInFloat * 2], pTriangleData[triangleVertexSizeInFloat * 2 + 1]);
XMFLOAT3 barycentricWeights[4] = {
XMFLOAT3(0.3333f, 0.3333f, 0.3333f),
XMFLOAT3(0.5f, 0.25f, 0.25f),
XMFLOAT3(0.25f, 0.5f, 0.25f),
XMFLOAT3(0.25f, 0.25f, 0.50f)
};
float tolerance = 0.001f;
for (unsigned i = 0; i < sizeof(barycentricWeights) / sizeof(XMFLOAT3); ++i) {
float w0 = barycentricWeights[i].x;
float w1 = barycentricWeights[i].y;
float w2 = barycentricWeights[i].z;
float x1 = w0 * p0.x + w1 * p1.x + w2 * p2.x;
float y1 = w0 * p0.y + w1 * p1.y + w2 * p2.y;
// map from x1 y1 to rtv pixels
int pixelX = (int)((x1 + 1) * (width - 1) / 2);
int pixelY = (int)((1 - y1) * (height - 1) / 2);
int offset = pixelSize * (pixelX + pixelY * width) / sizeof(pPixels[0]);
LogCommentFmt(L"location %u %u, value %f, %f, %f, %f", pixelX, pixelY, pPixels[offset], pPixels[offset + 1], pPixels[offset + 2], pPixels[offset + 3]);

// If the ordering constraint is met, then this pixel's RGBA should be all 1.0's
// since the shader only returns float4<1.0,1.0,1.0,1.0> when this condition is met.
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset] , 1.0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 1], 1.0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 2], 1.0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 3], 1.0, tolerance));
}

bob80905 marked this conversation as resolved.
Show resolved Hide resolved
}

//SavePixelsToFile(pPixels, DXGI_FORMAT_R32G32B32A32_FLOAT, width, height, L"barycentric.bmp");

}

static const char RawBufferTestShaderDeclarations[] =
Expand Down