Skip to content

Commit

Permalink
add barycentrics ordering check onto existing barycentrics test (micr…
Browse files Browse the repository at this point in the history
…osoft#4635)

* add barycentrics ordering check onto existing barycentrics test, with extra shader
  • Loading branch information
bob80905 authored Dec 15, 2022
1 parent ffe931e commit ee0994e
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 35 deletions.
44 changes: 37 additions & 7 deletions tools/clang/test/HLSL/ShaderOpArith.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1493,12 +1493,9 @@

<ShaderOp Name="Barycentrics" PS="PS" VS="VS">
<RootSignature>RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)</RootSignature>
<Resource Name="VBuffer" Dimension="BUFFER" Width="1024" Flags="ALLOW_UNORDERED_ACCESS" InitialResourceState="COPY_DEST" Init="FromBytes" ReadBack="true">
{ { 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 } }
</Resource>
<Resource Name="RTarget" Dimension="TEXTURE2D" Width="1280" Height="2400" Format="R32G32B32A32_FLOAT" Flags="ALLOW_RENDER_TARGET" InitialResourceState="COPY_DEST" ReadBack="true" />
<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" />
<DescriptorHeap Name="RtvHeap" NumDescriptors="1" Type="RTV">
<Descriptor Name="RTarget" Kind="RTV"/>
</DescriptorHeap>
Expand Down Expand Up @@ -1535,7 +1532,40 @@
float4 vColor2 = GetAttributeAtVertex(input.color, 2);
return bary.x * vColor0 + bary.y * vColor1 + bary.z * vColor2;
}
]]>
]]>
</Shader>

<!-- This part of the shader op is for the barycentrics ordering part of the barycentrics test -->
<Shader Name="PSordering" Target="ps_6_1" EntryPoint="PSMain" Text="@MAIN"/>
<Shader Name="VSordering" 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
return float4(float3(GetAttributeAtVertex(input.svid, 0),
GetAttributeAtVertex(input.svid, 1),
GetAttributeAtVertex(input.svid, 2)) * 0.5, 1.0);
}
]]>
</Shader>
</ShaderOp>

Expand Down
119 changes: 91 additions & 28 deletions tools/clang/unittests/HLSL/ExecutionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8988,30 +8988,15 @@ TEST_F(ExecutionTest, CBufferTestHalf) {
}
}

TEST_F(ExecutionTest, BarycentricsTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
CComPtr<IStream> pStream;
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice, D3D_SHADER_MODEL_6_1))
return;

if (!DoesDeviceSupportBarycentrics(pDevice)) {
WEX::Logging::Log::Comment(L"Device does not support barycentrics.");
WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped);
return;
}

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "Barycentrics", nullptr);
void TestBarycentricVariant(bool checkOrdering, std::shared_ptr<ShaderOpTestResult> test){
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();

const float *pPixels = (float *)data.data();
// Get the vertex of barycentric coordinate using VBuffer
MappedData triangleData;
Expand All @@ -9025,33 +9010,106 @@ TEST_F(ExecutionTest, BarycentricsTest) {
XMFLOAT2 p0(pTriangleData[0], pTriangleData[1]);
XMFLOAT2 p1(pTriangleData[triangleVertexSizeInFloat], pTriangleData[triangleVertexSizeInFloat + 1]);
XMFLOAT2 p2(pTriangleData[triangleVertexSizeInFloat * 2], pTriangleData[triangleVertexSizeInFloat * 2 + 1]);


// Seems like the 3 floats must add up to 1 to get accurate results.
XMFLOAT3 barycentricWeights[4] = {
XMFLOAT3(0.3333f, 0.3333f, 0.3333f),
XMFLOAT3(0.4f, 0.2f, 0.4f),
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;
float tolerance = 0.02f;
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 pixelX = (int)round((x1 + 1) * (width - 1) / 2.0);
int pixelY = (int)round((1 - y1) * (height - 1) / 2.0);
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));
if (!checkOrdering){
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));
}
else{
// 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] , 0.0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 1], 0.5, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 2], 1.0, tolerance));
VERIFY_IS_TRUE(CompareFloatEpsilon(pPixels[offset + 3], 1.0, tolerance));
}
}
}

st::ShaderOpTest::TInitCallbackFn MakeBarycentricsResourceInitCallbackFn(int &vertexShift){
return [&](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 * vertexShift)) % barysize];
*p = tempfloat;
}
};

}

TEST_F(ExecutionTest, BarycentricsTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
CComPtr<IStream> pStream;
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice, D3D_SHADER_MODEL_6_1))
return;

if (!DoesDeviceSupportBarycentrics(pDevice)) {
WEX::Logging::Log::Comment(L"Device does not support barycentrics.");
WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped);
return;
}
//SavePixelsToFile(pPixels, DXGI_FORMAT_R32G32B32A32_FLOAT, width, height, L"barycentric.bmp");

DXASSERT_NOMSG(pStream != nullptr);
std::shared_ptr<st::ShaderOpSet> ShaderOpSet =
std::make_shared<st::ShaderOpSet>();
st::ParseShaderOpSetFromStream(pStream, ShaderOpSet.get());
st::ShaderOp* pShaderOp =
ShaderOpSet->GetShaderOp("Barycentrics");

int test_iteration = 0;
auto ResourceCallbackFnNoShift = MakeBarycentricsResourceInitCallbackFn(test_iteration);

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTestAfterParse(pDevice, m_support, "Barycentrics", ResourceCallbackFnNoShift, ShaderOpSet);
TestBarycentricVariant(false, test);

// Now test that barycentric ordering is consistent
LogCommentFmt(L"Now testing that the barycentric ordering constraint is upheld for each pixel...");
pShaderOp->VS = pShaderOp->GetString("VSordering");
pShaderOp->PS = pShaderOp->GetString("PSordering");
for(; test_iteration < 3; test_iteration++)
{
auto ResourceCallbackFn = MakeBarycentricsResourceInitCallbackFn(test_iteration);

std::shared_ptr<ShaderOpTestResult> test2 = RunShaderOpTestAfterParse(pDevice, m_support, "Barycentrics", ResourceCallbackFn, ShaderOpSet);
TestBarycentricVariant(true, test2);
}
}


static const char RawBufferTestShaderDeclarations[] =
"// Note: COMPONENT_TYPE and COMPONENT_SIZE will be defined via compiler option -D\r\n"
"typedef COMPONENT_TYPE scalar; \r\n"
Expand Down Expand Up @@ -11488,7 +11546,12 @@ TEST_F(ExecutionTest, IsNormalTest) {
st::ParseShaderOpSetFromStream(pStream, ShaderOpSet.get());
st::ShaderOp *pShaderOp = ShaderOpSet->GetShaderOp("IsNormal");
vector<st::ShaderOpRootValue> fallbackRootValues = pShaderOp->RootValues;


D3D_SHADER_MODEL sm = D3D_SHADER_MODEL_6_0;
LogCommentFmt(L"\r\nVerifying isNormal in shader "
L"model 6.%1u",
((UINT)sm & 0x0f));

size_t count = Validation_Input->size();

auto ShaderInitFn = MakeShaderReplacementCallback(
Expand Down

0 comments on commit ee0994e

Please sign in to comment.