Skip to content

Commit

Permalink
Adds documentation and tests for min/max intrinsics. (microsoft#81)
Browse files Browse the repository at this point in the history
Improves xml-based tests to allow existing devices to be used.
Improves dxexp to print out SM6/wave/i64 support under experimental mode.
Bumps the hctversion to 0.7
  • Loading branch information
tex3d authored Feb 17, 2017
1 parent 98f201b commit 7bdef32
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 43 deletions.
25 changes: 25 additions & 0 deletions tools/clang/test/HLSL/ShaderOpArith.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,31 @@
]]>
</Shader>
</ShaderOp>
<ShaderOp Name="MinMax" CS="CS" DispatchX="10" DispatchY="10">
<RootSignature>RootFlags(0), UAV(u0)</RootSignature>
<Resource Name="SPrimitives" Dimension="BUFFER" Width="1024" Flags="ALLOW_UNORDERED_ACCESS" InitialResourceState="COPY_DEST" TransitionTo="UNORDERED_ACCESS" Init="ByName" ReadBack="true" />
<RootValues>
<RootValue Index="0" ResName="SPrimitives" />
</RootValues>
<Shader Name="CS" Target="cs_6_0">
<![CDATA[
struct SMinMaxElem {
float f_fa;
float f_fb;
float f_fmin_o;
float f_fmax_o;
};
RWStructuredBuffer<SMinMaxElem> g_buf : register(u0);
[numthreads(10,10,1)]
void main(uint GI : SV_GroupIndex) {
SMinMaxElem l = g_buf[GI];
l.f_fmin_o = min(l.f_fa, l.f_fb);
l.f_fmax_o = max(l.f_fa, l.f_fb);
g_buf[GI] = l;
};
]]>
</Shader>
</ShaderOp>
<ShaderOp Name="OOB" PS="PS" VS="VS">
<RootSignature>RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT), CBV(b0), DescriptorTable(SRV(t0,numDescriptors=2))</RootSignature>
<Resource Name="CB0" Dimension="BUFFER" InitialResourceState="COPY_DEST" Init="FromBytes" TransitionTo="VERTEX_AND_CONSTANT_BUFFER">
Expand Down
178 changes: 137 additions & 41 deletions tools/clang/unittests/HLSL/ExecutionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class ExecutionTest {
TEST_METHOD(BasicComputeTest);
TEST_METHOD(BasicTriangleTest);
TEST_METHOD(BasicTriangleOpTest);
TEST_METHOD(MinMaxTest);
TEST_METHOD(OutOfBoundsTest);
TEST_METHOD(SaturateTest);
TEST_METHOD(SignTest);
Expand Down Expand Up @@ -617,6 +618,18 @@ class ExecutionTest {
}
}

