diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index 50402cbd1..1ece3ec12 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -182,3 +182,75 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) EXPECT_EQ(tensorA->vector(), resultAsync); EXPECT_EQ(tensorB->vector(), resultAsync); } + +TEST(TestAsyncOperations, TestManagerAsyncExecutionTimeout) +{ + uint32_t size = 10; + + std::string shader(R"( + #version 450 + + layout (local_size_x = 1) in; + + layout(set = 0, binding = 0) buffer b { float pb[]; }; + + shared uint sharedTotal[1]; + + void main() { + uint index = gl_GlobalInvocationID.x; + + sharedTotal[0] = 0; + + for (int i = 0; i < 100000000; i++) + { + atomicAdd(sharedTotal[0], 1); + } + + pb[index] = sharedTotal[0]; + } + )"); + + std::vector spirv = compileSource(shader); + + std::vector data(size, 0.0); + std::vector resultAsync(size, 100000000); + + kp::Manager mgr; + + std::shared_ptr> tensorA = mgr.tensor(data); + std::shared_ptr> tensorB = mgr.tensor(data); + + std::shared_ptr sq1 = mgr.sequence(); + std::shared_ptr sq2 = mgr.sequence(); + + sq1->eval({ tensorA, tensorB }); + + std::shared_ptr algo1 = mgr.algorithm({ tensorA }, spirv); + std::shared_ptr algo2 = mgr.algorithm({ tensorB }, spirv); + + auto startSync = std::chrono::high_resolution_clock::now(); + + // AMD Drivers in Windows may see an error in this line due to timeout. + // In order to fix this, it requires a change on Windows registries. + // More details on this can be found here: https://docs.substance3d.com/spdoc/gpu-drivers-crash-with-long-computations-128745489.html + // Context on solution discussed in github: https://github.com/KomputeProject/kompute/issues/196#issuecomment-808866505 + sq1->evalAsync(algo1); + sq2->evalAsync(algo2); + + sq1->evalAwait(1); + sq2->evalAwait(1); + + auto endSync = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(endSync - startSync) + .count(); + + // The time should several orders of magnitude smaller (in this 100k instead of 1m ns) + EXPECT_LT(duration, 100000); + + sq1->evalAsync({ tensorA, tensorB }); + sq1->evalAwait(); + + EXPECT_EQ(tensorA->vector(), resultAsync); + EXPECT_EQ(tensorB->vector(), resultAsync); +} diff --git a/test/TestManager.cpp b/test/TestManager.cpp index d7791c3f2..21edc72c7 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -78,3 +78,29 @@ TEST(TestManager, TestListDevices) EXPECT_GT(devices.size(), 0); EXPECT_GT(devices[0].getProperties().deviceName.size(), 0); } + +TEST(TestManager, TestClearDestroy) +{ + kp::Manager mgr; + + // Running within scope to run clear + { + std::shared_ptr> tensorLHS = mgr.tensor({ 0, 1, 2 }); + std::shared_ptr> tensorRHS = mgr.tensor({ 2, 4, 6 }); + std::shared_ptr> tensorOutput = mgr.tensor({ 0, 0, 0 }); + + std::vector> params = { tensorLHS, + tensorRHS, + tensorOutput }; + + mgr.sequence()->eval(params); + mgr.sequence()->eval(params, mgr.algorithm()); + mgr.sequence()->eval(params); + + EXPECT_EQ(tensorOutput->vector(), std::vector({ 0, 4, 12 })); + } + + mgr.clear(); + + mgr.destroy(); +} diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 45466d4a1..7f63c208f 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -219,3 +219,60 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } + +TEST(TestAlgoUtils, TestAlgorithmUtilFunctions) +{ + + kp::Manager mgr; + + // Default tensor constructor simplifies creation of float values + auto tensorInA = mgr.tensor({ 2., 2., 2. }); + auto tensorInB = mgr.tensor({ 1., 2., 3. }); + // Explicit type constructor supports int, in32, double, float and int + auto tensorOutA = mgr.tensorT({ 0, 0, 0 }); + auto tensorOutB = mgr.tensorT({ 0, 0, 0 }); + + std::string shader = (R"( + #version 450 + + layout (local_size_x = 1) in; + + // The input tensors bind index is relative to index in parameter passed + layout(set = 0, binding = 0) buffer buf_in_a { float in_a[]; }; + layout(set = 0, binding = 1) buffer buf_in_b { float in_b[]; }; + layout(set = 0, binding = 2) buffer buf_out_a { uint out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { uint out_b[]; }; + + // Kompute supports push constants updated on dispatch + layout(push_constant) uniform PushConstants { + float val; + } push_const; + + // Kompute also supports spec constants on initalization + layout(constant_id = 0) const float const_one = 0; + + void main() { + uint index = gl_GlobalInvocationID.x; + out_a[index] += uint( in_a[index] * in_b[index] ); + out_b[index] += uint( const_one * push_const.val ); + } + )"); + + std::vector> params = { + tensorInA, tensorInB, tensorOutA, tensorOutB + }; + + kp::Workgroup workgroup({ 3, 1, 1 }); + kp::Constants specConsts({ 2 }); + kp::Constants pushConsts({ 2.0 }); + + auto algorithm = mgr.algorithm(params, + compileSource(shader), + workgroup, + specConsts, + pushConsts); + + EXPECT_EQ(algorithm->getWorkgroup(), workgroup); + EXPECT_EQ(algorithm->getPush(), pushConsts); + EXPECT_EQ(algorithm->getSpecializationConstants(), specConsts); +} diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 7b432aebd..6e9687562 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -133,3 +133,112 @@ TEST(TestSequence, SequenceTimestamps) EXPECT_EQ(timestamps.size(), 6); // 1 timestamp at start + 1 after each operation } + +TEST(TestSequence, UtilsClearRecordingRunning) +{ + kp::Manager mgr; + + std::shared_ptr sq = mgr.sequence(); + + std::shared_ptr> tensorA = mgr.tensor({ 1, 2, 3 }); + std::shared_ptr> tensorB = mgr.tensor({ 2, 2, 2 }); + std::shared_ptr> tensorOut = mgr.tensor({ 0, 0, 0 }); + + sq->eval({ tensorA, tensorB, tensorOut }); + + std::vector spirv = compileSource(R"( + #version 450 + + layout (local_size_x = 1) in; + + // The input tensors bind index is relative to index in parameter passed + layout(set = 0, binding = 0) buffer bina { float tina[]; }; + layout(set = 0, binding = 1) buffer binb { float tinb[]; }; + layout(set = 0, binding = 2) buffer bout { float tout[]; }; + + void main() { + uint index = gl_GlobalInvocationID.x; + tout[index] = tina[index] * tinb[index]; + } + )"); + + std::shared_ptr algo = + mgr.algorithm({ tensorA, tensorB, tensorOut }, spirv); + + sq->record(algo)->record( + { tensorA, tensorB, tensorOut }); + + EXPECT_TRUE(sq->isRecording()); + + // Running clear to confirm it clears + sq->clear(); + + EXPECT_FALSE(sq->isRecording()); + + sq->evalAsync(); + + EXPECT_TRUE(sq->isRunning()); + + sq->evalAwait(); + + EXPECT_FALSE(sq->isRunning()); + + EXPECT_EQ(tensorOut->vector(), std::vector({ 2, 4, 6 })); +} + +TEST(TestSequence, CorrectSequenceRunningError) +{ + kp::Manager mgr; + + std::shared_ptr sq = mgr.sequence(); + + std::shared_ptr> tensorA = mgr.tensor({ 1, 2, 3 }); + std::shared_ptr> tensorB = mgr.tensor({ 2, 2, 2 }); + std::shared_ptr> tensorOut = mgr.tensor({ 0, 0, 0 }); + + sq->eval({ tensorA, tensorB, tensorOut }); + + std::vector spirv = compileSource(R"( + #version 450 + + layout (local_size_x = 1) in; + + // The input tensors bind index is relative to index in parameter passed + layout(set = 0, binding = 0) buffer bina { float tina[]; }; + layout(set = 0, binding = 1) buffer binb { float tinb[]; }; + layout(set = 0, binding = 2) buffer bout { float tout[]; }; + + void main() { + uint index = gl_GlobalInvocationID.x; + tout[index] = tina[index] * tinb[index]; + } + )"); + + std::shared_ptr algo = + mgr.algorithm({ tensorA, tensorB, tensorOut }, spirv); + + sq->record(algo)->record( + { tensorA, tensorB, tensorOut }); + + EXPECT_TRUE(sq->isRecording()); + + sq->evalAsync(); + + EXPECT_TRUE(sq->isRunning()); + + // Sequence should throw when running + EXPECT_ANY_THROW(sq->begin()); + EXPECT_ANY_THROW(sq->end()); + EXPECT_ANY_THROW(sq->evalAsync()); + + // Errors should still not get into inconsystent state + sq->evalAwait(); + + // Sequence should not throw when finished + EXPECT_NO_THROW(sq->evalAwait()); + EXPECT_NO_THROW(sq->evalAwait(10)); + + EXPECT_FALSE(sq->isRunning()); + + EXPECT_EQ(tensorOut->vector(), std::vector({ 2, 4, 6 })); +} diff --git a/test/TestTensor.cpp b/test/TestTensor.cpp index cba3cd961..32c870a7b 100644 --- a/test/TestTensor.cpp +++ b/test/TestTensor.cpp @@ -10,5 +10,35 @@ TEST(TestTensor, ConstructorData) std::vector vec{ 0, 1, 2 }; std::shared_ptr> tensor = mgr.tensor(vec); EXPECT_EQ(tensor->size(), vec.size()); + EXPECT_EQ(tensor->dataTypeMemorySize(), sizeof(float)); EXPECT_EQ(tensor->vector(), vec); } + +TEST(TestTensor, DataTypes) +{ + kp::Manager mgr; + + { + std::vector vec{ 0, 1, 2 }; + std::shared_ptr> tensor = mgr.tensor(vec); + EXPECT_EQ(tensor->dataType(), kp::Tensor::TensorDataTypes::eFloat); + } + + { + std::vector vec{ 0, 1, 2 }; + std::shared_ptr> tensor = mgr.tensorT(vec); + EXPECT_EQ(tensor->dataType(), kp::Tensor::TensorDataTypes::eInt); + } + + { + std::vector vec{ 0, 1, 2 }; + std::shared_ptr> tensor = mgr.tensorT(vec); + EXPECT_EQ(tensor->dataType(), kp::Tensor::TensorDataTypes::eUnsignedInt); + } + + { + std::vector vec{ 0, 1, 2 }; + std::shared_ptr> tensor = mgr.tensorT(vec); + EXPECT_EQ(tensor->dataType(), kp::Tensor::TensorDataTypes::eDouble); + } +}