void ReadHlslDataIntoNewStream(LPCWSTR relativePath, IStream **ppStream) {
VERIFY_SUCCEEDED(m_support.Initialize());
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcBlobEncoding> pBlob;
CComPtr<IStream> pStream;
std::wstring path = GetPathToHlslDataFile(relativePath);
VERIFY_SUCCEEDED(m_support.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(pLibrary->CreateBlobFromFile(path.c_str(), nullptr, &pBlob));
VERIFY_SUCCEEDED(pLibrary->CreateStreamFromBlobReadOnly(pBlob, &pStream));
*ppStream = pStream.Detach();
}

void RecordRenderAndReadback(ID3D12GraphicsCommandList *pList,
ID3D12DescriptorHeap *pRtvHeap,
UINT rtvDescriptorSize,
Expand Down Expand Up @@ -825,9 +838,9 @@ TEST_F(ExecutionTest, BasicComputeTest) {
static const int DispatchGroupCount = 1;

CComPtr<ID3D12Device> pDevice;

if (!CreateDevice(&pDevice))
return;

std::vector<uint32_t> values;
SetupComputeValuePattern(values, ThreadsPerGroup * DispatchGroupCount);
VERIFY_ARE_EQUAL(values[0], 0);
Expand Down Expand Up @@ -1016,9 +1029,9 @@ TEST_F(ExecutionTest, Int64Test) {
static const int DispatchGroupCount = 1;

CComPtr<ID3D12Device> pDevice;

if (!CreateDevice(&pDevice))
return;

if (!DoesDeviceSupportInt64(pDevice)) {
// Optional feature, so it's correct to not support it if declared as such.
WEX::Logging::Log::Comment(L"Device does not support int64 operations.");
Expand Down Expand Up @@ -1047,9 +1060,9 @@ TEST_F(ExecutionTest, SignTest) {
static const int DispatchGroupCount = 1;

CComPtr<ID3D12Device> pDevice;

if (!CreateDevice(&pDevice))
return;

std::vector<uint32_t> values = { (uint32_t)-3, (uint32_t)-2, (uint32_t)-1, 0, 1, 2, 3, 4};
RunRWByteBufferComputeTest(pDevice, pShader, values);
VERIFY_ARE_EQUAL(values[0], -1);
Expand Down Expand Up @@ -1159,9 +1172,9 @@ TEST_F(ExecutionTest, WaveIntrinsicsTest) {
static const int DispatchGroupCount = 1;

CComPtr<ID3D12Device> pDevice;

if (!CreateDevice(&pDevice))
return;

if (!DoesDeviceSupportWaveOps(pDevice)) {
// Optional feature, so it's correct to not support it if declared as such.
WEX::Logging::Log::Comment(L"Device does not support wave operations.");
Expand Down Expand Up @@ -1809,7 +1822,8 @@ static float g_SinCosFloats[] = {
};

std::shared_ptr<ShaderOpTestResult>
RunShaderOpTest(dxc::DxcDllSupport &support, IStream *pStream, LPCSTR pName,
RunShaderOpTest(ID3D12Device *pDevice, dxc::DxcDllSupport &support,
IStream *pStream, LPCSTR pName,
st::ShaderOpTest::TInitCallbackFn pInitCallback) {
DXASSERT_NOMSG(pStream != nullptr);
std::shared_ptr<st::ShaderOpSet> ShaderOpSet =
Expand Down Expand Up @@ -1860,18 +1874,15 @@ static bool isdenorm(double d) {

TEST_F(ExecutionTest, DoShaderOpArithTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
VERIFY_SUCCEEDED(m_support.Initialize());
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcBlobEncoding> pBlob;
CComPtr<IStream> pStream;
std::wstring path = GetPathToHlslDataFile(L"ShaderOpArith.xml");
VERIFY_SUCCEEDED(m_support.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(pLibrary->CreateBlobFromFile(path.c_str(), nullptr, &pBlob));
VERIFY_SUCCEEDED(pLibrary->CreateStreamFromBlobReadOnly(pBlob, &pStream));
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

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

// Single operation test at the moment.
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(m_support, pStream, "SinCos",
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "SinCos",
[](LPCSTR Name, std::vector<BYTE> &Data) {
// Initialize the SPrimitives buffer.
VERIFY_IS_TRUE(0 == _stricmp(Name, "SPrimitives"));
Expand Down Expand Up @@ -1933,20 +1944,111 @@ TEST_F(ExecutionTest, DoShaderOpArithTest) {
}
}

static float ifdenorm_flushf(float a) {
return isdenorm(a) ? copysign(0.0f, a) : a;
}

static bool ifdenorm_flushf_eq(float a, float b) {
return ifdenorm_flushf(a) == ifdenorm_flushf(b);
}

static bool ifdenorm_flushf_eq_or_nans(float a, float b) {
if (isnan(a) && isnan(b)) return true;
return ifdenorm_flushf(a) == ifdenorm_flushf(b);
}

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

struct SMinMaxElem {
float f_fa;
float f_fb;
float f_fmin_o;
float f_fmax_o;
};
float TestValues[] = {
-(INFINITY),
-1.0f,
-(FLT_MIN/2),
-0.0f,
0.0f,
FLT_MIN / 2,
1.0f,
INFINITY,
NAN
};

// Single operation test at the moment.
CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice))
return;

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "MinMax",
[&TestValues](LPCSTR Name, std::vector<BYTE> &Data) {
// Initialize the SPrimitives buffer.
VERIFY_IS_TRUE(0 == _stricmp(Name, "SPrimitives"));
size_t count = 10 * 10;
size_t size = sizeof(SMinMaxElem) * count;
Data.resize(size);
SMinMaxElem *pElems = (SMinMaxElem *)Data.data();
for (size_t a = 0; a < 10; ++a) {
float fa = TestValues[a % _countof(TestValues)];
for (size_t b = 0; b < 10; ++b) {
SMinMaxElem *p = &pElems[a * 10 + b];
ZeroMemory(p, sizeof(*p));
p->f_fa = fa;
p->f_fb = TestValues[b % _countof(TestValues)];
}
}
});
MappedData data;
test->Test->GetReadBackData("SPrimitives", &data);
// data.dump(); // Uncomment to dump raw bytes from buffer.

unsigned count = 10 * 10;
SMinMaxElem *pPrimitives = (SMinMaxElem *)data.data();
WEX::TestExecution::DisableVerifyExceptions dve;
static const float Error = 0.0008f;
for (unsigned i = 0; i < count; ++i) {
SMinMaxElem *p = &pPrimitives[i];
float fa = p->f_fa;
float fb = p->f_fb;
float fmin = p->f_fmin_o;
float fmax = p->f_fmax_o;
LogCommentFmt(L"Element #%u, a %f, b %f, min=%f, max=%f", i, fa, fb, fmin, fmax);
if (isnan(fa)) {
VERIFY_IS_TRUE(ifdenorm_flushf_eq_or_nans(fmin, fb));
VERIFY_IS_TRUE(ifdenorm_flushf_eq_or_nans(fmax, fb));
}
else if (isnan(fb)) {
VERIFY_IS_TRUE(ifdenorm_flushf_eq_or_nans(fmin, fa));
VERIFY_IS_TRUE(ifdenorm_flushf_eq_or_nans(fmax, fa));
}
else {
// Flushing is allowed - check both cases.
float fmax_0 = fa >= fb ? fa : fb;
float fmax_1 = ifdenorm_flushf(fmax_0);
VERIFY_IS_TRUE(fmax == fmax_0 || fmax == fmax_1);
float fmin_0 = fa < fb ? fa : fb;
float fmin_1 = ifdenorm_flushf(fmin_0);
VERIFY_IS_TRUE(fmin == fmin_0 || fmin == fmin_1);
}
}
}

TEST_F(ExecutionTest, OutOfBoundsTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
VERIFY_SUCCEEDED(m_support.Initialize());
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcBlobEncoding> pBlob;
CComPtr<IStream> pStream;
std::wstring path = GetPathToHlslDataFile(L"ShaderOpArith.xml");
VERIFY_SUCCEEDED(m_support.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(pLibrary->CreateBlobFromFile(path.c_str(), nullptr, &pBlob));
VERIFY_SUCCEEDED(pLibrary->CreateStreamFromBlobReadOnly(pBlob, &pStream));
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

// Single operation test at the moment.
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(m_support, pStream, "OOB", nullptr);
CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice))
return;

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "OOB", nullptr);
MappedData data;
// Read back to CPU and examine contents - should get pure red.
{
Expand All @@ -1960,18 +2062,15 @@ TEST_F(ExecutionTest, OutOfBoundsTest) {

TEST_F(ExecutionTest, SaturateTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
VERIFY_SUCCEEDED(m_support.Initialize());
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcBlobEncoding> pBlob;
CComPtr<IStream> pStream;
std::wstring path = GetPathToHlslDataFile(L"ShaderOpArith.xml");
VERIFY_SUCCEEDED(m_support.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(pLibrary->CreateBlobFromFile(path.c_str(), nullptr, &pBlob));
VERIFY_SUCCEEDED(pLibrary->CreateStreamFromBlobReadOnly(pBlob, &pStream));
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

// Single operation test at the moment.
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(m_support, pStream, "Saturate", nullptr);
CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice))
return;

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "Saturate", nullptr);
MappedData data;
test->Test->GetReadBackData("U0", &data);
const float *pValues = (float *)data.data();
Expand All @@ -1989,18 +2088,15 @@ TEST_F(ExecutionTest, SaturateTest) {

TEST_F(ExecutionTest, BasicTriangleOpTest) {
WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
VERIFY_SUCCEEDED(m_support.Initialize());
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcBlobEncoding> pBlob;
CComPtr<IStream> pStream;
std::wstring path = GetPathToHlslDataFile(L"ShaderOpArith.xml");
VERIFY_SUCCEEDED(m_support.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(pLibrary->CreateBlobFromFile(path.c_str(), nullptr, &pBlob));
VERIFY_SUCCEEDED(pLibrary->CreateStreamFromBlobReadOnly(pBlob, &pStream));
ReadHlslDataIntoNewStream(L"ShaderOpArith.xml", &pStream);

// Single operation test at the moment.
std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(m_support, pStream, "Triangle", nullptr);
CComPtr<ID3D12Device> pDevice;
if (!CreateDevice(&pDevice))
return;

std::shared_ptr<ShaderOpTestResult> test = RunShaderOpTest(pDevice, m_support, pStream, "Triangle", nullptr);
MappedData data;
D3D12_RESOURCE_DESC &D = test->ShaderOp->GetResourceByName("RTarget")->Desc;
UINT width = (UINT64)D.Width;
Expand Down
6 changes: 5 additions & 1 deletion tools/clang/unittests/HLSL/ShaderOpTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,10 @@ void ShaderOpTest::SetRootValues(ID3D12GraphicsCommandList *pList,
}
}

void ShaderOpTest::SetDevice(ID3D12Device *pDevice) {
m_pDevice = pDevice;
}

void ShaderOpTest::SetDxcSupport(dxc::DxcDllSupport *pDxcSupport) {
m_pDxcSupport = pDxcSupport;
}
Expand All @@ -942,7 +946,7 @@ void ShaderOpTest::SetInitCallback(TInitCallbackFn InitCallbackFn) {
void ShaderOpTest::SetupRenderTarget(ShaderOp *pShaderOp, ID3D12Device *pDevice,
ID3D12CommandQueue *pCommandQueue,
ID3D12Resource *pRenderTarget) {
m_pDevice = pDevice;
SetDevice(pDevice);
m_CommandList.Queue = pCommandQueue;
// Simplification - add the render target name if missing, set it up 'by hand' if not.
if (pShaderOp->RenderTargets.empty()) {
Expand Down
1 change: 1 addition & 0 deletions tools/clang/unittests/HLSL/ShaderOpTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ class ShaderOpTest {
void GetReadBackData(LPCSTR pResourceName, MappedData *pData);
void RunShaderOp(ShaderOp *pShaderOp);
void RunShaderOp(std::shared_ptr<ShaderOp> pShaderOp);
void SetDevice(ID3D12Device* pDevice);
void SetDxcSupport(dxc::DxcDllSupport *pDxcSupport);
void SetInitCallback(TInitCallbackFn InitCallbackFn);
void SetupRenderTarget(ShaderOp *pShaderOp, ID3D12Device *pDevice,
Expand Down
Loading

0 comments on commit 7bdef32

Please sign in to comment.