From 9aae5d69dbf7ebfb09d0069743667497d2779cfa Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Wed, 24 Feb 2021 08:39:09 +0000 Subject: [PATCH 01/91] Initial checkpoint with reasonable workflow --- .ccls | 1 + .../app/src/main/cpp/KomputeModelML.cpp | 2 +- examples/array_multiplication/src/Main.cpp | 2 +- .../kompute_summator/KomputeSummatorNode.cpp | 2 +- .../gdnative_shared/src/KomputeSummator.cpp | 2 +- .../kompute_model_ml/KomputeModelMLNode.cpp | 4 +- .../gdnative_shared/src/KomputeModelML.cpp | 4 +- examples/logistic_regression/src/Main.cpp | 2 +- python/src/docstrings.hpp | 40 +- python/src/main.cpp | 20 +- python/test/test_kompute.py | 418 ++++++++++++++++++ single_include/AggregateHeaders.cpp | 2 +- single_include/kompute/Kompute.hpp | 22 +- src/Algorithm.cpp | 123 ++++-- src/Manager.cpp | 84 ++-- src/OpAlgoBase.cpp | 176 -------- src/OpAlgoCreate.cpp | 51 +++ src/OpAlgoDispatch.cpp | 59 +++ src/OpAlgoLhsRhsOut.cpp | 16 +- src/OpTensorCopy.cpp | 22 +- src/OpTensorCreate.cpp | 46 ++ src/OpTensorSyncDevice.cpp | 19 +- src/OpTensorSyncLocal.cpp | 26 +- src/Sequence.cpp | 43 +- src/Tensor.cpp | 72 +-- src/include/kompute/Algorithm.hpp | 42 +- src/include/kompute/Manager.hpp | 19 +- src/include/kompute/Sequence.hpp | 19 - src/include/kompute/Tensor.hpp | 35 +- .../kompute/operations/OpAlgoCreate.hpp | 77 ++++ .../{OpAlgoBase.hpp => OpAlgoDispatch.hpp} | 63 +-- .../kompute/operations/OpAlgoLhsRhsOut.hpp | 20 +- src/include/kompute/operations/OpBase.hpp | 122 +++-- src/include/kompute/operations/OpMult.hpp | 6 +- .../kompute/operations/OpTensorCopy.hpp | 12 +- .../kompute/operations/OpTensorCreate.hpp | 71 +++ .../kompute/operations/OpTensorSyncDevice.hpp | 15 +- .../kompute/operations/OpTensorSyncLocal.hpp | 12 +- test/TestAsyncOperations.cpp | 8 +- test/TestDestroy.cpp | 26 +- test/TestLogisticRegression.cpp | 4 +- test/TestMultipleAlgoExecutions.cpp | 26 +- test/TestOpAlgoLoopsPassingData.cpp | 2 +- test/TestOpShadersFromStringAndFile.cpp | 12 +- test/TestSpecializationConstant.cpp | 2 +- test/TestWorkgroup.cpp | 2 +- 46 files changed, 1158 insertions(+), 695 deletions(-) delete mode 100644 src/OpAlgoBase.cpp create mode 100644 src/OpAlgoCreate.cpp create mode 100644 src/OpAlgoDispatch.cpp create mode 100644 src/OpTensorCreate.cpp create mode 100644 src/include/kompute/operations/OpAlgoCreate.hpp rename src/include/kompute/operations/{OpAlgoBase.hpp => OpAlgoDispatch.hpp} (64%) create mode 100644 src/include/kompute/operations/OpTensorCreate.hpp diff --git a/.ccls b/.ccls index 460b00b9d..da06a9763 100644 --- a/.ccls +++ b/.ccls @@ -19,6 +19,7 @@ -I./external/googletest/googletest/include/ -I./external/glslang/ -I./external/spdlog/include/ +-I./external/fmt/include/ -I./src/include/ -I./single_include/ -I./vk_ndk_wrapper_include/ diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp index e39657022..f1884760a 100755 --- a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp +++ b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp @@ -52,7 +52,7 @@ void KomputeModelML::train(std::vector yData, std::vector xIData, sq->record({ wIn, bIn }); // Newer versions of Android are able to use shaderc to read raw string - sq->record( + sq->record( params, kp::Shader::compile_source(LR_SHADER)); sq->record({ wOutI, wOutJ, bOut, lOut }); diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index 0fb704a90..8ec611e15 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -37,7 +37,7 @@ int main() } )"); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorInA, tensorInB, tensorOut }, kp::Shader::compile_source(shader)); diff --git a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp index c0b68595b..2e9f1bc00 100644 --- a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp +++ b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp @@ -59,7 +59,7 @@ void KomputeSummatorNode::_init() { { this->mSecondaryTensor }); // Then we run the operation with both tensors - sq->record( + sq->record( { this->mPrimaryTensor, this->mSecondaryTensor }, kp::Shader::compile_source(shader)); diff --git a/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp b/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp index feb674cd3..ece095c8e 100644 --- a/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp +++ b/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp @@ -56,7 +56,7 @@ void KomputeSummator::_init() { { this->mSecondaryTensor }); // Then we run the operation with both tensors - this->mSequence->record( + this->mSequence->record( { this->mPrimaryTensor, this->mSecondaryTensor }, kp::Shader::compile_source(shader)); diff --git a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp index c304deffd..57490a8d4 100644 --- a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp +++ b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp @@ -62,11 +62,11 @@ void KomputeModelMLNode::train(Array yArr, Array xIArr, Array xJArr) { #ifdef KOMPUTE_ANDROID_SHADER_FROM_STRING // Newer versions of Android are able to use shaderc to read raw string - sq->record( + sq->record( params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); #else // Older versions of Android require the SPIRV binary directly - sq->record( + sq->record( params, std::vector( kp::shader_data::shaders_glsl_logisticregression_comp_spv, kp::shader_data::shaders_glsl_logisticregression_comp_spv diff --git a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp index f16c3c24b..1a01febd0 100644 --- a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp +++ b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp @@ -66,11 +66,11 @@ void KomputeModelML::train(Array yArr, Array xIArr, Array xJArr) { #ifdef KOMPUTE_ANDROID_SHADER_FROM_STRING // Newer versions of Android are able to use shaderc to read raw string - sq->record( + sq->record( params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); #else // Older versions of Android require the SPIRV binary directly - sq->record( + sq->record( params, std::vector( kp::shader_data::shaders_glsl_logisticregression_comp_spv, kp::shader_data::shaders_glsl_logisticregression_comp_spv diff --git a/examples/logistic_regression/src/Main.cpp b/examples/logistic_regression/src/Main.cpp index e342e7a2b..769699ca7 100755 --- a/examples/logistic_regression/src/Main.cpp +++ b/examples/logistic_regression/src/Main.cpp @@ -44,7 +44,7 @@ int main() sq->record({ wIn, bIn }); - sq->record( + sq->record( params, std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv diff --git a/python/src/docstrings.hpp b/python/src/docstrings.hpp index 36f481b94..2000421c3 100644 --- a/python/src/docstrings.hpp +++ b/python/src/docstrings.hpp @@ -266,23 +266,23 @@ The type of tensor to initialize @param syncDataToGPU Whether to sync the data to GPU memory @returns Initialized Tensor with memory Syncd to GPU device)doc"; -static const char *__doc_kp_OpAlgoBase = +static const char *__doc_kp_OpAlgoCreate = R"doc(Operation that provides a general abstraction that simplifies the use of algorithm and parameter components which can be used with shaders. By default it enables the user to provide a dynamic number of tensors which are then passed as inputs.)doc"; -static const char *__doc_kp_OpAlgoBase_KomputeWorkgroup = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_KomputeWorkgroup_x = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_x = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_KomputeWorkgroup_y = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_y = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_KomputeWorkgroup_z = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_z = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_OpAlgoBase = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; +static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; -static const char *__doc_kp_OpAlgoBase_OpAlgoBase_2 = +static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_2 = R"doc(Default constructor with parameters that provides the bare minimum requirements for the operations to be able to create and manage their sub-components. @@ -295,7 +295,7 @@ shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) @param komputeWorkgroup Optional parameter to specify the layout for processing)doc"; -static const char *__doc_kp_OpAlgoBase_OpAlgoBase_3 = +static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_3 = R"doc(Constructor that enables a file to be passed to the operation with the contents of the shader. This can be either in raw format or in compiled SPIR-V binary format. @@ -308,7 +308,7 @@ shaderFilePath Parameter to specify the shader to load (either in spirv or raw format) @param komputeWorkgroup Optional parameter to specify the layout for processing)doc"; -static const char *__doc_kp_OpAlgoBase_OpAlgoBase_4 = +static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_4 = R"doc(Constructor that enables raw shader data to be passed to the main operation which can be either in raw shader glsl code or in compiled SPIR-V binary. @@ -321,37 +321,37 @@ shaderDataRaw Optional parameter to specify the shader data either in binary or raw form @param komputeWorkgroup Optional parameter to specify the layout for processing)doc"; -static const char *__doc_kp_OpAlgoBase_fetchSpirvBinaryData = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_fetchSpirvBinaryData = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_init = +static const char *__doc_kp_OpAlgoCreate_init = R"doc(The init function is responsible for the initialisation of the algorithm component based on the parameters specified, and allows for extensibility on the options provided. Further dependent classes can perform more specific checks such as ensuring tensors provided are initialised, etc.)doc"; -static const char *__doc_kp_OpAlgoBase_mAlgorithm = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_mAlgorithm = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_mFreeAlgorithm = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_mFreeAlgorithm = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_mKomputeWorkgroup = R"doc()doc"; +static const char *__doc_kp_OpAlgoCreate_mKomputeWorkgroup = R"doc()doc"; -static const char *__doc_kp_OpAlgoBase_mShaderDataRaw = +static const char *__doc_kp_OpAlgoCreate_mShaderDataRaw = R"doc(< Optional member variable which can be provided to contain either the raw shader content or the spirv binary content)doc"; -static const char *__doc_kp_OpAlgoBase_mShaderFilePath = -R"doc(< Optional member variable which can be provided for the OpAlgoBase to +static const char *__doc_kp_OpAlgoCreate_mShaderFilePath = +R"doc(< Optional member variable which can be provided for the OpAlgoCreate to find the data automatically and load for processing)doc"; -static const char *__doc_kp_OpAlgoBase_postEval = +static const char *__doc_kp_OpAlgoCreate_postEval = R"doc(Executes after the recorded commands are submitted, and performs a copy of the GPU Device memory into the staging buffer so the output data can be retrieved.)doc"; -static const char *__doc_kp_OpAlgoBase_preEval = R"doc(Does not perform any preEval commands.)doc"; +static const char *__doc_kp_OpAlgoCreate_preEval = R"doc(Does not perform any preEval commands.)doc"; -static const char *__doc_kp_OpAlgoBase_record = +static const char *__doc_kp_OpAlgoCreate_record = R"doc(This records the commands that are to be sent to the GPU. This includes the barriers that ensure the memory has been copied before going in and out of the shader, as well as the dispatch operation that diff --git a/python/src/main.cpp b/python/src/main.cpp index ab7d64e41..6f99a193b 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -133,7 +133,7 @@ PYBIND11_MODULE(kp, m) { .def("record_tensor_sync_local", &kp::Sequence::record, "Records operation to sync tensor(s) from GPU memory to local memory") .def("record_algo_file", &kp::Sequence::record< - kp::OpAlgoBase, + kp::OpAlgoCreate, const std::string&, kp::Workgroup, kp::Constants>, @@ -148,7 +148,7 @@ PYBIND11_MODULE(kp, m) { py::buffer_info info(py::buffer(bytes).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); - return self.record( + return self.record( tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); }, "Records an operation using a custom shader provided as spirv bytes", @@ -211,7 +211,7 @@ PYBIND11_MODULE(kp, m) { .def("eval_tensor_sync_local_def", &kp::Manager::evalOpDefault, "Evaluates operation to sync tensor(s) from GPU memory to local memory with new anonymous Sequence") .def("eval_algo_file_def", &kp::Manager::evalOpDefault< - kp::OpAlgoBase, + kp::OpAlgoCreate, const std::string&, kp::Workgroup, kp::Constants>, @@ -226,7 +226,7 @@ PYBIND11_MODULE(kp, m) { py::buffer_info info(py::buffer(bytes).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); - self.evalOpDefault( + self.evalOpDefault( tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); }, "Evaluates an operation using a custom shader provided as spirv bytes with new anonymous Sequence", @@ -240,7 +240,7 @@ PYBIND11_MODULE(kp, m) { .def("eval_tensor_sync_local", &kp::Manager::evalOp, "Evaluates operation to sync tensor(s) from GPU memory to local memory with explicitly named Sequence") .def("eval_algo_file", &kp::Manager::evalOp< - kp::OpAlgoBase, + kp::OpAlgoCreate, const std::string&, kp::Workgroup, kp::Constants>, @@ -256,7 +256,7 @@ PYBIND11_MODULE(kp, m) { py::buffer_info info(py::buffer(bytes).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); - self.evalOp( + self.evalOp( tensors, sequenceName, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); }, "Evaluates an operation using a custom shader provided as spirv bytes with explicitly named Sequence", @@ -270,7 +270,7 @@ PYBIND11_MODULE(kp, m) { .def("eval_async_tensor_sync_local_def", &kp::Manager::evalOpAsyncDefault, "Evaluates asynchronously operation to sync tensor(s) from GPU memory to local memory with anonymous Sequence") .def("eval_async_algo_file_def", &kp::Manager::evalOpAsyncDefault< - kp::OpAlgoBase, + kp::OpAlgoCreate, const std::string&, kp::Workgroup, kp::Constants>, @@ -285,7 +285,7 @@ PYBIND11_MODULE(kp, m) { py::buffer_info info(py::buffer(bytes).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); - self.evalOpAsyncDefault( + self.evalOpAsyncDefault( tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); }, "Evaluates asynchronously an operation using a custom shader provided as raw string or spirv bytes with anonymous Sequence", @@ -299,7 +299,7 @@ PYBIND11_MODULE(kp, m) { .def("eval_async_tensor_sync_local", &kp::Manager::evalOpAsync, "Evaluates asynchronously operation to sync tensor(s) from GPU memory to local memory with explicitly named Sequence") .def("eval_async_algo_file", &kp::Manager::evalOpAsync< - kp::OpAlgoBase, + kp::OpAlgoCreate, const std::string&, kp::Workgroup, kp::Constants>, @@ -315,7 +315,7 @@ PYBIND11_MODULE(kp, m) { py::buffer_info info(py::buffer(bytes).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); - self.evalOpAsync( + self.evalOpAsync( tensors, sequenceName, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); }, "Evaluates asynchronously an operation using a custom shader provided as raw string or spirv bytes with explicitly named Sequence", diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index b998532b8..a6e4ead3a 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -28,6 +28,424 @@ def test_opalgobase_file(): assert tensor_out.data() == [2.0, 4.0, 6.0] +params = [kp.Tensor([2, 2, 2]), kp.Tensor([1, 2, 3]), kp.Tensor([0, 0, 0])] + +mgr = kp.Manager() +op_ct = kp.OpTensorCreate(params) +op_ct = mgr.rebuild(op_ct) +mgr.eval_op(op_ct) + +algo = kp.Algo(params, spirv) +op_ac = kp.OpAlgoCreate(algo) +op_ac = mgr.rebuild(op_ac) +mgr.eval_op(op_ac) + +op_ac = kp.OpAlgoCreate(kp.Algo(params, spirv)) +mgr.eval_op(kp.OpAlgoCreate(algo)) + + +mgr = kp.Manager() + +op_ct = kp.OpTensorCreate(mgr, params) # This initialises operation +op_ct.eval() + +algo = kp.Algo(params, spirv) +op_ac = kp.OpAlgoCreate(mgr, algo) +op_ct.eval() + +op_tsd = kp.OpTensorSyncDevice(mgr, params) +op_ad = kp.OpAlgoDispatch(mgr, algo) +op_tsl = kp.OpTensorSyncLocal(mgr, params) + +sq = kp.Sequence(mgr, "newSeq") +sq.record([op_tsd, op_ad, op_tsl]) +sq.eval() +sq.destroy() + +# Explore consistent interface: +op_tsd = kp.OpTensorSyncDevice(sq, params) +op_ad = kp.OpAlgoDispatch(sq, algo) +op_tsl = kp.OpTensorSyncLocal(sq, params) + +op_tsd.record() +op_ad.record() +op_tsl.record() + +sq.eval() + + + +auto params = ...; +std::string shader = "..."; +std::vector spirv = kp::Shader::compile_source(shader); + +// Example passing mgr +kp::Manager mgr; + +kp::OpTensorCreate op_tc(mgr, params); +op_tc.eval() + +kp::Algorithm algo(params, spirv); +kp::OpAlgoCreate op_ac(mgr, algo); +op_ac.eval() + +op_ac.destroy() +op_tc.destroy() + +kp::OpTensorAlgoCreate op_c(mgr, params, algo); +op_c.eval() + +kp::Sequence sq(mgr); + +kp::OpTensorSyncDevice op_tsd(mgr, params); +kp::OpAlgoDispatch op_ad(mgr, algo); +kp::OpTensorSyncLocal op_tsl(mgr, params); + +sq.record({op_tsd, op_ad, op_tsl}) + +for(...) { + sq.eval(); + + tensorA... +} + +###### +####### +####### +####### +####### +###### +// Example not passing mgr +kp::Manager mgr; + +std::shared_ptr op_tc_1{ new kp::OpTensorCreate(params) }; +auto sq_1 = mgr.eval(op_tc_1); // Initialises and stores op as part of new sequence +mgr.eval(op_tc_1); // Fails as this op can only be "initialised" once +mgr.destroy(op_tc_1); +mgr.eval(op_tc_1); // This works as it's a new setup +mgr.eval(params); // Fails as tensors already created +// NOT ALLOED TO DELETE JUST TENSORS ANYMORE - SEE BELOW +mgr.destroy(params); // Sends to inconsistent state as op_tc_1 will still destroy these parameters +mgr.destroy(op_tc_1, recursive=false); // Destroys only operation, which is useful when you need to ensure another operation owns the parameters +auto op_tc_2 = mgr.eval(params); +std::shared_ptr op_tc_2{ new kp::OpTensorCreate(params) }; // fails as tensors already created +op_tc_2.destroy(); // Manager still holds dangling reference so requires explicit termination in manager +mgr.destroy(op_tc_2); +auto op_tc_3 = mgr.eval({ new kp::OpTensorCreate(params) }); + +std::shared_ptr algo{ new kp::Algorithm(params, spirv, kp::Workgroup(), kp::SpecConst(), kp::PushConst()) }; +std::shared_ptr op_ac_1{ new kp::OpAlgoCreate(algo) }; +mgr.eval(op_ac_1); // Initialises and stores op as part of manager +mgr.eval(op_ac_1); // Fails as this op can only be "initialised" once +mgr.destroy(op_ac_1); + +std::shared_ptr op_ac_2 = + mgr.eval({ new kp::OpAlgoCreate(params, { new kp::Algorithm(spirv) }) }); + +std::shared_ptr op_amc{ new kp::OpAlgoMultCreate(params) }; +mgr.eval(op_amc); + +std::shared_ptr algo_mult = op_amc.algorithm() +std::vector> params = op_amc.tensors() + +auto op_tsd = std::make_shared(params); +auto op_ad = std::make_shared(algo); +auto op_ad = std::make_shared(algo); +auto op_tsl = std::make_shared(mgr, params); + +op_params = {op_tsd, op_ad, op_tsl}; + +mgr.record(op_params); +mgr.eval(); // Runs recorded default sequence + +mgr.record(op_params, clear=false); // Non-create ops ok if rerun +mgr.eval(); // Runs twice the recorded paams + +mgr.record("namedSeq", op_params); +mgr.eval("namedSeq"); + +kp::Manager mgrAsync(0, {0, 2}); +mgr.sequence("namedSeq2", 0); // Create named sequence with queue in index 0 +mgr.sequence("namedSeq3", 1); + +mgr.eval_async("namedSeq2", op_params); // Clear, record params and eval +mgr.eval_async("namedSeq3", op_params); // Clear, record params and eval + +mgr.eval_await("namedSeq2"); +mgr.eval_await("namedSeq3"); + +mgr.destroy("namedSeq"); // Destroy named sequence +mgr.destroy({"namedSeq2", "namedSeq3"}); // Destroy multiple named sequences +mgr.destroy("namedSeq"); // Error + + + + +mgr = kp.Manager(0, [0, 2]) + +// Manager does not need to manage seq anymore +sq_1 = kp.Sequence(mgr, 0) + +t1 = kp.Tensor(sq_1, [0, 0, 0]) +t2 = kp.Tensor(sq_1, [0, 1, 2]) + +algo = kp.Algorithm(sq_1) + +op_tc = kp.OpTensorCreate(sq_1, params) +op_tsd = kp.OpTensorSyncDevice(sq_1, params) +op_ac = kp.OpAlgoCreate(sq_1, algo) +op_ad = kp.OpAlgoDispatch(sq_1, algo) + +sq_1.clear() + +op_tc.record() +op_tsd.record() +op_ac.record() +op_ad.record() +op_ad.record() +op_ad.record() + +sq_1.eval() + + +std::shared_ptr mgr = kp::ManagerSP(0, {0, 1}); + +std::shared_ptr sq_2 = kp::SequenceSP(mgr, 1) + +std::shared_ptr t1 = kp::TensorSP(sq_2, {1, 2, 3}); +std::shared_ptr t2 = kp::TensorSP(sq_2, {2, 3, 4}); + +auto params = ... + +std::shared_ptr algo2 = kp::AlgorithmSP(sq_2, params, spirv, workgroup); + +// How do we deal with this? +{ + auto op_1 = kp::OpTensorSyncDevice(sq_2, params) + auto op_2 = kp::OpAlgoDispatch(sq_2, algo) +} + +sq_2.eval() + + +// HEAP ONLY - This would fail + +kp::Manager mgr = kp::Manager(0, {0, 1}); + +kp::Sequence sq_2 = kp::Sequence(mgr, 1) + +kp::Tensor t1 = kp::Tensor(sq_2, {1, 2, 3}); +kp::Tensor t2 = kp::Tensor(sq_2, {2, 3, 4}); + +auto params = ... + +kp::Algorithm algo2 = kp::AlgorithmSP(sq_2, params, spirv, workgroup); + +// How do we deal with this? +{ + auto op_1 = kp::OpTensorSyncDevice(sq_2, params) + auto op_2 = kp::OpAlgoDispatch(sq_2, algo) +} + +sq_2.eval() + + + + + +kp::Manager mgr = kp::Manager(0, {0, 1}); + +kp::Sequence sq_2 = kp::Sequence(mgr, 1) + +kp::Tensor t1 = kp::Tensor(sq_2, {1, 2, 3}); +kp::Tensor t2 = kp::Tensor(sq_2, {2, 3, 4}); + +auto params = ... + +kp::Algorithm* algo2 = new kp::Algorithm(sq_2, params, spirv, workgroup); + +// How do we deal with this? +{ + auto op_1 = kp::OpTensorSyncDevice(sq_2, params) + auto op_2 = kp::OpAlgoDispatch(sq_2, algo) +} + +sq_2.eval() + + + + + + +kp::Manager mgr = kp::Manager; + +auto sq_2 = mgr.sequence() + +{ + // What if we want to use tensor in a different sequence? + auto t1 = sq_2.tensor({1, 2, 3}); + auto t2 = sq_2.tensor({1, 2, 3}); + + auto algo2 = sq_2.algorithm(); + + sq_2.record(kp::OpTensorRebuild({ t1 })) + sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) + sq_2.record(kp::OpTensorSyncDevice(prams)) + sq_2.record(kp::OpAlgoDispatch(prams, algo2)) +} + +sq_2.eval() + + + +kp::Manager mgr = kp::Manager; + +auto t1 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared +auto t2 = mgr.tensor({1, 2, 3}); + +auto algo2 = mgr.algorithm(); + +{ + auto sq_2 = mgr.sequence() + + { + sq_2.record(kp::OpTensorRebuild({ t1 })) // record only supports move operator && + sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) + sq_2.record(kp::OpTensorSyncDevice(prams)) + sq_2.record(kp::OpAlgoDispatch(prams, algo2)) + } + + sq_2.eval() +} + + + +// What about only tensors being init with it + + +{ + kp::Manager mgr = kp::Manager; + + auto t0 = mgr.tensor({0, 0, 0}) + + { + auto t1 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared (refc 1) + + { + auto sq_2 = mgr.sequence() + + { + + auto t2 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared (refc 1) + auto algo2 = mgr.algorithm(); // Held as weak ptr but passed as shared (refc 1) + + params = {t1, t2} + + sq_2.record(kp::OpTensorRebuild(params, {1, 2, 3, 4})) // Refc is now 2 for 3 for params + sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) // refc is now 2 for algo2, 3 for parms + sq_2.record(kp::OpTensorSyncDevice(prams)) // refc for params 4 + sq_2.record(kp::OpAlgoDispatch(prams, algo2)) // refc for params 5, 3 for algo2 + } + + sq_2.eval() // all refcs stil valid + } // seq destroyed so refc for algo2 and t2 drops to 0, gets destroyed, t1 has 1 + } // t1 refc drops to 0, gets destroyed + // refc of t0 is still 1 + + mgr.gc() // Iterates through all tensor, sequence and algo weak_ptr and removes unused + + // can we have something like + mgr.sequence() + .record(kp::OpTensorRebuild(params, {1, 2, 3, 4})) + .record(kp::OpAlgoDispatch(params, algo2)) + .eval(); + +}// refc is destroyed by manager manually, the rest are empty shells so ignored + + + + +kp::Manager mgr = kp::Manager(0, {0, 1}); + +std::shared_ptr t1 = mgr.tensor({1, 2, 3}); +std::shared_ptr t2 = mgr.tensor({1, 2, 3}); + +auto params = ... + +std::shared_ptr algo2 = mgr.algorithm(params, spirv, workgroup); + +sq_2.record(prams) +sq_2.record(algo) + + +// WHY NO MORE DETROY TENSORS: + + * std::shared_ptr op_tc1{ kp::OpTensorCreate(params) }; + * { + * std::shared_ptr op_tc2{ kp::OpTensorCreate(params) }; + * mgr.eval(op_tc2); + * mgr.destroy(params); + * + * mgr.eval(op_tc1); + * + * } // op_tc1 is destroyed and all parameters are freed + + + +// NO LONGER ALLOWED: Mainly as manager now needs to regsiter ops +// If we still want it, then sequence wil have to hold ref to manager +auto sq = mgr.sequence(); + +auto op_tsd = std::make_shared(params); +auto op_ad = std::make_shared(algo); +auto op_tsl = std::make_shared(mgr, params); + +sq.record({op_tsd, op_ad, op_tsl}); // Clear and record +sq.eval(); +sq.record({op_tsd, op_ad, op_tsl}, clear=false); // record on top +sq.eval(); +sq.clear(); // explicitly clear + + + + + +mgr = kp.Manager() + +op_ct = kp.OpTensorCreate(params) +mgr.eval(op_ct) + +algo = kp.Algo(params, spirv) +op_ac = kp.OpAlgoCreate(algo) +mgr.eval(op_ac) # Runs init on operator function (below shows explicit steps) + +op_tsd = kp.OpTensorSyncDevice(params) +op_ad = kp.OpAlgoDispatch(algo) +op_tsl = kp.OpTensorSyncLocal(params) + +sq = mgr.sequence() +sq.record([op_tsd, op_ad, op_tsl]) +sq.eval() +sq.eval() +sq.eval() + +mgr.eval(op_ac) # Would fail as algo is initialised +mgr.destroy(op_ac) # Destroys Op and Algo owned object +mgr.eval(op_ac) # Succeeds with new +mgr.destroy(op_ac) +mgr.init(op_ac) +mgr.eval(op_ac, init=False) + + + + + + + + + + + def test_shader_str(): """ Test basic OpAlgoBase operation diff --git a/single_include/AggregateHeaders.cpp b/single_include/AggregateHeaders.cpp index 725d04f51..599607c43 100644 --- a/single_include/AggregateHeaders.cpp +++ b/single_include/AggregateHeaders.cpp @@ -6,7 +6,7 @@ #include "kompute/Manager.hpp" #include "kompute/Sequence.hpp" #include "kompute/operations/OpBase.hpp" -#include "kompute/operations/OpAlgoBase.hpp" +#include "kompute/operations/OpAlgoCreate.hpp" #include "kompute/operations/OpAlgoLhsRhsOut.hpp" #include "kompute/operations/OpMult.hpp" #include "kompute/operations/OpTensorCopy.hpp" diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index c77f72d3b..b366dbf09 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1893,14 +1893,14 @@ namespace kp { * By default it enables the user to provide a dynamic number of tensors * which are then passed as inputs. */ -class OpAlgoBase : public OpBase +class OpAlgoCreate : public OpBase { public: /** * Base constructor, should not be used unless explicitly intended. */ - OpAlgoBase(); + OpAlgoCreate(); /** * Default constructor with parameters that provides the bare minimum @@ -1914,7 +1914,7 @@ class OpAlgoBase : public OpBase * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, + OpAlgoCreate(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr commandBuffer, std::vector>& tensors, @@ -1933,7 +1933,7 @@ class OpAlgoBase : public OpBase * @param shaderFilePath Parameter to specify the shader to load (either in spirv or raw format) * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, + OpAlgoCreate(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr commandBuffer, std::vector>& tensors, @@ -1952,7 +1952,7 @@ class OpAlgoBase : public OpBase * @param shaderDataRaw Optional parameter to specify the shader data either in binary or raw form * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, + OpAlgoCreate(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr commandBuffer, std::vector>& tensors, @@ -1964,7 +1964,7 @@ class OpAlgoBase : public OpBase * Default destructor, which is in charge of destroying the algorithm * components but does not destroy the underlying tensors */ - virtual ~OpAlgoBase() override; + virtual ~OpAlgoCreate() override; /** * The init function is responsible for the initialisation of the algorithm @@ -2005,9 +2005,9 @@ class OpAlgoBase : public OpBase // -------------- ALWAYS OWNED RESOURCES - Workgroup mKomputeWorkgroup; + Workgroup mWorkgroup; - std::string mShaderFilePath; ///< Optional member variable which can be provided for the OpAlgoBase to find the data automatically and load for processing + std::string mShaderFilePath; ///< Optional member variable which can be provided for the OpAlgoCreate to find the data automatically and load for processing std::vector mShaderDataRaw; ///< Optional member variable which can be provided to contain either the raw shader content or the spirv binary content virtual std::vector fetchSpirvBinaryData(); @@ -2024,7 +2024,7 @@ namespace kp { * right hand and left hand side datapoints together with a single output. * The expected data passed is two input tensors and one output tensor. */ -class OpAlgoLhsRhsOut : public OpAlgoBase +class OpAlgoLhsRhsOut : public OpAlgoCreate { public: /** @@ -2102,7 +2102,7 @@ namespace kp { * Operation that performs multiplication on two tensors and outpus on third * tensor. */ -class OpMult : public OpAlgoBase +class OpMult : public OpAlgoCreate { public: /** @@ -2128,7 +2128,7 @@ class OpMult : public OpAlgoBase std::shared_ptr commandBuffer, std::vector> tensors, const Workgroup& komputeWorkgroup = {}) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) + : OpAlgoCreate(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 3217ee99a..512e0e5aa 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -4,36 +4,62 @@ namespace kp { -Algorithm::Algorithm() -{ - KP_LOG_DEBUG("Kompute Algorithm base constructor"); -} - -Algorithm::Algorithm(std::shared_ptr device, - std::shared_ptr commandBuffer, - const Constants& specializationConstants) +Algorithm::Algorithm( + std::shared_ptr device, + const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants, + const Constants& pushConstants) { KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); this->mDevice = device; - this->mCommandBuffer = commandBuffer; - this->mSpecializationConstants = specializationConstants; + this->setWorkgroup(workgroup); + this->mPushConstants = pushConstants; + this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); } Algorithm::~Algorithm() { KP_LOG_DEBUG("Kompute Algorithm Destructor started"); + this->freeMemoryDestroyGPUResources(); +} + +void +Algorithm::rebuild( + const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants, + const Constants& pushConstants) +{ + KP_LOG_DEBUG("Kompute Algorithm rebuild started"); + + // Descriptor pool is created first so if available then destroy all before rebuild + if (this->mFreeDescriptorPool) { + this->freeMemoryDestroyGPUResources(); + } + + this->createParameters(tensors); + this->createShaderModule(); + this->createPipeline(); +} + +void +Algorithm::freeMemoryDestroyGPUResources() { + if (!this->mDevice) { - KP_LOG_ERROR( - "Kompute Algorithm destructor reached with null Device pointer"); + KP_LOG_WARN( + "Kompute Algorithm destroy function reached with null Device pointer"); return; } if (this->mFreePipeline) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline"); if (!this->mPipeline) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy " "pipeline but it is null"); } this->mDevice->destroy( @@ -44,7 +70,7 @@ Algorithm::~Algorithm() if (this->mFreePipelineCache) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline cache"); if (!this->mPipelineCache) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy " "pipeline cache but it is null"); } this->mDevice->destroy( @@ -55,7 +81,7 @@ Algorithm::~Algorithm() if (this->mFreePipelineLayout) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline layout"); if (!this->mPipelineLayout) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy " "pipeline layout but it is null"); } this->mDevice->destroy( @@ -66,7 +92,7 @@ Algorithm::~Algorithm() if (this->mFreeShaderModule) { KP_LOG_DEBUG("Kompute Algorithm Destroying shader module"); if (!this->mShaderModule) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy shader " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy shader " "module but it is null"); } this->mDevice->destroy( @@ -77,7 +103,7 @@ Algorithm::~Algorithm() if (this->mFreeDescriptorSet) { KP_LOG_DEBUG("Kompute Algorithm Freeing Descriptor Set"); if (!this->mDescriptorSet) { - KP_LOG_ERROR( + KP_LOG_WARN( "Kompute Algorithm Error requested to free descriptor set"); } this->mDevice->freeDescriptorSets( @@ -87,7 +113,7 @@ Algorithm::~Algorithm() if (this->mFreeDescriptorSetLayout) { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Set Layout"); if (!this->mDescriptorSetLayout) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy " "descriptor set layout but it is null"); } this->mDevice->destroy( @@ -98,7 +124,7 @@ Algorithm::~Algorithm() if (this->mFreeDescriptorPool) { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Pool"); if (!this->mDescriptorPool) { - KP_LOG_ERROR("Kompute Algorithm Error requested to destroy " + KP_LOG_WARN("Kompute Algorithm Error requested to destroy " "descriptor pool but it is null"); } this->mDevice->destroy( @@ -108,27 +134,7 @@ Algorithm::~Algorithm() } void -Algorithm::init(const std::vector& shaderFileData, - std::vector> tensorParams) -{ - KP_LOG_DEBUG("Kompute Algorithm init started"); - - this->createParameters(tensorParams); - this->createShaderModule(shaderFileData); - - for (std::shared_ptr tensor : tensorParams) { - this->mSpecializationConstants.push_back(tensor->size()); - } - - this->createPipeline(); -} - -void -Algorithm::createDescriptorPool() -{} - -void -Algorithm::createParameters(std::vector>& tensorParams) +Algorithm::createParameters(const std::vector>& tensorParams) { KP_LOG_DEBUG("Kompute Algorithm createParameters started"); @@ -207,17 +213,17 @@ Algorithm::createParameters(std::vector>& tensorParams) } void -Algorithm::createShaderModule(const std::vector& shaderFileData) +Algorithm::createShaderModule() { KP_LOG_DEBUG("Kompute Algorithm createShaderModule started"); vk::ShaderModuleCreateInfo shaderModuleInfo( vk::ShaderModuleCreateFlags(), - sizeof(uint32_t) * shaderFileData.size(), - shaderFileData.data()); + sizeof(uint32_t) * this->mSpirv.size(), + this->mSpirv.data()); KP_LOG_DEBUG("Kompute Algorithm Creating shader module. ShaderFileSize: {}", - shaderFileData.size()); + this->mSpirv.size()); this->mFreeShaderModule = true; this->mShaderModule = std::make_shared(); this->mDevice->createShaderModule( @@ -300,21 +306,42 @@ Algorithm::createPipeline() } void -Algorithm::recordDispatch(uint32_t x, uint32_t y, uint32_t z) +Algorithm::recordDispatch(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute Algorithm calling record dispatch"); - this->mCommandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, + commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, *this->mPipeline); - this->mCommandBuffer->bindDescriptorSets(vk::PipelineBindPoint::eCompute, + commandBuffer->bindDescriptorSets(vk::PipelineBindPoint::eCompute, *this->mPipelineLayout, 0, // First set *this->mDescriptorSet, nullptr // Dispatcher ); - this->mCommandBuffer->dispatch(x, y, z); + commandBuffer->dispatch(this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); +} + +void +Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { + // The dispatch size is set up based on either explicitly provided template + // parameters or by default it would take the shape and size of the tensors + if (workgroup[0] > 0) { + // If at least the x value is provided we use mainly the parameters + // provided + this->mWorkgroup = { + workgroup[0], + workgroup[1] > 0 ? workgroup[1] : 1, + workgroup[2] > 0 ? workgroup[2] : 1 + }; + } else { + this->mWorkgroup = { minSize, 1, 1 }; + } + KP_LOG_INFO("Kompute OpAlgoCreate dispatch size X: {}, Y: {}, Z: {}", + this->mWorkgroup[0], + this->mWorkgroup[1], + this->mWorkgroup[2]); } } diff --git a/src/Manager.cpp b/src/Manager.cpp index 18b2bf289..7bd629165 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -61,21 +61,30 @@ Manager::~Manager() if (this->mManagedSequences.size()) { KP_LOG_DEBUG("Kompute Manager explicitly running destructor for " "managed sequences"); - for (const std::pair>& sqPair : - this->mManagedSequences) { - sqPair.second->freeMemoryDestroyGPUResources(); + for (const std::weak_ptr& weakSq : this->mManagedSequences) { + if (std::shared_ptr sq = weakSq.lock()) { + sq->freeMemoryDestroyGPUResources(); + } } this->mManagedSequences.clear(); } + if (this->mManagedAlgorithms.size()) { + KP_LOG_DEBUG("Kompute Manager explicitly freeing algorithms"); + for (const std::weak_ptr& weakAlgorithm : this->mManagedAlgorithms) { + if (std::shared_ptr algorithm = weakAlgorithm.lock()) { + algorithm->freeMemoryDestroyGPUResources(); + } + } + this->mManagedTensors.clear(); + } + if (this->mManagedTensors.size()) { KP_LOG_DEBUG("Kompute Manager explicitly freeing tensors"); - for (const std::shared_ptr& tensor : this->mManagedTensors) { - if (!tensor->isInit()) { - KP_LOG_ERROR("Kompute Manager attempted to free managed tensor " - "but not tensor is not initialised"); + for (const std::weak_ptr& weakTensor : this->mManagedTensors) { + if (std::shared_ptr tensor = weakTensor.lock()) { + tensor->freeMemoryDestroyGPUResources(); } - tensor->freeMemoryDestroyGPUResources(); } this->mManagedTensors.clear(); } @@ -111,32 +120,21 @@ Manager::~Manager() } std::shared_ptr -Manager::sequence(std::string sequenceName, uint32_t queueIndex) +Manager::sequence(uint32_t queueIndex) { KP_LOG_DEBUG("Kompute Manager sequence() with sequenceName: {} " "and queueIndex: {}", - sequenceName, queueIndex); - std::shared_ptr sq = nullptr; + std::shared_ptr sq = + std::make_shared(this->mPhysicalDevice, + this->mDevice, + this->mComputeQueues[queueIndex], + this->mComputeQueueFamilyIndices[queueIndex]); - std::unordered_map>::iterator found = - this->mManagedSequences.find(sequenceName); + this->mManagedSequences.insert(sq); - if (found == this->mManagedSequences.end()) { - std::shared_ptr sq = - std::make_shared(this->mPhysicalDevice, - this->mDevice, - this->mComputeQueues[queueIndex], - this->mComputeQueueFamilyIndices[queueIndex]); - sq->init(); - - this->mManagedSequences.insert({ sequenceName, sq }); - - return sq; - } else { - return found->second; - } + return sq; } void @@ -334,13 +332,10 @@ Manager::tensor( Tensor::TensorTypes tensorType, bool syncDataToGPU) { - KP_LOG_DEBUG("Kompute Manager tensor triggered"); + KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - KP_LOG_DEBUG("Kompute Manager creating new tensor shared ptr"); - std::shared_ptr tensor = - std::make_shared(kp::Tensor(data, tensorType)); - - tensor->init(this->mPhysicalDevice, this->mDevice); + std::shared_ptr tensor = std::make_shared( + kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType)); if (syncDataToGPU) { this->evalOpDefault({ tensor }); @@ -349,6 +344,29 @@ Manager::tensor( return tensor; } +std::shared_ptr +Manager::algorithm( + const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants, + const Constants& pushConstants) { + + KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); + + std::shared_ptr algorithm = std::make_shared( + kp::Algorithm( + this->mDevice, + tensors, + spirv, + workgroup, + specializationConstants, + pushConstants)); + + this->mManagedAlgorithms.insert(algorithm); + + return algorithm; +} void Manager::rebuild(std::vector> tensors, diff --git a/src/OpAlgoBase.cpp b/src/OpAlgoBase.cpp deleted file mode 100644 index 71b61beb5..000000000 --- a/src/OpAlgoBase.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once - -#include "kompute/operations/OpAlgoBase.hpp" - -namespace kp { - -OpAlgoBase::OpAlgoBase() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase constructor base"); -} - -OpAlgoBase::OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const Workgroup& komputeWorkgroup, - const Constants& specializationConstants) - : OpBase(physicalDevice, device, commandBuffer, tensors) -{ - KP_LOG_DEBUG("Kompute OpAlgoBase constructor with params numTensors: {}", - tensors.size()); - - // The dispatch size is set up based on either explicitly provided template - // parameters or by default it would take the shape and size of the tensors - if (komputeWorkgroup[0] > 0) { - // If at least the x value is provided we use mainly the parameters - // provided - this->mKomputeWorkgroup = { - komputeWorkgroup[0], - komputeWorkgroup[1] > 0 ? komputeWorkgroup[1] : 1, - komputeWorkgroup[2] > 0 ? komputeWorkgroup[2] : 1 - }; - } else { - this->mKomputeWorkgroup = { tensors[0]->size(), 1, 1 }; - } - KP_LOG_INFO("Kompute OpAlgoBase dispatch size X: {}, Y: {}, Z: {}", - this->mKomputeWorkgroup[0], - this->mKomputeWorkgroup[1], - this->mKomputeWorkgroup[2]); - - this->mAlgorithm = std::make_shared(device, commandBuffer, specializationConstants); -} - -OpAlgoBase::OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - std::string shaderFilePath, - const Workgroup& komputeWorkgroup, - const Constants& specializationConstants) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, komputeWorkgroup, specializationConstants) -{ - KP_LOG_DEBUG( - "Kompute OpAlgoBase shaderFilePath constructo with shaderfile path: {}", - shaderFilePath); - - this->mShaderFilePath = shaderFilePath; -} - -OpAlgoBase::OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const std::vector& shaderDataRaw, - const Workgroup& komputeWorkgroup, - const Constants& specializationConstants) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, komputeWorkgroup, specializationConstants) -{ - KP_LOG_DEBUG("Kompute OpAlgoBase shaderFilePath constructo with shader raw " - "data length: {}", - shaderDataRaw.size()); - - this->mShaderDataRaw = shaderDataRaw; -} - -OpAlgoBase::~OpAlgoBase() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase destructor started"); -} - -void -OpAlgoBase::init() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase init called"); - - if (this->mTensors.size() < 1) { - throw std::runtime_error( - "Kompute OpAlgoBase called with less than 1 tensor"); - } - - for (std::shared_ptr tensor : this->mTensors) { - if (!tensor->isInit()) { - throw std::runtime_error( - "Kompute OpAlgoBase validation failed; all tensor parameters " - "must be initialised."); - } - } - - KP_LOG_DEBUG("Kompute OpAlgoBase fetching spirv data"); - - std::vector shaderFileData = this->fetchSpirvBinaryData(); - - KP_LOG_DEBUG("Kompute OpAlgoBase Initialising algorithm component"); - - this->mAlgorithm->init(shaderFileData, this->mTensors); -} - -void -OpAlgoBase::record() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase record called"); - - // Barrier to ensure the data is finished writing to buffer memory - for (std::shared_ptr tensor : this->mTensors) { - tensor->recordBufferMemoryBarrier( - this->mCommandBuffer, - vk::AccessFlagBits::eHostWrite, - vk::AccessFlagBits::eShaderRead, - vk::PipelineStageFlagBits::eHost, - vk::PipelineStageFlagBits::eComputeShader); - } - - this->mAlgorithm->recordDispatch(this->mKomputeWorkgroup[0], - this->mKomputeWorkgroup[1], - this->mKomputeWorkgroup[2]); -} - -void -OpAlgoBase::preEval() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase preEval called"); -} - -void -OpAlgoBase::postEval() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase postSubmit called"); -} - -std::vector -OpAlgoBase::fetchSpirvBinaryData() -{ - KP_LOG_DEBUG("Kompute OpAlgoBase Running fetchSpirvBinaryData"); - - if (this->mShaderFilePath.size()) { - KP_LOG_DEBUG("Kompute OpAlgoBase Reading data from file path"); - - std::ifstream fileStream(this->mShaderFilePath, - std::ios::binary | std::ios::in | - std::ios::ate); - - if (!fileStream.good()) { - throw std::runtime_error("Error reading file: " + - this->mShaderFilePath); - } - - size_t shaderFileSize = fileStream.tellg(); - fileStream.seekg(0, std::ios::beg); - char* shaderDataRaw = new char[shaderFileSize]; - fileStream.read(shaderDataRaw, shaderFileSize); - fileStream.close(); - - KP_LOG_WARN("Kompute OpAlgoBase fetched {} bytes", shaderFileSize); - - return std::vector((uint32_t*)shaderDataRaw, (uint32_t*)(shaderDataRaw + shaderFileSize)); - } else if (this->mShaderDataRaw.size()) { - KP_LOG_DEBUG("Kompute OpAlgoBase Reading data from data provided"); - return this->mShaderDataRaw; - } else { - throw std::runtime_error( - "Kompute OpAlgoBase Error reached fetchSpirvBinaryData but neither " - "filepath nor data provided"); - } -} - -} diff --git a/src/OpAlgoCreate.cpp b/src/OpAlgoCreate.cpp new file mode 100644 index 000000000..008cf9bbe --- /dev/null +++ b/src/OpAlgoCreate.cpp @@ -0,0 +1,51 @@ +#pragma once + +#include "kompute/operations/OpAlgoCreate.hpp" + +namespace kp { + +OpAlgoCreate::OpAlgoCreate(std::vector> tensors, + std::shared_ptr algorithm) + : OpBase(tensors, algorithm) +{ + KP_LOG_DEBUG("Kompute OpAlgoCreate constructor"); + + this->mManagesAlgorithm = true; + this->mManagesTensors = false; +} + +OpAlgoCreate::~OpAlgoCreate() +{ + KP_LOG_DEBUG("Kompute OpAlgoCreate destructor started"); +} + +void +OpAlgoCreate::init( + std::shared_ptr physicalDevice, + std::shared_ptr device) { + + KP_LOG_DEBUG("Kompute OpAlgoCreate init started"); + + // Explicitly calling top level function to create algo + OpBase::init(physicalDevice, device); +} + +void +OpAlgoCreate::record(std::shared_ptr commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpAlgoCreate record called"); +} + +void +OpAlgoCreate::preEval() +{ + KP_LOG_DEBUG("Kompute OpAlgoCreate preEval called"); +} + +void +OpAlgoCreate::postEval() +{ + KP_LOG_DEBUG("Kompute OpAlgoCreate postSubmit called"); +} + +} diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp new file mode 100644 index 000000000..25d2ba519 --- /dev/null +++ b/src/OpAlgoDispatch.cpp @@ -0,0 +1,59 @@ +#pragma once + +#include "kompute/operations/OpAlgoDispatch.hpp" + +namespace kp { + +OpAlgoDispatch::OpAlgoDispatch(std::vector> tensors, + std::shared_ptr algorithm) + : OpBase(tensors, algorithm) +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); + + this->mManagesAlgorithm = false; + this->mManagesTensors = false; +} + +OpAlgoDispatch::~OpAlgoDispatch() +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch destructor started"); +} + +void +OpAlgoDispatch::init(std::shared_ptr physicalDevice, + std::shared_ptr device) +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch init called"); +} + +void +OpAlgoDispatch::record(std::shared_ptr commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch record called"); + + // Barrier to ensure the data is finished writing to buffer memory + for (std::shared_ptr tensor : this->mTensors) { + tensor->recordBufferMemoryBarrier( + commandBuffer, + vk::AccessFlagBits::eHostWrite, + vk::AccessFlagBits::eShaderRead, + vk::PipelineStageFlagBits::eHost, + vk::PipelineStageFlagBits::eComputeShader); + } + + this->mAlgorithm->recordDispatch(commandBuffer); +} + +void +OpAlgoDispatch::preEval() +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch preEval called"); +} + +void +OpAlgoDispatch::postEval() +{ + KP_LOG_DEBUG("Kompute OpAlgoDispatch postSubmit called"); +} + +} diff --git a/src/OpAlgoLhsRhsOut.cpp b/src/OpAlgoLhsRhsOut.cpp index c93e5c55b..89eb15c60 100644 --- a/src/OpAlgoLhsRhsOut.cpp +++ b/src/OpAlgoLhsRhsOut.cpp @@ -10,15 +10,12 @@ OpAlgoLhsRhsOut::OpAlgoLhsRhsOut() } OpAlgoLhsRhsOut::OpAlgoLhsRhsOut( - std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors, - const Workgroup& komputeWorkgroup) + std::vector>& tensors, + std::shared_ptr algorithm) // The inheritance is initialised with the copyOutputData to false given that // this depencendant class handles the transfer of data via staging buffers in // a granular way. - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, komputeWorkgroup) + : OpAlgoCreate(tensors, algorithm) { KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut constructor with params"); } @@ -29,7 +26,8 @@ OpAlgoLhsRhsOut::~OpAlgoLhsRhsOut() } void -OpAlgoLhsRhsOut::init() +OpAlgoLhsRhsOut::init(std::shared_ptr physicalDevice, + std::shared_ptr device) { KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut init called"); @@ -70,12 +68,10 @@ OpAlgoLhsRhsOut::init() std::vector shaderFileData = this->fetchSpirvBinaryData(); KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut Initialising algorithm component"); - - this->mAlgorithm->init(shaderFileData, this->mTensors); } void -OpAlgoLhsRhsOut::record() +OpAlgoLhsRhsOut::record(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut record called"); diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 98450dc82..8f88eeb65 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -3,18 +3,13 @@ namespace kp { -OpTensorCopy::OpTensorCopy() -{ - KP_LOG_DEBUG("Kompute OpTensorCopy constructor base"); -} - -OpTensorCopy::OpTensorCopy(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors) - : OpBase(physicalDevice, device, commandBuffer, tensors) +OpTensorCopy::OpTensorCopy(std::vector> tensors) + : OpBase(tensors, nullptr) { KP_LOG_DEBUG("Kompute OpTensorCopy constructor with params"); + + this->mManagesTensors = false; + this->mManagesAlgorithm = false; } OpTensorCopy::~OpTensorCopy() @@ -23,7 +18,8 @@ OpTensorCopy::~OpTensorCopy() } void -OpTensorCopy::init() +OpTensorCopy::init(std::shared_ptr physicalDevice, + std::shared_ptr device) { KP_LOG_DEBUG("Kompute OpTensorCopy init called"); @@ -46,14 +42,14 @@ OpTensorCopy::init() } void -OpTensorCopy::record() +OpTensorCopy::record(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy record called"); // We iterate from the second tensor onwards and record a copy to all for (size_t i = 1; i < this->mTensors.size(); i++) { this->mTensors[i]->recordCopyFrom( - this->mCommandBuffer, this->mTensors[0], false); + commandBuffer, this->mTensors[0], false); } } diff --git a/src/OpTensorCreate.cpp b/src/OpTensorCreate.cpp new file mode 100644 index 000000000..a343f1510 --- /dev/null +++ b/src/OpTensorCreate.cpp @@ -0,0 +1,46 @@ + +#include "kompute/operations/OpTensorCreate.hpp" + +namespace kp { + +OpTensorCreate::OpTensorCreate( + std::vector>& tensors) + : OpBase(tensors, nullptr) +{ + KP_LOG_DEBUG("Compute OpTensorCreate constructor with params"); + this->mManagesTensors = true; +} + +OpTensorCreate::~OpTensorCreate() +{ + KP_LOG_DEBUG("Kompute OpTensorCreate destructor started"); +} + +void +OpTensorCreate::init(std::shared_ptr physicalDevice, + std::shared_ptr device) +{ + KP_LOG_DEBUG("Kompute OpTensorCreate init called"); + + OpBase::init(physicalDevice, device); +} + +void +OpTensorCreate::record(std::shared_ptr commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpTensorCreate record called"); +} + +void +OpTensorCreate::preEval() +{ + KP_LOG_DEBUG("Kompute OpTensorCreate preEval called"); +} + +void +OpTensorCreate::postEval() +{ + KP_LOG_DEBUG("Kompute OpTensorCreate postEval called"); +} + +} diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index fdd153898..872f82365 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -1,21 +1,11 @@ -#include "kompute/Tensor.hpp" - #include "kompute/operations/OpTensorSyncDevice.hpp" namespace kp { -OpTensorSyncDevice::OpTensorSyncDevice() -{ - KP_LOG_DEBUG("Kompute OpTensorSyncDevice constructor base"); -} - OpTensorSyncDevice::OpTensorSyncDevice( - std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, std::vector> tensors) - : OpBase(physicalDevice, device, commandBuffer, tensors) + : OpBase(tensors, nullptr) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice constructor with params"); } @@ -26,7 +16,8 @@ OpTensorSyncDevice::~OpTensorSyncDevice() } void -OpTensorSyncDevice::init() +OpTensorSyncDevice::init(std::shared_ptr physicalDevice, + std::shared_ptr device) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice init called"); @@ -50,14 +41,14 @@ OpTensorSyncDevice::init() } void -OpTensorSyncDevice::record() +OpTensorSyncDevice::record(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice record called"); for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { this->mTensors[i]->recordCopyFromStagingToDevice( - this->mCommandBuffer, false); + commandBuffer, false); } } } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index ccf8e70ce..fd98b092d 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -5,19 +5,14 @@ namespace kp { -OpTensorSyncLocal::OpTensorSyncLocal() -{ - KP_LOG_DEBUG("Kompute OpTensorSyncLocal constructor base"); -} - OpTensorSyncLocal::OpTensorSyncLocal( - std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, std::vector> tensors) - : OpBase(physicalDevice, device, commandBuffer, tensors) + : OpBase(tensors, nullptr) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal constructor with params"); + + this->mManagesTensors = false; + this->mManagesAlgorithm = false; } OpTensorSyncLocal::~OpTensorSyncLocal() @@ -26,7 +21,8 @@ OpTensorSyncLocal::~OpTensorSyncLocal() } void -OpTensorSyncLocal::init() +OpTensorSyncLocal::init(std::shared_ptr physicalDevice, + std::shared_ptr device) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal init called"); @@ -40,24 +36,18 @@ OpTensorSyncLocal::init() throw std::runtime_error( "Kompute OpTensorSyncLocal: Tensor has not been initialized"); } - if (tensor->tensorType() == Tensor::TensorTypes::eStorage) { - KP_LOG_WARN( - "Kompute OpTensorSyncLocal tensor parameter is of type " - "TensorTypes::eStorage and hence cannot be used to receive or " - "pass data."); - } } } void -OpTensorSyncLocal::record() +OpTensorSyncLocal::record(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal record called"); for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { this->mTensors[i]->recordCopyFromDeviceToStaging( - this->mCommandBuffer, true); + commandBuffer, true); } } } diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 19fdf11e8..4f6596efb 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -3,12 +3,6 @@ namespace kp { -Sequence::Sequence() -{ - KP_LOG_DEBUG("Kompute Sequence base constructor"); - this->mIsInit = false; -} - Sequence::Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, @@ -20,28 +14,16 @@ Sequence::Sequence(std::shared_ptr physicalDevice, this->mDevice = device; this->mComputeQueue = computeQueue; this->mQueueIndex = queueIndex; - this->mIsInit = false; + + this->createCommandPool(); + this->createCommandBuffer(); } Sequence::~Sequence() { KP_LOG_DEBUG("Kompute Sequence Destructor started"); - if (!this->mIsInit) { - KP_LOG_INFO("Kompute Sequence destructor called but sequence is not " - "initialized so no need to removing GPU resources."); - return; - } else { - this->freeMemoryDestroyGPUResources(); - } -} - -void -Sequence::init() -{ - this->createCommandPool(); - this->createCommandBuffer(); - this->mIsInit = true; + this->freeMemoryDestroyGPUResources(); } bool @@ -194,28 +176,14 @@ Sequence::isRecording() return this->mRecording; } -bool -Sequence::isInit() -{ - return this->mIsInit; -} - void Sequence::freeMemoryDestroyGPUResources() { KP_LOG_DEBUG("Kompute Sequence freeMemoryDestroyGPUResources called"); - if (!this->mIsInit) { - KP_LOG_ERROR("Kompute Sequence freeMemoryDestroyGPUResources called " - "but Sequence is not initialized so there's no relevant " - "GPU resources."); - return; - } - if (!this->mDevice) { KP_LOG_ERROR("Kompute Sequence freeMemoryDestroyGPUResources called " "with null Device pointer"); - this->mIsInit = false; return; } @@ -225,7 +193,6 @@ Sequence::freeMemoryDestroyGPUResources() KP_LOG_ERROR( "Kompute Sequence freeMemoryDestroyGPUResources called with null " "CommandPool pointer"); - this->mIsInit = false; return; } this->mDevice->freeCommandBuffers( @@ -239,7 +206,6 @@ Sequence::freeMemoryDestroyGPUResources() KP_LOG_ERROR( "Kompute Sequence freeMemoryDestroyGPUResources called with null " "CommandPool pointer"); - this->mIsInit = false; return; } this->mDevice->destroy( @@ -253,7 +219,6 @@ Sequence::freeMemoryDestroyGPUResources() this->mOperations.clear(); } - this->mIsInit = false; } void diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 16b7f9e80..1ae4662f8 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -3,23 +3,19 @@ namespace kp { -Tensor::Tensor() +Tensor::Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType) { - KP_LOG_DEBUG("Kompute Tensor base constructor"); - this->mTensorType = TensorTypes::eDevice; -} - -Tensor::Tensor(const std::vector& data, TensorTypes tensorType) -{ -#if DEBUG KP_LOG_DEBUG("Kompute Tensor constructor data length: {}, and type: {}", data.size(), tensorType); -#endif - this->mData = data; - this->mShape = { static_cast(data.size()) }; - this->mTensorType = tensorType; + this->mPhysicalDevice = physicalDevice; + this->mDevice = device; + + this->rebuild(data, tensorType); } Tensor::~Tensor() @@ -27,25 +23,25 @@ Tensor::~Tensor() KP_LOG_DEBUG("Kompute Tensor destructor started. Type: {}", this->tensorType()); - if (this->isInit()) { - this->freeMemoryDestroyGPUResources(); - } + this->freeMemoryDestroyGPUResources(); KP_LOG_DEBUG("Kompute Tensor destructor success"); } void -Tensor::init(std::shared_ptr physicalDevice, - std::shared_ptr device) +Tensor::rebuild(const std::vector& data, + TensorTypes tensorType) { - KP_LOG_DEBUG("Kompute Tensor running init with Vulkan params and num data " - "elementS: {}", - this->mData.size()); + KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", + data.size()); - this->mPhysicalDevice = physicalDevice; - this->mDevice = device; + this->mData = data; + this->mTensorType = tensorType; - this->mIsInit = true; + if (this->mPrimaryBuffer || this->mPrimaryMemory) { + KP_LOG_DEBUG("Kompute Tensor destroying existing resources before rebuild"); + this->freeMemoryDestroyGPUResources(); + } this->allocateMemoryCreateGPUResources(); } @@ -71,13 +67,7 @@ Tensor::memorySize() uint32_t Tensor::size() { - return this->mShape[0]; -} - -std::array -Tensor::shape() -{ - return this->mShape; + return static_cast(this->mData.size()); } Tensor::TensorTypes @@ -86,12 +76,6 @@ Tensor::tensorType() return this->mTensorType; } -bool -Tensor::isInit() -{ - return this->mIsInit && this->mPrimaryBuffer && this->mPrimaryMemory; -} - void Tensor::setData(const std::vector& data) { @@ -166,11 +150,6 @@ Tensor::copyBuffer(std::shared_ptr commandBuffer, bool createBarrier) { - if (!this->mIsInit) { - throw std::runtime_error( - "Kompute Tensor attempted to run copyBuffer without init"); - } - commandBuffer->copyBuffer(*bufferFrom, *bufferTo, copyRegion); if (createBarrier) { @@ -344,11 +323,6 @@ Tensor::allocateMemoryCreateGPUResources() { KP_LOG_DEBUG("Kompute Tensor creating buffer"); - if (!this->mIsInit) { - throw std::runtime_error( - "Kompute Tensor attempted to run createBuffer without init"); - } - if (!this->mPhysicalDevice) { throw std::runtime_error("Kompute Tensor phyisical device is null"); } @@ -457,9 +431,7 @@ Tensor::allocateBindMemory(std::shared_ptr buffer, void Tensor::freeMemoryDestroyGPUResources() { - KP_LOG_DEBUG("Kompute Tensor started freeMemoryDestroyGPUResources"); - - this->mIsInit = false; + KP_LOG_DEBUG("Kompute Tensor started freeMemoryDestroyGPUResources()"); if (!this->mDevice) { KP_LOG_ERROR( @@ -519,7 +491,7 @@ Tensor::freeMemoryDestroyGPUResources() } } - KP_LOG_DEBUG("Kompute Tensor successful freeMemoryDestroyGPUResources"); + KP_LOG_DEBUG("Kompute Tensor successful freeMemoryDestroyGPUResources()"); } } diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index a859b79d3..4016c5efb 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -13,11 +13,6 @@ namespace kp { class Algorithm { public: - /** - Base constructor for Algorithm. Should not be used unless explicit - intended. - */ - Algorithm(); /** * Default constructor for Algorithm @@ -26,9 +21,13 @@ public: * @param commandBuffer The vulkan command buffer to bind the pipeline and * shaders */ - Algorithm(std::shared_ptr device, - std::shared_ptr commandBuffer, - const Constants& specializationConstants = {}); + Algorithm( + std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -39,8 +38,16 @@ public: * @specalizationInstalces The specialization parameters to pass to the function * processing */ - void init(const std::vector& shaderFileData, - std::vector> tensorParams); + void rebuild( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + bool isInit(); + + void freeMemoryDestroyGPUResources(); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -56,12 +63,13 @@ public: * @param y Layout Y dispatch value * @param z Layout Z dispatch value */ - void recordDispatch(uint32_t x = 1, uint32_t y = 1, uint32_t z = 1); + void recordDispatch(std::shared_ptr commandBuffer); + + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mDevice; - std::shared_ptr mCommandBuffer; // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mDescriptorSetLayout; @@ -80,15 +88,19 @@ private: bool mFreePipeline = false; // -------------- ALWAYS OWNED RESOURCES + std::vector mSpirv; Constants mSpecializationConstants; + Constants mPushConstants; + Workgroup mWorkgroup; + + bool mIsInit; // Create util functions - void createShaderModule(const std::vector& shaderFileData); + void createShaderModule(); void createPipeline(); // Parameters - void createParameters(std::vector>& tensorParams); - void createDescriptorPool(); + void createParameters(const std::vector>& tensorParams); }; } // End namespace kp diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 1ca302b3c..3615d74c0 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -67,9 +67,7 @@ class Manager * @param queueIndex The queue to use from the available queues * @return Shared pointer to the manager owned sequence resource */ - std::shared_ptr sequence( - std::string sequenceName = KP_DEFAULT_SESSION, - uint32_t queueIndex = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0); /** * Function that evaluates operation against named sequence. @@ -228,6 +226,13 @@ class Manager Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, bool syncDataToGPU = true); + std::shared_ptr algorithm( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + /** * Function that simplifies the common workflow of tensor initialisation. It * will take the constructor parameters for a Tensor and will will us it to @@ -312,10 +317,10 @@ class Manager bool mFreeDevice = false; // -------------- ALWAYS OWNED RESOURCES - std::set> mManagedTensors; - - std::unordered_map> - mManagedSequences; + std::set> mManagedTensors; + std::set> mManagedSequences; + std::set> mManagedAlgorithms; + //std::unique_ptr mDefaultSequence; std::vector mComputeQueueFamilyIndices; std::vector> mComputeQueues; diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index d96910894..eeecd0a04 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -12,11 +12,6 @@ namespace kp { class Sequence { public: - /** - * Base constructor for Sequence. Should not be used unless explicit - * intended. - */ - Sequence(); /** * Main constructor for sequence which requires core vulkan components to * generate all dependent resources. @@ -36,12 +31,6 @@ class Sequence */ ~Sequence(); - /** - * Initialises sequence including the creation of the command pool and the - * command buffer. - */ - void init(); - /** * Begins recording commands for commands to be submitted into the command * buffer. @@ -99,13 +88,6 @@ class Sequence */ bool isRunning(); - /** - * Returns true if the sequence has been successfully initialised. - * - * @return Boolean stating if sequence has been initialised. - */ - bool isInit(); - /** * Destroys and frees the GPU resources which include the buffer and memory * and sets the sequence as init=False. @@ -179,7 +161,6 @@ class Sequence std::vector> mOperations; // State - bool mIsInit = false; bool mRecording = false; bool mIsRunning = false; diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 5d9fb07df..40adcc700 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -2,8 +2,6 @@ #include "kompute/Core.hpp" -#define KP_MAX_DIM_SIZE 1 - namespace kp { /** @@ -30,11 +28,6 @@ class Tensor eStorage = 2, ///< Type is Device memory (only) }; - /** - * Base constructor, should not be used unless explicitly intended. - */ - Tensor(); - /** * Default constructor with data provided which would be used to create the * respective vulkan buffer and memory. @@ -43,8 +36,10 @@ class Tensor * tensor * @param tensorType Type for the tensor which is of type TensorTypes */ - Tensor(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); + Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); /** * Destructor which is in charge of freeing vulkan resources unless they @@ -58,8 +53,8 @@ class Tensor * would only be created for the tensors of type TensorType::eDevice as * otherwise there is no need to copy from host memory. */ - void init(std::shared_ptr physicalDevice, - std::shared_ptr device); + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -91,26 +86,13 @@ class Tensor * @return Unsigned integer representing the total number of elements */ uint32_t size(); - /** - * Returns the shape of the tensor, which includes the number of dimensions - * and the size per dimension. - * - * @return Array containing the sizes for each dimension. Zero means - * respective dimension is not active. - */ - std::array shape(); + /** * Retrieve the tensor type of the Tensor * * @return Tensor type of tensor */ TensorTypes tensorType(); - /** - * Returns true if the tensor initialisation function has been carried out - * successful, which would mean that the buffer and memory will have been - * provisioned. - */ - bool isInit(); /** * Sets / resets the vector data of the tensor. This function does not @@ -214,9 +196,6 @@ class Tensor TensorTypes mTensorType = TensorTypes::eDevice; - std::array mShape; - bool mIsInit = false; - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, vk::BufferUsageFlags bufferUsageFlags); diff --git a/src/include/kompute/operations/OpAlgoCreate.hpp b/src/include/kompute/operations/OpAlgoCreate.hpp new file mode 100644 index 000000000..3f5c859a2 --- /dev/null +++ b/src/include/kompute/operations/OpAlgoCreate.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include + +#include "kompute/Core.hpp" + +#include "kompute/shaders/shaderopmult.hpp" + +#include "kompute/Algorithm.hpp" +#include "kompute/Tensor.hpp" + +#include "kompute/operations/OpBase.hpp" + +namespace kp { + +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * By default it enables the user to provide a dynamic number of tensors + * which are then passed as inputs. + */ +class OpAlgoCreate : public OpBase +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) + * @param komputeWorkgroup Optional parameter to specify the layout for processing + */ + OpAlgoCreate(std::vector> tensors, + std::shared_ptr algorithm); + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpAlgoCreate() override; + + + virtual void init( + std::shared_ptr physicalDevice, + std::shared_ptr device) override; + + /** + * This records the commands that are to be sent to the GPU. This includes + * the barriers that ensure the memory has been copied before going in and + * out of the shader, as well as the dispatch operation that sends the + * shader processing to the gpu. This function also records the GPU memory + * copy of the output data for the staging buffer so it can be read by the + * host. + */ + virtual void record(std::shared_ptr commandBuffer) override; + + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Executes after the recorded commands are submitted, and performs a copy + * of the GPU Device memory into the staging buffer so the output data can + * be retrieved. + */ + virtual void postEval() override; +}; + +} // End namespace kp + diff --git a/src/include/kompute/operations/OpAlgoBase.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp similarity index 64% rename from src/include/kompute/operations/OpAlgoBase.hpp rename to src/include/kompute/operations/OpAlgoDispatch.hpp index 49d0569b7..7763aa9b9 100644 --- a/src/include/kompute/operations/OpAlgoBase.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -1,14 +1,8 @@ #pragma once -#include - #include "kompute/Core.hpp" - -#include "kompute/shaders/shaderopmult.hpp" - #include "kompute/Algorithm.hpp" #include "kompute/Tensor.hpp" - #include "kompute/operations/OpBase.hpp" namespace kp { @@ -19,15 +13,10 @@ namespace kp { * By default it enables the user to provide a dynamic number of tensors * which are then passed as inputs. */ -class OpAlgoBase : public OpBase +class OpAlgoDispatch : public OpBase { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpAlgoBase(); - /** * Default constructor with parameters that provides the bare minimum * requirements for the operations to be able to create and manage their @@ -40,12 +29,8 @@ class OpAlgoBase : public OpBase * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); + OpAlgoDispatch(std::vector> tensors, + std::shared_ptr algorithm); /** * Constructor that enables a file to be passed to the operation with @@ -59,13 +44,9 @@ class OpAlgoBase : public OpBase * @param shaderFilePath Parameter to specify the shader to load (either in spirv or raw format) * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - std::string shaderFilePath, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); + OpAlgoDispatch(std::vector>& tensors, + std::shared_ptr& algorithm, + std::string shaderFilePath); /** * Constructor that enables raw shader data to be passed to the main operation @@ -78,19 +59,15 @@ class OpAlgoBase : public OpBase * @param shaderDataRaw Optional parameter to specify the shader data either in binary or raw form * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const std::vector& shaderDataRaw, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); + OpAlgoDispatch(std::vector>& tensors, + std::shared_ptr& algorithm, + const std::vector& shaderDataRaw); /** * Default destructor, which is in charge of destroying the algorithm * components but does not destroy the underlying tensors */ - virtual ~OpAlgoBase() override; + virtual ~OpAlgoDispatch() override; /** * The init function is responsible for the initialisation of the algorithm @@ -98,7 +75,8 @@ class OpAlgoBase : public OpBase * on the options provided. Further dependent classes can perform more * specific checks such as ensuring tensors provided are initialised, etc. */ - virtual void init() override; + virtual void init(std::shared_ptr physicalDevice, + std::shared_ptr device) override; /** * This records the commands that are to be sent to the GPU. This includes @@ -108,7 +86,7 @@ class OpAlgoBase : public OpBase * copy of the output data for the staging buffer so it can be read by the * host. */ - virtual void record() override; + virtual void record(std::shared_ptr commandBuffer) override; /** @@ -123,21 +101,6 @@ class OpAlgoBase : public OpBase */ virtual void postEval() override; - protected: - // -------------- NEVER OWNED RESOURCES - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mAlgorithm; - bool mFreeAlgorithm = false; - - // -------------- ALWAYS OWNED RESOURCES - - Workgroup mKomputeWorkgroup; - - std::string mShaderFilePath; ///< Optional member variable which can be provided for the OpAlgoBase to find the data automatically and load for processing - std::vector mShaderDataRaw; ///< Optional member variable which can be provided to contain either the raw shader content or the spirv binary content - - virtual std::vector fetchSpirvBinaryData(); }; } // End namespace kp diff --git a/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp b/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp index c1223e738..65cdf14a1 100644 --- a/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp +++ b/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp @@ -7,7 +7,7 @@ #include "kompute/Algorithm.hpp" #include "kompute/Tensor.hpp" -#include "kompute/operations/OpAlgoBase.hpp" +#include "kompute/operations/OpAlgoCreate.hpp" namespace kp { @@ -16,13 +16,9 @@ namespace kp { * right hand and left hand side datapoints together with a single output. * The expected data passed is two input tensors and one output tensor. */ -class OpAlgoLhsRhsOut : public OpAlgoBase +class OpAlgoLhsRhsOut : public OpAlgoCreate { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpAlgoLhsRhsOut(); /** * Default constructor with parameters that provides the bare minimum @@ -36,11 +32,8 @@ class OpAlgoLhsRhsOut : public OpAlgoBase * @param freeTensors Whether operation manages the memory of the Tensors * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpAlgoLhsRhsOut(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors, - const Workgroup& komputeWorkgroup = {}); + OpAlgoLhsRhsOut(std::vector>& tensors, + std::shared_ptr algorithm); /** * Default destructor, which is in charge of destroying the algorithm @@ -54,7 +47,8 @@ class OpAlgoLhsRhsOut : public OpAlgoBase * tensors, and creates the algorithm component which processes the * computation. */ - virtual void init() override; + virtual void init(std::shared_ptr physicalDevice, + std::shared_ptr device) override; /** * This records the commands that are to be sent to the GPU. This includes @@ -64,7 +58,7 @@ class OpAlgoLhsRhsOut : public OpAlgoBase * copy of the output data for the staging buffer so it can be read by the * host. */ - virtual void record() override; + virtual void record(std::shared_ptr commandBuffer) override; /** * Executes after the recorded commands are submitted, and performs a copy diff --git a/src/include/kompute/operations/OpBase.hpp b/src/include/kompute/operations/OpBase.hpp index bc9ee59a6..f54d01390 100644 --- a/src/include/kompute/operations/OpBase.hpp +++ b/src/include/kompute/operations/OpBase.hpp @@ -3,6 +3,7 @@ #include "kompute/Core.hpp" #include "kompute/Tensor.hpp" +#include "kompute/Algorithm.hpp" namespace kp { @@ -17,10 +18,6 @@ namespace kp { class OpBase { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpBase() { KP_LOG_DEBUG("Compute OpBase base constructor"); } /** * Default constructor with parameters that provides the bare minimum @@ -32,17 +29,13 @@ class OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that are to be used in this operation */ - OpBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors) + OpBase(std::vector>& tensors, + std::shared_ptr algorithm) { KP_LOG_DEBUG("Compute OpBase constructor with params"); - - this->mPhysicalDevice = physicalDevice; - this->mDevice = device; - this->mCommandBuffer = commandBuffer; this->mTensors = tensors; + this->mAlgorithm = algorithm; + this->mIsInit = false; } /** @@ -53,37 +46,89 @@ class OpBase virtual ~OpBase() { KP_LOG_DEBUG("Kompute OpBase destructor started"); + this->destroy(); + } - if (!this->mDevice) { - KP_LOG_WARN("Kompute OpBase destructor called with empty device"); - return; - } + virtual std::shared_ptr algorithm() { + return this->mAlgorithm; + } - if (this->mFreeTensors) { - KP_LOG_DEBUG("Kompute OpBase freeing tensors"); - for (std::shared_ptr tensor : this->mTensors) { - if (tensor && tensor->isInit()) { - tensor->freeMemoryDestroyGPUResources(); - } else { - KP_LOG_WARN("Kompute OpBase expected to free " - "tensor but has already been freed."); - } - } - } + virtual std::vector> tensors() { + return this->mTensors; + } + + virtual bool isInit() { + return this->mIsInit; } /** * The init function is responsible for setting up all the resources and * should be called after the Operation has been created. */ - virtual void init() = 0; + // TODO: Potentially remove physicalDevice in favour of memoryProperties (for tensor) + virtual void init( + std::shared_ptr physicalDevice, + std::shared_ptr device) { + + if (this->mTensors.size() < 1) { + throw std::runtime_error("Kompute OpBase init called with 0 tensors"); + } + + if (this->mManagesTensors) { + for (std::shared_ptr tensor : this->mTensors) { + if (tensor->isInit()) { + // TODO: Evaluate whether throwing runtime error or just writing error log + throw std::runtime_error( + "Kompute OpTensorCreate: Tensor has already been initialized"); + } + else { + tensor->init(physicalDevice, device); + } + } + } + + if (this->mManagesAlgorithm) { + this->mAlgorithm->init(device, this->mTensors); + } + } + + virtual void destroy() { + if (!this->mIsInit) { + KP_LOG_WARN("Kompute OpBase destroy called but not initialised"); + } + + if (this->mManagesTensors) { + for (const std::shared_ptr& tensor : this->mTensors) { + if (!tensor->isInit()) { + KP_LOG_WARN("Kompute OpBase attempted to free managed tensor " + "but tensor is not initialised"); + } else { + KP_LOG_DEBUG("Kompute OpBase freeing tensor"); + tensor->freeMemoryDestroyGPUResources(); + } + } + this->mTensors.clear(); + } + + if (this->mManagesAlgorithm) { + if (this->mAlgorithm && this->mAlgorithm->isInit()) { + KP_LOG_DEBUG("Kompute OpBase freeing tensor"); + this->mAlgorithm->freeMemoryDestroyGPUResources(); + } else { + KP_LOG_WARN("Kompute OpBase attempted to free managed algorithm" + "but algorithm is not initialised"); + } + } + + this->mIsInit = false; + } /** * The record function is intended to only send a record command or run * commands that are expected to record operations that are to be submitted * as a batch into the GPU. */ - virtual void record() = 0; + virtual void record(std::shared_ptr commandBuffer) = 0; /** * Pre eval is called before the Sequence has called eval and submitted the commands to @@ -106,19 +151,14 @@ class OpBase virtual void postEval() = 0; protected: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr - mPhysicalDevice; ///< Vulkan Physical Device - std::shared_ptr mDevice; ///< Vulkan Logical Device - std::shared_ptr - mCommandBuffer; ///< Vulkan Command Buffer - // -------------- OPTIONALLY OWNED RESOURCES - std::vector> - mTensors; ///< Tensors referenced by operation that can be managed - ///< optionally by operation - bool mFreeTensors = false; ///< Explicit boolean that specifies whether the - ///< tensors are freed (if they are managed) + std::vector> mTensors; + bool mManagesTensors = false; + std::shared_ptr mAlgorithm; + bool mManagesAlgorithm = false; + + // -------------- ALWAYS OWNED RESOURCES + bool mIsInit; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index a921fb2d2..485210f0a 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -11,7 +11,7 @@ #include "kompute/Algorithm.hpp" #include "kompute/Tensor.hpp" -#include "kompute/operations/OpAlgoBase.hpp" +#include "kompute/operations/OpAlgoCreate.hpp" namespace kp { @@ -19,7 +19,7 @@ namespace kp { * Operation that performs multiplication on two tensors and outpus on third * tensor. */ -class OpMult : public OpAlgoBase +class OpMult : public OpAlgoCreate { public: /** @@ -45,7 +45,7 @@ class OpMult : public OpAlgoBase std::shared_ptr commandBuffer, std::vector> tensors, const Workgroup& komputeWorkgroup = {}) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) + : OpAlgoCreate(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); diff --git a/src/include/kompute/operations/OpTensorCopy.hpp b/src/include/kompute/operations/OpTensorCopy.hpp index 7edbaeb66..d35139e8c 100644 --- a/src/include/kompute/operations/OpTensorCopy.hpp +++ b/src/include/kompute/operations/OpTensorCopy.hpp @@ -14,8 +14,6 @@ namespace kp { class OpTensorCopy : public OpBase { public: - OpTensorCopy(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. * @@ -24,10 +22,7 @@ class OpTensorCopy : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorCopy(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorCopy(std::vector> tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. @@ -37,12 +32,13 @@ class OpTensorCopy : public OpBase /** * Performs basic checks such as ensuring there are at least two tensors provided, that they are initialised and that they are not of type TensorTypes::eStorage. */ - void init() override; + void init(std::shared_ptr physicalDevice, + std::shared_ptr device) override; /** * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. diff --git a/src/include/kompute/operations/OpTensorCreate.hpp b/src/include/kompute/operations/OpTensorCreate.hpp new file mode 100644 index 000000000..b4ac80862 --- /dev/null +++ b/src/include/kompute/operations/OpTensorCreate.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "kompute/Core.hpp" + +#include "kompute/operations/OpBase.hpp" +#include "kompute/Tensor.hpp" +#include "kompute/Algorithm.hpp" + +namespace kp { + +/** + * Base Operation which provides the high level interface that Kompute + * operations implement in order to perform a set of actions in the GPU. + * + * Operations can perform actions on tensors, and optionally can also own an + * Algorithm with respective parameters. kp::Operations with kp::Algorithms + * would inherit from kp::OpBaseAlgo. + */ +class OpTensorCreate : public OpBase +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + */ + OpTensorCreate(std::vector>& tensors); + + /** + * Default destructor for OpTensorCreate class. This OpTensorCreate destructor class should + * always be called to destroy and free owned resources unless it is + * intended to destroy the resources in the parent class. + */ + virtual ~OpTensorCreate() override; + + /** + * The init function is responsible for setting up all the resources and + * should be called after the Operation has been created. + */ + virtual void init( + std::shared_ptr physicalDevice, + std::shared_ptr device) override; + + /** + * Record runs the core actions to create the tensors. For device tensors + * it records a copyCommand to move the data from the staging tensor to the + * device tensor. The mapping for staging tensors happens in the init function + * not in the record function. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Performs a copy back into the main tensor to ensure that the data + * contained is the one that is now being stored in the GPU. + */ + virtual void postEval() override; + +}; + +} // End namespace kp diff --git a/src/include/kompute/operations/OpTensorSyncDevice.hpp b/src/include/kompute/operations/OpTensorSyncDevice.hpp index b80cc1db0..35e97a475 100644 --- a/src/include/kompute/operations/OpTensorSyncDevice.hpp +++ b/src/include/kompute/operations/OpTensorSyncDevice.hpp @@ -1,9 +1,8 @@ #pragma once #include "kompute/Core.hpp" - +#include "kompute/operations/OpBase.hpp" #include "kompute/Tensor.hpp" - #include "kompute/operations/OpBase.hpp" namespace kp { @@ -14,8 +13,6 @@ namespace kp { class OpTensorSyncDevice : public OpBase { public: - OpTensorSyncDevice(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. * @@ -24,10 +21,7 @@ class OpTensorSyncDevice : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncDevice(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorSyncDevice(std::vector> tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. @@ -37,12 +31,13 @@ class OpTensorSyncDevice : public OpBase /** * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. */ - void init() override; + void init(std::shared_ptr physicalDevice, + std::shared_ptr device) override; /** * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. diff --git a/src/include/kompute/operations/OpTensorSyncLocal.hpp b/src/include/kompute/operations/OpTensorSyncLocal.hpp index dd4549b00..eebdd7084 100644 --- a/src/include/kompute/operations/OpTensorSyncLocal.hpp +++ b/src/include/kompute/operations/OpTensorSyncLocal.hpp @@ -14,8 +14,6 @@ namespace kp { class OpTensorSyncLocal : public OpBase { public: - OpTensorSyncLocal(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. * @@ -24,10 +22,7 @@ class OpTensorSyncLocal : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncLocal(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorSyncLocal(std::vector> tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. @@ -37,12 +32,13 @@ class OpTensorSyncLocal : public OpBase /** * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. */ - void init() override; + void init(std::shared_ptr physicalDevice, + std::shared_ptr device) override; /** * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index d5d48b9d4..42efdff9b 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -54,7 +54,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) auto startSync = std::chrono::high_resolution_clock::now(); for (uint32_t i = 0; i < numParallel; i++) { - mgr.evalOpDefault( + mgr.evalOpDefault( { inputsSyncB[i] }, kp::Shader::compile_source(shader)); } @@ -86,7 +86,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) auto startAsync = std::chrono::high_resolution_clock::now(); for (uint32_t i = 0; i < numParallel; i++) { - mgrAsync.evalOpAsync( + mgrAsync.evalOpAsync( { inputsAsyncB[i] }, "async" + std::to_string(i), kp::Shader::compile_source(shader)); @@ -153,10 +153,10 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) std::vector result = kp::Shader::compile_source(shader); - mgr.evalOpAsync( + mgr.evalOpAsync( { tensorA }, "asyncOne", kp::Shader::compile_source(shader)); - mgr.evalOpAsync( + mgr.evalOpAsync( { tensorB }, "asyncTwo", kp::Shader::compile_source(shader)); mgr.evalOpAwait("asyncOne"); diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 43f6ef028..940fdf722 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -27,7 +27,7 @@ TEST(TestDestroy, TestDestroyTensorSingle) sq = mgr.sequence(); sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -70,7 +70,7 @@ TEST(TestDestroy, TestDestroyTensorVector) sq = mgr.sequence(); sq->begin(); - sq->record( + sq->record( { tensorA, tensorB }, kp::Shader::compile_source(shader)); sq->end(); @@ -135,7 +135,7 @@ TEST(TestDestroy, TestDestroySequenceSingle) sq = mgr.sequence(); sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -175,14 +175,14 @@ TEST(TestDestroy, TestDestroySequenceVector) sq1 = mgr.sequence("One"); sq1->begin(); - sq1->record( + sq1->record( { tensorA }, kp::Shader::compile_source(shader)); sq1->end(); sq1->eval(); sq2 = mgr.sequence("Two"); sq2->begin(); - sq2->record( + sq2->record( { tensorA }, kp::Shader::compile_source(shader)); sq2->end(); sq2->eval(); @@ -216,11 +216,11 @@ TEST(TestDestroy, TestDestroySequenceNameSingleInsideManager) { mgr.rebuild({ tensorA }); - mgr.evalOp( + mgr.evalOp( { tensorA }, "one", kp::Shader::compile_source(shader)); - mgr.evalOp( + mgr.evalOp( { tensorA }, "two", kp::Shader::compile_source(shader)); @@ -256,7 +256,7 @@ TEST(TestDestroy, TestDestroySequenceNameSingleOutsideManager) sq1 = mgr.sequence("One"); sq1->begin(); - sq1->record( + sq1->record( { tensorA }, kp::Shader::compile_source(shader)); sq1->end(); sq1->eval(); @@ -289,11 +289,11 @@ TEST(TestDestroy, TestDestroySequenceNameVectorInsideManager) { mgr.rebuild({ tensorA }); - mgr.evalOp( + mgr.evalOp( { tensorA }, "one", kp::Shader::compile_source(shader)); - mgr.evalOp( + mgr.evalOp( { tensorA }, "two", kp::Shader::compile_source(shader)); @@ -323,11 +323,11 @@ TEST(TestDestroy, TestDestroySequenceNameVectorOutsideManager) { mgr.rebuild({ tensorA }); - mgr.evalOp( + mgr.evalOp( { tensorA }, "one", kp::Shader::compile_source(shader)); - mgr.evalOp( + mgr.evalOp( { tensorA }, "two", kp::Shader::compile_source(shader)); @@ -357,7 +357,7 @@ TEST(TestDestroy, TestDestroySequenceNameDefaultOutsideManager) { mgr.rebuild({ tensorA }); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorA }, kp::Shader::compile_source(shader)); diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index e0f0b0e62..00425ddc0 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -41,7 +41,7 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) sq->record({ wIn, bIn }); - sq->record( + sq->record( params, std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, @@ -120,7 +120,7 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) // Record op algo base sq->begin(); - sq->record( + sq->record( params, std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index d82091a84..a1503cc83 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -27,11 +27,11 @@ TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) { sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->record({ tensorA }); @@ -72,19 +72,19 @@ TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) // Then perform the computations sq->begin(); - sq->record({ tensorA }, + sq->record({ tensorA }, kp::Shader::compile_source(shader)); sq->end(); sq->eval(); sq->begin(); - sq->record({ tensorA }, + sq->record({ tensorA }, kp::Shader::compile_source(shader)); sq->end(); sq->eval(); sq->begin(); - sq->record({ tensorA }, + sq->record({ tensorA }, kp::Shader::compile_source(shader)); sq->end(); sq->eval(); @@ -121,7 +121,7 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -134,7 +134,7 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -147,7 +147,7 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -205,7 +205,7 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); @@ -263,7 +263,7 @@ TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrOpCreate) } )"); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorInA, tensorInB, tensorOut }, kp::Shader::compile_source(shader)); @@ -306,7 +306,7 @@ TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrMgrCreate) mgr.evalOpDefault( { tensorInA, tensorInB, tensorOut }); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorInA, tensorInB, tensorOut }, kp::Shader::compile_source(shader)); @@ -339,7 +339,7 @@ TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) sq = mgr.sequence(); sq->begin(); - sq->record( + sq->record( { tensorA }, kp::Shader::compile_source(shader)); sq->end(); diff --git a/test/TestOpAlgoLoopsPassingData.cpp b/test/TestOpAlgoLoopsPassingData.cpp index 77a5fd8d4..83cbb6619 100644 --- a/test/TestOpAlgoLoopsPassingData.cpp +++ b/test/TestOpAlgoLoopsPassingData.cpp @@ -51,7 +51,7 @@ TEST(TestProcessingIterations, IterateThroughMultipleSumAndCopies) sq->begin(); - sq->record( + sq->record( { tensorA, tensorB }, kp::Shader::compile_source(shader)); diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index 1fd121966..09908b722 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -5,7 +5,7 @@ #include "kompute_test/shaders/shadertest_op_custom_shader.hpp" -TEST(TestOpAlgoBase, ShaderRawDataFromConstructor) +TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) { kp::Manager mgr; @@ -28,7 +28,7 @@ TEST(TestOpAlgoBase, ShaderRawDataFromConstructor) } )"); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorA, tensorB }, kp::Shader::compile_source(shader)); mgr.evalOpDefault({ tensorA, tensorB }); @@ -37,7 +37,7 @@ TEST(TestOpAlgoBase, ShaderRawDataFromConstructor) EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); } -TEST(TestOpAlgoBase, ShaderCompiledDataFromConstructor) +TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) { kp::Manager mgr; @@ -45,7 +45,7 @@ TEST(TestOpAlgoBase, ShaderCompiledDataFromConstructor) std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; mgr.rebuild({ tensorA, tensorB }); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorA, tensorB }, std::vector( (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, @@ -59,7 +59,7 @@ TEST(TestOpAlgoBase, ShaderCompiledDataFromConstructor) EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); } -TEST(TestOpAlgoBase, ShaderCompiledDataFromFile) +TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) { kp::Manager mgr; @@ -67,7 +67,7 @@ TEST(TestOpAlgoBase, ShaderCompiledDataFromFile) std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; mgr.rebuild({ tensorA, tensorB }); - mgr.evalOpDefault( + mgr.evalOpDefault( { tensorA, tensorB }, "test/shaders/glsl/test_op_custom_shader.comp.spv"); mgr.evalOpDefault({ tensorA, tensorB }); diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index 5a3066d07..e4075314f 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -33,7 +33,7 @@ TEST(TestSpecializationConstants, TestTwoConstants) auto spec = kp::Constants({5.0, 0.3}); sq->begin(); - sq->record( + sq->record( { tensorA, tensorB }, kp::Shader::compile_source(shader), kp::Workgroup(), spec); diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 1116fcbe5..015874546 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -23,7 +23,7 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) sq = mgr.sequence(); sq->begin(); - sq->record( + sq->record( { tensorA, tensorB }, std::vector( (uint32_t*)kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv, From 635fdb02beeaa365f16b1c90bd6c68a859ed4756 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Wed, 24 Feb 2021 22:26:02 +0000 Subject: [PATCH 02/91] Added baseline functionality including new memory models --- single_include/AggregateHeaders.cpp | 2 - single_include/kompute/Kompute.hpp | 880 ++++-------------- src/Manager.cpp | 134 +-- src/OpAlgoCreate.cpp | 51 - src/OpAlgoDispatch.cpp | 14 +- src/OpAlgoLhsRhsOut.cpp | 118 --- src/OpTensorCopy.cpp | 35 +- src/OpTensorCreate.cpp | 46 - src/OpTensorSyncDevice.cpp | 35 +- src/OpTensorSyncLocal.cpp | 30 +- src/Sequence.cpp | 90 +- src/include/kompute/Manager.hpp | 222 +---- src/include/kompute/Sequence.hpp | 80 +- .../kompute/operations/OpAlgoCreate.hpp | 77 -- .../kompute/operations/OpAlgoDispatch.hpp | 61 +- .../kompute/operations/OpAlgoLhsRhsOut.hpp | 78 -- src/include/kompute/operations/OpBase.hpp | 104 --- src/include/kompute/operations/OpMult.hpp | 44 +- .../kompute/operations/OpTensorCopy.hpp | 10 +- .../kompute/operations/OpTensorCreate.hpp | 71 -- .../kompute/operations/OpTensorSyncDevice.hpp | 10 +- .../kompute/operations/OpTensorSyncLocal.hpp | 10 +- 22 files changed, 283 insertions(+), 1919 deletions(-) delete mode 100644 src/OpAlgoCreate.cpp delete mode 100644 src/OpAlgoLhsRhsOut.cpp delete mode 100644 src/OpTensorCreate.cpp delete mode 100644 src/include/kompute/operations/OpAlgoCreate.hpp delete mode 100644 src/include/kompute/operations/OpAlgoLhsRhsOut.hpp delete mode 100644 src/include/kompute/operations/OpTensorCreate.hpp diff --git a/single_include/AggregateHeaders.cpp b/single_include/AggregateHeaders.cpp index 599607c43..da1eaabf0 100644 --- a/single_include/AggregateHeaders.cpp +++ b/single_include/AggregateHeaders.cpp @@ -6,8 +6,6 @@ #include "kompute/Manager.hpp" #include "kompute/Sequence.hpp" #include "kompute/operations/OpBase.hpp" -#include "kompute/operations/OpAlgoCreate.hpp" -#include "kompute/operations/OpAlgoLhsRhsOut.hpp" #include "kompute/operations/OpMult.hpp" #include "kompute/operations/OpTensorCopy.hpp" #include "kompute/operations/OpTensorSyncDevice.hpp" diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index b366dbf09..629ff6a4b 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -871,8 +871,6 @@ static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; #include #include -#define KP_MAX_DIM_SIZE 1 - namespace kp { /** @@ -899,11 +897,6 @@ class Tensor eStorage = 2, ///< Type is Device memory (only) }; - /** - * Base constructor, should not be used unless explicitly intended. - */ - Tensor(); - /** * Default constructor with data provided which would be used to create the * respective vulkan buffer and memory. @@ -912,8 +905,10 @@ class Tensor * tensor * @param tensorType Type for the tensor which is of type TensorTypes */ - Tensor(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); + Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); /** * Destructor which is in charge of freeing vulkan resources unless they @@ -927,8 +922,8 @@ class Tensor * would only be created for the tensors of type TensorType::eDevice as * otherwise there is no need to copy from host memory. */ - void init(std::shared_ptr physicalDevice, - std::shared_ptr device); + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -960,26 +955,13 @@ class Tensor * @return Unsigned integer representing the total number of elements */ uint32_t size(); - /** - * Returns the shape of the tensor, which includes the number of dimensions - * and the size per dimension. - * - * @return Array containing the sizes for each dimension. Zero means - * respective dimension is not active. - */ - std::array shape(); + /** * Retrieve the tensor type of the Tensor * * @return Tensor type of tensor */ TensorTypes tensorType(); - /** - * Returns true if the tensor initialisation function has been carried out - * successful, which would mean that the buffer and memory will have been - * provisioned. - */ - bool isInit(); /** * Sets / resets the vector data of the tensor. This function does not @@ -1083,9 +1065,6 @@ class Tensor TensorTypes mTensorType = TensorTypes::eDevice; - std::array mShape; - bool mIsInit = false; - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, vk::BufferUsageFlags bufferUsageFlags); @@ -1111,6 +1090,107 @@ class Tensor namespace kp { +/** + Abstraction for compute shaders that are run on top of tensors grouped via + ParameterGroups (which group descriptorsets) +*/ +class Algorithm +{ +public: + + /** + * Default constructor for Algorithm + * + * @param device The Vulkan device to use for creating resources + * @param commandBuffer The vulkan command buffer to bind the pipeline and + * shaders + */ + Algorithm( + std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + /** + * Initialiser for the shader data provided to the algorithm as well as + * tensor parameters that will be used in shader. + * + * @param shaderFileData The bytes in spir-v format of the shader + * @tensorParams The Tensors to be used in the Algorithm / shader for + * @specalizationInstalces The specialization parameters to pass to the function + * processing + */ + void rebuild( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + bool isInit(); + + void freeMemoryDestroyGPUResources(); + + /** + * Destructor for Algorithm which is responsible for freeing and desroying + * respective pipelines and owned parameter groups. + */ + ~Algorithm(); + + /** + * Records the dispatch function with the provided template parameters or + * alternatively using the size of the tensor by default. + * + * @param x Layout X dispatch value + * @param y Layout Y dispatch value + * @param z Layout Z dispatch value + */ + void recordDispatch(std::shared_ptr commandBuffer); + + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + +private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mDevice; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mDescriptorSetLayout; + bool mFreeDescriptorSetLayout = false; + std::shared_ptr mDescriptorPool; + bool mFreeDescriptorPool = false; + std::shared_ptr mDescriptorSet; + bool mFreeDescriptorSet = false; + std::shared_ptr mShaderModule; + bool mFreeShaderModule = false; + std::shared_ptr mPipelineLayout; + bool mFreePipelineLayout = false; + std::shared_ptr mPipelineCache; + bool mFreePipelineCache = false; + std::shared_ptr mPipeline; + bool mFreePipeline = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mSpirv; + Constants mSpecializationConstants; + Constants mPushConstants; + Workgroup mWorkgroup; + + bool mIsInit; + + // Create util functions + void createShaderModule(); + void createPipeline(); + + // Parameters + void createParameters(const std::vector>& tensorParams); +}; + +} // End namespace kp + +namespace kp { + /** * Base Operation which provides the high level interface that Kompute * operations implement in order to perform a set of actions in the GPU. @@ -1122,33 +1202,6 @@ namespace kp { class OpBase { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpBase() { KP_LOG_DEBUG("Compute OpBase base constructor"); } - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - */ - OpBase(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors) - { - KP_LOG_DEBUG("Compute OpBase constructor with params"); - - this->mPhysicalDevice = physicalDevice; - this->mDevice = device; - this->mCommandBuffer = commandBuffer; - this->mTensors = tensors; - } /** * Default destructor for OpBase class. This OpBase destructor class should @@ -1158,37 +1211,14 @@ class OpBase virtual ~OpBase() { KP_LOG_DEBUG("Kompute OpBase destructor started"); - - if (!this->mDevice) { - KP_LOG_WARN("Kompute OpBase destructor called with empty device"); - return; - } - - if (this->mFreeTensors) { - KP_LOG_DEBUG("Kompute OpBase freeing tensors"); - for (std::shared_ptr tensor : this->mTensors) { - if (tensor && tensor->isInit()) { - tensor->freeMemoryDestroyGPUResources(); - } else { - KP_LOG_WARN("Kompute OpBase expected to free " - "tensor but has already been freed."); - } - } - } } - /** - * The init function is responsible for setting up all the resources and - * should be called after the Operation has been created. - */ - virtual void init() = 0; - /** * The record function is intended to only send a record command or run * commands that are expected to record operations that are to be submitted * as a batch into the GPU. */ - virtual void record() = 0; + virtual void record(std::shared_ptr commandBuffer) = 0; /** * Pre eval is called before the Sequence has called eval and submitted the commands to @@ -1209,21 +1239,6 @@ class OpBase * provided by the user. */ virtual void postEval() = 0; - - protected: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr - mPhysicalDevice; ///< Vulkan Physical Device - std::shared_ptr mDevice; ///< Vulkan Logical Device - std::shared_ptr - mCommandBuffer; ///< Vulkan Command Buffer - - // -------------- OPTIONALLY OWNED RESOURCES - std::vector> - mTensors; ///< Tensors referenced by operation that can be managed - ///< optionally by operation - bool mFreeTensors = false; ///< Explicit boolean that specifies whether the - ///< tensors are freed (if they are managed) }; } // End namespace kp @@ -1233,14 +1248,9 @@ namespace kp { /** * Container of operations that can be sent to GPU as batch */ -class Sequence +class Sequence: public std::enable_shared_from_this { public: - /** - * Base constructor for Sequence. Should not be used unless explicit - * intended. - */ - Sequence(); /** * Main constructor for sequence which requires core vulkan components to * generate all dependent resources. @@ -1261,10 +1271,21 @@ class Sequence ~Sequence(); /** - * Initialises sequence including the creation of the command pool and the - * command buffer. + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. */ - void init(); + std::shared_ptr record(std::shared_ptr op); + + /** + * Clear function clears all operations currently recorded and starts recording again. + */ + void clear(); /** * Begins recording commands for commands to be submitted into the command @@ -1272,7 +1293,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool begin(); + void begin(); /** * Ends the recording and stops recording commands when the record command @@ -1280,7 +1301,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool end(); + void end(); /** * Eval sends all the recorded and stored operations in the vector of @@ -1288,7 +1309,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool eval(); + std::shared_ptr eval(); /** * Eval Async sends all the recorded and stored operations in the vector of @@ -1297,7 +1318,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool evalAsync(); + std::shared_ptr evalAsync(); /** * Eval Await waits for the fence to finish processing and then once it @@ -1306,7 +1327,7 @@ class Sequence * @param waitFor Number of milliseconds to wait before timing out. * @return Boolean stating whether execution was successful. */ - bool evalAwait(uint64_t waitFor = UINT64_MAX); + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** * Returns true if the sequence is currently in recording activated. @@ -1323,68 +1344,12 @@ class Sequence */ bool isRunning(); - /** - * Returns true if the sequence has been successfully initialised. - * - * @return Boolean stating if sequence has been initialised. - */ - bool isInit(); - /** * Destroys and frees the GPU resources which include the buffer and memory * and sets the sequence as init=False. */ void freeMemoryDestroyGPUResources(); - /** - * Record function for operation to be added to the GPU queue in batch. This - * template requires classes to be derived from the OpBase class. This - * function also requires the Sequence to be recording, otherwise it will - * not be able to add the operation. - * - * @param tensors Vector of tensors to use for the operation - * @param TArgs Template parameters that are used to initialise operation - * which allows for extensible configurations on initialisation. - */ - template - bool record(std::vector> tensors, TArgs&&... params) - { - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence record function started"); - - if (!this->isRecording()) { - KP_LOG_ERROR( - "Kompute sequence record attempted when not record BEGIN"); - return false; - } - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - T* op = new T(this->mPhysicalDevice, - this->mDevice, - this->mCommandBuffer, - tensors, - std::forward(params)...); - - OpBase* baseOp = dynamic_cast(op); - - std::unique_ptr baseOpPtr{ baseOp }; - - KP_LOG_DEBUG( - "Kompute Sequence running init on OpBase derived class instance"); - baseOpPtr->init(); - - KP_LOG_DEBUG( - "Kompute Sequence running record on OpBase derived class instance"); - baseOpPtr->record(); - - mOperations.push_back(std::move(baseOpPtr)); - - return true; - } - private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice = nullptr; @@ -1400,10 +1365,9 @@ class Sequence // -------------- ALWAYS OWNED RESOURCES vk::Fence mFence; - std::vector> mOperations; + std::vector> mOperations; // State - bool mIsInit = false; bool mRecording = false; bool mIsRunning = false; @@ -1422,8 +1386,6 @@ namespace kp { class OpTensorSyncDevice : public OpBase { public: - OpTensorSyncDevice(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. * @@ -1432,25 +1394,17 @@ class OpTensorSyncDevice : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncDevice(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorSyncDevice(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorSyncDevice() override; - /** - * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. - */ - void init() override; - /** * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. @@ -1463,6 +1417,8 @@ class OpTensorSyncDevice : public OpBase virtual void postEval() override; private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp @@ -1525,150 +1481,7 @@ class Manager * @param queueIndex The queue to use from the available queues * @return Shared pointer to the manager owned sequence resource */ - std::shared_ptr sequence( - std::string sequenceName = KP_DEFAULT_SESSION, - uint32_t queueIndex = 0); - - /** - * Function that evaluates operation against named sequence. - * - * @param tensors The tensors to be used in the operation recorded - * @param sequenceName The name of the sequence to be retrieved or created - * @param TArgs Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOp(std::vector> tensors, - std::string sequenceName, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOp triggered"); - std::shared_ptr sq = - this->sequence(sequenceName); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence BEGIN"); - sq->begin(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence RECORD"); - sq->record(tensors, std::forward(params)...); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence END"); - sq->end(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence EVAL"); - sq->eval(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence SUCCESS"); - } - - /** - * Function that evaluates operation against a newly created sequence. - * - * @param tensors The tensors to be used in the operation recorded - * @param TArgs Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpDefault(std::vector> tensors, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOp Default triggered"); - this->mCurrentSequenceIndex++; - this->evalOp( - tensors, KP_DEFAULT_SESSION, std::forward(params)...); - } - - /** - * Function that evaluates operation against named sequence asynchronously. - * - * @param tensors The tensors to be used in the operation recorded - * @param sequenceName The name of the sequence to be retrieved or created - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpAsync(std::vector> tensors, - std::string sequenceName, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOpAsync triggered"); - - std::shared_ptr sq = - this->sequence(sequenceName); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence BEGIN"); - sq->begin(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence RECORD"); - sq->record(tensors, std::forward(params)...); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence END"); - sq->end(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence EVAL"); - sq->evalAsync(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence SUCCESS"); - } - - /** - * Operation that evaluates operation against default sequence - * asynchronously. - * - * @param tensors The tensors to be used in the operation recorded - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpAsyncDefault(std::vector> tensors, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOpAsyncDefault triggered"); - this->mCurrentSequenceIndex++; - this->evalOpAsync( - tensors, KP_DEFAULT_SESSION, std::forward(params)...); - } - - /** - * Operation that awaits for named sequence to finish. - * - * @param sequenceName The name of the sequence to wait for termination - * @param waitFor The amount of time to wait before timing out - */ - void evalOpAwait(std::string sequenceName, uint64_t waitFor = UINT64_MAX) - { - KP_LOG_DEBUG("Kompute Manager evalOpAwait triggered with sequence {}", - sequenceName); - std::unordered_map>::iterator - found = this->mManagedSequences.find(sequenceName); - - if (found != this->mManagedSequences.end()) { - if (std::shared_ptr sq = found->second) { - KP_LOG_DEBUG("Kompute Manager evalOpAwait running sequence " - "Sequence EVAL AWAIT"); - if (sq->isRunning()) { - sq->evalAwait(waitFor); - } - } - KP_LOG_DEBUG( - "Kompute Manager evalOpAwait running sequence SUCCESS"); - } else { - KP_LOG_ERROR("Kompute Manager evalOpAwait Sequence not found"); - } - } - - /** - * Operation that awaits for default sequence to finish. - * - * @param tensors The tensors to be used in the operation recorded - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - void evalOpAwaitDefault(uint64_t waitFor = UINT64_MAX) - { - KP_LOG_DEBUG("Kompute Manager evalOpAwaitDefault triggered"); - this->evalOpAwait(KP_DEFAULT_SESSION, waitFor); - } + std::shared_ptr sequence(uint32_t queueIndex = 0); /** * Function that simplifies the common workflow of tensor creation and @@ -1686,79 +1499,12 @@ class Manager Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, bool syncDataToGPU = true); - /** - * Function that simplifies the common workflow of tensor initialisation. It - * will take the constructor parameters for a Tensor and will will us it to - * create a new Tensor. The tensor memory will then be managed and owned by - * the manager. - * - * @param tensors Array of tensors to rebuild - * @param syncDataToGPU Whether to sync the data to GPU memory - */ - void rebuild(std::vector> tensors, - bool syncDataToGPU = true); - - /** - * Function that simplifies the common workflow of tensor initialisation. It - * will take the constructor parameters for a Tensor and will will us it to - * create a new Tensor. The tensor memory will then be managed and owned by - * the manager. - * - * @param tensors Single tensor to rebuild - * @param syncDataToGPU Whether to sync the data to GPU memory - */ - void rebuild(std::shared_ptr tensor, - bool syncDataToGPU = true); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * single tensor. - * - * @param tensors Single tensor to rebuild - */ - void destroy(std::shared_ptr tensor); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * vector of tensors. - * - * @param tensors Single tensor to rebuild - */ - void destroy(std::vector> tensors); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * vector of sequences. Destroying by sequence name is more efficent - * and hence recommended instead of by object. - * - * @param sequences Vector for shared ptrs with sequences to destroy - */ - void destroy(std::vector> sequences); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * single sequence. Destroying by sequence name is more efficent - * and hence recommended instead of by object. - * - * @param sequences Single sequence to rebuild - */ - void destroy(std::shared_ptr sequence); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * sequence by name. - * - * @param sequenceName Single name of named sequence to destroy - */ - void destroy(const std::string& sequenceName); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * sequences using vector of named sequence names. - * - * @param sequenceName Vector of sequence names to destroy - */ - void destroy(const std::vector& sequenceNames); + std::shared_ptr algorithm( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); private: // -------------- OPTIONALLY OWNED RESOURCES @@ -1770,10 +1516,9 @@ class Manager bool mFreeDevice = false; // -------------- ALWAYS OWNED RESOURCES - std::set> mManagedTensors; - - std::unordered_map> - mManagedSequences; + std::vector> mManagedTensors; + std::vector> mManagedSequences; + std::vector> mManagedAlgorithms; std::vector mComputeQueueFamilyIndices; std::vector> mComputeQueues; @@ -1798,181 +1543,24 @@ class Manager namespace kp { -/** - Abstraction for compute shaders that are run on top of tensors grouped via - ParameterGroups (which group descriptorsets) -*/ -class Algorithm -{ -public: - /** - Base constructor for Algorithm. Should not be used unless explicit - intended. - */ - Algorithm(); - - /** - * Default constructor for Algorithm - * - * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders - */ - Algorithm(std::shared_ptr device, - std::shared_ptr commandBuffer, - const Constants& specializationConstants = {}); - - /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. - * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing - */ - void init(const std::vector& shaderFileData, - std::vector> tensorParams); - - /** - * Destructor for Algorithm which is responsible for freeing and desroying - * respective pipelines and owned parameter groups. - */ - ~Algorithm(); - - /** - * Records the dispatch function with the provided template parameters or - * alternatively using the size of the tensor by default. - * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value - */ - void recordDispatch(uint32_t x = 1, uint32_t y = 1, uint32_t z = 1); - -private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mDevice; - std::shared_ptr mCommandBuffer; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mDescriptorSetLayout; - bool mFreeDescriptorSetLayout = false; - std::shared_ptr mDescriptorPool; - bool mFreeDescriptorPool = false; - std::shared_ptr mDescriptorSet; - bool mFreeDescriptorSet = false; - std::shared_ptr mShaderModule; - bool mFreeShaderModule = false; - std::shared_ptr mPipelineLayout; - bool mFreePipelineLayout = false; - std::shared_ptr mPipelineCache; - bool mFreePipelineCache = false; - std::shared_ptr mPipeline; - bool mFreePipeline = false; - - // -------------- ALWAYS OWNED RESOURCES - Constants mSpecializationConstants; - - // Create util functions - void createShaderModule(const std::vector& shaderFileData); - void createPipeline(); - - // Parameters - void createParameters(std::vector>& tensorParams); - void createDescriptorPool(); -}; - -} // End namespace kp - -namespace kp { - /** * Operation that provides a general abstraction that simplifies the use of * algorithm and parameter components which can be used with shaders. * By default it enables the user to provide a dynamic number of tensors * which are then passed as inputs. */ -class OpAlgoCreate : public OpBase +class OpAlgoDispatch : public OpBase { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpAlgoCreate(); - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoCreate(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); - - /** - * Constructor that enables a file to be passed to the operation with - * the contents of the shader. This can be either in raw format or in - * compiled SPIR-V binary format. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderFilePath Parameter to specify the shader to load (either in spirv or raw format) - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoCreate(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - std::string shaderFilePath, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); - - /** - * Constructor that enables raw shader data to be passed to the main operation - * which can be either in raw shader glsl code or in compiled SPIR-V binary. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderDataRaw Optional parameter to specify the shader data either in binary or raw form - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoCreate(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector>& tensors, - const std::vector& shaderDataRaw, - const Workgroup& komputeWorkgroup = {}, - const Constants& specializationConstants = {}); + OpAlgoDispatch(const std::vector>& tensors, + const std::shared_ptr& algorithm); /** * Default destructor, which is in charge of destroying the algorithm * components but does not destroy the underlying tensors */ - virtual ~OpAlgoCreate() override; - - /** - * The init function is responsible for the initialisation of the algorithm - * component based on the parameters specified, and allows for extensibility - * on the options provided. Further dependent classes can perform more - * specific checks such as ensuring tensors provided are initialised, etc. - */ - virtual void init() override; + virtual ~OpAlgoDispatch() override; /** * This records the commands that are to be sent to the GPU. This includes @@ -1982,7 +1570,7 @@ class OpAlgoCreate : public OpBase * copy of the output data for the staging buffer so it can be read by the * host. */ - virtual void record() override; + virtual void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. @@ -1996,121 +1584,23 @@ class OpAlgoCreate : public OpBase */ virtual void postEval() override; - protected: - // -------------- NEVER OWNED RESOURCES - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mAlgorithm; - bool mFreeAlgorithm = false; - +private: // -------------- ALWAYS OWNED RESOURCES - - Workgroup mWorkgroup; - - std::string mShaderFilePath; ///< Optional member variable which can be provided for the OpAlgoCreate to find the data automatically and load for processing - std::vector mShaderDataRaw; ///< Optional member variable which can be provided to contain either the raw shader content or the spirv binary content - - virtual std::vector fetchSpirvBinaryData(); + std::vector> mTensors; + std::shared_ptr mAlgorithm; }; } // End namespace kp -#include - -namespace kp { - -/** - * Operation base class to simplify the creation of operations that require - * right hand and left hand side datapoints together with a single output. - * The expected data passed is two input tensors and one output tensor. - */ -class OpAlgoLhsRhsOut : public OpAlgoCreate -{ - public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpAlgoLhsRhsOut(); - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param freeTensors Whether operation manages the memory of the Tensors - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoLhsRhsOut(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors, - const Workgroup& komputeWorkgroup = {}); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoLhsRhsOut() override; - - /** - * The init function is responsible for ensuring that all of the tensors - * provided are aligned with requirements such as LHS, RHS and Output - * tensors, and creates the algorithm component which processes the - * computation. - */ - virtual void init() override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - - protected: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mTensorLHS; ///< Reference to the parameter used in the left hand side equation of the shader - std::shared_ptr mTensorRHS; ///< Reference to the parameter used in the right hand side equation of the shader - std::shared_ptr mTensorOutput; ///< Reference to the parameter used in the output of the shader and will be copied with a staging vector -}; - -} // End namespace kp - -#include - -#if RELEASE - -#endif - namespace kp { /** * Operation that performs multiplication on two tensors and outpus on third * tensor. */ -class OpMult : public OpAlgoCreate +class OpMult : public OpAlgoDispatch { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpMult() { - - } /** * Default constructor with parameters that provides the bare minimum @@ -2123,46 +1613,30 @@ class OpMult : public OpAlgoCreate * @param tensors Tensors that are to be used in this operation * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpMult(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors, - const Workgroup& komputeWorkgroup = {}) - : OpAlgoCreate(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(tensors, algorithm) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); -#ifndef RELEASE - this->mShaderFilePath = "shaders/glsl/opmult.comp.spv"; -#endif - } + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } -#if RELEASE - /** - * If RELEASE=1 it will be using the static version of the shader which is - * loaded using this file directly. Otherwise it should not override the function. - */ - std::vector fetchSpirvBinaryData() override - { - KP_LOG_WARN( - "Kompute OpMult Running shaders directly from header"); - - return std::vector( + std::vector spirv( (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + algorithm->rebuild(tensors, spirv, Workgroup({tensors[0]->size()})); } -#endif /** * Default destructor, which is in charge of destroying the algorithm * components but does not destroy the underlying tensors */ - ~OpMult() override { + virtual ~OpMult() override { KP_LOG_DEBUG("Kompute OpMult destructor started"); } - }; } // End namespace kp @@ -2175,8 +1649,6 @@ namespace kp { class OpTensorCopy : public OpBase { public: - OpTensorCopy(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. * @@ -2185,25 +1657,17 @@ class OpTensorCopy : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorCopy(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorCopy(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorCopy() override; - /** - * Performs basic checks such as ensuring there are at least two tensors provided, that they are initialised and that they are not of type TensorTypes::eStorage. - */ - void init() override; - /** * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. @@ -2216,6 +1680,8 @@ class OpTensorCopy : public OpBase virtual void postEval() override; private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp @@ -2228,8 +1694,6 @@ namespace kp { class OpTensorSyncLocal : public OpBase { public: - OpTensorSyncLocal(); - /** * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. * @@ -2238,25 +1702,17 @@ class OpTensorSyncLocal : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncLocal(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); + OpTensorSyncLocal(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorSyncLocal() override; - /** - * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. - */ - void init() override; - /** * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. */ - void record() override; + void record(std::shared_ptr commandBuffer) override; /** * Does not perform any preEval commands. @@ -2269,6 +1725,8 @@ class OpTensorSyncLocal : public OpBase virtual void postEval() override; private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp diff --git a/src/Manager.cpp b/src/Manager.cpp index 7bd629165..ba0249f1d 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -132,7 +132,7 @@ Manager::sequence(uint32_t queueIndex) this->mComputeQueues[queueIndex], this->mComputeQueueFamilyIndices[queueIndex]); - this->mManagedSequences.insert(sq); + this->mManagedSequences.push_back(sq); return sq; } @@ -337,10 +337,7 @@ Manager::tensor( std::shared_ptr tensor = std::make_shared( kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType)); - if (syncDataToGPU) { - this->evalOpDefault({ tensor }); - } - this->mManagedTensors.insert(tensor); + this->mManagedTensors.push_back(tensor); return tensor; } @@ -363,134 +360,9 @@ Manager::algorithm( specializationConstants, pushConstants)); - this->mManagedAlgorithms.insert(algorithm); + this->mManagedAlgorithms.push_back(algorithm); return algorithm; } -void -Manager::rebuild(std::vector> tensors, - bool syncDataToGPU) -{ - KP_LOG_DEBUG("Kompute Manager rebuild triggered"); - for (std::shared_ptr tensor : tensors) { - - // False syncData to run all tensors at once instead one by one - this->rebuild(tensor, false); - } - - if (syncDataToGPU) { - this->evalOpDefault(tensors); - } -} - -void -Manager::rebuild(std::shared_ptr tensor, - bool syncDataToGPU) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Tensor triggered"); - - if (tensor->isInit()) { - tensor->freeMemoryDestroyGPUResources(); - } - - tensor->init(this->mPhysicalDevice, this->mDevice); - - std::set>::iterator it = - this->mManagedTensors.find(tensor); - if (it == this->mManagedTensors.end()) { - this->mManagedTensors.insert(tensor); - } - - if (syncDataToGPU) { - this->evalOpDefault({ tensor }); - } -} - -void -Manager::destroy(std::shared_ptr tensor) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Tensor triggered"); - - if (tensor->isInit()) { - tensor->freeMemoryDestroyGPUResources(); - } - - // TODO: Confirm not limiting destroying tensors owned by this manager allowed - std::set>::iterator it = - this->mManagedTensors.find(tensor); - - if (it != this->mManagedTensors.end()) { - this->mManagedTensors.erase(tensor); - } -} - -void -Manager::destroy(std::vector> tensors) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Tensor triggered"); - - for (std::shared_ptr tensor : tensors) { - this->destroy(tensor); - } -} - -void -Manager::destroy(std::vector> sequences) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Sequence triggered"); - - for (std::shared_ptr sequence : sequences) { - this->destroy(sequence); - } -} - -void -Manager::destroy(std::shared_ptr sequence) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Sequence triggered"); - - // Inefficient but required to delete by value - // Depending on the amount of named sequences created may be worth creating - // a set to ensure efficient delete. - for (std::unordered_map>::iterator it = this->mManagedSequences.begin(); it != this->mManagedSequences.end(); it++) { - if (it->second == sequence) { - this->mManagedSequences.erase(it); - break; - } - } - - if (sequence->isInit()) { - sequence->freeMemoryDestroyGPUResources(); - } -} - -void -Manager::destroy(const std::string& sequenceName) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Sequence triggered"); - - std::unordered_map>::iterator - found = this->mManagedSequences.find(sequenceName); - - if (found != this->mManagedSequences.end()) { - // We don't call destroy(sequence) as erasing sequence by name more efficient - if (found->second->isInit()) { - found->second->freeMemoryDestroyGPUResources(); - } - this->mManagedSequences.erase(sequenceName); - } -} - -void -Manager::destroy(const std::vector& sequenceNames) -{ - KP_LOG_DEBUG("Kompute Manager rebuild Sequence triggered"); - - for (const std::string& sequenceName : sequenceNames) { - this->destroy(sequenceName); - } -} - - } diff --git a/src/OpAlgoCreate.cpp b/src/OpAlgoCreate.cpp deleted file mode 100644 index 008cf9bbe..000000000 --- a/src/OpAlgoCreate.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "kompute/operations/OpAlgoCreate.hpp" - -namespace kp { - -OpAlgoCreate::OpAlgoCreate(std::vector> tensors, - std::shared_ptr algorithm) - : OpBase(tensors, algorithm) -{ - KP_LOG_DEBUG("Kompute OpAlgoCreate constructor"); - - this->mManagesAlgorithm = true; - this->mManagesTensors = false; -} - -OpAlgoCreate::~OpAlgoCreate() -{ - KP_LOG_DEBUG("Kompute OpAlgoCreate destructor started"); -} - -void -OpAlgoCreate::init( - std::shared_ptr physicalDevice, - std::shared_ptr device) { - - KP_LOG_DEBUG("Kompute OpAlgoCreate init started"); - - // Explicitly calling top level function to create algo - OpBase::init(physicalDevice, device); -} - -void -OpAlgoCreate::record(std::shared_ptr commandBuffer) -{ - KP_LOG_DEBUG("Kompute OpAlgoCreate record called"); -} - -void -OpAlgoCreate::preEval() -{ - KP_LOG_DEBUG("Kompute OpAlgoCreate preEval called"); -} - -void -OpAlgoCreate::postEval() -{ - KP_LOG_DEBUG("Kompute OpAlgoCreate postSubmit called"); -} - -} diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 25d2ba519..3623fcddd 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -4,14 +4,11 @@ namespace kp { -OpAlgoDispatch::OpAlgoDispatch(std::vector> tensors, - std::shared_ptr algorithm) - : OpBase(tensors, algorithm) +OpAlgoDispatch::OpAlgoDispatch(const std::vector>& tensors, + const std::shared_ptr& algorithm) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); - this->mManagesAlgorithm = false; - this->mManagesTensors = false; } OpAlgoDispatch::~OpAlgoDispatch() @@ -19,13 +16,6 @@ OpAlgoDispatch::~OpAlgoDispatch() KP_LOG_DEBUG("Kompute OpAlgoDispatch destructor started"); } -void -OpAlgoDispatch::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpAlgoDispatch init called"); -} - void OpAlgoDispatch::record(std::shared_ptr commandBuffer) { diff --git a/src/OpAlgoLhsRhsOut.cpp b/src/OpAlgoLhsRhsOut.cpp deleted file mode 100644 index 89eb15c60..000000000 --- a/src/OpAlgoLhsRhsOut.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include "kompute/operations/OpAlgoLhsRhsOut.hpp" - -namespace kp { - -OpAlgoLhsRhsOut::OpAlgoLhsRhsOut() -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut constructor base"); -} - -OpAlgoLhsRhsOut::OpAlgoLhsRhsOut( - std::vector>& tensors, - std::shared_ptr algorithm) - // The inheritance is initialised with the copyOutputData to false given that - // this depencendant class handles the transfer of data via staging buffers in - // a granular way. - : OpAlgoCreate(tensors, algorithm) -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut constructor with params"); -} - -OpAlgoLhsRhsOut::~OpAlgoLhsRhsOut() -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut destructor started"); -} - -void -OpAlgoLhsRhsOut::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut init called"); - - if (this->mTensors.size() < 3) { - throw std::runtime_error( - "Kompute OpAlgoLhsRhsOut called with less than 1 tensor"); - } else if (this->mTensors.size() > 3) { - KP_LOG_WARN( - "Kompute OpAlgoLhsRhsOut called with more than 3 this->mTensors"); - } - - this->mTensorLHS = this->mTensors[0]; - this->mTensorRHS = this->mTensors[1]; - this->mTensorOutput = this->mTensors[2]; - - if (!(this->mTensorLHS->isInit() && this->mTensorRHS->isInit() && - this->mTensorOutput->isInit())) { - throw std::runtime_error( - "Kompute OpAlgoLhsRhsOut all tensor parameters must be initialised. " - "LHS: " + - std::to_string(this->mTensorLHS->isInit()) + - " RHS: " + std::to_string(this->mTensorRHS->isInit()) + - " Output: " + std::to_string(this->mTensorOutput->isInit())); - } - - if (!(this->mTensorLHS->size() == this->mTensorRHS->size() && - this->mTensorRHS->size() == this->mTensorOutput->size())) { - throw std::runtime_error( - "Kompute OpAlgoLhsRhsOut all tensor parameters must be the same size " - "LHS: " + - std::to_string(this->mTensorLHS->size()) + - " RHS: " + std::to_string(this->mTensorRHS->size()) + - " Output: " + std::to_string(this->mTensorOutput->size())); - } - - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut fetching spirv data"); - - std::vector shaderFileData = this->fetchSpirvBinaryData(); - - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut Initialising algorithm component"); -} - -void -OpAlgoLhsRhsOut::record(std::shared_ptr commandBuffer) -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut record called"); - - // Barrier to ensure the data is finished writing to buffer memory - this->mTensorLHS->recordBufferMemoryBarrier( - this->mCommandBuffer, - vk::AccessFlagBits::eHostWrite, - vk::AccessFlagBits::eShaderRead, - vk::PipelineStageFlagBits::eHost, - vk::PipelineStageFlagBits::eComputeShader); - this->mTensorRHS->recordBufferMemoryBarrier( - this->mCommandBuffer, - vk::AccessFlagBits::eHostWrite, - vk::AccessFlagBits::eShaderRead, - vk::PipelineStageFlagBits::eHost, - vk::PipelineStageFlagBits::eComputeShader); - - this->mAlgorithm->recordDispatch(this->mKomputeWorkgroup[0], - this->mKomputeWorkgroup[1], - this->mKomputeWorkgroup[2]); - - // Barrier to ensure the shader code is executed before buffer read - this->mTensorOutput->recordBufferMemoryBarrier( - this->mCommandBuffer, - vk::AccessFlagBits::eShaderWrite, - vk::AccessFlagBits::eTransferRead, - vk::PipelineStageFlagBits::eComputeShader, - vk::PipelineStageFlagBits::eTransfer); - - if (this->mTensorOutput->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensorOutput->recordCopyFromDeviceToStaging(this->mCommandBuffer, - true); - } -} - -void -OpAlgoLhsRhsOut::postEval() -{ - KP_LOG_DEBUG("Kompute OpAlgoLhsRhsOut postSubmit called"); - - this->mTensorOutput->mapDataFromHostMemory(); -} - -} diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 8f88eeb65..374fe4ea1 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -3,42 +3,21 @@ namespace kp { -OpTensorCopy::OpTensorCopy(std::vector> tensors) - : OpBase(tensors, nullptr) +OpTensorCopy::OpTensorCopy(const std::vector>& tensors) { KP_LOG_DEBUG("Kompute OpTensorCopy constructor with params"); - this->mManagesTensors = false; - this->mManagesAlgorithm = false; -} - -OpTensorCopy::~OpTensorCopy() -{ - KP_LOG_DEBUG("Kompute OpTensorCopy destructor started"); -} - -void -OpTensorCopy::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpTensorCopy init called"); - if (this->mTensors.size() < 2) { throw std::runtime_error( "Kompute OpTensorCopy called with less than 2 tensor"); } - for (std::shared_ptr tensor : this->mTensors) { - if (!tensor->isInit()) { - throw std::runtime_error( - "Kompute OpTensorCopy tensor parameter has not been initialized"); - } - if (tensor->tensorType() == Tensor::TensorTypes::eStorage) { - throw std::runtime_error("Kompute OpTensorCopy tensor parameter is " - "of TensorTypes::eStorage and hence " - "cannot be used to receive or pass data."); - } - } + this->mTensors = tensors; +} + +OpTensorCopy::~OpTensorCopy() +{ + KP_LOG_DEBUG("Kompute OpTensorCopy destructor started"); } void diff --git a/src/OpTensorCreate.cpp b/src/OpTensorCreate.cpp deleted file mode 100644 index a343f1510..000000000 --- a/src/OpTensorCreate.cpp +++ /dev/null @@ -1,46 +0,0 @@ - -#include "kompute/operations/OpTensorCreate.hpp" - -namespace kp { - -OpTensorCreate::OpTensorCreate( - std::vector>& tensors) - : OpBase(tensors, nullptr) -{ - KP_LOG_DEBUG("Compute OpTensorCreate constructor with params"); - this->mManagesTensors = true; -} - -OpTensorCreate::~OpTensorCreate() -{ - KP_LOG_DEBUG("Kompute OpTensorCreate destructor started"); -} - -void -OpTensorCreate::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpTensorCreate init called"); - - OpBase::init(physicalDevice, device); -} - -void -OpTensorCreate::record(std::shared_ptr commandBuffer) -{ - KP_LOG_DEBUG("Kompute OpTensorCreate record called"); -} - -void -OpTensorCreate::preEval() -{ - KP_LOG_DEBUG("Kompute OpTensorCreate preEval called"); -} - -void -OpTensorCreate::postEval() -{ - KP_LOG_DEBUG("Kompute OpTensorCreate postEval called"); -} - -} diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index 872f82365..2cdd4e443 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -4,10 +4,16 @@ namespace kp { OpTensorSyncDevice::OpTensorSyncDevice( - std::vector> tensors) - : OpBase(tensors, nullptr) + const std::vector>& tensors) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice constructor with params"); + + if (tensors.size() < 1) { + throw std::runtime_error( + "Kompute OpTensorSyncDevice called with less than 1 tensor"); + } + + this->mTensors = tensors; } OpTensorSyncDevice::~OpTensorSyncDevice() @@ -15,31 +21,6 @@ OpTensorSyncDevice::~OpTensorSyncDevice() KP_LOG_DEBUG("Kompute OpTensorSyncDevice destructor started"); } -void -OpTensorSyncDevice::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpTensorSyncDevice init called"); - - if (this->mTensors.size() < 1) { - throw std::runtime_error( - "Kompute OpTensorSyncDevice called with less than 1 tensor"); - } - - for (std::shared_ptr tensor : this->mTensors) { - if (!tensor->isInit()) { - throw std::runtime_error("Kompute OpTensorSyncDevice: Tensor param " - "has not been initialized"); - } - if (tensor->tensorType() == Tensor::TensorTypes::eStorage) { - KP_LOG_WARN( - "Kompute OpTensorSyncLocal tensor parameter is of type " - "TensorTypes::eStorage and hence cannot be used to receive or " - "pass data."); - } - } -} - void OpTensorSyncDevice::record(std::shared_ptr commandBuffer) { diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index fd98b092d..3cd022bf2 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -6,13 +6,16 @@ namespace kp { OpTensorSyncLocal::OpTensorSyncLocal( - std::vector> tensors) - : OpBase(tensors, nullptr) + const std::vector>& tensors) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal constructor with params"); - this->mManagesTensors = false; - this->mManagesAlgorithm = false; + if (tensors.size() < 1) { + throw std::runtime_error( + "Kompute OpTensorSyncLocal called with less than 1 tensor"); + } + + this->mTensors = tensors; } OpTensorSyncLocal::~OpTensorSyncLocal() @@ -20,25 +23,6 @@ OpTensorSyncLocal::~OpTensorSyncLocal() KP_LOG_DEBUG("Kompute OpTensorSyncLocal destructor started"); } -void -OpTensorSyncLocal::init(std::shared_ptr physicalDevice, - std::shared_ptr device) -{ - KP_LOG_DEBUG("Kompute OpTensorSyncLocal init called"); - - if (this->mTensors.size() < 1) { - throw std::runtime_error( - "Kompute OpTensorSyncLocal called with less than 1 tensor"); - } - - for (std::shared_ptr tensor : this->mTensors) { - if (!tensor->isInit()) { - throw std::runtime_error( - "Kompute OpTensorSyncLocal: Tensor has not been initialized"); - } - } -} - void OpTensorSyncLocal::record(std::shared_ptr commandBuffer) { diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 4f6596efb..20e441500 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -26,96 +26,60 @@ Sequence::~Sequence() this->freeMemoryDestroyGPUResources(); } -bool +void Sequence::begin() { KP_LOG_DEBUG("Kompute sequence called BEGIN"); if (this->isRecording()) { - KP_LOG_WARN("Kompute Sequence begin called when already recording"); - return false; + KP_LOG_DEBUG("Kompute Sequence begin called when already recording"); + return; } if (this->isRunning()) { - KP_LOG_WARN( - "Kompute Sequence begin called when sequence still running"); - return false; - } - - if (!this->mCommandPool) { - throw std::runtime_error("Kompute Sequence command pool is null"); - } - - if (this->mOperations.size()) { - KP_LOG_INFO("Kompute Sequence clearing previous operations"); - this->mOperations.clear(); + throw std::runtime_error("Kompute Sequence begin called when sequence still running"); } if (!this->mRecording) { KP_LOG_INFO("Kompute Sequence command recording BEGIN"); this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); this->mRecording = true; - } else { - KP_LOG_WARN("Kompute Sequence attempted to start command recording " - "but recording already started"); } - return true; } -bool +void Sequence::end() { KP_LOG_DEBUG("Kompute Sequence calling END"); if (!this->isRecording()) { KP_LOG_WARN("Kompute Sequence end called when not recording"); - return false; - } - - if (!this->mCommandPool) { - throw std::runtime_error("Kompute Sequence command pool is null"); - } - - if (this->mRecording) { + return; + } + else { KP_LOG_INFO("Kompute Sequence command recording END"); this->mCommandBuffer->end(); this->mRecording = false; - } else { - KP_LOG_WARN("Kompute Sequence attempted to end command recording but " - "recording not started"); } - return true; } -bool +std::shared_ptr Sequence::eval() { KP_LOG_DEBUG("Kompute sequence EVAL BEGIN"); - bool evalResult = this->evalAsync(); - if (!evalResult) { - KP_LOG_DEBUG("Kompute sequence EVAL FAILURE"); - return false; - } - - evalResult = this->evalAwait(); - - KP_LOG_DEBUG("Kompute sequence EVAL SUCCESS"); - - return evalResult; + return this->evalAsync()->evalAwait(); } -bool +std::shared_ptr Sequence::evalAsync() { if (this->isRecording()) { - KP_LOG_WARN("Kompute Sequence evalAsync called when still recording"); - return false; + this->end(); } if (this->mIsRunning) { - KP_LOG_WARN("Kompute Sequence evalAsync called when an eval async was " + throw std::runtime_error("Kompute Sequence evalAsync called when an eval async was " "called without successful wait"); - return false; } this->mIsRunning = true; @@ -134,15 +98,15 @@ Sequence::evalAsync() this->mComputeQueue->submit(1, &submitInfo, this->mFence); - return true; + return shared_from_this(); } -bool +std::shared_ptr Sequence::evalAwait(uint64_t waitFor) { if (!this->mIsRunning) { KP_LOG_WARN("Kompute Sequence evalAwait called without existing eval"); - return false; + return shared_from_this(); } vk::Result result = @@ -153,15 +117,15 @@ Sequence::evalAwait(uint64_t waitFor) this->mIsRunning = false; if (result == vk::Result::eTimeout) { - KP_LOG_WARN("Kompute Sequence evalAwait timed out"); - return false; + KP_LOG_WARN("Kompute Sequence evalAwait reached timeout of {}", waitFor); + return shared_from_this(); } for (size_t i = 0; i < this->mOperations.size(); i++) { this->mOperations[i]->postEval(); } - return true; + return shared_from_this(); } bool @@ -221,6 +185,22 @@ Sequence::freeMemoryDestroyGPUResources() } +std::shared_ptr +Sequence::record(std::shared_ptr op) +{ + KP_LOG_DEBUG("Kompute Sequence record function started"); + + this->begin(); + + KP_LOG_DEBUG( + "Kompute Sequence running record on OpBase derived class instance"); + op->record(this->mCommandBuffer); + + this->mOperations.push_back(op); + + return shared_from_this(); +} + void Sequence::createCommandPool() { diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 3615d74c0..cd808952a 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -69,147 +69,6 @@ class Manager */ std::shared_ptr sequence(uint32_t queueIndex = 0); - /** - * Function that evaluates operation against named sequence. - * - * @param tensors The tensors to be used in the operation recorded - * @param sequenceName The name of the sequence to be retrieved or created - * @param TArgs Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOp(std::vector> tensors, - std::string sequenceName, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOp triggered"); - std::shared_ptr sq = - this->sequence(sequenceName); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence BEGIN"); - sq->begin(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence RECORD"); - sq->record(tensors, std::forward(params)...); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence END"); - sq->end(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence EVAL"); - sq->eval(); - - KP_LOG_DEBUG("Kompute Manager evalOp running sequence SUCCESS"); - } - - /** - * Function that evaluates operation against a newly created sequence. - * - * @param tensors The tensors to be used in the operation recorded - * @param TArgs Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpDefault(std::vector> tensors, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOp Default triggered"); - this->mCurrentSequenceIndex++; - this->evalOp( - tensors, KP_DEFAULT_SESSION, std::forward(params)...); - } - - /** - * Function that evaluates operation against named sequence asynchronously. - * - * @param tensors The tensors to be used in the operation recorded - * @param sequenceName The name of the sequence to be retrieved or created - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpAsync(std::vector> tensors, - std::string sequenceName, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOpAsync triggered"); - - std::shared_ptr sq = - this->sequence(sequenceName); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence BEGIN"); - sq->begin(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence RECORD"); - sq->record(tensors, std::forward(params)...); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence END"); - sq->end(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence EVAL"); - sq->evalAsync(); - - KP_LOG_DEBUG("Kompute Manager evalOpAsync running sequence SUCCESS"); - } - - /** - * Operation that evaluates operation against default sequence - * asynchronously. - * - * @param tensors The tensors to be used in the operation recorded - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - template - void evalOpAsyncDefault(std::vector> tensors, - TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Manager evalOpAsyncDefault triggered"); - this->mCurrentSequenceIndex++; - this->evalOpAsync( - tensors, KP_DEFAULT_SESSION, std::forward(params)...); - } - - /** - * Operation that awaits for named sequence to finish. - * - * @param sequenceName The name of the sequence to wait for termination - * @param waitFor The amount of time to wait before timing out - */ - void evalOpAwait(std::string sequenceName, uint64_t waitFor = UINT64_MAX) - { - KP_LOG_DEBUG("Kompute Manager evalOpAwait triggered with sequence {}", - sequenceName); - std::unordered_map>::iterator - found = this->mManagedSequences.find(sequenceName); - - if (found != this->mManagedSequences.end()) { - if (std::shared_ptr sq = found->second) { - KP_LOG_DEBUG("Kompute Manager evalOpAwait running sequence " - "Sequence EVAL AWAIT"); - if (sq->isRunning()) { - sq->evalAwait(waitFor); - } - } - KP_LOG_DEBUG( - "Kompute Manager evalOpAwait running sequence SUCCESS"); - } else { - KP_LOG_ERROR("Kompute Manager evalOpAwait Sequence not found"); - } - } - - /** - * Operation that awaits for default sequence to finish. - * - * @param tensors The tensors to be used in the operation recorded - * @param params Template parameters that will be used to initialise - * Operation to allow for extensible configurations on initialisation - */ - void evalOpAwaitDefault(uint64_t waitFor = UINT64_MAX) - { - KP_LOG_DEBUG("Kompute Manager evalOpAwaitDefault triggered"); - this->evalOpAwait(KP_DEFAULT_SESSION, waitFor); - } - /** * Function that simplifies the common workflow of tensor creation and * initialization. It will take the constructor parameters for a Tensor @@ -233,80 +92,6 @@ class Manager const Constants& specializationConstants = {}, const Constants& pushConstants = {}); - /** - * Function that simplifies the common workflow of tensor initialisation. It - * will take the constructor parameters for a Tensor and will will us it to - * create a new Tensor. The tensor memory will then be managed and owned by - * the manager. - * - * @param tensors Array of tensors to rebuild - * @param syncDataToGPU Whether to sync the data to GPU memory - */ - void rebuild(std::vector> tensors, - bool syncDataToGPU = true); - - /** - * Function that simplifies the common workflow of tensor initialisation. It - * will take the constructor parameters for a Tensor and will will us it to - * create a new Tensor. The tensor memory will then be managed and owned by - * the manager. - * - * @param tensors Single tensor to rebuild - * @param syncDataToGPU Whether to sync the data to GPU memory - */ - void rebuild(std::shared_ptr tensor, - bool syncDataToGPU = true); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * single tensor. - * - * @param tensors Single tensor to rebuild - */ - void destroy(std::shared_ptr tensor); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * vector of tensors. - * - * @param tensors Single tensor to rebuild - */ - void destroy(std::vector> tensors); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * vector of sequences. Destroying by sequence name is more efficent - * and hence recommended instead of by object. - * - * @param sequences Vector for shared ptrs with sequences to destroy - */ - void destroy(std::vector> sequences); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * single sequence. Destroying by sequence name is more efficent - * and hence recommended instead of by object. - * - * @param sequences Single sequence to rebuild - */ - void destroy(std::shared_ptr sequence); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * sequence by name. - * - * @param sequenceName Single name of named sequence to destroy - */ - void destroy(const std::string& sequenceName); - - /** - * Destroy owned Vulkan GPU resources and free GPU memory for - * sequences using vector of named sequence names. - * - * @param sequenceName Vector of sequence names to destroy - */ - void destroy(const std::vector& sequenceNames); - private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; @@ -317,10 +102,9 @@ class Manager bool mFreeDevice = false; // -------------- ALWAYS OWNED RESOURCES - std::set> mManagedTensors; - std::set> mManagedSequences; - std::set> mManagedAlgorithms; - //std::unique_ptr mDefaultSequence; + std::vector> mManagedTensors; + std::vector> mManagedSequences; + std::vector> mManagedAlgorithms; std::vector mComputeQueueFamilyIndices; std::vector> mComputeQueues; diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index eeecd0a04..47827d729 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -9,7 +9,7 @@ namespace kp { /** * Container of operations that can be sent to GPU as batch */ -class Sequence +class Sequence: public std::enable_shared_from_this { public: /** @@ -31,13 +31,30 @@ class Sequence */ ~Sequence(); + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + */ + std::shared_ptr record(std::shared_ptr op); + + /** + * Clear function clears all operations currently recorded and starts recording again. + */ + void clear(); + /** * Begins recording commands for commands to be submitted into the command * buffer. * * @return Boolean stating whether execution was successful. */ - bool begin(); + void begin(); /** * Ends the recording and stops recording commands when the record command @@ -45,7 +62,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool end(); + void end(); /** * Eval sends all the recorded and stored operations in the vector of @@ -53,7 +70,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool eval(); + std::shared_ptr eval(); /** * Eval Async sends all the recorded and stored operations in the vector of @@ -62,7 +79,7 @@ class Sequence * * @return Boolean stating whether execution was successful. */ - bool evalAsync(); + std::shared_ptr evalAsync(); /** * Eval Await waits for the fence to finish processing and then once it @@ -71,7 +88,7 @@ class Sequence * @param waitFor Number of milliseconds to wait before timing out. * @return Boolean stating whether execution was successful. */ - bool evalAwait(uint64_t waitFor = UINT64_MAX); + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** * Returns true if the sequence is currently in recording activated. @@ -94,55 +111,6 @@ class Sequence */ void freeMemoryDestroyGPUResources(); - /** - * Record function for operation to be added to the GPU queue in batch. This - * template requires classes to be derived from the OpBase class. This - * function also requires the Sequence to be recording, otherwise it will - * not be able to add the operation. - * - * @param tensors Vector of tensors to use for the operation - * @param TArgs Template parameters that are used to initialise operation - * which allows for extensible configurations on initialisation. - */ - template - bool record(std::vector> tensors, TArgs&&... params) - { - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence record function started"); - - if (!this->isRecording()) { - KP_LOG_ERROR( - "Kompute sequence record attempted when not record BEGIN"); - return false; - } - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - T* op = new T(this->mPhysicalDevice, - this->mDevice, - this->mCommandBuffer, - tensors, - std::forward(params)...); - - OpBase* baseOp = dynamic_cast(op); - - std::unique_ptr baseOpPtr{ baseOp }; - - KP_LOG_DEBUG( - "Kompute Sequence running init on OpBase derived class instance"); - baseOpPtr->init(); - - KP_LOG_DEBUG( - "Kompute Sequence running record on OpBase derived class instance"); - baseOpPtr->record(); - - mOperations.push_back(std::move(baseOpPtr)); - - return true; - } - private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice = nullptr; @@ -158,7 +126,7 @@ class Sequence // -------------- ALWAYS OWNED RESOURCES vk::Fence mFence; - std::vector> mOperations; + std::vector> mOperations; // State bool mRecording = false; diff --git a/src/include/kompute/operations/OpAlgoCreate.hpp b/src/include/kompute/operations/OpAlgoCreate.hpp deleted file mode 100644 index 3f5c859a2..000000000 --- a/src/include/kompute/operations/OpAlgoCreate.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include - -#include "kompute/Core.hpp" - -#include "kompute/shaders/shaderopmult.hpp" - -#include "kompute/Algorithm.hpp" -#include "kompute/Tensor.hpp" - -#include "kompute/operations/OpBase.hpp" - -namespace kp { - -/** - * Operation that provides a general abstraction that simplifies the use of - * algorithm and parameter components which can be used with shaders. - * By default it enables the user to provide a dynamic number of tensors - * which are then passed as inputs. - */ -class OpAlgoCreate : public OpBase -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoCreate(std::vector> tensors, - std::shared_ptr algorithm); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoCreate() override; - - - virtual void init( - std::shared_ptr physicalDevice, - std::shared_ptr device) override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; -}; - -} // End namespace kp - diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index 7763aa9b9..0af5b5fba 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,51 +17,8 @@ class OpAlgoDispatch : public OpBase { public: - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderFilePath Optional parameter to specify the shader to load (either in spirv or raw format) - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoDispatch(std::vector> tensors, - std::shared_ptr algorithm); - - /** - * Constructor that enables a file to be passed to the operation with - * the contents of the shader. This can be either in raw format or in - * compiled SPIR-V binary format. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderFilePath Parameter to specify the shader to load (either in spirv or raw format) - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoDispatch(std::vector>& tensors, - std::shared_ptr& algorithm, - std::string shaderFilePath); - - /** - * Constructor that enables raw shader data to be passed to the main operation - * which can be either in raw shader glsl code or in compiled SPIR-V binary. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param shaderDataRaw Optional parameter to specify the shader data either in binary or raw form - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoDispatch(std::vector>& tensors, - std::shared_ptr& algorithm, - const std::vector& shaderDataRaw); + OpAlgoDispatch(const std::vector>& tensors, + const std::shared_ptr& algorithm); /** * Default destructor, which is in charge of destroying the algorithm @@ -69,15 +26,6 @@ class OpAlgoDispatch : public OpBase */ virtual ~OpAlgoDispatch() override; - /** - * The init function is responsible for the initialisation of the algorithm - * component based on the parameters specified, and allows for extensibility - * on the options provided. Further dependent classes can perform more - * specific checks such as ensuring tensors provided are initialised, etc. - */ - virtual void init(std::shared_ptr physicalDevice, - std::shared_ptr device) override; - /** * This records the commands that are to be sent to the GPU. This includes * the barriers that ensure the memory has been copied before going in and @@ -88,7 +36,6 @@ class OpAlgoDispatch : public OpBase */ virtual void record(std::shared_ptr commandBuffer) override; - /** * Does not perform any preEval commands. */ @@ -101,6 +48,10 @@ class OpAlgoDispatch : public OpBase */ virtual void postEval() override; +private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; + std::shared_ptr mAlgorithm; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp b/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp deleted file mode 100644 index 65cdf14a1..000000000 --- a/src/include/kompute/operations/OpAlgoLhsRhsOut.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include - -#include "kompute/Core.hpp" - -#include "kompute/Algorithm.hpp" -#include "kompute/Tensor.hpp" - -#include "kompute/operations/OpAlgoCreate.hpp" - -namespace kp { - -/** - * Operation base class to simplify the creation of operations that require - * right hand and left hand side datapoints together with a single output. - * The expected data passed is two input tensors and one output tensor. - */ -class OpAlgoLhsRhsOut : public OpAlgoCreate -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param freeTensors Whether operation manages the memory of the Tensors - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpAlgoLhsRhsOut(std::vector>& tensors, - std::shared_ptr algorithm); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoLhsRhsOut() override; - - /** - * The init function is responsible for ensuring that all of the tensors - * provided are aligned with requirements such as LHS, RHS and Output - * tensors, and creates the algorithm component which processes the - * computation. - */ - virtual void init(std::shared_ptr physicalDevice, - std::shared_ptr device) override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - - protected: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mTensorLHS; ///< Reference to the parameter used in the left hand side equation of the shader - std::shared_ptr mTensorRHS; ///< Reference to the parameter used in the right hand side equation of the shader - std::shared_ptr mTensorOutput; ///< Reference to the parameter used in the output of the shader and will be copied with a staging vector -}; - -} // End namespace kp - diff --git a/src/include/kompute/operations/OpBase.hpp b/src/include/kompute/operations/OpBase.hpp index f54d01390..fd628cf02 100644 --- a/src/include/kompute/operations/OpBase.hpp +++ b/src/include/kompute/operations/OpBase.hpp @@ -19,25 +19,6 @@ class OpBase { public: - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - */ - OpBase(std::vector>& tensors, - std::shared_ptr algorithm) - { - KP_LOG_DEBUG("Compute OpBase constructor with params"); - this->mTensors = tensors; - this->mAlgorithm = algorithm; - this->mIsInit = false; - } - /** * Default destructor for OpBase class. This OpBase destructor class should * always be called to destroy and free owned resources unless it is @@ -46,81 +27,6 @@ class OpBase virtual ~OpBase() { KP_LOG_DEBUG("Kompute OpBase destructor started"); - this->destroy(); - } - - virtual std::shared_ptr algorithm() { - return this->mAlgorithm; - } - - virtual std::vector> tensors() { - return this->mTensors; - } - - virtual bool isInit() { - return this->mIsInit; - } - - /** - * The init function is responsible for setting up all the resources and - * should be called after the Operation has been created. - */ - // TODO: Potentially remove physicalDevice in favour of memoryProperties (for tensor) - virtual void init( - std::shared_ptr physicalDevice, - std::shared_ptr device) { - - if (this->mTensors.size() < 1) { - throw std::runtime_error("Kompute OpBase init called with 0 tensors"); - } - - if (this->mManagesTensors) { - for (std::shared_ptr tensor : this->mTensors) { - if (tensor->isInit()) { - // TODO: Evaluate whether throwing runtime error or just writing error log - throw std::runtime_error( - "Kompute OpTensorCreate: Tensor has already been initialized"); - } - else { - tensor->init(physicalDevice, device); - } - } - } - - if (this->mManagesAlgorithm) { - this->mAlgorithm->init(device, this->mTensors); - } - } - - virtual void destroy() { - if (!this->mIsInit) { - KP_LOG_WARN("Kompute OpBase destroy called but not initialised"); - } - - if (this->mManagesTensors) { - for (const std::shared_ptr& tensor : this->mTensors) { - if (!tensor->isInit()) { - KP_LOG_WARN("Kompute OpBase attempted to free managed tensor " - "but tensor is not initialised"); - } else { - KP_LOG_DEBUG("Kompute OpBase freeing tensor"); - tensor->freeMemoryDestroyGPUResources(); - } - } - this->mTensors.clear(); - } - - if (this->mManagesAlgorithm) { - if (this->mAlgorithm && this->mAlgorithm->isInit()) { - KP_LOG_DEBUG("Kompute OpBase freeing tensor"); - this->mAlgorithm->freeMemoryDestroyGPUResources(); - } else { - KP_LOG_WARN("Kompute OpBase attempted to free managed algorithm" - "but algorithm is not initialised"); - } - } - - this->mIsInit = false; } /** @@ -149,16 +55,6 @@ class OpBase * provided by the user. */ virtual void postEval() = 0; - - protected: - // -------------- OPTIONALLY OWNED RESOURCES - std::vector> mTensors; - bool mManagesTensors = false; - std::shared_ptr mAlgorithm; - bool mManagesAlgorithm = false; - - // -------------- ALWAYS OWNED RESOURCES - bool mIsInit; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index 485210f0a..184a30cd9 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -4,14 +4,12 @@ #include "kompute/Core.hpp" -#if RELEASE #include "kompute/shaders/shaderopmult.hpp" -#endif #include "kompute/Algorithm.hpp" #include "kompute/Tensor.hpp" -#include "kompute/operations/OpAlgoCreate.hpp" +#include "kompute/operations/OpAlgoDispatch.hpp" namespace kp { @@ -19,15 +17,9 @@ namespace kp { * Operation that performs multiplication on two tensors and outpus on third * tensor. */ -class OpMult : public OpAlgoCreate +class OpMult : public OpAlgoDispatch { public: - /** - * Base constructor, should not be used unless explicitly intended. - */ - OpMult() { - - } /** * Default constructor with parameters that provides the bare minimum @@ -40,46 +32,30 @@ class OpMult : public OpAlgoCreate * @param tensors Tensors that are to be used in this operation * @param komputeWorkgroup Optional parameter to specify the layout for processing */ - OpMult(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors, - const Workgroup& komputeWorkgroup = {}) - : OpAlgoCreate(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup) + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(tensors, algorithm) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); -#ifndef RELEASE - this->mShaderFilePath = "shaders/glsl/opmult.comp.spv"; -#endif - } + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } -#if RELEASE - /** - * If RELEASE=1 it will be using the static version of the shader which is - * loaded using this file directly. Otherwise it should not override the function. - */ - std::vector fetchSpirvBinaryData() override - { - KP_LOG_WARN( - "Kompute OpMult Running shaders directly from header"); - - return std::vector( + std::vector spirv( (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + algorithm->rebuild(tensors, spirv, Workgroup({tensors[0]->size()})); } -#endif /** * Default destructor, which is in charge of destroying the algorithm * components but does not destroy the underlying tensors */ - ~OpMult() override { + virtual ~OpMult() override { KP_LOG_DEBUG("Kompute OpMult destructor started"); } - }; } // End namespace kp diff --git a/src/include/kompute/operations/OpTensorCopy.hpp b/src/include/kompute/operations/OpTensorCopy.hpp index d35139e8c..01fad8334 100644 --- a/src/include/kompute/operations/OpTensorCopy.hpp +++ b/src/include/kompute/operations/OpTensorCopy.hpp @@ -22,19 +22,13 @@ class OpTensorCopy : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorCopy(std::vector> tensors); + OpTensorCopy(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorCopy() override; - /** - * Performs basic checks such as ensuring there are at least two tensors provided, that they are initialised and that they are not of type TensorTypes::eStorage. - */ - void init(std::shared_ptr physicalDevice, - std::shared_ptr device) override; - /** * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. */ @@ -51,6 +45,8 @@ class OpTensorCopy : public OpBase virtual void postEval() override; private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpTensorCreate.hpp b/src/include/kompute/operations/OpTensorCreate.hpp deleted file mode 100644 index b4ac80862..000000000 --- a/src/include/kompute/operations/OpTensorCreate.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include "kompute/Core.hpp" - -#include "kompute/operations/OpBase.hpp" -#include "kompute/Tensor.hpp" -#include "kompute/Algorithm.hpp" - -namespace kp { - -/** - * Base Operation which provides the high level interface that Kompute - * operations implement in order to perform a set of actions in the GPU. - * - * Operations can perform actions on tensors, and optionally can also own an - * Algorithm with respective parameters. kp::Operations with kp::Algorithms - * would inherit from kp::OpBaseAlgo. - */ -class OpTensorCreate : public OpBase -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - */ - OpTensorCreate(std::vector>& tensors); - - /** - * Default destructor for OpTensorCreate class. This OpTensorCreate destructor class should - * always be called to destroy and free owned resources unless it is - * intended to destroy the resources in the parent class. - */ - virtual ~OpTensorCreate() override; - - /** - * The init function is responsible for setting up all the resources and - * should be called after the Operation has been created. - */ - virtual void init( - std::shared_ptr physicalDevice, - std::shared_ptr device) override; - - /** - * Record runs the core actions to create the tensors. For device tensors - * it records a copyCommand to move the data from the staging tensor to the - * device tensor. The mapping for staging tensors happens in the init function - * not in the record function. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Performs a copy back into the main tensor to ensure that the data - * contained is the one that is now being stored in the GPU. - */ - virtual void postEval() override; - -}; - -} // End namespace kp diff --git a/src/include/kompute/operations/OpTensorSyncDevice.hpp b/src/include/kompute/operations/OpTensorSyncDevice.hpp index 35e97a475..8addce188 100644 --- a/src/include/kompute/operations/OpTensorSyncDevice.hpp +++ b/src/include/kompute/operations/OpTensorSyncDevice.hpp @@ -21,19 +21,13 @@ class OpTensorSyncDevice : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncDevice(std::vector> tensors); + OpTensorSyncDevice(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorSyncDevice() override; - /** - * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. - */ - void init(std::shared_ptr physicalDevice, - std::shared_ptr device) override; - /** * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. */ @@ -50,6 +44,8 @@ class OpTensorSyncDevice : public OpBase virtual void postEval() override; private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpTensorSyncLocal.hpp b/src/include/kompute/operations/OpTensorSyncLocal.hpp index eebdd7084..7df8ccdd7 100644 --- a/src/include/kompute/operations/OpTensorSyncLocal.hpp +++ b/src/include/kompute/operations/OpTensorSyncLocal.hpp @@ -22,19 +22,13 @@ class OpTensorSyncLocal : public OpBase * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ - OpTensorSyncLocal(std::vector> tensors); + OpTensorSyncLocal(const std::vector>& tensors); /** * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. */ ~OpTensorSyncLocal() override; - /** - * Performs basic checks such as ensuring that there is at least one tensor provided with min memory of 1 element. - */ - void init(std::shared_ptr physicalDevice, - std::shared_ptr device) override; - /** * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. */ @@ -52,6 +46,8 @@ class OpTensorSyncLocal : public OpBase private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; }; } // End namespace kp From f35a62ee9d19209ba535f89646c3143d8d869f54 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Wed, 24 Feb 2021 22:53:46 +0000 Subject: [PATCH 03/91] Updated core initial running test --- src/Algorithm.cpp | 13 ++++++++++++ src/Manager.cpp | 48 ++++++++++++++++++++++-------------------- test/CMakeLists.txt | 3 ++- test/TestWorkgroup.cpp | 41 ++++++++++++++++++------------------ 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 512e0e5aa..d9dda9dfa 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -37,6 +37,11 @@ Algorithm::rebuild( { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); + this->mSpirv = spirv; + this->mWorkgroup = workgroup; + this->mSpecializationConstants = specializationConstants; + this->mPushConstants = pushConstants; + // Descriptor pool is created first so if available then destroy all before rebuild if (this->mFreeDescriptorPool) { this->freeMemoryDestroyGPUResources(); @@ -303,6 +308,8 @@ Algorithm::createPipeline() this->mFreePipeline = true; this->mPipeline = std::make_shared(pipelineResult); + + KP_LOG_DEBUG("Kompute Algorithm Create Pipeline Success"); } void @@ -313,6 +320,8 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, *this->mPipeline); + KP_LOG_DEBUG("Kompute Algorithm pipeline bound"); + commandBuffer->bindDescriptorSets(vk::PipelineBindPoint::eCompute, *this->mPipelineLayout, 0, // First set @@ -320,7 +329,11 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) nullptr // Dispatcher ); + KP_LOG_DEBUG("Kompute Algorithm descriptor sets bound"); + commandBuffer->dispatch(this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); + + KP_LOG_DEBUG("Kompute Algorithm dispatch success"); } void diff --git a/src/Manager.cpp b/src/Manager.cpp index ba0249f1d..9b12bdf53 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -119,24 +119,6 @@ Manager::~Manager() } } -std::shared_ptr -Manager::sequence(uint32_t queueIndex) -{ - KP_LOG_DEBUG("Kompute Manager sequence() with sequenceName: {} " - "and queueIndex: {}", - queueIndex); - - std::shared_ptr sq = - std::make_shared(this->mPhysicalDevice, - this->mDevice, - this->mComputeQueues[queueIndex], - this->mComputeQueueFamilyIndices[queueIndex]); - - this->mManagedSequences.push_back(sq); - - return sq; -} - void Manager::createInstance() { @@ -334,13 +316,15 @@ Manager::tensor( { KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - std::shared_ptr tensor = std::make_shared( - kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType)); + std::shared_ptr tensor{ + new kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType) }; this->mManagedTensors.push_back(tensor); return tensor; } + + std::shared_ptr Manager::algorithm( const std::vector>& tensors, @@ -351,18 +335,36 @@ Manager::algorithm( KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); - std::shared_ptr algorithm = std::make_shared( - kp::Algorithm( + std::shared_ptr algorithm{ + new kp::Algorithm( this->mDevice, tensors, spirv, workgroup, specializationConstants, - pushConstants)); + pushConstants)}; this->mManagedAlgorithms.push_back(algorithm); return algorithm; } +std::shared_ptr +Manager::sequence(uint32_t queueIndex) +{ + KP_LOG_DEBUG("Kompute Manager sequence() with sequenceName: {} " + "and queueIndex: {}", + queueIndex); + + std::shared_ptr sq{ + new kp::Sequence(this->mPhysicalDevice, + this->mDevice, + this->mComputeQueues[queueIndex], + this->mComputeQueueFamilyIndices[queueIndex]) }; + + this->mManagedSequences.push_back(sq); + + return sq; +} + } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a30792aad..3102ec648 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,7 +11,8 @@ else() endif() file(GLOB test_kompute_CPP - "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/TestWorkgroup.cpp" ) add_executable(test_kompute ${test_kompute_CPP}) diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 015874546..3b5942f70 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -8,41 +8,40 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) { - std::shared_ptr tensorA{ new kp::Tensor(std::vector(16 * 8)) }; - std::shared_ptr tensorB{ new kp::Tensor(std::vector(16 * 8)) }; - { std::shared_ptr sq = nullptr; { kp::Manager mgr; - mgr.rebuild({ tensorA, tensorB }); + std::shared_ptr tensorA = mgr.tensor(std::vector(16 * 8)); + std::shared_ptr tensorB = mgr.tensor(std::vector(16 * 8)); + + std::vector> params = {tensorA, tensorB}; + + std::vector spirv( + (uint32_t*)kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv, + (uint32_t*)(kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv + + kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv_len)); kp::Workgroup workgroup = {16, 8, 1}; - sq = mgr.sequence(); - sq->begin(); - sq->record( - { tensorA, tensorB }, - std::vector( - (uint32_t*)kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv, - (uint32_t*)(kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv + - kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv_len)), - workgroup); - sq->end(); + std::shared_ptr algorithm = mgr.algorithm(params, spirv, workgroup); + sq = mgr.sequence(); + sq->record(std::make_shared(params)); + sq->record(std::make_shared(params, algorithm)); + sq->record(std::make_shared(params)); sq->eval(); - mgr.evalOpDefault({ tensorA, tensorB }); + std::vector expectedA = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}; + + std::vector expectedB = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; + + EXPECT_EQ(tensorA->data(), expectedA); + EXPECT_EQ(tensorB->data(), expectedB); } } - std::vector expectedA = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}; - - std::vector expectedB = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; - - EXPECT_EQ(tensorA->data(), expectedA); - EXPECT_EQ(tensorB->data(), expectedB); } From 3f1288271d4336f2a976a0ee98ef0bbb7d11caab Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 25 Feb 2021 08:15:44 +0000 Subject: [PATCH 04/91] Working initial base --- Makefile | 2 +- src/Algorithm.cpp | 44 ++++++++++++++++++++++++++++++------------ src/Manager.cpp | 8 ++++---- src/OpAlgoDispatch.cpp | 2 ++ src/Sequence.cpp | 17 +++++++++++----- src/Tensor.cpp | 4 ++++ test/TestWorkgroup.cpp | 19 +++++++++--------- 7 files changed, 65 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 8f39a254a..74a6822b0 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,6 @@ MK_KOMPUTE_EXTRA_CXX_FLAGS ?= "" mk_cmake: cmake \ -Bbuild \ - $(MK_CMAKE_EXTRA_FLAGS) \ -DKOMPUTE_EXTRA_CXX_FLAGS=$(MK_KOMPUTE_EXTRA_CXX_FLAGS) \ -DCMAKE_BUILD_TYPE=$(MK_BUILD_TYPE) \ -DCMAKE_INSTALL_PREFIX=$(MK_INSTALL_PATH) \ @@ -69,6 +68,7 @@ mk_cmake: -DKOMPUTE_OPT_BUILD_SINGLE_HEADER=1 \ -DKOMPUTE_OPT_ENABLE_SPDLOG=1 \ -DKOMPUTE_OPT_CODE_COVERAGE=1 \ + $(MK_CMAKE_EXTRA_FLAGS) \ -G "Unix Makefiles" mk_build_all: diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index d9dda9dfa..6ebb08efe 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -15,8 +15,6 @@ Algorithm::Algorithm( KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); this->mDevice = device; - this->setWorkgroup(workgroup); - this->mPushConstants = pushConstants; this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); } @@ -37,8 +35,8 @@ Algorithm::rebuild( { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); + this->setWorkgroup(workgroup); this->mSpirv = spirv; - this->mWorkgroup = workgroup; this->mSpecializationConstants = specializationConstants; this->mPushConstants = pushConstants; @@ -300,14 +298,20 @@ Algorithm::createPipeline() throw std::runtime_error("Failed to create pipeline result: " + vk::to_string(pipelineResult.result)); } -#else - vk::Pipeline pipelineResult = - this->mDevice->createComputePipeline(*this->mPipelineCache, pipelineInfo); + + vk::Pipeline& pipeline = pipelineResult.value; + this->mPipeline = std::make_shared(pipeline); this->mFreePipeline = true; +#else + vk::Pipeline pipeline = + this->mDevice->createComputePipeline(*this->mPipelineCache, pipelineInfo); + this->mPipeline = std::make_shared(pipeline); #endif - this->mFreePipeline = true; - this->mPipeline = std::make_shared(pipelineResult); + // TODO: Update to consistent + // this->mPipeline = std::make_shared(); + // this->mDevice->createComputePipelines( + // *this->mPipelineCache, 1, &pipelineInfo, nullptr, this->mPipeline.get()); KP_LOG_DEBUG("Kompute Algorithm Create Pipeline Success"); } @@ -317,6 +321,20 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute Algorithm calling record dispatch"); + if(this->mPipelineCache) { + KP_LOG_WARN("Value valid"); + } + else { + KP_LOG_WARN("NOT Value valid"); + } + + if(this->mPipeline) { + KP_LOG_WARN("Value valid"); + } + else { + KP_LOG_WARN("NOT Value valid"); + } + commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, *this->mPipeline); @@ -338,6 +356,12 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) void Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { + + KP_LOG_INFO("Kompute OpAlgoCreate setting dispatch size X: {}, Y: {}, Z: {}", + this->mWorkgroup[0], + this->mWorkgroup[1], + this->mWorkgroup[2]); + // The dispatch size is set up based on either explicitly provided template // parameters or by default it would take the shape and size of the tensors if (workgroup[0] > 0) { @@ -351,10 +375,6 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { } else { this->mWorkgroup = { minSize, 1, 1 }; } - KP_LOG_INFO("Kompute OpAlgoCreate dispatch size X: {}, Y: {}, Z: {}", - this->mWorkgroup[0], - this->mWorkgroup[1], - this->mWorkgroup[2]); } } diff --git a/src/Manager.cpp b/src/Manager.cpp index 9b12bdf53..04e3a7e8b 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -4,6 +4,8 @@ #include "kompute/Manager.hpp" +#include "kompute/operations/OpAlgoDispatch.hpp" + namespace kp { #if DEBUG @@ -76,7 +78,7 @@ Manager::~Manager() algorithm->freeMemoryDestroyGPUResources(); } } - this->mManagedTensors.clear(); + this->mManagedAlgorithms.clear(); } if (this->mManagedTensors.size()) { @@ -324,7 +326,6 @@ Manager::tensor( return tensor; } - std::shared_ptr Manager::algorithm( const std::vector>& tensors, @@ -352,8 +353,7 @@ Manager::algorithm( std::shared_ptr Manager::sequence(uint32_t queueIndex) { - KP_LOG_DEBUG("Kompute Manager sequence() with sequenceName: {} " - "and queueIndex: {}", + KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", queueIndex); std::shared_ptr sq{ diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 3623fcddd..b4ecdcf57 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -9,6 +9,8 @@ OpAlgoDispatch::OpAlgoDispatch(const std::vector>& tenso { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); + this->mTensors = tensors; + this->mAlgorithm = algorithm; } OpAlgoDispatch::~OpAlgoDispatch() diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 20e441500..da8771cc3 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -40,11 +40,9 @@ Sequence::begin() throw std::runtime_error("Kompute Sequence begin called when sequence still running"); } - if (!this->mRecording) { - KP_LOG_INFO("Kompute Sequence command recording BEGIN"); - this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); - this->mRecording = true; - } + KP_LOG_INFO("Kompute Sequence command now started recording"); + this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); + this->mRecording = true; } void @@ -161,6 +159,10 @@ Sequence::freeMemoryDestroyGPUResources() } this->mDevice->freeCommandBuffers( *this->mCommandPool, 1, this->mCommandBuffer.get()); + + this->mCommandBuffer = nullptr; + this->mFreeCommandBuffer = false; + KP_LOG_DEBUG("Kompute Sequence Freed CommandBuffer"); } @@ -175,6 +177,10 @@ Sequence::freeMemoryDestroyGPUResources() this->mDevice->destroy( *this->mCommandPool, (vk::Optional)nullptr); + + this->mCommandPool = nullptr; + this->mFreeCommandPool = false; + KP_LOG_DEBUG("Kompute Sequence Destroyed CommandPool"); } @@ -194,6 +200,7 @@ Sequence::record(std::shared_ptr op) KP_LOG_DEBUG( "Kompute Sequence running record on OpBase derived class instance"); + op->record(this->mCommandBuffer); this->mOperations.push_back(op); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 1ae4662f8..3078acd8a 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -449,6 +449,7 @@ Tensor::freeMemoryDestroyGPUResources() *this->mPrimaryBuffer, (vk::Optional)nullptr); this->mPrimaryBuffer = nullptr; + this->mFreePrimaryBuffer = false; } } @@ -462,6 +463,7 @@ Tensor::freeMemoryDestroyGPUResources() *this->mStagingBuffer, (vk::Optional)nullptr); this->mStagingBuffer = nullptr; + this->mFreeStagingBuffer = false; } } @@ -475,6 +477,7 @@ Tensor::freeMemoryDestroyGPUResources() *this->mPrimaryMemory, (vk::Optional)nullptr); this->mPrimaryMemory = nullptr; + this->mFreePrimaryMemory = false; } } @@ -488,6 +491,7 @@ Tensor::freeMemoryDestroyGPUResources() *this->mStagingMemory, (vk::Optional)nullptr); this->mStagingMemory = nullptr; + this->mFreeStagingMemory = false; } } diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 3b5942f70..73cbdce61 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -8,14 +8,16 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) { + std::shared_ptr tensorA = nullptr; + std::shared_ptr tensorB = nullptr; { std::shared_ptr sq = nullptr; { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor(std::vector(16 * 8)); - std::shared_ptr tensorB = mgr.tensor(std::vector(16 * 8)); + tensorA = mgr.tensor(std::vector(16 * 8)); + tensorB = mgr.tensor(std::vector(16 * 8)); std::vector> params = {tensorA, tensorB}; @@ -33,15 +35,14 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) sq->record(std::make_shared(params, algorithm)); sq->record(std::make_shared(params)); sq->eval(); - - std::vector expectedA = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}; - - std::vector expectedB = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; - - EXPECT_EQ(tensorA->data(), expectedA); - EXPECT_EQ(tensorB->data(), expectedB); } } + std::vector expectedA = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}; + + std::vector expectedB = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; + + EXPECT_EQ(tensorA->data(), expectedA); + EXPECT_EQ(tensorB->data(), expectedB); } From 6378583a233269902f7d6d5cdd96f232fedd1741 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 25 Feb 2021 22:33:08 +0000 Subject: [PATCH 05/91] Further tests added to new structure --- single_include/AggregateHeaders.cpp | 1 + single_include/kompute/Kompute.hpp | 212 +++++++++--- src/Algorithm.cpp | 59 +++- src/Manager.cpp | 76 ++++- src/OpAlgoDispatch.cpp | 6 +- src/Sequence.cpp | 33 +- src/Tensor.cpp | 15 +- src/include/kompute/Algorithm.hpp | 16 +- src/include/kompute/Manager.hpp | 13 +- src/include/kompute/Sequence.hpp | 178 ++++++++-- src/include/kompute/Tensor.hpp | 4 +- .../kompute/operations/OpAlgoDispatch.hpp | 4 +- test/CMakeLists.txt | 3 +- test/TestAsyncOperations.cpp | 62 ++-- test/TestDestroy.cpp | 315 ++---------------- test/TestLogisticRegression.cpp | 144 ++++---- test/TestWorkgroup.cpp | 9 +- 17 files changed, 636 insertions(+), 514 deletions(-) diff --git a/single_include/AggregateHeaders.cpp b/single_include/AggregateHeaders.cpp index da1eaabf0..d47892cf1 100644 --- a/single_include/AggregateHeaders.cpp +++ b/single_include/AggregateHeaders.cpp @@ -10,5 +10,6 @@ #include "kompute/operations/OpTensorCopy.hpp" #include "kompute/operations/OpTensorSyncDevice.hpp" #include "kompute/operations/OpTensorSyncLocal.hpp" +#include "kompute/operations/OpAlgoDispatch.hpp" #include "kompute/Algorithm.hpp" #include "kompute/Tensor.hpp" diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 629ff6a4b..01a2e7522 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -928,7 +928,9 @@ class Tensor /** * Destroys and frees the GPU resources which include the buffer and memory. */ - void freeMemoryDestroyGPUResources(); + void destroy(); + + bool isInit(); /** * Returns the vector of data currently contained by the Tensor. It is @@ -1129,10 +1131,6 @@ public: const Constants& specializationConstants = {}, const Constants& pushConstants = {}); - bool isInit(); - - void freeMemoryDestroyGPUResources(); - /** * Destructor for Algorithm which is responsible for freeing and desroying * respective pipelines and owned parameter groups. @@ -1149,11 +1147,21 @@ public: */ void recordDispatch(std::shared_ptr commandBuffer); + bool isInit(); + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + const Workgroup& getWorkgroup(); + const Constants& getSpecializationConstants(); + const Constants& getPushConstants(); + const std::vector>& getTensors(); + + void destroy(); + private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mDevice; + std::vector> mTensors; // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mDescriptorSetLayout; @@ -1184,7 +1192,7 @@ private: void createPipeline(); // Parameters - void createParameters(const std::vector>& tensorParams); + void createParameters(); }; } // End namespace kp @@ -1270,6 +1278,10 @@ class Sequence: public std::enable_shared_from_this */ ~Sequence(); + /** + */ + std::shared_ptr record(std::shared_ptr op); + /** * Record function for operation to be added to the GPU queue in batch. This * template requires classes to be derived from the OpBase class. This @@ -1280,7 +1292,146 @@ class Sequence: public std::enable_shared_from_this * @param TArgs Template parameters that are used to initialise operation * which allows for extensible configurations on initialisation. */ - std::shared_ptr record(std::shared_ptr op); + template + std::shared_ptr + record(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->record(op); + } + template + std::shared_ptr + record(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->record(op); + } + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + std::shared_ptr eval(); + + std::shared_ptr eval(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + eval(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->eval(op); + } + // Needded as otherise can't use initialiser list + template + std::shared_ptr + eval(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->eval(op); + } + + /** + * Eval Async sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. EvalAwait() must + * be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAsync(); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + evalAsync(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->evalAsync(op); + } + // Needed as otherwise it's not possible to use initializer lists + template + std::shared_ptr + evalAsync(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->evalAsync(op); + } + + /** + * Eval Await waits for the fence to finish processing and then once it + * finishes, it runs the postEval of all operations. + * + * @param waitFor Number of milliseconds to wait before timing out. + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** * Clear function clears all operations currently recorded and starts recording again. @@ -1303,32 +1454,6 @@ class Sequence: public std::enable_shared_from_this */ void end(); - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr eval(); - - /** - * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAsync(); - - /** - * Eval Await waits for the fence to finish processing and then once it - * finishes, it runs the postEval of all operations. - * - * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); - /** * Returns true if the sequence is currently in recording activated. * @@ -1336,6 +1461,8 @@ class Sequence: public std::enable_shared_from_this */ bool isRecording(); + bool isInit(); + /** * Returns true if the sequence is currently running - mostly used for async * workloads. @@ -1348,7 +1475,7 @@ class Sequence: public std::enable_shared_from_this * Destroys and frees the GPU resources which include the buffer and memory * and sets the sequence as init=False. */ - void freeMemoryDestroyGPUResources(); + void destroy(); private: // -------------- NEVER OWNED RESOURCES @@ -1444,6 +1571,8 @@ class Manager * they would like to create the resources on. * * @param physicalDeviceIndex The index of the physical device to use + * @param manageResources (Optional) Whether to manage the memory of the + * resources created and destroy when the manager is destroyed. * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation * @param totalQueues The total number of compute queues to create. @@ -1462,8 +1591,7 @@ class Manager */ Manager(std::shared_ptr instance, std::shared_ptr physicalDevice, - std::shared_ptr device, - uint32_t physicalDeviceIndex); + std::shared_ptr device); /** * Manager destructor which would ensure all owned resources are destroyed @@ -1506,12 +1634,14 @@ class Manager const Constants& specializationConstants = {}, const Constants& pushConstants = {}); + void destroy(); + void clear(); + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; bool mFreeInstance = false; std::shared_ptr mPhysicalDevice = nullptr; - uint32_t mPhysicalDeviceIndex = -1; std::shared_ptr mDevice = nullptr; bool mFreeDevice = false; @@ -1523,7 +1653,7 @@ class Manager std::vector mComputeQueueFamilyIndices; std::vector> mComputeQueues; - uint32_t mCurrentSequenceIndex = -1; + bool mManageResources = false; #if DEBUG #ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS @@ -1534,7 +1664,7 @@ class Manager // Create functions void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}); + void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); }; } // End namespace kp @@ -1553,8 +1683,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::vector>& tensors, - const std::shared_ptr& algorithm); + OpAlgoDispatch(const std::shared_ptr& algorithm); /** * Default destructor, which is in charge of destroying the algorithm @@ -1586,7 +1715,6 @@ class OpAlgoDispatch : public OpBase private: // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; std::shared_ptr mAlgorithm; }; diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 6ebb08efe..aee9ddd36 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -22,7 +22,7 @@ Algorithm::~Algorithm() { KP_LOG_DEBUG("Kompute Algorithm Destructor started"); - this->freeMemoryDestroyGPUResources(); + this->destroy(); } void @@ -35,23 +35,35 @@ Algorithm::rebuild( { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); - this->setWorkgroup(workgroup); + this->mTensors = tensors; this->mSpirv = spirv; this->mSpecializationConstants = specializationConstants; this->mPushConstants = pushConstants; + this->setWorkgroup(workgroup); // Descriptor pool is created first so if available then destroy all before rebuild if (this->mFreeDescriptorPool) { - this->freeMemoryDestroyGPUResources(); + this->destroy(); } - this->createParameters(tensors); + this->createParameters(); this->createShaderModule(); this->createPipeline(); } +bool +Algorithm::isInit() { + return this->mPipeline && + this->mPipelineCache && + this->mPipelineLayout && + this->mDescriptorPool && + this->mDescriptorSet && + this->mDescriptorSetLayout && + this->mShaderModule; +} + void -Algorithm::freeMemoryDestroyGPUResources() { +Algorithm::destroy() { if (!this->mDevice) { KP_LOG_WARN( @@ -68,6 +80,7 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mPipeline, (vk::Optional)nullptr); + this->mPipeline = nullptr; } if (this->mFreePipelineCache) { @@ -79,6 +92,7 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mPipelineCache, (vk::Optional)nullptr); + this->mPipelineCache = nullptr; } if (this->mFreePipelineLayout) { @@ -90,6 +104,7 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mPipelineLayout, (vk::Optional)nullptr); + this->mPipelineLayout = nullptr; } if (this->mFreeShaderModule) { @@ -101,6 +116,7 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mShaderModule, (vk::Optional)nullptr); + this->mShaderModule = nullptr; } if (this->mFreeDescriptorSet) { @@ -111,6 +127,7 @@ Algorithm::freeMemoryDestroyGPUResources() { } this->mDevice->freeDescriptorSets( *this->mDescriptorPool, 1, this->mDescriptorSet.get()); + this->mDescriptorSet = nullptr; } if (this->mFreeDescriptorSetLayout) { @@ -122,6 +139,7 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mDescriptorSetLayout, (vk::Optional)nullptr); + this->mDescriptorSetLayout = nullptr; } if (this->mFreeDescriptorPool) { @@ -133,18 +151,19 @@ Algorithm::freeMemoryDestroyGPUResources() { this->mDevice->destroy( *this->mDescriptorPool, (vk::Optional)nullptr); + this->mDescriptorPool = nullptr; } } void -Algorithm::createParameters(const std::vector>& tensorParams) +Algorithm::createParameters() { KP_LOG_DEBUG("Kompute Algorithm createParameters started"); std::vector descriptorPoolSizes = { vk::DescriptorPoolSize( vk::DescriptorType::eStorageBuffer, - static_cast(tensorParams.size()) // Descriptor count + static_cast(this->mTensors.size()) // Descriptor count ) }; @@ -161,7 +180,7 @@ Algorithm::createParameters(const std::vector>& tensorPa this->mFreeDescriptorPool = true; std::vector descriptorSetBindings; - for (size_t i = 0; i < tensorParams.size(); i++) { + for (size_t i = 0; i < this->mTensors.size(); i++) { descriptorSetBindings.push_back( vk::DescriptorSetLayoutBinding(i, // Binding index vk::DescriptorType::eStorageBuffer, @@ -193,11 +212,11 @@ Algorithm::createParameters(const std::vector>& tensorPa this->mFreeDescriptorSet = true; KP_LOG_DEBUG("Kompute Algorithm updating descriptor sets"); - for (size_t i = 0; i < tensorParams.size(); i++) { + for (size_t i = 0; i < this->mTensors.size(); i++) { std::vector computeWriteDescriptorSets; vk::DescriptorBufferInfo descriptorBufferInfo = - tensorParams[i]->constructDescriptorBufferInfo(); + this->mTensors[i]->constructDescriptorBufferInfo(); computeWriteDescriptorSets.push_back( vk::WriteDescriptorSet(*this->mDescriptorSet, @@ -377,4 +396,24 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { } } +const Workgroup& +Algorithm::getWorkgroup() { + return this->mWorkgroup; +} + +const Constants& +Algorithm::getSpecializationConstants() { + return this->mSpecializationConstants; +} + +const Constants& +Algorithm::getPushConstants() { + return this->mPushConstants; +} + +const std::vector>& +Algorithm::getTensors() { + return this->mTensors; +} + } diff --git a/src/Manager.cpp b/src/Manager.cpp index 04e3a7e8b..833069d9f 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -33,26 +33,33 @@ Manager::Manager() Manager::Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices) { - this->mPhysicalDeviceIndex = physicalDeviceIndex; + this->mManageResources = false; this->createInstance(); - this->createDevice(familyQueueIndices); + this->createDevice(familyQueueIndices, physicalDeviceIndex); } Manager::Manager(std::shared_ptr instance, std::shared_ptr physicalDevice, - std::shared_ptr device, - uint32_t physicalDeviceIndex) + std::shared_ptr device) { + this->mManageResources = true; + this->mInstance = instance; this->mPhysicalDevice = physicalDevice; this->mDevice = device; - this->mPhysicalDeviceIndex = physicalDeviceIndex; } Manager::~Manager() { KP_LOG_DEBUG("Kompute Manager Destructor started"); + this->destroy(); +} + +void +Manager::destroy() { + + KP_LOG_DEBUG("Kompute Manager destroy() started"); if (this->mDevice == nullptr) { KP_LOG_ERROR( @@ -60,32 +67,32 @@ Manager::~Manager() return; } - if (this->mManagedSequences.size()) { + if (this->mManageResources && this->mManagedSequences.size()) { KP_LOG_DEBUG("Kompute Manager explicitly running destructor for " "managed sequences"); for (const std::weak_ptr& weakSq : this->mManagedSequences) { if (std::shared_ptr sq = weakSq.lock()) { - sq->freeMemoryDestroyGPUResources(); + sq->destroy(); } } this->mManagedSequences.clear(); } - if (this->mManagedAlgorithms.size()) { + if (this->mManageResources && this->mManagedAlgorithms.size()) { KP_LOG_DEBUG("Kompute Manager explicitly freeing algorithms"); for (const std::weak_ptr& weakAlgorithm : this->mManagedAlgorithms) { if (std::shared_ptr algorithm = weakAlgorithm.lock()) { - algorithm->freeMemoryDestroyGPUResources(); + algorithm->destroy(); } } this->mManagedAlgorithms.clear(); } - if (this->mManagedTensors.size()) { + if (this->mManageResources && this->mManagedTensors.size()) { KP_LOG_DEBUG("Kompute Manager explicitly freeing tensors"); for (const std::weak_ptr& weakTensor : this->mManagedTensors) { if (std::shared_ptr tensor = weakTensor.lock()) { - tensor->freeMemoryDestroyGPUResources(); + tensor->destroy(); } } this->mManagedTensors.clear(); @@ -95,6 +102,7 @@ Manager::~Manager() KP_LOG_INFO("Destroying device"); this->mDevice->destroy( (vk::Optional)nullptr); + this->mDevice = nullptr; KP_LOG_DEBUG("Kompute Manager Destroyed Device"); } @@ -109,6 +117,7 @@ Manager::~Manager() if (this->mDebugReportCallback) { this->mInstance->destroyDebugReportCallbackEXT( this->mDebugReportCallback, nullptr, this->mDebugDispatcher); + this->mInstance = nullptr; KP_LOG_DEBUG("Kompute Manager Destroyed Debug Report Callback"); } #endif @@ -117,6 +126,7 @@ Manager::~Manager() if (this->mFreeInstance) { this->mInstance->destroy( (vk::Optional)nullptr); + this->mInstance = nullptr; KP_LOG_DEBUG("Kompute Manager Destroyed Instance"); } } @@ -207,7 +217,31 @@ Manager::createInstance() } void -Manager::createDevice(const std::vector& familyQueueIndices) +Manager::clear() { + if (this->mManageResources) { + this->mManagedTensors.erase( + std::remove_if( + begin(this->mManagedTensors), + end(this->mManagedTensors), + [](std::weak_ptr t) {return t.expired();}), + end(this->mManagedTensors)); + this->mManagedAlgorithms.erase( + std::remove_if( + begin(this->mManagedAlgorithms), + end(this->mManagedAlgorithms), + [](std::weak_ptr t) {return t.expired();}), + end(this->mManagedAlgorithms)); + this->mManagedSequences.erase( + std::remove_if( + begin(this->mManagedSequences), + end(this->mManagedSequences), + [](std::weak_ptr t) {return t.expired();}), + end(this->mManagedSequences)); + } +} + +void +Manager::createDevice(const std::vector& familyQueueIndices, uint32_t physicalDeviceIndex) { KP_LOG_DEBUG("Kompute Manager creating Device"); @@ -215,7 +249,7 @@ Manager::createDevice(const std::vector& familyQueueIndices) if (this->mInstance == nullptr) { throw std::runtime_error("Kompute Manager instance is null"); } - if (this->mPhysicalDeviceIndex < 0) { + if (physicalDeviceIndex < 0) { throw std::runtime_error( "Kompute Manager physical device index not provided"); } @@ -226,7 +260,7 @@ Manager::createDevice(const std::vector& familyQueueIndices) this->mInstance->enumeratePhysicalDevices(); vk::PhysicalDevice physicalDevice = - physicalDevices[this->mPhysicalDeviceIndex]; + physicalDevices[physicalDeviceIndex]; this->mPhysicalDevice = std::make_shared(physicalDevice); @@ -235,7 +269,7 @@ Manager::createDevice(const std::vector& familyQueueIndices) physicalDevice.getProperties(); KP_LOG_INFO("Using physical device index {} found {}", - this->mPhysicalDeviceIndex, + physicalDeviceIndex, physicalDeviceProperties.deviceName); if (!familyQueueIndices.size()) { @@ -321,7 +355,9 @@ Manager::tensor( std::shared_ptr tensor{ new kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType) }; - this->mManagedTensors.push_back(tensor); + if (this->mManageResources) { + this->mManagedTensors.push_back(tensor); + } return tensor; } @@ -345,7 +381,9 @@ Manager::algorithm( specializationConstants, pushConstants)}; - this->mManagedAlgorithms.push_back(algorithm); + if (this->mManageResources) { + this->mManagedAlgorithms.push_back(algorithm); + } return algorithm; } @@ -362,7 +400,9 @@ Manager::sequence(uint32_t queueIndex) this->mComputeQueues[queueIndex], this->mComputeQueueFamilyIndices[queueIndex]) }; - this->mManagedSequences.push_back(sq); + if (this->mManageResources) { + this->mManagedSequences.push_back(sq); + } return sq; } diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index b4ecdcf57..a20900189 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -4,12 +4,10 @@ namespace kp { -OpAlgoDispatch::OpAlgoDispatch(const std::vector>& tensors, - const std::shared_ptr& algorithm) +OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); - this->mTensors = tensors; this->mAlgorithm = algorithm; } @@ -24,7 +22,7 @@ OpAlgoDispatch::record(std::shared_ptr commandBuffer) KP_LOG_DEBUG("Kompute OpAlgoDispatch record called"); // Barrier to ensure the data is finished writing to buffer memory - for (std::shared_ptr tensor : this->mTensors) { + for (const std::shared_ptr& tensor : this->mAlgorithm->getTensors()) { tensor->recordBufferMemoryBarrier( commandBuffer, vk::AccessFlagBits::eHostWrite, diff --git a/src/Sequence.cpp b/src/Sequence.cpp index da8771cc3..52e147aaa 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -61,6 +61,12 @@ Sequence::end() } } +void +Sequence::clear() { + KP_LOG_DEBUG("Kompute Sequence calling clear"); + this->end(); +} + std::shared_ptr Sequence::eval() { @@ -69,6 +75,13 @@ Sequence::eval() return this->evalAsync()->evalAwait(); } +std::shared_ptr +Sequence::eval(std::shared_ptr op) { + this->clear(); + this->record(op); + this->eval(); +} + std::shared_ptr Sequence::evalAsync() { @@ -138,8 +151,16 @@ Sequence::isRecording() return this->mRecording; } +bool +Sequence::isInit() { + return this->mDevice && + this->mCommandPool && + this->mCommandBuffer && + this->mComputeQueue; +} + void -Sequence::freeMemoryDestroyGPUResources() +Sequence::destroy() { KP_LOG_DEBUG("Kompute Sequence freeMemoryDestroyGPUResources called"); @@ -189,6 +210,16 @@ Sequence::freeMemoryDestroyGPUResources() this->mOperations.clear(); } + if (this->mDevice) { + this->mDevice = nullptr; + } + if (this->mPhysicalDevice) { + this->mPhysicalDevice = nullptr; + } + if (this->mComputeQueue) { + this->mComputeQueue = nullptr; + } + } std::shared_ptr diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 3078acd8a..461e3ca1e 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -76,6 +76,15 @@ Tensor::tensorType() return this->mTensorType; } +bool +Tensor::isInit() { + return this->mDevice && + this->mPrimaryBuffer && + this->mPrimaryMemory && + this->mStagingBuffer && + this->mStagingMemory; +} + void Tensor::setData(const std::vector& data) { @@ -429,7 +438,7 @@ Tensor::allocateBindMemory(std::shared_ptr buffer, } void -Tensor::freeMemoryDestroyGPUResources() +Tensor::destroy() { KP_LOG_DEBUG("Kompute Tensor started freeMemoryDestroyGPUResources()"); @@ -495,6 +504,10 @@ Tensor::freeMemoryDestroyGPUResources() } } + if (this->mDevice) { + this->mDevice = nullptr; + } + KP_LOG_DEBUG("Kompute Tensor successful freeMemoryDestroyGPUResources()"); } diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index 4016c5efb..8b37f3e9a 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -45,10 +45,6 @@ public: const Constants& specializationConstants = {}, const Constants& pushConstants = {}); - bool isInit(); - - void freeMemoryDestroyGPUResources(); - /** * Destructor for Algorithm which is responsible for freeing and desroying * respective pipelines and owned parameter groups. @@ -65,11 +61,21 @@ public: */ void recordDispatch(std::shared_ptr commandBuffer); + bool isInit(); + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + const Workgroup& getWorkgroup(); + const Constants& getSpecializationConstants(); + const Constants& getPushConstants(); + const std::vector>& getTensors(); + + void destroy(); + private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mDevice; + std::vector> mTensors; // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mDescriptorSetLayout; @@ -100,7 +106,7 @@ private: void createPipeline(); // Parameters - void createParameters(const std::vector>& tensorParams); + void createParameters(); }; } // End namespace kp diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index cd808952a..0e97f12fa 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -30,6 +30,8 @@ class Manager * they would like to create the resources on. * * @param physicalDeviceIndex The index of the physical device to use + * @param manageResources (Optional) Whether to manage the memory of the + * resources created and destroy when the manager is destroyed. * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation * @param totalQueues The total number of compute queues to create. @@ -48,8 +50,7 @@ class Manager */ Manager(std::shared_ptr instance, std::shared_ptr physicalDevice, - std::shared_ptr device, - uint32_t physicalDeviceIndex); + std::shared_ptr device); /** * Manager destructor which would ensure all owned resources are destroyed @@ -92,12 +93,14 @@ class Manager const Constants& specializationConstants = {}, const Constants& pushConstants = {}); + void destroy(); + void clear(); + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; bool mFreeInstance = false; std::shared_ptr mPhysicalDevice = nullptr; - uint32_t mPhysicalDeviceIndex = -1; std::shared_ptr mDevice = nullptr; bool mFreeDevice = false; @@ -109,7 +112,7 @@ class Manager std::vector mComputeQueueFamilyIndices; std::vector> mComputeQueues; - uint32_t mCurrentSequenceIndex = -1; + bool mManageResources = false; #if DEBUG #ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS @@ -120,7 +123,7 @@ class Manager // Create functions void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}); + void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); }; } // End namespace kp diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index 47827d729..e3bac936e 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -31,6 +31,10 @@ class Sequence: public std::enable_shared_from_this */ ~Sequence(); + /** + */ + std::shared_ptr record(std::shared_ptr op); + /** * Record function for operation to be added to the GPU queue in batch. This * template requires classes to be derived from the OpBase class. This @@ -41,7 +45,148 @@ class Sequence: public std::enable_shared_from_this * @param TArgs Template parameters that are used to initialise operation * which allows for extensible configurations on initialisation. */ - std::shared_ptr record(std::shared_ptr op); + template + std::shared_ptr + record(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->record(op); + } + template + std::shared_ptr + record(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->record(op); + } + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + std::shared_ptr eval(); + + std::shared_ptr eval(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + // TODO: Aim to have only a single function with tensors/algorithm + template + std::shared_ptr + eval(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + // TODO: Aim to be able to handle errors when returning without throw except + return this->eval(op); + } + // Needded as otherise can't use initialiser list + template + std::shared_ptr + eval(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->eval(op); + } + + /** + * Eval Async sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. EvalAwait() must + * be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAsync(); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + evalAsync(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->evalAsync(op); + } + // Needed as otherwise it's not possible to use initializer lists + template + std::shared_ptr + evalAsync(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->evalAsync(op); + } + + /** + * Eval Await waits for the fence to finish processing and then once it + * finishes, it runs the postEval of all operations. + * + * @param waitFor Number of milliseconds to wait before timing out. + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** * Clear function clears all operations currently recorded and starts recording again. @@ -64,32 +209,6 @@ class Sequence: public std::enable_shared_from_this */ void end(); - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr eval(); - - /** - * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAsync(); - - /** - * Eval Await waits for the fence to finish processing and then once it - * finishes, it runs the postEval of all operations. - * - * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); - /** * Returns true if the sequence is currently in recording activated. * @@ -97,6 +216,9 @@ class Sequence: public std::enable_shared_from_this */ bool isRecording(); + + bool isInit(); + /** * Returns true if the sequence is currently running - mostly used for async * workloads. @@ -109,7 +231,7 @@ class Sequence: public std::enable_shared_from_this * Destroys and frees the GPU resources which include the buffer and memory * and sets the sequence as init=False. */ - void freeMemoryDestroyGPUResources(); + void destroy(); private: // -------------- NEVER OWNED RESOURCES diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 40adcc700..c3521b7a8 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -59,7 +59,9 @@ class Tensor /** * Destroys and frees the GPU resources which include the buffer and memory. */ - void freeMemoryDestroyGPUResources(); + void destroy(); + + bool isInit(); /** * Returns the vector of data currently contained by the Tensor. It is diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index 0af5b5fba..1b5ab1bf0 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,8 +17,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::vector>& tensors, - const std::shared_ptr& algorithm); + OpAlgoDispatch(const std::shared_ptr& algorithm); /** * Default destructor, which is in charge of destroying the algorithm @@ -50,7 +49,6 @@ class OpAlgoDispatch : public OpBase private: // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; std::shared_ptr mAlgorithm; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3102ec648..a30792aad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,8 +11,7 @@ else() endif() file(GLOB test_kompute_CPP - "${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/TestWorkgroup.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" ) add_executable(test_kompute ${test_kompute_CPP}) diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index 42efdff9b..6e5ba8adf 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -37,25 +37,32 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) } )"); + std::vector spirv = kp::Shader::compile_source(shader); + std::vector data(size, 0.0); std::vector resultSync(size, 100000000); std::vector resultAsync(size, 100000000); kp::Manager mgr; + std::shared_ptr sq = mgr.sequence(); + std::vector> inputsSyncB; + std::vector> algorithms; for (uint32_t i = 0; i < numParallel; i++) { - inputsSyncB.push_back(std::make_shared(kp::Tensor(data))); + inputsSyncB.push_back(mgr.tensor(data)); + algorithms.push_back(mgr.algorithm({ inputsSyncB[i] }, spirv)); } - mgr.rebuild(inputsSyncB); + sq->eval(inputsSyncB); + + mgr.sequence()->eval(inputsSyncB); auto startSync = std::chrono::high_resolution_clock::now(); for (uint32_t i = 0; i < numParallel; i++) { - mgr.evalOpDefault( - { inputsSyncB[i] }, kp::Shader::compile_source(shader)); + sq->eval(algorithms[i]); } auto endSync = std::chrono::high_resolution_clock::now(); @@ -63,7 +70,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) std::chrono::duration_cast(endSync - startSync) .count(); - mgr.evalOpDefault(inputsSyncB); + sq->eval(inputsSyncB); for (uint32_t i = 0; i < numParallel; i++) { EXPECT_EQ(inputsSyncB[i]->data(), resultSync); @@ -74,26 +81,23 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) std::vector> inputsAsyncB; for (uint32_t i = 0; i < numParallel; i++) { - inputsAsyncB.push_back(std::make_shared(kp::Tensor(data))); + inputsAsyncB.push_back(mgr.tensor(data)); } - mgrAsync.rebuild(inputsAsyncB); + std::vector> sqs; for (uint32_t i = 0; i < numParallel; i++) { - mgrAsync.sequence("async" + std::to_string(i), i); + sqs.push_back(mgrAsync.sequence(i)); } auto startAsync = std::chrono::high_resolution_clock::now(); for (uint32_t i = 0; i < numParallel; i++) { - mgrAsync.evalOpAsync( - { inputsAsyncB[i] }, - "async" + std::to_string(i), - kp::Shader::compile_source(shader)); + sqs[i]->evalAsync(algorithms[i]); } for (uint32_t i = 0; i < numParallel; i++) { - mgrAsync.evalOpAwait("async" + std::to_string(i)); + sqs[i]->evalAwait(); } auto endAsync = std::chrono::high_resolution_clock::now(); @@ -101,7 +105,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) endAsync - startAsync) .count(); - mgrAsync.evalOpDefault({ inputsAsyncB }); + sq->eval({ inputsAsyncB }); for (uint32_t i = 0; i < numParallel; i++) { EXPECT_EQ(inputsAsyncB[i]->data(), resultAsync); @@ -138,32 +142,32 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) } )"); + std::vector spirv = kp::Shader::compile_source(shader); + std::vector data(size, 0.0); std::vector resultAsync(size, 100000000); kp::Manager mgr; - std::shared_ptr tensorA{ new kp::Tensor(data) }; - std::shared_ptr tensorB{ new kp::Tensor(data) }; + std::shared_ptr tensorA = mgr.tensor(data); + std::shared_ptr tensorB = mgr.tensor(data); - mgr.sequence("asyncOne"); - mgr.sequence("asyncTwo"); + std::shared_ptr sq1 = mgr.sequence(); + std::shared_ptr sq2 = mgr.sequence(); - mgr.rebuild({ tensorA, tensorB }); + sq1->eval({ tensorA, tensorB }); - std::vector result = kp::Shader::compile_source(shader); + std::shared_ptr algo1 = mgr.algorithm({tensorA}); + std::shared_ptr algo2 = mgr.algorithm({tensorB}); - mgr.evalOpAsync( - { tensorA }, "asyncOne", kp::Shader::compile_source(shader)); + sq1->evalAsync(algo1); + sq2->evalAsync(algo2); - mgr.evalOpAsync( - { tensorB }, "asyncTwo", kp::Shader::compile_source(shader)); + sq1->evalAwait(); + sq2->evalAwait(); - mgr.evalOpAwait("asyncOne"); - mgr.evalOpAwait("asyncTwo"); - - mgr.evalOpAsyncDefault({ tensorA, tensorB }); - mgr.evalOpAwaitDefault(); + sq1->evalAsync({ tensorA, tensorB }); + sq1->evalAwait(); EXPECT_EQ(tensorA->data(), resultAsync); EXPECT_EQ(tensorB->data(), resultAsync); diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 940fdf722..43b7c8e1a 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -5,7 +5,7 @@ TEST(TestDestroy, TestDestroyTensorSingle) { - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; + std::shared_ptr tensorA = nullptr; std::string shader(R"( #version 450 @@ -16,37 +16,36 @@ TEST(TestDestroy, TestDestroyTensorSingle) pa[index] = pa[index] + 1; })"); + std::vector spirv = kp::Shader::compile_source(shader); + { std::shared_ptr sq = nullptr; { kp::Manager mgr; - mgr.rebuild({ tensorA }); + tensorA = mgr.tensor({ 0, 0, 0 }); - sq = mgr.sequence(); + std::shared_ptr algo = + mgr.algorithm({ tensorA }, spirv); - sq->begin(); - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq->end(); - - sq->eval(); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy(tensorA); + mgr.sequence() + ->record(algo) + ->eval() + ->eval(algo->getTensors()); + tensorA->destroy(); EXPECT_FALSE(tensorA->isInit()); } + EXPECT_FALSE(tensorA->isInit()); } EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); } TEST(TestDestroy, TestDestroyTensorVector) { - std::shared_ptr tensorA{ new kp::Tensor({ 1, 1, 1 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 1, 1, 1 }) }; + std::shared_ptr tensorA = nullptr; + std::shared_ptr tensorB = nullptr; std::string shader(R"( #version 450 @@ -58,6 +57,7 @@ TEST(TestDestroy, TestDestroyTensorVector) pa[index] = pa[index] + 1; pb[index] = pb[index] + 2; })"); + std::vector spirv = kp::Shader::compile_source(shader); { std::shared_ptr sq = nullptr; @@ -65,20 +65,20 @@ TEST(TestDestroy, TestDestroyTensorVector) { kp::Manager mgr; - mgr.rebuild({ tensorA, tensorB }); + tensorA = mgr.tensor({ 1, 1, 1 }); + tensorB = mgr.tensor({ 1, 1, 1 }); - sq = mgr.sequence(); + std::shared_ptr algo = + mgr.algorithm({tensorA, tensorB}, spirv); - sq->begin(); - sq->record( - { tensorA, tensorB }, kp::Shader::compile_source(shader)); - sq->end(); + mgr.sequence() + ->record(algo->getTensors()) + ->record(algo) + ->record(algo->getTensors()) + ->eval(); - sq->eval(); - - mgr.evalOpDefault({ tensorA, tensorB }); - - mgr.destroy({ tensorA, tensorB }); + tensorA->destroy(); + tensorB->destroy(); EXPECT_FALSE(tensorA->isInit()); EXPECT_FALSE(tensorB->isInit()); @@ -88,32 +88,9 @@ TEST(TestDestroy, TestDestroyTensorVector) EXPECT_EQ(tensorB->data(), std::vector({ 3, 3, 3 })); } -TEST(TestDestroy, TestDestroyTensorVectorUninitialised) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 1, 1, 1 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 1, 1, 1 }) }; - - { - std::shared_ptr sq = nullptr; - - { - kp::Manager mgr; - - mgr.rebuild({ tensorA, tensorB }); - - mgr.destroy({ tensorA, tensorB }); - - EXPECT_FALSE(tensorA->isInit()); - EXPECT_FALSE(tensorB->isInit()); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); -} - TEST(TestDestroy, TestDestroySequenceSingle) { - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; + std::shared_ptr tensorA = nullptr; std::string shader(R"( #version 450 @@ -124,26 +101,21 @@ TEST(TestDestroy, TestDestroySequenceSingle) pa[index] = pa[index] + 1; })"); + std::vector spirv = kp::Shader::compile_source(shader); + { std::shared_ptr sq = nullptr; { kp::Manager mgr; - mgr.rebuild({ tensorA }); + tensorA = mgr.tensor({0, 0, 0}); - sq = mgr.sequence(); - - sq->begin(); - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq->end(); - - sq->eval(); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy(sq); + mgr.sequence() + ->record({tensorA}) + ->record(mgr.algorithm({tensorA}, spirv)) + ->record({tensorA}) + ->eval(); EXPECT_FALSE(sq->isInit()); } @@ -151,220 +123,3 @@ TEST(TestDestroy, TestDestroySequenceSingle) EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); } -TEST(TestDestroy, TestDestroySequenceVector) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - std::shared_ptr sq1 = nullptr; - std::shared_ptr sq2 = nullptr; - - { - kp::Manager mgr; - - mgr.rebuild({ tensorA }); - - sq1 = mgr.sequence("One"); - sq1->begin(); - sq1->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq1->end(); - sq1->eval(); - - sq2 = mgr.sequence("Two"); - sq2->begin(); - sq2->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq2->end(); - sq2->eval(); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy({ sq1, sq2 }); - - EXPECT_FALSE(sq1->isInit()); - EXPECT_FALSE(sq2->isInit()); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 2, 2, 2 })); -} - -TEST(TestDestroy, TestDestroySequenceNameSingleInsideManager) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - kp::Manager mgr; - { - mgr.rebuild({ tensorA }); - - mgr.evalOp( - { tensorA }, "one", - kp::Shader::compile_source(shader)); - - mgr.evalOp( - { tensorA }, "two", - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy("one"); - mgr.destroy("two"); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 2, 2, 2 })); -} - -TEST(TestDestroy, TestDestroySequenceNameSingleOutsideManager) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - std::shared_ptr sq1 = nullptr; - - { - kp::Manager mgr; - - mgr.rebuild({ tensorA }); - - sq1 = mgr.sequence("One"); - sq1->begin(); - sq1->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq1->end(); - sq1->eval(); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy("One"); - - EXPECT_FALSE(sq1->isInit()); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); -} - -TEST(TestDestroy, TestDestroySequenceNameVectorInsideManager) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - kp::Manager mgr; - { - mgr.rebuild({ tensorA }); - - mgr.evalOp( - { tensorA }, "one", - kp::Shader::compile_source(shader)); - - mgr.evalOp( - { tensorA }, "two", - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy(std::vector({"one", "two"})); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 2, 2, 2 })); -} - -TEST(TestDestroy, TestDestroySequenceNameVectorOutsideManager) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - kp::Manager mgr; - { - mgr.rebuild({ tensorA }); - - mgr.evalOp( - { tensorA }, "one", - kp::Shader::compile_source(shader)); - - mgr.evalOp( - { tensorA }, "two", - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy(std::vector({"one", "two"})); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 2, 2, 2 })); -} - -TEST(TestDestroy, TestDestroySequenceNameDefaultOutsideManager) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - kp::Manager mgr; - { - mgr.rebuild({ tensorA }); - - mgr.evalOpDefault( - { tensorA }, - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorA }); - - mgr.destroy(KP_DEFAULT_SESSION); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); -} diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index 00425ddc0..16c08afb9 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -11,47 +11,40 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) uint32_t ITERATIONS = 100; float learningRate = 0.1; - std::shared_ptr xI{ new kp::Tensor({ 0, 1, 1, 1, 1 }) }; - std::shared_ptr xJ{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; - - std::shared_ptr y{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; - - std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; - std::shared_ptr wOutI{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - std::shared_ptr wOutJ{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; - std::shared_ptr bOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::shared_ptr lOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; - { kp::Manager mgr; - mgr.rebuild(params); + std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr sq = mgr.sequence(); + std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); - // Record op algo base - sq->begin(); + std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - sq->record({ wIn, bIn }); + std::shared_ptr bIn = mgr.tensor({ 0 }); + std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); - sq->record( - params, - std::vector( + std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; + + std::vector spirv = std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + - kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)), - kp::Workgroup(), kp::Constants({5.0})); + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - sq->record({ wOutI, wOutJ, bOut, lOut }); + std::shared_ptr algorithm = + mgr.algorithm(params, spirv, kp::Workgroup(), kp::Constants({5.0})); - sq->end(); + std::shared_ptr sq = + mgr.sequence() + ->record({ wIn, bIn }) + ->record(algorithm) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -64,21 +57,21 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) bIn->data()[0] -= learningRate * bOut->data()[j]; } } + + // Based on the inputs the outputs should be at least: + // * wi < 0.01 + // * wj > 1.0 + // * b < 0 + // TODO: Add EXPECT_DOUBLE_EQ instead + EXPECT_LT(wIn->data()[0], 0.01); + EXPECT_GT(wIn->data()[1], 1.0); + EXPECT_LT(bIn->data()[0], 0.0); + + KP_LOG_WARN("Result wIn i: {}, wIn j: {}, bIn: {}", + wIn->data()[0], + wIn->data()[1], + bIn->data()[0]); } - - // Based on the inputs the outputs should be at least: - // * wi < 0.01 - // * wj > 1.0 - // * b < 0 - // TODO: Add EXPECT_DOUBLE_EQ instead - EXPECT_LT(wIn->data()[0], 0.01); - EXPECT_GT(wIn->data()[1], 1.0); - EXPECT_LT(bIn->data()[0], 0.0); - - KP_LOG_WARN("Result wIn i: {}, wIn j: {}, bIn: {}", - wIn->data()[0], - wIn->data()[1], - bIn->data()[0]); } TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) @@ -87,50 +80,43 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) uint32_t ITERATIONS = 100; float learningRate = 0.1; - kp::Constants wInVec = { 0.001, 0.001 }; - std::vector bInVec = { 0 }; - - std::shared_ptr xI{ new kp::Tensor({ 0, 1, 1, 1, 1 }) }; - std::shared_ptr xJ{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; - - std::shared_ptr y{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; - - std::shared_ptr wIn{ new kp::Tensor( - wInVec, kp::Tensor::TensorTypes::eHost) }; - std::shared_ptr wOutI{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - std::shared_ptr wOutJ{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::shared_ptr bIn{ new kp::Tensor( - bInVec, kp::Tensor::TensorTypes::eHost) }; - std::shared_ptr bOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::shared_ptr lOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - - std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; - { kp::Manager mgr; - mgr.rebuild(params); + std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr sq = mgr.sequence(); + std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); - // Record op algo base - sq->begin(); + std::shared_ptr wIn = mgr.tensor( + { 0.001, 0.001 }, kp::Tensor::TensorTypes::eHost); + std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - sq->record( - params, - std::vector( + std::shared_ptr bIn = mgr.tensor( + { 0 }, + kp::Tensor::TensorTypes::eHost); + std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + + std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; + + std::vector spirv = std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + - kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)), - kp::Workgroup(), kp::Constants({5.0})); + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - sq->record({ wOutI, wOutJ, bOut, lOut }); + std::shared_ptr algorithm = + mgr.algorithm(params, spirv, kp::Workgroup(), kp::Constants({5.0})); - sq->end(); + std::shared_ptr sq = + mgr.sequence() + ->record({ wIn, bIn }) + ->record(algorithm) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -145,7 +131,6 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) wIn->mapDataIntoHostMemory(); bIn->mapDataIntoHostMemory(); } - } // Based on the inputs the outputs should be at least: // * wi < 0.01 @@ -160,4 +145,5 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) wIn->data()[0], wIn->data()[1], bIn->data()[0]); + } } diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 73cbdce61..59ab1ff10 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -3,9 +3,6 @@ #include "kompute/Kompute.hpp" -#include "kompute_test/shaders/shadertest_workgroup.hpp" - - TEST(TestWorkgroup, TestSimpleWorkgroup) { std::shared_ptr tensorA = nullptr; @@ -31,9 +28,9 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) std::shared_ptr algorithm = mgr.algorithm(params, spirv, workgroup); sq = mgr.sequence(); - sq->record(std::make_shared(params)); - sq->record(std::make_shared(params, algorithm)); - sq->record(std::make_shared(params)); + sq->record(params); + sq->record(params, algorithm); + sq->record(params); sq->eval(); } } From fb617d17225876b31b23e5c6429f01b1f3ebf69f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 25 Feb 2021 23:09:54 +0000 Subject: [PATCH 06/91] Initial base set of tests aligned with new interface --- single_include/kompute/Kompute.hpp | 7 +- src/Sequence.cpp | 19 +- src/Tensor.cpp | 8 +- src/include/kompute/Sequence.hpp | 1 + src/include/kompute/operations/OpMult.hpp | 4 +- test/TestManager.cpp | 128 +--- test/TestMultipleAlgoExecutions.cpp | 696 +++++++++++----------- test/TestOpAlgoLoopsPassingData.cpp | 80 --- test/TestOpShadersFromStringAndFile.cpp | 140 ++--- test/TestOpTensorCopy.cpp | 320 +++++----- test/TestOpTensorCreate.cpp | 304 +++++----- test/TestOpTensorSync.cpp | 104 ++-- test/TestSequence.cpp | 72 +-- test/TestSpecializationConstant.cpp | 56 +- test/TestTensor.cpp | 36 +- test/TestWorkgroup.cpp | 4 +- 16 files changed, 908 insertions(+), 1071 deletions(-) delete mode 100644 test/TestOpAlgoLoopsPassingData.cpp diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 01a2e7522..e60a5bdf0 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1341,6 +1341,7 @@ class Sequence: public std::enable_shared_from_this * * @return shared_ptr of the Sequence class itself */ + // TODO: Aim to have only a single function with tensors/algorithm template std::shared_ptr eval(std::vector> tensors, TArgs&&... params) @@ -1355,6 +1356,7 @@ class Sequence: public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; + // TODO: Aim to be able to handle errors when returning without throw except return this->eval(op); } // Needded as otherise can't use initialiser list @@ -1383,6 +1385,7 @@ class Sequence: public std::enable_shared_from_this * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); + std::shared_ptr evalAsync(std::shared_ptr op); /** * Eval sends all the recorded and stored operations in the vector of @@ -1742,7 +1745,7 @@ class OpMult : public OpAlgoDispatch * @param komputeWorkgroup Optional parameter to specify the layout for processing */ OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(tensors, algorithm) + : OpAlgoDispatch(algorithm) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); @@ -1755,7 +1758,7 @@ class OpMult : public OpAlgoDispatch (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - algorithm->rebuild(tensors, spirv, Workgroup({tensors[0]->size()})); + algorithm->rebuild(tensors, spirv); } /** diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 52e147aaa..e341e734c 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -23,7 +23,7 @@ Sequence::~Sequence() { KP_LOG_DEBUG("Kompute Sequence Destructor started"); - this->freeMemoryDestroyGPUResources(); + this->destroy(); } void @@ -112,6 +112,15 @@ Sequence::evalAsync() return shared_from_this(); } +std::shared_ptr +Sequence::evalAsync(std::shared_ptr op) +{ + this->clear(); + this->record(op); + this->evalAsync(); + return shared_from_this(); +} + std::shared_ptr Sequence::evalAwait(uint64_t waitFor) { @@ -162,10 +171,10 @@ Sequence::isInit() { void Sequence::destroy() { - KP_LOG_DEBUG("Kompute Sequence freeMemoryDestroyGPUResources called"); + KP_LOG_DEBUG("Kompute Sequence destroy called"); if (!this->mDevice) { - KP_LOG_ERROR("Kompute Sequence freeMemoryDestroyGPUResources called " + KP_LOG_ERROR("Kompute Sequence destroy called " "with null Device pointer"); return; } @@ -174,7 +183,7 @@ Sequence::destroy() KP_LOG_INFO("Freeing CommandBuffer"); if (!this->mCommandBuffer) { KP_LOG_ERROR( - "Kompute Sequence freeMemoryDestroyGPUResources called with null " + "Kompute Sequence destroy called with null " "CommandPool pointer"); return; } @@ -191,7 +200,7 @@ Sequence::destroy() KP_LOG_INFO("Destroying CommandPool"); if (this->mCommandPool == nullptr) { KP_LOG_ERROR( - "Kompute Sequence freeMemoryDestroyGPUResources called with null " + "Kompute Sequence destroy called with null " "CommandPool pointer"); return; } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 461e3ca1e..f5341830f 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -23,7 +23,7 @@ Tensor::~Tensor() KP_LOG_DEBUG("Kompute Tensor destructor started. Type: {}", this->tensorType()); - this->freeMemoryDestroyGPUResources(); + this->destroy(); KP_LOG_DEBUG("Kompute Tensor destructor success"); } @@ -40,7 +40,7 @@ Tensor::rebuild(const std::vector& data, if (this->mPrimaryBuffer || this->mPrimaryMemory) { KP_LOG_DEBUG("Kompute Tensor destroying existing resources before rebuild"); - this->freeMemoryDestroyGPUResources(); + this->destroy(); } this->allocateMemoryCreateGPUResources(); @@ -440,7 +440,7 @@ Tensor::allocateBindMemory(std::shared_ptr buffer, void Tensor::destroy() { - KP_LOG_DEBUG("Kompute Tensor started freeMemoryDestroyGPUResources()"); + KP_LOG_DEBUG("Kompute Tensor started destroy()"); if (!this->mDevice) { KP_LOG_ERROR( @@ -508,7 +508,7 @@ Tensor::destroy() this->mDevice = nullptr; } - KP_LOG_DEBUG("Kompute Tensor successful freeMemoryDestroyGPUResources()"); + KP_LOG_DEBUG("Kompute Tensor successful destroy()"); } } diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index e3bac936e..fa3b399e8 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -138,6 +138,7 @@ class Sequence: public std::enable_shared_from_this * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); + std::shared_ptr evalAsync(std::shared_ptr op); /** * Eval sends all the recorded and stored operations in the vector of diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index 184a30cd9..992b0e8a0 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -33,7 +33,7 @@ class OpMult : public OpAlgoDispatch * @param komputeWorkgroup Optional parameter to specify the layout for processing */ OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(tensors, algorithm) + : OpAlgoDispatch(algorithm) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); @@ -46,7 +46,7 @@ class OpMult : public OpAlgoDispatch (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - algorithm->rebuild(tensors, spirv, Workgroup({tensors[0]->size()})); + algorithm->rebuild(tensors, spirv); } /** diff --git a/test/TestManager.cpp b/test/TestManager.cpp index 754941561..f87e81159 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -3,53 +3,41 @@ #include "kompute/Kompute.hpp" -TEST(TestManager, EndToEndOpMultFlow) +TEST(TestManager, EndToEndOpMultEvalFlow) { kp::Manager mgr; - std::shared_ptr tensorLHS{ new kp::Tensor({ 0, 1, 2 }) }; - mgr.rebuild({ tensorLHS }); + 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::shared_ptr tensorRHS{ new kp::Tensor({ 2, 4, 6 }) }; - mgr.rebuild({ tensorRHS }); + std::vector> params = + { tensorLHS, tensorRHS, tensorOutput }; - std::shared_ptr tensorOutput{ new kp::Tensor({ 0, 0, 0 }) }; - - mgr.rebuild({ tensorOutput }); - - mgr.evalOpDefault({ tensorLHS, tensorRHS, tensorOutput }); - - mgr.evalOpDefault({ tensorOutput }); + mgr.sequence() + ->eval(params) + ->eval(params, mgr.algorithm()) + ->eval(params); EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } -TEST(TestManager, OpMultSequenceFlow) +TEST(TestManager, EndToEndOpMultSeqFlow) { - - std::shared_ptr tensorLHS{ new kp::Tensor({ 0, 1, 2 }) }; - - std::shared_ptr tensorRHS{ new kp::Tensor({ 2, 4, 6 }) }; - - std::shared_ptr tensorOutput{ new kp::Tensor({ 0, 0, 0 }) }; - kp::Manager mgr; - { - mgr.rebuild({ tensorLHS, tensorRHS, tensorOutput }); + 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::shared_ptr sq = - mgr.sequence("newSequence"); + std::vector> params = + { tensorLHS, tensorRHS, tensorOutput }; - sq->begin(); - - sq->record({ tensorLHS, tensorRHS, tensorOutput }); - - sq->record({ tensorOutput }); - - sq->end(); - sq->eval(); - } + mgr.sequence() + ->record(params) + ->record(params, mgr.algorithm()) + ->record(params) + ->eval(); EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } @@ -58,75 +46,17 @@ TEST(TestManager, TestMultipleSequences) { kp::Manager mgr; - std::shared_ptr sqOne = - mgr.sequence("sqOne"); + 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::shared_ptr sqTwo = - mgr.sequence("sqTwo"); + std::vector> params = + { tensorLHS, tensorRHS, tensorOutput }; - std::shared_ptr sqOneRef = - mgr.sequence("sqOne"); - - std::shared_ptr sqTwoRef = - mgr.sequence("sqTwo"); - - EXPECT_EQ(sqOne, sqOneRef); - EXPECT_NE(sqTwo, sqOneRef); - EXPECT_EQ(sqTwo, sqTwoRef); - EXPECT_NE(sqOneRef, sqTwoRef); -} - -TEST(TestManager, TestMultipleTensorsAtOnce) -{ - - std::shared_ptr tensorLHS{ new kp::Tensor({ 0, 1, 2 }) }; - - std::shared_ptr tensorRHS{ new kp::Tensor({ 2, 4, 6 }) }; - - std::shared_ptr tensorOutput{ new kp::Tensor({ 0, 0, 0 }) }; - - kp::Manager mgr; - - std::shared_ptr sq = - mgr.sequence("newSequence"); - - { - mgr.rebuild({ tensorLHS, tensorRHS, tensorOutput }); - - EXPECT_TRUE(tensorLHS->isInit()); - EXPECT_TRUE(tensorRHS->isInit()); - EXPECT_TRUE(tensorOutput->isInit()); - - sq->begin(); - - sq->record({ tensorLHS, tensorRHS, tensorOutput }); - - sq->record({ tensorOutput }); - - sq->end(); - sq->eval(); - } + mgr.sequence()->eval(params); + mgr.sequence()->eval(params, mgr.algorithm()); + mgr.sequence()->eval(params); EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } -TEST(TestManager, TestCreateInitTensor) -{ - kp::Manager mgr; - - std::shared_ptr tensorA = mgr.tensor({ 0, 1, 2 }); - std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); - - mgr.evalOpDefault({ tensorA, tensorB }); - - mgr.evalOpDefault({ tensorB }); - - EXPECT_EQ(tensorB->data(), std::vector({ 0, 1, 2 })); - - std::shared_ptr tensorC = - mgr.tensor({ 0, 0, 0 }, kp::Tensor::TensorTypes::eHost); - - mgr.evalOpDefault({ tensorA, tensorC }); - - EXPECT_EQ(tensorC->data(), std::vector({ 0, 1, 2 })); -} diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index a1503cc83..74f7b3c52 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -3,351 +3,351 @@ #include "kompute/Kompute.hpp" -TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) -{ - - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - mgr.rebuild({ tensorA }); - - std::shared_ptr sq = - mgr.sequence("newSequence"); - - { - sq->begin(); - - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - - sq->record({ tensorA }); - - sq->end(); - sq->eval(); - } - - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -} - -TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) -{ - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - mgr.rebuild({ tensorA }, false); - - std::shared_ptr sqTensor = mgr.sequence(); - - std::shared_ptr sq = mgr.sequence(); - - // First create the tensor in a separate sequence - sqTensor->begin(); - sqTensor->record({ tensorA }); - sqTensor->end(); - sqTensor->eval(); - - // Then perform the computations - sq->begin(); - sq->record({ tensorA }, - kp::Shader::compile_source(shader)); - sq->end(); - sq->eval(); - - sq->begin(); - sq->record({ tensorA }, - kp::Shader::compile_source(shader)); - sq->end(); - sq->eval(); - - sq->begin(); - sq->record({ tensorA }, - kp::Shader::compile_source(shader)); - sq->end(); - sq->eval(); - - sq->begin(); - sq->record({ tensorA }); - sq->end(); - sq->eval(); - - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -} - -TEST(TestMultipleAlgoExecutions, MultipleSequences) -{ - - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - mgr.rebuild({ tensorA }); - - { - std::shared_ptr sq = - mgr.sequence("newSequence"); - - sq->begin(); - - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - - sq->end(); - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("newSequence2"); - - sq->begin(); - - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - - sq->end(); - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("newSequence3"); - - sq->begin(); - - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - - sq->end(); - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("newSequence5"); - - sq->begin(); - - sq->record({ tensorA }); - - sq->end(); - sq->eval(); - } - - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -} - -TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) -{ - - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - mgr.rebuild({ tensorA }, false); - - { - std::shared_ptr sq = - mgr.sequence("newSequence"); - - sq->begin(); - - sq->record({ tensorA }); - - sq->end(); - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("newSequence2"); - - sq->begin(); - - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - - sq->end(); - - sq->eval(); - sq->eval(); - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("newSequence3"); - - sq->begin(); - - sq->record({ tensorA }); - - sq->end(); - - sq->eval(); - sq->eval(); - sq->eval(); - } - - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -} - -TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrOpCreate) -{ - - kp::Manager mgr; - - std::shared_ptr tensorInA{ new kp::Tensor({ 2.0, 4.0, 6.0 }) }; - std::shared_ptr tensorInB{ new kp::Tensor({ 0.0, 1.0, 2.0 }) }; - std::shared_ptr tensorOut{ new kp::Tensor({ 0.0, 0.0, 0.0 }) }; - - mgr.rebuild({ tensorInA, tensorInB, tensorOut }); - - std::string shader(R"( - // The version to use - #version 450 - - // The execution structure - layout (local_size_x = 1) in; - - // The buffers are provided via the tensors - layout(binding = 0) buffer bufA { float a[]; }; - layout(binding = 1) buffer bufB { float b[]; }; - layout(binding = 2) buffer bufOut { float o[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - - o[index] = a[index] * b[index]; - } - )"); - - mgr.evalOpDefault( - { tensorInA, tensorInB, tensorOut }, - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorOut }); - - EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); -} - -TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrMgrCreate) -{ - - kp::Manager mgr; - - auto tensorInA = mgr.tensor( - { 2.0, 4.0, 6.0 }, kp::Tensor::TensorTypes::eDevice, false); - auto tensorInB = mgr.tensor( - { 0.0, 1.0, 2.0 }, kp::Tensor::TensorTypes::eDevice, false); - auto tensorOut = mgr.tensor( - { 0.0, 0.0, 0.0 }, kp::Tensor::TensorTypes::eDevice, false); - - std::string shader(R"( - // The version to use - #version 450 - - // The execution structure - layout (local_size_x = 1) in; - - // The buffers are provided via the tensors - layout(binding = 0) buffer bufA { float a[]; }; - layout(binding = 1) buffer bufB { float b[]; }; - layout(binding = 2) buffer bufOut { float o[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - - o[index] = a[index] * b[index]; - } - )"); - - mgr.evalOpDefault( - { tensorInA, tensorInB, tensorOut }); - - mgr.evalOpDefault( - { tensorInA, tensorInB, tensorOut }, - kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorOut }); - - EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); -} - -TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) -{ - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - { - std::shared_ptr sq = nullptr; - - { - kp::Manager mgr; - - mgr.rebuild({ tensorA }); - - sq = mgr.sequence(); - - sq->begin(); - sq->record( - { tensorA }, kp::Shader::compile_source(shader)); - sq->end(); - - sq->eval(); - - mgr.evalOpDefault({ tensorA }); - } - } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); -} - +//TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) +//{ +// +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// +// std::string shader(R"( +// #version 450 +// layout (local_size_x = 1) in; +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pa[index] = pa[index] + 1; +// })"); +// +// mgr.rebuild({ tensorA }); +// +// std::shared_ptr sq = +// mgr.sequence("newSequence"); +// +// { +// sq->begin(); +// +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// +// sq->record({ tensorA }); +// +// sq->end(); +// sq->eval(); +// } +// +// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +//} +// +//TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) +//{ +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// +// std::string shader(R"( +// #version 450 +// layout (local_size_x = 1) in; +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pa[index] = pa[index] + 1; +// })"); +// +// mgr.rebuild({ tensorA }, false); +// +// std::shared_ptr sqTensor = mgr.sequence(); +// +// std::shared_ptr sq = mgr.sequence(); +// +// // First create the tensor in a separate sequence +// sqTensor->begin(); +// sqTensor->record({ tensorA }); +// sqTensor->end(); +// sqTensor->eval(); +// +// // Then perform the computations +// sq->begin(); +// sq->record({ tensorA }, +// kp::Shader::compile_source(shader)); +// sq->end(); +// sq->eval(); +// +// sq->begin(); +// sq->record({ tensorA }, +// kp::Shader::compile_source(shader)); +// sq->end(); +// sq->eval(); +// +// sq->begin(); +// sq->record({ tensorA }, +// kp::Shader::compile_source(shader)); +// sq->end(); +// sq->eval(); +// +// sq->begin(); +// sq->record({ tensorA }); +// sq->end(); +// sq->eval(); +// +// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +//} +// +//TEST(TestMultipleAlgoExecutions, MultipleSequences) +//{ +// +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// +// std::string shader(R"( +// #version 450 +// layout (local_size_x = 1) in; +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pa[index] = pa[index] + 1; +// })"); +// +// mgr.rebuild({ tensorA }); +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence"); +// +// sq->begin(); +// +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// +// sq->end(); +// sq->eval(); +// } +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence2"); +// +// sq->begin(); +// +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// +// sq->end(); +// sq->eval(); +// } +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence3"); +// +// sq->begin(); +// +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// +// sq->end(); +// sq->eval(); +// } +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence5"); +// +// sq->begin(); +// +// sq->record({ tensorA }); +// +// sq->end(); +// sq->eval(); +// } +// +// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +//} +// +//TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) +//{ +// +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// +// std::string shader(R"( +// #version 450 +// layout (local_size_x = 1) in; +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pa[index] = pa[index] + 1; +// })"); +// +// mgr.rebuild({ tensorA }, false); +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence"); +// +// sq->begin(); +// +// sq->record({ tensorA }); +// +// sq->end(); +// sq->eval(); +// } +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence2"); +// +// sq->begin(); +// +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// +// sq->end(); +// +// sq->eval(); +// sq->eval(); +// sq->eval(); +// } +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence3"); +// +// sq->begin(); +// +// sq->record({ tensorA }); +// +// sq->end(); +// +// sq->eval(); +// sq->eval(); +// sq->eval(); +// } +// +// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +//} +// +//TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrOpCreate) +//{ +// +// kp::Manager mgr; +// +// std::shared_ptr tensorInA{ new kp::Tensor({ 2.0, 4.0, 6.0 }) }; +// std::shared_ptr tensorInB{ new kp::Tensor({ 0.0, 1.0, 2.0 }) }; +// std::shared_ptr tensorOut{ new kp::Tensor({ 0.0, 0.0, 0.0 }) }; +// +// mgr.rebuild({ tensorInA, tensorInB, tensorOut }); +// +// std::string shader(R"( +// // The version to use +// #version 450 +// +// // The execution structure +// layout (local_size_x = 1) in; +// +// // The buffers are provided via the tensors +// layout(binding = 0) buffer bufA { float a[]; }; +// layout(binding = 1) buffer bufB { float b[]; }; +// layout(binding = 2) buffer bufOut { float o[]; }; +// +// void main() { +// uint index = gl_GlobalInvocationID.x; +// +// o[index] = a[index] * b[index]; +// } +// )"); +// +// mgr.evalOpDefault( +// { tensorInA, tensorInB, tensorOut }, +// kp::Shader::compile_source(shader)); +// +// mgr.evalOpDefault({ tensorOut }); +// +// EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); +//} +// +//TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrMgrCreate) +//{ +// +// kp::Manager mgr; +// +// auto tensorInA = mgr.tensor( +// { 2.0, 4.0, 6.0 }, kp::Tensor::TensorTypes::eDevice, false); +// auto tensorInB = mgr.tensor( +// { 0.0, 1.0, 2.0 }, kp::Tensor::TensorTypes::eDevice, false); +// auto tensorOut = mgr.tensor( +// { 0.0, 0.0, 0.0 }, kp::Tensor::TensorTypes::eDevice, false); +// +// std::string shader(R"( +// // The version to use +// #version 450 +// +// // The execution structure +// layout (local_size_x = 1) in; +// +// // The buffers are provided via the tensors +// layout(binding = 0) buffer bufA { float a[]; }; +// layout(binding = 1) buffer bufB { float b[]; }; +// layout(binding = 2) buffer bufOut { float o[]; }; +// +// void main() { +// uint index = gl_GlobalInvocationID.x; +// +// o[index] = a[index] * b[index]; +// } +// )"); +// +// mgr.evalOpDefault( +// { tensorInA, tensorInB, tensorOut }); +// +// mgr.evalOpDefault( +// { tensorInA, tensorInB, tensorOut }, +// kp::Shader::compile_source(shader)); +// +// mgr.evalOpDefault({ tensorOut }); +// +// EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); +//} +// +//TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) +//{ +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// +// std::string shader(R"( +// #version 450 +// layout (local_size_x = 1) in; +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pa[index] = pa[index] + 1; +// })"); +// +// { +// std::shared_ptr sq = nullptr; +// +// { +// kp::Manager mgr; +// +// mgr.rebuild({ tensorA }); +// +// sq = mgr.sequence(); +// +// sq->begin(); +// sq->record( +// { tensorA }, kp::Shader::compile_source(shader)); +// sq->end(); +// +// sq->eval(); +// +// mgr.evalOpDefault({ tensorA }); +// } +// } +// EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); +//} +// diff --git a/test/TestOpAlgoLoopsPassingData.cpp b/test/TestOpAlgoLoopsPassingData.cpp deleted file mode 100644 index 83cbb6619..000000000 --- a/test/TestOpAlgoLoopsPassingData.cpp +++ /dev/null @@ -1,80 +0,0 @@ - -#include "gtest/gtest.h" - -#include "kompute/Kompute.hpp" - -TEST(TestProcessingIterations, IterateThroughMultipleSumAndCopies) -{ - kp::Manager mgr; - - float TOTAL_ITER = 10; - - std::vector testExpectedOutVec = { TOTAL_ITER, - TOTAL_ITER, - TOTAL_ITER }; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - - layout (local_size_x = 1) in; - - layout(set = 0, binding = 0) buffer a { float pa[]; }; - layout(set = 0, binding = 1) buffer b { float pb[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - pb[index] = pa[index] + 1; - } - )"); - - mgr.rebuild({ tensorA, tensorB }, false); - - { - std::shared_ptr sq = - mgr.sequence("default"); - - sq->begin(); - - sq->record({ tensorA, tensorB }); - - sq->end(); - - sq->eval(); - } - - { - std::shared_ptr sq = - mgr.sequence("run"); - - sq->begin(); - - sq->record( - { tensorA, tensorB }, - kp::Shader::compile_source(shader)); - - sq->record({ tensorB, tensorA }); - sq->end(); - - for (size_t i = 0; i < TOTAL_ITER; i++) { - sq->eval(); - } - } - - { - std::shared_ptr sq = - mgr.sequence("export"); - - sq->begin(); - - sq->record({ tensorA, tensorB }); - - sq->end(); - - sq->eval(); - } - - EXPECT_EQ(tensorA->data(), testExpectedOutVec); -} diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index 09908b722..60cb8af24 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -5,73 +5,73 @@ #include "kompute_test/shaders/shadertest_op_custom_shader.hpp" -TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) -{ - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - mgr.rebuild({ tensorA, tensorB }); - - std::string shader(R"( - #version 450 - - layout (local_size_x = 1) in; - - layout(set = 0, binding = 0) buffer a { float pa[]; }; - layout(set = 0, binding = 1) buffer b { float pb[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - pb[index] = pa[index]; - pa[index] = index; - } - )"); - - mgr.evalOpDefault( - { tensorA, tensorB }, kp::Shader::compile_source(shader)); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); -} - -TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) -{ - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - mgr.rebuild({ tensorA, tensorB }); - - mgr.evalOpDefault( - { tensorA, tensorB }, - std::vector( - (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, - (uint32_t*)(kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv + - kp::shader_data:: - test_shaders_glsl_test_op_custom_shader_comp_spv_len))); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); -} - -TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) -{ - kp::Manager mgr; - - std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - mgr.rebuild({ tensorA, tensorB }); - - mgr.evalOpDefault( - { tensorA, tensorB }, "test/shaders/glsl/test_op_custom_shader.comp.spv"); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); -} +//TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) +//{ +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; +// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; +// mgr.rebuild({ tensorA, tensorB }); +// +// std::string shader(R"( +// #version 450 +// +// layout (local_size_x = 1) in; +// +// layout(set = 0, binding = 0) buffer a { float pa[]; }; +// layout(set = 0, binding = 1) buffer b { float pb[]; }; +// +// void main() { +// uint index = gl_GlobalInvocationID.x; +// pb[index] = pa[index]; +// pa[index] = index; +// } +// )"); +// +// mgr.evalOpDefault( +// { tensorA, tensorB }, kp::Shader::compile_source(shader)); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); +// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +//} +// +//TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) +//{ +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; +// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; +// mgr.rebuild({ tensorA, tensorB }); +// +// mgr.evalOpDefault( +// { tensorA, tensorB }, +// std::vector( +// (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, +// (uint32_t*)(kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv + +// kp::shader_data:: +// test_shaders_glsl_test_op_custom_shader_comp_spv_len))); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); +// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +//} +// +//TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) +//{ +// kp::Manager mgr; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; +// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; +// mgr.rebuild({ tensorA, tensorB }); +// +// mgr.evalOpDefault( +// { tensorA, tensorB }, "test/shaders/glsl/test_op_custom_shader.comp.spv"); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); +// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +//} diff --git a/test/TestOpTensorCopy.cpp b/test/TestOpTensorCopy.cpp index 3f2bc9500..449e30da1 100644 --- a/test/TestOpTensorCopy.cpp +++ b/test/TestOpTensorCopy.cpp @@ -3,163 +3,163 @@ #include "kompute/Kompute.hpp" -TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) -{ - - kp::Manager mgr; - - std::vector testVecA{ 1, 2, 3 }; - std::vector testVecB{ 0, 0, 0 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - mgr.rebuild({ tensorA, tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), tensorB->data()); - - // Making sure the GPU holds the same data - mgr.evalOpDefault({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); -} - -TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) -{ - - kp::Manager mgr; - - std::vector testVecA{ 2, 3, 4 }; - std::vector testVecB{ 0, 0, 0 }; - std::vector testVecC{ 0, 0, 0 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - std::shared_ptr tensorC{ new kp::Tensor(testVecC) }; - - mgr.rebuild({ tensorA, tensorB, tensorC }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - EXPECT_TRUE(tensorC->isInit()); - - mgr.evalOpDefault({ tensorA, tensorB, tensorC }); - - EXPECT_EQ(tensorA->data(), tensorB->data()); - EXPECT_EQ(tensorA->data(), tensorC->data()); - - // Making sure the GPU holds the same data - mgr.evalOpDefault({ tensorB, tensorC }); - EXPECT_EQ(tensorA->data(), tensorB->data()); - EXPECT_EQ(tensorA->data(), tensorC->data()); -} - -TEST(TestOpTensorCopy, CopyDeviceToHostTensor) -{ - - kp::Manager mgr; - - std::vector testVecA{ 3, 4, 5 }; - std::vector testVecB{ 0, 0, 0 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor( - testVecB, kp::Tensor::TensorTypes::eHost) }; - - mgr.rebuild({ tensorA, tensorB }, false); - - // Only calling sync on device type tensor - mgr.evalOpDefault({ tensorA }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), tensorB->data()); - - // Making sure the GPU holds the same data - mgr.evalOpDefault({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); -} - -TEST(TestOpTensorCopy, CopyHostToDeviceTensor) -{ - - kp::Manager mgr; - - std::vector testVecA{ 4, 5, 6 }; - std::vector testVecB{ 0, 0, 0 }; - - std::shared_ptr tensorA{ new kp::Tensor( - testVecA, kp::Tensor::TensorTypes::eHost) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - mgr.rebuild({ tensorA, tensorB }, false); - - // Manually copy data into host memory of Tensor - tensorA->mapDataIntoHostMemory(); - - // Only calling sync on device type tensor - mgr.evalOpDefault({ tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), tensorB->data()); - - // Making sure the GPU holds the same data - mgr.evalOpDefault({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); -} - -TEST(TestOpTensorCopy, CopyHostToHostTensor) -{ - - kp::Manager mgr; - - std::vector testVecA{ 5, 6, 7 }; - std::vector testVecB{ 0, 0, 0 }; - - std::shared_ptr tensorA{ new kp::Tensor( - testVecA, kp::Tensor::TensorTypes::eHost) }; - std::shared_ptr tensorB{ new kp::Tensor( - testVecB, kp::Tensor::TensorTypes::eHost) }; - - mgr.rebuild({ tensorA, tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - mgr.evalOpDefault({ tensorA, tensorB }); - - EXPECT_EQ(tensorA->data(), tensorB->data()); - - // Making sure the GPU holds the same data - mgr.evalOpDefault({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); -} - -TEST(TestOpTensorCopy, SingleTensorShouldFail) -{ - - kp::Manager mgr; - - std::vector testVecA{ 6, 7, 8 }; - - std::shared_ptr tensorA{ new kp::Tensor( - testVecA, kp::Tensor::TensorTypes::eHost) }; - - mgr.rebuild({ tensorA }, false); - - EXPECT_TRUE(tensorA->isInit()); - - EXPECT_THROW(mgr.evalOpDefault({ tensorA }), - std::runtime_error); -} +//TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 1, 2, 3 }; +// std::vector testVecB{ 0, 0, 0 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// mgr.rebuild({ tensorA, tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// +// // Making sure the GPU holds the same data +// mgr.evalOpDefault({ tensorB }); +// EXPECT_EQ(tensorA->data(), tensorB->data()); +//} +// +//TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 2, 3, 4 }; +// std::vector testVecB{ 0, 0, 0 }; +// std::vector testVecC{ 0, 0, 0 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// std::shared_ptr tensorC{ new kp::Tensor(testVecC) }; +// +// mgr.rebuild({ tensorA, tensorB, tensorC }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// EXPECT_TRUE(tensorC->isInit()); +// +// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); +// +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// EXPECT_EQ(tensorA->data(), tensorC->data()); +// +// // Making sure the GPU holds the same data +// mgr.evalOpDefault({ tensorB, tensorC }); +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// EXPECT_EQ(tensorA->data(), tensorC->data()); +//} +// +//TEST(TestOpTensorCopy, CopyDeviceToHostTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 3, 4, 5 }; +// std::vector testVecB{ 0, 0, 0 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor( +// testVecB, kp::Tensor::TensorTypes::eHost) }; +// +// mgr.rebuild({ tensorA, tensorB }, false); +// +// // Only calling sync on device type tensor +// mgr.evalOpDefault({ tensorA }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// +// // Making sure the GPU holds the same data +// mgr.evalOpDefault({ tensorB }); +// EXPECT_EQ(tensorA->data(), tensorB->data()); +//} +// +//TEST(TestOpTensorCopy, CopyHostToDeviceTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 4, 5, 6 }; +// std::vector testVecB{ 0, 0, 0 }; +// +// std::shared_ptr tensorA{ new kp::Tensor( +// testVecA, kp::Tensor::TensorTypes::eHost) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// mgr.rebuild({ tensorA, tensorB }, false); +// +// // Manually copy data into host memory of Tensor +// tensorA->mapDataIntoHostMemory(); +// +// // Only calling sync on device type tensor +// mgr.evalOpDefault({ tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// +// // Making sure the GPU holds the same data +// mgr.evalOpDefault({ tensorB }); +// EXPECT_EQ(tensorA->data(), tensorB->data()); +//} +// +//TEST(TestOpTensorCopy, CopyHostToHostTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 5, 6, 7 }; +// std::vector testVecB{ 0, 0, 0 }; +// +// std::shared_ptr tensorA{ new kp::Tensor( +// testVecA, kp::Tensor::TensorTypes::eHost) }; +// std::shared_ptr tensorB{ new kp::Tensor( +// testVecB, kp::Tensor::TensorTypes::eHost) }; +// +// mgr.rebuild({ tensorA, tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// mgr.evalOpDefault({ tensorA, tensorB }); +// +// EXPECT_EQ(tensorA->data(), tensorB->data()); +// +// // Making sure the GPU holds the same data +// mgr.evalOpDefault({ tensorB }); +// EXPECT_EQ(tensorA->data(), tensorB->data()); +//} +// +//TEST(TestOpTensorCopy, SingleTensorShouldFail) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 6, 7, 8 }; +// +// std::shared_ptr tensorA{ new kp::Tensor( +// testVecA, kp::Tensor::TensorTypes::eHost) }; +// +// mgr.rebuild({ tensorA }, false); +// +// EXPECT_TRUE(tensorA->isInit()); +// +// EXPECT_THROW(mgr.evalOpDefault({ tensorA }), +// std::runtime_error); +//} diff --git a/test/TestOpTensorCreate.cpp b/test/TestOpTensorCreate.cpp index ca3473576..0d7a26524 100644 --- a/test/TestOpTensorCreate.cpp +++ b/test/TestOpTensorCreate.cpp @@ -3,155 +3,155 @@ #include "kompute/Kompute.hpp" -TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) -{ - std::vector testVecA{ 9, 8, 7 }; - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - - { - kp::Manager mgr; - - mgr.rebuild({ tensorA }); - - EXPECT_TRUE(tensorA->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - } - - EXPECT_FALSE(tensorA->isInit()); -} - -TEST(TestOpTensorCreate, CreateMultipleTensorSingleOp) -{ - - kp::Manager mgr; - - std::vector testVecA{ 9, 8, 7 }; - std::vector testVecB{ 6, 5, 4 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - mgr.rebuild({ tensorA, tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); -} - -TEST(TestOpTensorCreate, CreateMultipleTensorMultipleOp) -{ - - kp::Manager mgr; - - std::vector testVecA{ 9, 8, 7 }; - std::vector testVecB{ 6, 5, 4 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - mgr.rebuild({ tensorA }); - mgr.rebuild({ tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); -} - -TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerDestroyed) -{ - - std::vector testVecA{ 9, 8, 7 }; - std::vector testVecB{ 6, 5, 4 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - { - kp::Manager mgr; - mgr.rebuild({ tensorA }); - mgr.rebuild({ tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); - } - - EXPECT_FALSE(tensorA->isInit()); - EXPECT_FALSE(tensorB->isInit()); -} - -TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerNOTDestroyed) -{ - - std::vector testVecA{ 9, 8, 7 }; - std::vector testVecB{ 6, 5, 4 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - kp::Manager mgr; - - { - mgr.rebuild({ tensorA }); - mgr.rebuild({ tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); - } - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); -} - -TEST(TestOpTensorCreate, NoErrorIfTensorFreedBefore) -{ - - std::vector testVecA{ 9, 8, 7 }; - std::vector testVecB{ 6, 5, 4 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; - - kp::Manager mgr; - - mgr.rebuild({ tensorA }); - mgr.rebuild({ tensorB }); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); - - tensorA->freeMemoryDestroyGPUResources(); - tensorB->freeMemoryDestroyGPUResources(); - EXPECT_FALSE(tensorA->isInit()); - EXPECT_FALSE(tensorB->isInit()); -} - -TEST(TestOpTensorCreate, ExceptionOnZeroSizeTensor) -{ - std::vector testVecA; - - std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; - - kp::Manager mgr; - - try { - mgr.rebuild({ tensorA }); - } catch (const std::runtime_error& err) { - // check exception - ASSERT_TRUE(std::string(err.what()).find("zero-sized") != - std::string::npos); - } -} +//TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) +//{ +// std::vector testVecA{ 9, 8, 7 }; +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// +// { +// kp::Manager mgr; +// +// mgr.rebuild({ tensorA }); +// +// EXPECT_TRUE(tensorA->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// } +// +// EXPECT_FALSE(tensorA->isInit()); +//} +// +//TEST(TestOpTensorCreate, CreateMultipleTensorSingleOp) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 9, 8, 7 }; +// std::vector testVecB{ 6, 5, 4 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// mgr.rebuild({ tensorA, tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// EXPECT_EQ(tensorB->data(), testVecB); +//} +// +//TEST(TestOpTensorCreate, CreateMultipleTensorMultipleOp) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecA{ 9, 8, 7 }; +// std::vector testVecB{ 6, 5, 4 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// mgr.rebuild({ tensorA }); +// mgr.rebuild({ tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// EXPECT_EQ(tensorB->data(), testVecB); +//} +// +//TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerDestroyed) +//{ +// +// std::vector testVecA{ 9, 8, 7 }; +// std::vector testVecB{ 6, 5, 4 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// { +// kp::Manager mgr; +// mgr.rebuild({ tensorA }); +// mgr.rebuild({ tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// EXPECT_EQ(tensorB->data(), testVecB); +// } +// +// EXPECT_FALSE(tensorA->isInit()); +// EXPECT_FALSE(tensorB->isInit()); +//} +// +//TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerNOTDestroyed) +//{ +// +// std::vector testVecA{ 9, 8, 7 }; +// std::vector testVecB{ 6, 5, 4 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// kp::Manager mgr; +// +// { +// mgr.rebuild({ tensorA }); +// mgr.rebuild({ tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// EXPECT_EQ(tensorB->data(), testVecB); +// } +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +//} +// +//TEST(TestOpTensorCreate, NoErrorIfTensorFreedBefore) +//{ +// +// std::vector testVecA{ 9, 8, 7 }; +// std::vector testVecB{ 6, 5, 4 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; +// +// kp::Manager mgr; +// +// mgr.rebuild({ tensorA }); +// mgr.rebuild({ tensorB }); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// +// EXPECT_EQ(tensorA->data(), testVecA); +// EXPECT_EQ(tensorB->data(), testVecB); +// +// tensorA->freeMemoryDestroyGPUResources(); +// tensorB->freeMemoryDestroyGPUResources(); +// EXPECT_FALSE(tensorA->isInit()); +// EXPECT_FALSE(tensorB->isInit()); +//} +// +//TEST(TestOpTensorCreate, ExceptionOnZeroSizeTensor) +//{ +// std::vector testVecA; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; +// +// kp::Manager mgr; +// +// try { +// mgr.rebuild({ tensorA }); +// } catch (const std::runtime_error& err) { +// // check exception +// ASSERT_TRUE(std::string(err.what()).find("zero-sized") != +// std::string::npos); +// } +//} diff --git a/test/TestOpTensorSync.cpp b/test/TestOpTensorSync.cpp index f992805f5..8e8c4cda2 100644 --- a/test/TestOpTensorSync.cpp +++ b/test/TestOpTensorSync.cpp @@ -3,55 +3,55 @@ #include "kompute/Kompute.hpp" -TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) -{ - - kp::Manager mgr; - - std::vector testVecPreA{ 0, 0, 0 }; - std::vector testVecPostA{ 9, 8, 7 }; - - std::shared_ptr tensorA{ new kp::Tensor(testVecPreA) }; - - mgr.rebuild({ tensorA }, false); - - EXPECT_TRUE(tensorA->isInit()); - - tensorA->setData(testVecPostA); - - mgr.evalOpDefault({ tensorA }); - - mgr.evalOpDefault({ tensorA }); - - EXPECT_EQ(tensorA->data(), testVecPostA); -} - -TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) -{ - - kp::Manager mgr; - - std::vector testVec{ 9, 8, 7 }; - - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - std::shared_ptr tensorC{ new kp::Tensor({ 0, 0, 0 }) }; - - mgr.rebuild({ tensorA, tensorB, tensorC }, false); - - EXPECT_TRUE(tensorA->isInit()); - EXPECT_TRUE(tensorB->isInit()); - EXPECT_TRUE(tensorC->isInit()); - - tensorA->setData(testVec); - - mgr.evalOpDefault({ tensorA }); - - mgr.evalOpDefault({ tensorA, tensorB, tensorC }); - - mgr.evalOpDefault({ tensorA, tensorB, tensorC }); - - EXPECT_EQ(tensorA->data(), testVec); - EXPECT_EQ(tensorB->data(), testVec); - EXPECT_EQ(tensorC->data(), testVec); -} +//TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVecPreA{ 0, 0, 0 }; +// std::vector testVecPostA{ 9, 8, 7 }; +// +// std::shared_ptr tensorA{ new kp::Tensor(testVecPreA) }; +// +// mgr.rebuild({ tensorA }, false); +// +// EXPECT_TRUE(tensorA->isInit()); +// +// tensorA->setData(testVecPostA); +// +// mgr.evalOpDefault({ tensorA }); +// +// mgr.evalOpDefault({ tensorA }); +// +// EXPECT_EQ(tensorA->data(), testVecPostA); +//} +// +//TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) +//{ +// +// kp::Manager mgr; +// +// std::vector testVec{ 9, 8, 7 }; +// +// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; +// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; +// std::shared_ptr tensorC{ new kp::Tensor({ 0, 0, 0 }) }; +// +// mgr.rebuild({ tensorA, tensorB, tensorC }, false); +// +// EXPECT_TRUE(tensorA->isInit()); +// EXPECT_TRUE(tensorB->isInit()); +// EXPECT_TRUE(tensorC->isInit()); +// +// tensorA->setData(testVec); +// +// mgr.evalOpDefault({ tensorA }); +// +// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); +// +// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); +// +// EXPECT_EQ(tensorA->data(), testVec); +// EXPECT_EQ(tensorB->data(), testVec); +// EXPECT_EQ(tensorC->data(), testVec); +//} diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 0dec484b5..8c1ae3751 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -3,40 +3,40 @@ #include "kompute/Kompute.hpp" -TEST(TestSequence, CmdBufSequenceBeginEnd) -{ - kp::Manager mgr; - - { - std::shared_ptr sq = - mgr.sequence("newSequence"); - - EXPECT_TRUE(sq->eval()); - EXPECT_TRUE(!sq->isRecording()); - EXPECT_TRUE(sq->begin()); - EXPECT_TRUE(sq->isRecording()); - EXPECT_TRUE(!sq->begin()); - EXPECT_TRUE(sq->isRecording()); - EXPECT_TRUE(sq->end()); - EXPECT_TRUE(!sq->isRecording()); - EXPECT_TRUE(!sq->end()); - EXPECT_TRUE(!sq->isRecording()); - EXPECT_TRUE(sq->eval()); - } -} - -TEST(TestSequence, SequenceDestructorViaManager) -{ - std::shared_ptr sq = nullptr; - - { - kp::Manager mgr; - - sq = mgr.sequence("newSequence"); - - EXPECT_TRUE(sq->isInit()); - } - - EXPECT_FALSE(sq->isInit()); -} +//TEST(TestSequence, CmdBufSequenceBeginEnd) +//{ +// kp::Manager mgr; +// +// { +// std::shared_ptr sq = +// mgr.sequence("newSequence"); +// +// EXPECT_TRUE(sq->eval()); +// EXPECT_TRUE(!sq->isRecording()); +// EXPECT_TRUE(sq->begin()); +// EXPECT_TRUE(sq->isRecording()); +// EXPECT_TRUE(!sq->begin()); +// EXPECT_TRUE(sq->isRecording()); +// EXPECT_TRUE(sq->end()); +// EXPECT_TRUE(!sq->isRecording()); +// EXPECT_TRUE(!sq->end()); +// EXPECT_TRUE(!sq->isRecording()); +// EXPECT_TRUE(sq->eval()); +// } +//} +// +//TEST(TestSequence, SequenceDestructorViaManager) +//{ +// std::shared_ptr sq = nullptr; +// +// { +// kp::Manager mgr; +// +// sq = mgr.sequence("newSequence"); +// +// EXPECT_TRUE(sq->isInit()); +// } +// +// EXPECT_FALSE(sq->isInit()); +//} diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index e4075314f..d1e4cdc9e 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -4,46 +4,44 @@ TEST(TestSpecializationConstants, TestTwoConstants) { - std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; - std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; - - std::string shader(R"( - #version 450 - layout (constant_id = 0) const float cOne = 1; - layout (constant_id = 1) const float cTwo = 1; - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - layout(set = 0, binding = 1) buffer b { float pb[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = cOne; - pb[index] = cTwo; - })"); - { + std::string shader(R"( + #version 450 + layout (constant_id = 0) const float cOne = 1; + layout (constant_id = 1) const float cTwo = 1; + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + layout(set = 0, binding = 1) buffer b { float pb[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = cOne; + pb[index] = cTwo; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + std::shared_ptr sq = nullptr; { kp::Manager mgr; - mgr.rebuild({ tensorA, tensorB }); + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); - sq = mgr.sequence(); + std::vector> params = {tensorA, tensorB}; - auto spec = kp::Constants({5.0, 0.3}); + kp::Constants spec = kp::Constants({5.0, 0.3}); - sq->begin(); - sq->record( - { tensorA, tensorB }, - kp::Shader::compile_source(shader), - kp::Workgroup(), spec); - sq->end(); + std::shared_ptr algo = mgr.algorithm(params, spirv, {}, spec); - sq->eval(); + sq = mgr.sequence() + ->record(params) + ->record(algo) + ->record(params) + ->eval(); - mgr.evalOpDefault({ tensorA, tensorB }); + EXPECT_EQ(tensorA->data(), std::vector({ 5, 5, 5 })); + EXPECT_EQ(tensorB->data(), std::vector({ 0.3, 0.3, 0.3 })); } } - EXPECT_EQ(tensorA->data(), std::vector({ 5, 5, 5 })); - EXPECT_EQ(tensorB->data(), std::vector({ 0.3, 0.3, 0.3 })); } diff --git a/test/TestTensor.cpp b/test/TestTensor.cpp index 705c825f7..76ecbe60d 100644 --- a/test/TestTensor.cpp +++ b/test/TestTensor.cpp @@ -5,36 +5,10 @@ TEST(TestTensor, ConstructorData) { - std::vector vec{ 0, 1, 2 }; - kp::Tensor tensor(vec); - EXPECT_EQ(tensor.size(), vec.size()); - EXPECT_EQ(tensor.data(), vec); -} - -TEST(TestTensor, CopyFromHostData) -{ - std::vector vecA{ 0, 1, 2 }; - std::vector vecB{ 0, 0, 0 }; - - std::shared_ptr tensorA = - std::make_shared(vecA, kp::Tensor::TensorTypes::eHost); - std::shared_ptr tensorB = - std::make_shared(vecB, kp::Tensor::TensorTypes::eHost); - kp::Manager mgr; - - mgr.rebuild({ tensorA, tensorB }); - - if (std::shared_ptr sq = - mgr.sequence("new")) { - sq->begin(); - - sq->record({ tensorA, tensorB }); - - sq->end(); - - sq->eval(); - } - - EXPECT_EQ(tensorA->data(), tensorB->data()); + std::vector vec{ 0, 1, 2 }; + std::shared_ptr tensor = mgr.tensor(vec); + EXPECT_EQ(tensor->size(), vec.size()); + EXPECT_EQ(tensor->data(), vec); } + diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 59ab1ff10..5da0a4c54 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -3,6 +3,8 @@ #include "kompute/Kompute.hpp" +#include "kompute_test/shaders/shadertest_workgroup.hpp" + TEST(TestWorkgroup, TestSimpleWorkgroup) { std::shared_ptr tensorA = nullptr; @@ -29,7 +31,7 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) sq = mgr.sequence(); sq->record(params); - sq->record(params, algorithm); + sq->record(algorithm); sq->record(params); sq->eval(); } From 3304767f2cf0e1902fe190758e7a95c45d0bc6fd Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 26 Feb 2021 18:58:19 +0000 Subject: [PATCH 07/91] Updated to enable for opmult to work --- single_include/kompute/Kompute.hpp | 4 +- src/Algorithm.cpp | 37 ++++++++----------- src/Manager.cpp | 4 +- src/OpAlgoDispatch.cpp | 6 ++- src/OpTensorSyncDevice.cpp | 2 + src/Sequence.cpp | 4 +- .../kompute/operations/OpAlgoDispatch.hpp | 2 +- src/include/kompute/operations/OpMult.hpp | 2 +- test/TestAsyncOperations.cpp | 9 +++-- test/TestDestroy.cpp | 6 ++- test/TestLogisticRegression.cpp | 12 ++++-- 11 files changed, 49 insertions(+), 39 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index e60a5bdf0..79bc6e1b4 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1686,7 +1686,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::shared_ptr& algorithm); + OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); /** * Default destructor, which is in charge of destroying the algorithm @@ -1745,7 +1745,7 @@ class OpMult : public OpAlgoDispatch * @param komputeWorkgroup Optional parameter to specify the layout for processing */ OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm) + : OpAlgoDispatch(algorithm, true) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index aee9ddd36..f00bc1090 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -15,7 +15,14 @@ Algorithm::Algorithm( KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); this->mDevice = device; - this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); + + if (tensors.size() && spirv.size()) { + KP_LOG_INFO("Kompute Algorithm initialising with tensor size: {} and spirv size: {}", tensors.size(), spirv.size()); + this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); + } + else { + KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or spirv so not rebuilding vulkan components"); + } } Algorithm::~Algorithm() @@ -39,10 +46,10 @@ Algorithm::rebuild( this->mSpirv = spirv; this->mSpecializationConstants = specializationConstants; this->mPushConstants = pushConstants; - this->setWorkgroup(workgroup); + this->setWorkgroup(workgroup, this->mTensors.size() ? this->mTensors[0]->size() : 1); // Descriptor pool is created first so if available then destroy all before rebuild - if (this->mFreeDescriptorPool) { + if (this->isInit()) { this->destroy(); } @@ -340,20 +347,6 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) { KP_LOG_DEBUG("Kompute Algorithm calling record dispatch"); - if(this->mPipelineCache) { - KP_LOG_WARN("Value valid"); - } - else { - KP_LOG_WARN("NOT Value valid"); - } - - if(this->mPipeline) { - KP_LOG_WARN("Value valid"); - } - else { - KP_LOG_WARN("NOT Value valid"); - } - commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, *this->mPipeline); @@ -376,10 +369,7 @@ Algorithm::recordDispatch(std::shared_ptr commandBuffer) void Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { - KP_LOG_INFO("Kompute OpAlgoCreate setting dispatch size X: {}, Y: {}, Z: {}", - this->mWorkgroup[0], - this->mWorkgroup[1], - this->mWorkgroup[2]); + KP_LOG_INFO("Kompute OpAlgoCreate setting dispatch size"); // The dispatch size is set up based on either explicitly provided template // parameters or by default it would take the shape and size of the tensors @@ -394,6 +384,11 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { } else { this->mWorkgroup = { minSize, 1, 1 }; } + + KP_LOG_INFO("Kompute OpAlgoCreate set dispatch size X: {}, Y: {}, Z: {}", + this->mWorkgroup[0], + this->mWorkgroup[1], + this->mWorkgroup[2]); } const Workgroup& diff --git a/src/Manager.cpp b/src/Manager.cpp index 833069d9f..9f25e1826 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -33,7 +33,7 @@ Manager::Manager() Manager::Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices) { - this->mManageResources = false; + this->mManageResources = true; this->createInstance(); this->createDevice(familyQueueIndices, physicalDeviceIndex); @@ -43,7 +43,7 @@ Manager::Manager(std::shared_ptr instance, std::shared_ptr physicalDevice, std::shared_ptr device) { - this->mManageResources = true; + this->mManageResources = false; this->mInstance = instance; this->mPhysicalDevice = physicalDevice; diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index a20900189..09050fba0 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -4,10 +4,14 @@ namespace kp { -OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm) +OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); + if (!skipAlgoCheck && !algorithm->isInit()) { + throw std::runtime_error("Kompute OpAlgoDispatch constructor with non initialised algorithm"); + } + this->mAlgorithm = algorithm; } diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index 2cdd4e443..a73224297 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -19,6 +19,8 @@ OpTensorSyncDevice::OpTensorSyncDevice( OpTensorSyncDevice::~OpTensorSyncDevice() { KP_LOG_DEBUG("Kompute OpTensorSyncDevice destructor started"); + + this->mTensors.clear(); } void diff --git a/src/Sequence.cpp b/src/Sequence.cpp index e341e734c..9424e119d 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -78,8 +78,7 @@ Sequence::eval() std::shared_ptr Sequence::eval(std::shared_ptr op) { this->clear(); - this->record(op); - this->eval(); + return this->record(op)->eval(); } std::shared_ptr @@ -88,6 +87,7 @@ Sequence::evalAsync() if (this->isRecording()) { this->end(); } + if (this->mIsRunning) { throw std::runtime_error("Kompute Sequence evalAsync called when an eval async was " "called without successful wait"); diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index 1b5ab1bf0..e61c3166d 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,7 +17,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::shared_ptr& algorithm); + OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); /** * Default destructor, which is in charge of destroying the algorithm diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index 992b0e8a0..fea38bdee 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -33,7 +33,7 @@ class OpMult : public OpAlgoDispatch * @param komputeWorkgroup Optional parameter to specify the layout for processing */ OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm) + : OpAlgoDispatch(algorithm, true) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index 6e5ba8adf..da9f8d887 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -80,8 +80,11 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) std::vector> inputsAsyncB; + std::vector> algosAsync; + for (uint32_t i = 0; i < numParallel; i++) { inputsAsyncB.push_back(mgr.tensor(data)); + algosAsync.push_back(mgr.algorithm({inputsAsyncB[i]}, spirv)); } std::vector> sqs; @@ -93,7 +96,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) auto startAsync = std::chrono::high_resolution_clock::now(); for (uint32_t i = 0; i < numParallel; i++) { - sqs[i]->evalAsync(algorithms[i]); + sqs[i]->evalAsync(algosAsync[i]); } for (uint32_t i = 0; i < numParallel; i++) { @@ -157,8 +160,8 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) sq1->eval({ tensorA, tensorB }); - std::shared_ptr algo1 = mgr.algorithm({tensorA}); - std::shared_ptr algo2 = mgr.algorithm({tensorB}); + std::shared_ptr algo1 = mgr.algorithm({tensorA}, spirv); + std::shared_ptr algo2 = mgr.algorithm({tensorB}, spirv); sq1->evalAsync(algo1); sq2->evalAsync(algo2); diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 43b7c8e1a..cf753267e 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -74,7 +74,7 @@ TEST(TestDestroy, TestDestroyTensorVector) mgr.sequence() ->record(algo->getTensors()) ->record(algo) - ->record(algo->getTensors()) + ->record(algo->getTensors()) ->eval(); tensorA->destroy(); @@ -111,12 +111,14 @@ TEST(TestDestroy, TestDestroySequenceSingle) tensorA = mgr.tensor({0, 0, 0}); - mgr.sequence() + sq = mgr.sequence() ->record({tensorA}) ->record(mgr.algorithm({tensorA}, spirv)) ->record({tensorA}) ->eval(); + sq->destroy(); + EXPECT_FALSE(sq->isInit()); } } diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index 16c08afb9..f7ad9eda1 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -32,13 +32,15 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) wIn, wOutI, wOutJ, bIn, bOut, lOut }; + mgr.sequence()->eval(params); + std::vector spirv = std::vector( - (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, - (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + - kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); + (uint32_t*)kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv, + (uint32_t*)(kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv + + kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv_len)); std::shared_ptr algorithm = - mgr.algorithm(params, spirv, kp::Workgroup(), kp::Constants({5.0})); + mgr.algorithm(params, spirv, kp::Workgroup({5}), kp::Constants({5.0})); std::shared_ptr sq = mgr.sequence() @@ -104,6 +106,8 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) wIn, wOutI, wOutJ, bIn, bOut, lOut }; + mgr.sequence()->record(params)->eval(); + std::vector spirv = std::vector( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + From b3abbf1bb4836e96d30bc257c507f1d4a95a054a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 26 Feb 2021 19:21:26 +0000 Subject: [PATCH 08/91] Added algo executions tests --- test/TestMultipleAlgoExecutions.cpp | 538 ++++++++++------------------ 1 file changed, 190 insertions(+), 348 deletions(-) diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 74f7b3c52..0ddbbffc8 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -3,351 +3,193 @@ #include "kompute/Kompute.hpp" -//TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) -//{ -// -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// -// std::string shader(R"( -// #version 450 -// layout (local_size_x = 1) in; -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pa[index] = pa[index] + 1; -// })"); -// -// mgr.rebuild({ tensorA }); -// -// std::shared_ptr sq = -// mgr.sequence("newSequence"); -// -// { -// sq->begin(); -// -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// -// sq->record({ tensorA }); -// -// sq->end(); -// sq->eval(); -// } -// -// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -//} -// -//TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) -//{ -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// -// std::string shader(R"( -// #version 450 -// layout (local_size_x = 1) in; -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pa[index] = pa[index] + 1; -// })"); -// -// mgr.rebuild({ tensorA }, false); -// -// std::shared_ptr sqTensor = mgr.sequence(); -// -// std::shared_ptr sq = mgr.sequence(); -// -// // First create the tensor in a separate sequence -// sqTensor->begin(); -// sqTensor->record({ tensorA }); -// sqTensor->end(); -// sqTensor->eval(); -// -// // Then perform the computations -// sq->begin(); -// sq->record({ tensorA }, -// kp::Shader::compile_source(shader)); -// sq->end(); -// sq->eval(); -// -// sq->begin(); -// sq->record({ tensorA }, -// kp::Shader::compile_source(shader)); -// sq->end(); -// sq->eval(); -// -// sq->begin(); -// sq->record({ tensorA }, -// kp::Shader::compile_source(shader)); -// sq->end(); -// sq->eval(); -// -// sq->begin(); -// sq->record({ tensorA }); -// sq->end(); -// sq->eval(); -// -// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -//} -// -//TEST(TestMultipleAlgoExecutions, MultipleSequences) -//{ -// -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// -// std::string shader(R"( -// #version 450 -// layout (local_size_x = 1) in; -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pa[index] = pa[index] + 1; -// })"); -// -// mgr.rebuild({ tensorA }); -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence"); -// -// sq->begin(); -// -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// -// sq->end(); -// sq->eval(); -// } -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence2"); -// -// sq->begin(); -// -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// -// sq->end(); -// sq->eval(); -// } -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence3"); -// -// sq->begin(); -// -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// -// sq->end(); -// sq->eval(); -// } -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence5"); -// -// sq->begin(); -// -// sq->record({ tensorA }); -// -// sq->end(); -// sq->eval(); -// } -// -// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -//} -// -//TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) -//{ -// -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// -// std::string shader(R"( -// #version 450 -// layout (local_size_x = 1) in; -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pa[index] = pa[index] + 1; -// })"); -// -// mgr.rebuild({ tensorA }, false); -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence"); -// -// sq->begin(); -// -// sq->record({ tensorA }); -// -// sq->end(); -// sq->eval(); -// } -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence2"); -// -// sq->begin(); -// -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// -// sq->end(); -// -// sq->eval(); -// sq->eval(); -// sq->eval(); -// } -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence3"); -// -// sq->begin(); -// -// sq->record({ tensorA }); -// -// sq->end(); -// -// sq->eval(); -// sq->eval(); -// sq->eval(); -// } -// -// EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); -//} -// -//TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrOpCreate) -//{ -// -// kp::Manager mgr; -// -// std::shared_ptr tensorInA{ new kp::Tensor({ 2.0, 4.0, 6.0 }) }; -// std::shared_ptr tensorInB{ new kp::Tensor({ 0.0, 1.0, 2.0 }) }; -// std::shared_ptr tensorOut{ new kp::Tensor({ 0.0, 0.0, 0.0 }) }; -// -// mgr.rebuild({ tensorInA, tensorInB, tensorOut }); -// -// std::string shader(R"( -// // The version to use -// #version 450 -// -// // The execution structure -// layout (local_size_x = 1) in; -// -// // The buffers are provided via the tensors -// layout(binding = 0) buffer bufA { float a[]; }; -// layout(binding = 1) buffer bufB { float b[]; }; -// layout(binding = 2) buffer bufOut { float o[]; }; -// -// void main() { -// uint index = gl_GlobalInvocationID.x; -// -// o[index] = a[index] * b[index]; -// } -// )"); -// -// mgr.evalOpDefault( -// { tensorInA, tensorInB, tensorOut }, -// kp::Shader::compile_source(shader)); -// -// mgr.evalOpDefault({ tensorOut }); -// -// EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); -//} -// -//TEST(TestMultipleAlgoExecutions, ManagerEvalMultSourceStrMgrCreate) -//{ -// -// kp::Manager mgr; -// -// auto tensorInA = mgr.tensor( -// { 2.0, 4.0, 6.0 }, kp::Tensor::TensorTypes::eDevice, false); -// auto tensorInB = mgr.tensor( -// { 0.0, 1.0, 2.0 }, kp::Tensor::TensorTypes::eDevice, false); -// auto tensorOut = mgr.tensor( -// { 0.0, 0.0, 0.0 }, kp::Tensor::TensorTypes::eDevice, false); -// -// std::string shader(R"( -// // The version to use -// #version 450 -// -// // The execution structure -// layout (local_size_x = 1) in; -// -// // The buffers are provided via the tensors -// layout(binding = 0) buffer bufA { float a[]; }; -// layout(binding = 1) buffer bufB { float b[]; }; -// layout(binding = 2) buffer bufOut { float o[]; }; -// -// void main() { -// uint index = gl_GlobalInvocationID.x; -// -// o[index] = a[index] * b[index]; -// } -// )"); -// -// mgr.evalOpDefault( -// { tensorInA, tensorInB, tensorOut }); -// -// mgr.evalOpDefault( -// { tensorInA, tensorInB, tensorOut }, -// kp::Shader::compile_source(shader)); -// -// mgr.evalOpDefault({ tensorOut }); -// -// EXPECT_EQ(tensorOut->data(), std::vector({ 0.0, 4.0, 12.0 })); -//} -// -//TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) -//{ -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// -// std::string shader(R"( -// #version 450 -// layout (local_size_x = 1) in; -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pa[index] = pa[index] + 1; -// })"); -// -// { -// std::shared_ptr sq = nullptr; -// -// { -// kp::Manager mgr; -// -// mgr.rebuild({ tensorA }); -// -// sq = mgr.sequence(); -// -// sq->begin(); -// sq->record( -// { tensorA }, kp::Shader::compile_source(shader)); -// sq->end(); -// -// sq->eval(); -// -// mgr.evalOpDefault({ tensorA }); -// } -// } -// EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); -//} -// +TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) +{ + + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + { + mgr.sequence() + ->record({ tensorA }) + ->record(mgr.algorithm({tensorA}, spirv)) + ->record(mgr.algorithm({tensorA}, spirv)) + ->record(mgr.algorithm({tensorA}, spirv)) + ->record({ tensorA }) + ->eval(); + } + + EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +} + +TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) +{ + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + + std::shared_ptr sq = mgr.sequence(); + + mgr.sequence() + ->record({ tensorA }) + ->eval(); + + mgr.sequence() + ->record(algorithm) + ->eval(); + + mgr.sequence() + ->record(algorithm) + ->eval(); + + mgr.sequence() + ->record(algorithm) + ->eval(); + + mgr.sequence() + ->record({ tensorA }) + ->eval(); + + EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +} + +TEST(TestMultipleAlgoExecutions, MultipleSequences) +{ + + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + + std::shared_ptr sq = mgr.sequence(); + + sq->record({ tensorA })->eval(); + + sq->record(algorithm) + ->eval(); + + sq->record(algorithm) + ->eval(); + + sq->record(algorithm) + ->eval(); + + sq->record({ tensorA }) + ->eval(); + + EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +} + +TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) +{ + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + + std::shared_ptr sq = mgr.sequence(); + + sq->record({ tensorA })->eval(); + + sq->record(algorithm) + ->eval() + ->eval() + ->eval(); + + sq->record({ tensorA }) + ->eval(); + + EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +} + + +TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) +{ + std::shared_ptr tensorA = nullptr; + + { + std::shared_ptr sq = nullptr; + { + kp::Manager mgr; + + tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + + sq = mgr.sequence(); + + sq->record({ tensorA })->eval(); + + sq->record(algorithm) + ->eval() + ->eval() + ->eval(); + + sq->record({ tensorA }) + ->eval(); + } + } + + EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); +} + From fd0f0d3eb444c44568b61c2702e9e83a44abcc7a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 26 Feb 2021 21:36:20 +0000 Subject: [PATCH 09/91] Fixed all OpTensorSync tests --- src/OpTensorCopy.cpp | 4 +- test/TestOpTensorSync.cpp | 100 ++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 374fe4ea1..87f0a3b20 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -7,12 +7,12 @@ OpTensorCopy::OpTensorCopy(const std::vector>& tensors) { KP_LOG_DEBUG("Kompute OpTensorCopy constructor with params"); + this->mTensors = tensors; + if (this->mTensors.size() < 2) { throw std::runtime_error( "Kompute OpTensorCopy called with less than 2 tensor"); } - - this->mTensors = tensors; } OpTensorCopy::~OpTensorCopy() diff --git a/test/TestOpTensorSync.cpp b/test/TestOpTensorSync.cpp index 8e8c4cda2..55e02ad13 100644 --- a/test/TestOpTensorSync.cpp +++ b/test/TestOpTensorSync.cpp @@ -3,55 +3,51 @@ #include "kompute/Kompute.hpp" -//TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecPreA{ 0, 0, 0 }; -// std::vector testVecPostA{ 9, 8, 7 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecPreA) }; -// -// mgr.rebuild({ tensorA }, false); -// -// EXPECT_TRUE(tensorA->isInit()); -// -// tensorA->setData(testVecPostA); -// -// mgr.evalOpDefault({ tensorA }); -// -// mgr.evalOpDefault({ tensorA }); -// -// EXPECT_EQ(tensorA->data(), testVecPostA); -//} -// -//TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVec{ 9, 8, 7 }; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 0, 0, 0 }) }; -// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; -// std::shared_ptr tensorC{ new kp::Tensor({ 0, 0, 0 }) }; -// -// mgr.rebuild({ tensorA, tensorB, tensorC }, false); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// EXPECT_TRUE(tensorC->isInit()); -// -// tensorA->setData(testVec); -// -// mgr.evalOpDefault({ tensorA }); -// -// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); -// -// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); -// -// EXPECT_EQ(tensorA->data(), testVec); -// EXPECT_EQ(tensorB->data(), testVec); -// EXPECT_EQ(tensorC->data(), testVec); -//} +TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) +{ + + kp::Manager mgr; + + std::vector testVecPreA{ 0, 0, 0 }; + std::vector testVecPostA{ 9, 8, 7 }; + + std::shared_ptr tensorA = mgr.tensor(testVecPreA); + + EXPECT_TRUE(tensorA->isInit()); + + tensorA->setData(testVecPostA); + + mgr.sequence()->eval({ tensorA }); + + mgr.sequence()->eval({ tensorA }); + + EXPECT_EQ(tensorA->data(), testVecPostA); +} + +TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) +{ + + kp::Manager mgr; + + std::vector testVec{ 9, 8, 7 }; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr tensorC = mgr.tensor({ 0, 0, 0 }); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + EXPECT_TRUE(tensorC->isInit()); + + tensorA->setData(testVec); + + mgr.sequence()->eval({ tensorA }); + + mgr.sequence()->eval({ tensorA, tensorB, tensorC }); + + mgr.sequence()->eval({ tensorA, tensorB, tensorC }); + + EXPECT_EQ(tensorA->data(), testVec); + EXPECT_EQ(tensorB->data(), testVec); + EXPECT_EQ(tensorC->data(), testVec); +} From 7fb1515c93732f48894d37b317d68a01312c5ad3 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 07:42:38 +0000 Subject: [PATCH 10/91] Added TensorOpTensorCreate tests --- single_include/kompute/Kompute.hpp | 1544 +--------------------------- test/TestOpTensorCreate.cpp | 205 +--- 2 files changed, 54 insertions(+), 1695 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 79bc6e1b4..b100f17dd 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -318,1546 +318,4 @@ static const unsigned char shaders_glsl_opmult_comp_spv[] = { 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, - 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; -} -} -#endif // define SHADEROP_SHADEROPMULT_HPP - -/* - THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT - - --- - - Copyright 2020 The Institute for Ethical AI & Machine Learning - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef SHADEROP_SHADERLOGISTICREGRESSION_HPP -#define SHADEROP_SHADERLOGISTICREGRESSION_HPP - -namespace kp { -namespace shader_data { -static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; -} -} -#endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP - -#include -#include - -namespace kp { - -/** - * Structured data used in GPU operations. - * - * Tensors are the base building block in Kompute to perform operations across - * GPUs. Each tensor would have a respective Vulkan memory and buffer, which - * would be used to store their respective data. The tensors can be used for GPU - * data storage or transfer. - */ -class Tensor -{ - public: - /** - * Type for tensors created: Device allows memory to be transferred from - * staging buffers. Staging are host memory visible. Storage are device - * visible but are not set up to transfer or receive data (only for shader - * storage). - */ - enum class TensorTypes - { - eDevice = 0, ///< Type is device memory, source and destination - eHost = 1, ///< Type is host memory, source and destination - eStorage = 2, ///< Type is Device memory (only) - }; - - /** - * Default constructor with data provided which would be used to create the - * respective vulkan buffer and memory. - * - * @param data Non-zero-sized vector of data that will be used by the - * tensor - * @param tensorType Type for the tensor which is of type TensorTypes - */ - Tensor(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice); - - /** - * Destructor which is in charge of freeing vulkan resources unless they - * have been provided externally. - */ - ~Tensor(); - - /** - * Initialiser which calls the initialisation for all the respective tensors - * as well as creates the respective staging tensors. The staging tensors - * would only be created for the tensors of type TensorType::eDevice as - * otherwise there is no need to copy from host memory. - */ - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); - - /** - * Destroys and frees the GPU resources which include the buffer and memory. - */ - void destroy(); - - bool isInit(); - - /** - * Returns the vector of data currently contained by the Tensor. It is - * important to ensure that there is no out-of-sync data with the GPU - * memory. - * - * @return Reference to vector of elements representing the data in the - * tensor. - */ - std::vector& data(); - /** - * Overrides the subscript operator to expose the underlying data's - * subscript operator which in this case would be its underlying - * vector's. - * - * @param i The index where the element will be returned from. - * @return Returns the element in the position requested. - */ - float& operator[](int index); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - uint32_t size(); - - /** - * Retrieve the tensor type of the Tensor - * - * @return Tensor type of tensor - */ - TensorTypes tensorType(); - - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setData(const std::vector& data); - - /** - * Records a copy from the memory of the tensor provided to the current - * thensor. This is intended to pass memory into a processing, to perform - * a staging buffer transfer, or to gather output (between others). - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param copyFromTensor Tensor to copy the data from - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFrom(std::shared_ptr commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier); - - /** - * Records a copy from the internal staging memory to the device memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromStagingToDevice( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records a copy from the internal device memory to the staging memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromDeviceToStaging( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records the buffer memory barrier into the command buffer which - * ensures that relevant data transfers are carried out correctly. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param srcAccessMask Access flags for source access mask - * @param dstAccessMask Access flags for destination access mask - * @param scrStageMask Pipeline stage flags for source stage mask - * @param dstStageMask Pipeline stage flags for destination stage mask - */ - void recordBufferMemoryBarrier( - std::shared_ptr commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); - - /** - * Constructs a vulkan descriptor buffer info which can be used to specify - * and reference the underlying buffer component of the tensor without - * exposing it. - * - * @return Descriptor buffer info with own buffer - */ - vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice; - std::shared_ptr mDevice; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mPrimaryBuffer; - bool mFreePrimaryBuffer = false; - std::shared_ptr mStagingBuffer; - bool mFreeStagingBuffer = false; - std::shared_ptr mPrimaryMemory; - bool mFreePrimaryMemory = false; - std::shared_ptr mStagingMemory; - bool mFreeStagingMemory = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - - TensorTypes mTensorType = TensorTypes::eDevice; - - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer - void createBuffer(std::shared_ptr buffer, - vk::BufferUsageFlags bufferUsageFlags); - void allocateBindMemory(std::shared_ptr buffer, - std::shared_ptr memory, - vk::MemoryPropertyFlags memoryPropertyFlags); - void copyBuffer(std::shared_ptr commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); - - // Private util functions - vk::BufferUsageFlags getPrimaryBufferUsageFlags(); - vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); - vk::BufferUsageFlags getStagingBufferUsageFlags(); - vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - uint64_t memorySize(); -}; - -} // End namespace kp - -namespace kp { - -/** - Abstraction for compute shaders that are run on top of tensors grouped via - ParameterGroups (which group descriptorsets) -*/ -class Algorithm -{ -public: - - /** - * Default constructor for Algorithm - * - * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders - */ - Algorithm( - std::shared_ptr device, - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. - * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing - */ - void rebuild( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Destructor for Algorithm which is responsible for freeing and desroying - * respective pipelines and owned parameter groups. - */ - ~Algorithm(); - - /** - * Records the dispatch function with the provided template parameters or - * alternatively using the size of the tensor by default. - * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value - */ - void recordDispatch(std::shared_ptr commandBuffer); - - bool isInit(); - - void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); - - const Workgroup& getWorkgroup(); - const Constants& getSpecializationConstants(); - const Constants& getPushConstants(); - const std::vector>& getTensors(); - - void destroy(); - -private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mDevice; - std::vector> mTensors; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mDescriptorSetLayout; - bool mFreeDescriptorSetLayout = false; - std::shared_ptr mDescriptorPool; - bool mFreeDescriptorPool = false; - std::shared_ptr mDescriptorSet; - bool mFreeDescriptorSet = false; - std::shared_ptr mShaderModule; - bool mFreeShaderModule = false; - std::shared_ptr mPipelineLayout; - bool mFreePipelineLayout = false; - std::shared_ptr mPipelineCache; - bool mFreePipelineCache = false; - std::shared_ptr mPipeline; - bool mFreePipeline = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mSpirv; - Constants mSpecializationConstants; - Constants mPushConstants; - Workgroup mWorkgroup; - - bool mIsInit; - - // Create util functions - void createShaderModule(); - void createPipeline(); - - // Parameters - void createParameters(); -}; - -} // End namespace kp - -namespace kp { - -/** - * Base Operation which provides the high level interface that Kompute - * operations implement in order to perform a set of actions in the GPU. - * - * Operations can perform actions on tensors, and optionally can also own an - * Algorithm with respective parameters. kp::Operations with kp::Algorithms - * would inherit from kp::OpBaseAlgo. - */ -class OpBase -{ - public: - - /** - * Default destructor for OpBase class. This OpBase destructor class should - * always be called to destroy and free owned resources unless it is - * intended to destroy the resources in the parent class. - */ - virtual ~OpBase() - { - KP_LOG_DEBUG("Kompute OpBase destructor started"); - } - - /** - * The record function is intended to only send a record command or run - * commands that are expected to record operations that are to be submitted - * as a batch into the GPU. - */ - virtual void record(std::shared_ptr commandBuffer) = 0; - - /** - * Pre eval is called before the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any per-eval setup steps - * required as the computation iteration begins. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are created should be idempotent in case it's called multiple - * times in a row. - */ - virtual void preEval() = 0; - - /** - * Post eval is called after the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any tear-down steps - * required as the computation iteration finishes. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are destroyed should not require a re-init unless explicitly - * provided by the user. - */ - virtual void postEval() = 0; -}; - -} // End namespace kp - -namespace kp { - -/** - * Container of operations that can be sent to GPU as batch - */ -class Sequence: public std::enable_shared_from_this -{ - public: - /** - * Main constructor for sequence which requires core vulkan components to - * generate all dependent resources. - * - * @param physicalDevice Vulkan physical device - * @param device Vulkan logical device - * @param computeQueue Vulkan compute queue - * @param queueIndex Vulkan compute queue index in device - */ - Sequence(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr computeQueue, - uint32_t queueIndex); - /** - * Destructor for sequence which is responsible for cleaning all subsequent - * owned operations. - */ - ~Sequence(); - - /** - */ - std::shared_ptr record(std::shared_ptr op); - - /** - * Record function for operation to be added to the GPU queue in batch. This - * template requires classes to be derived from the OpBase class. This - * function also requires the Sequence to be recording, otherwise it will - * not be able to add the operation. - * - * @param tensors Vector of tensors to use for the operation - * @param TArgs Template parameters that are used to initialise operation - * which allows for extensible configurations on initialisation. - */ - template - std::shared_ptr - record(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->record(op); - } - template - std::shared_ptr - record(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->record(op); - } - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - std::shared_ptr eval(); - - std::shared_ptr eval(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - // TODO: Aim to have only a single function with tensors/algorithm - template - std::shared_ptr - eval(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - // TODO: Aim to be able to handle errors when returning without throw except - return this->eval(op); - } - // Needded as otherise can't use initialiser list - template - std::shared_ptr - eval(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->eval(op); - } - - /** - * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAsync(); - std::shared_ptr evalAsync(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - template - std::shared_ptr - evalAsync(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->evalAsync(op); - } - // Needed as otherwise it's not possible to use initializer lists - template - std::shared_ptr - evalAsync(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->evalAsync(op); - } - - /** - * Eval Await waits for the fence to finish processing and then once it - * finishes, it runs the postEval of all operations. - * - * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); - - /** - * Clear function clears all operations currently recorded and starts recording again. - */ - void clear(); - - /** - * Begins recording commands for commands to be submitted into the command - * buffer. - * - * @return Boolean stating whether execution was successful. - */ - void begin(); - - /** - * Ends the recording and stops recording commands when the record command - * is sent. - * - * @return Boolean stating whether execution was successful. - */ - void end(); - - /** - * Returns true if the sequence is currently in recording activated. - * - * @return Boolean stating if recording ongoing. - */ - bool isRecording(); - - bool isInit(); - - /** - * Returns true if the sequence is currently running - mostly used for async - * workloads. - * - * @return Boolean stating if currently running. - */ - bool isRunning(); - - /** - * Destroys and frees the GPU resources which include the buffer and memory - * and sets the sequence as init=False. - */ - void destroy(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - std::shared_ptr mComputeQueue = nullptr; - uint32_t mQueueIndex = -1; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mCommandPool = nullptr; - bool mFreeCommandPool = false; - std::shared_ptr mCommandBuffer = nullptr; - bool mFreeCommandBuffer = false; - - // -------------- ALWAYS OWNED RESOURCES - vk::Fence mFence; - std::vector> mOperations; - - // State - bool mRecording = false; - bool mIsRunning = false; - - // Create functions - void createCommandPool(); - void createCommandBuffer(); -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncDevice : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncDevice(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncDevice() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Does not perform any postEval commands. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -#define KP_DEFAULT_SESSION "DEFAULT" - -namespace kp { - -/** - Base orchestrator which creates and manages device and child components -*/ -class Manager -{ - public: - /** - Base constructor and default used which creates the base resources - including choosing the device 0 by default. - */ - Manager(); - - /** - * Similar to base constructor but allows the user to provide the device - * they would like to create the resources on. - * - * @param physicalDeviceIndex The index of the physical device to use - * @param manageResources (Optional) Whether to manage the memory of the - * resources created and destroy when the manager is destroyed. - * @param familyQueueIndices (Optional) List of queue indices to add for - * explicit allocation - * @param totalQueues The total number of compute queues to create. - */ - Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices = {}); - - /** - * Manager constructor which allows your own vulkan application to integrate - * with the vulkan kompute use. - * - * @param instance Vulkan compute instance to base this application - * @param physicalDevice Vulkan physical device to use for application - * @param device Vulkan logical device to use for all base resources - * @param physicalDeviceIndex Index for vulkan physical device used - */ - Manager(std::shared_ptr instance, - std::shared_ptr physicalDevice, - std::shared_ptr device); - - /** - * Manager destructor which would ensure all owned resources are destroyed - * unless explicitly stated that resources should not be destroyed or freed. - */ - ~Manager(); - - /** - * Get or create a managed Sequence that will be contained by this manager. - * If the named sequence does not currently exist, it would be created and - * initialised. - * - * @param sequenceName The name for the named sequence to be retrieved or - * created - * @param queueIndex The queue to use from the available queues - * @return Shared pointer to the manager owned sequence resource - */ - std::shared_ptr sequence(uint32_t queueIndex = 0); - - /** - * Function that simplifies the common workflow of tensor creation and - * initialization. It will take the constructor parameters for a Tensor - * and will will us it to create a new Tensor and then create it. The - * tensor memory will then be managed and owned by the manager. - * - * @param data The data to initialize the tensor with - * @param tensorType The type of tensor to initialize - * @param syncDataToGPU Whether to sync the data to GPU memory - * @returns Initialized Tensor with memory Syncd to GPU device - */ - std::shared_ptr tensor( - const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, - bool syncDataToGPU = true); - - std::shared_ptr algorithm( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - void destroy(); - void clear(); - - private: - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mInstance = nullptr; - bool mFreeInstance = false; - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - bool mFreeDevice = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector> mManagedTensors; - std::vector> mManagedSequences; - std::vector> mManagedAlgorithms; - - std::vector mComputeQueueFamilyIndices; - std::vector> mComputeQueues; - - bool mManageResources = false; - -#if DEBUG -#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS - vk::DebugReportCallbackEXT mDebugReportCallback; - vk::DispatchLoaderDynamic mDebugDispatcher; -#endif -#endif - - // Create functions - void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); -}; - -} // End namespace kp - -#include - -namespace kp { - -/** - * Operation that provides a general abstraction that simplifies the use of - * algorithm and parameter components which can be used with shaders. - * By default it enables the user to provide a dynamic number of tensors - * which are then passed as inputs. - */ -class OpAlgoDispatch : public OpBase -{ - public: - - OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoDispatch() override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - -private: - // -------------- ALWAYS OWNED RESOURCES - std::shared_ptr mAlgorithm; -}; - -} // End namespace kp - -namespace kp { - -/** - * Operation that performs multiplication on two tensors and outpus on third - * tensor. - */ -class OpMult : public OpAlgoDispatch -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm, true) - { - KP_LOG_DEBUG("Kompute OpMult constructor with params"); - - if (tensors.size() != 3) { - throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); - } - - std::vector spirv( - (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, - (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + - kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - - algorithm->rebuild(tensors, spirv); - } - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpMult() override { - KP_LOG_DEBUG("Kompute OpMult destructor started"); - } -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type -*/ -class OpTensorCopy : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorCopy(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorCopy() override; - - /** - * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Copies the local vectors for all the tensors to sync the data with the gpu. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncLocal : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncLocal(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncLocal() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * For host tensors it performs the map command from the host memory into local memory. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp + 0x08, 0x00, 0x00, 0x00, 0x69, \ No newline at end of file diff --git a/test/TestOpTensorCreate.cpp b/test/TestOpTensorCreate.cpp index 0d7a26524..14153427e 100644 --- a/test/TestOpTensorCreate.cpp +++ b/test/TestOpTensorCreate.cpp @@ -3,155 +3,56 @@ #include "kompute/Kompute.hpp" -//TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) -//{ -// std::vector testVecA{ 9, 8, 7 }; -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// -// { -// kp::Manager mgr; -// -// mgr.rebuild({ tensorA }); -// -// EXPECT_TRUE(tensorA->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// } -// -// EXPECT_FALSE(tensorA->isInit()); -//} -// -//TEST(TestOpTensorCreate, CreateMultipleTensorSingleOp) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 9, 8, 7 }; -// std::vector testVecB{ 6, 5, 4 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// mgr.rebuild({ tensorA, tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// EXPECT_EQ(tensorB->data(), testVecB); -//} -// -//TEST(TestOpTensorCreate, CreateMultipleTensorMultipleOp) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 9, 8, 7 }; -// std::vector testVecB{ 6, 5, 4 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// mgr.rebuild({ tensorA }); -// mgr.rebuild({ tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// EXPECT_EQ(tensorB->data(), testVecB); -//} -// -//TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerDestroyed) -//{ -// -// std::vector testVecA{ 9, 8, 7 }; -// std::vector testVecB{ 6, 5, 4 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// { -// kp::Manager mgr; -// mgr.rebuild({ tensorA }); -// mgr.rebuild({ tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// EXPECT_EQ(tensorB->data(), testVecB); -// } -// -// EXPECT_FALSE(tensorA->isInit()); -// EXPECT_FALSE(tensorB->isInit()); -//} -// -//TEST(TestOpTensorCreate, TestTensorMemoryManagedByManagerNOTDestroyed) -//{ -// -// std::vector testVecA{ 9, 8, 7 }; -// std::vector testVecB{ 6, 5, 4 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// kp::Manager mgr; -// -// { -// mgr.rebuild({ tensorA }); -// mgr.rebuild({ tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// EXPECT_EQ(tensorB->data(), testVecB); -// } -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -//} -// -//TEST(TestOpTensorCreate, NoErrorIfTensorFreedBefore) -//{ -// -// std::vector testVecA{ 9, 8, 7 }; -// std::vector testVecB{ 6, 5, 4 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// kp::Manager mgr; -// -// mgr.rebuild({ tensorA }); -// mgr.rebuild({ tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// EXPECT_EQ(tensorA->data(), testVecA); -// EXPECT_EQ(tensorB->data(), testVecB); -// -// tensorA->freeMemoryDestroyGPUResources(); -// tensorB->freeMemoryDestroyGPUResources(); -// EXPECT_FALSE(tensorA->isInit()); -// EXPECT_FALSE(tensorB->isInit()); -//} -// -//TEST(TestOpTensorCreate, ExceptionOnZeroSizeTensor) -//{ -// std::vector testVecA; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// -// kp::Manager mgr; -// -// try { -// mgr.rebuild({ tensorA }); -// } catch (const std::runtime_error& err) { -// // check exception -// ASSERT_TRUE(std::string(err.what()).find("zero-sized") != -// std::string::npos); -// } -//} +TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) +{ + std::vector testVecA{ 9, 8, 7 }; + std::shared_ptr tensorA = nullptr; + + { + kp::Manager mgr; + + tensorA = mgr.tensor(testVecA); + + EXPECT_TRUE(tensorA->isInit()); + + EXPECT_EQ(tensorA->data(), testVecA); + } + + EXPECT_FALSE(tensorA->isInit()); +} + +TEST(TestOpTensorCreate, NoErrorIfTensorFreedBefore) +{ + + std::vector testVecA{ 9, 8, 7 }; + std::vector testVecB{ 6, 5, 4 }; + + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor(testVecA); + std::shared_ptr tensorB = mgr.tensor(testVecB); + + EXPECT_EQ(tensorA->data(), testVecA); + EXPECT_EQ(tensorB->data(), testVecB); + + tensorA->destroy(); + tensorB->destroy(); + + EXPECT_FALSE(tensorA->isInit()); + EXPECT_FALSE(tensorB->isInit()); +} + +TEST(TestOpTensorCreate, ExceptionOnZeroSizeTensor) +{ + std::vector testVecA; + + kp::Manager mgr; + + try { + std::shared_ptr tensorA = mgr.tensor(testVecA); + } catch (const std::runtime_error& err) { + // check exception + ASSERT_TRUE(std::string(err.what()).find("zero-sized") != + std::string::npos); + } +} From 08a5543c5dd971ee0bdf9a238eb06a95c9b7da7d Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 08:11:23 +0000 Subject: [PATCH 11/91] Removed the brainstorming code in the py file --- python/test/test_kompute.py | 417 -------- single_include/kompute/Kompute.hpp | 1544 +++++++++++++++++++++++++++- 2 files changed, 1543 insertions(+), 418 deletions(-) diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index a6e4ead3a..2ba4d4868 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -28,423 +28,6 @@ def test_opalgobase_file(): assert tensor_out.data() == [2.0, 4.0, 6.0] -params = [kp.Tensor([2, 2, 2]), kp.Tensor([1, 2, 3]), kp.Tensor([0, 0, 0])] - -mgr = kp.Manager() -op_ct = kp.OpTensorCreate(params) -op_ct = mgr.rebuild(op_ct) -mgr.eval_op(op_ct) - -algo = kp.Algo(params, spirv) -op_ac = kp.OpAlgoCreate(algo) -op_ac = mgr.rebuild(op_ac) -mgr.eval_op(op_ac) - -op_ac = kp.OpAlgoCreate(kp.Algo(params, spirv)) -mgr.eval_op(kp.OpAlgoCreate(algo)) - - -mgr = kp.Manager() - -op_ct = kp.OpTensorCreate(mgr, params) # This initialises operation -op_ct.eval() - -algo = kp.Algo(params, spirv) -op_ac = kp.OpAlgoCreate(mgr, algo) -op_ct.eval() - -op_tsd = kp.OpTensorSyncDevice(mgr, params) -op_ad = kp.OpAlgoDispatch(mgr, algo) -op_tsl = kp.OpTensorSyncLocal(mgr, params) - -sq = kp.Sequence(mgr, "newSeq") -sq.record([op_tsd, op_ad, op_tsl]) -sq.eval() -sq.destroy() - -# Explore consistent interface: -op_tsd = kp.OpTensorSyncDevice(sq, params) -op_ad = kp.OpAlgoDispatch(sq, algo) -op_tsl = kp.OpTensorSyncLocal(sq, params) - -op_tsd.record() -op_ad.record() -op_tsl.record() - -sq.eval() - - - -auto params = ...; -std::string shader = "..."; -std::vector spirv = kp::Shader::compile_source(shader); - -// Example passing mgr -kp::Manager mgr; - -kp::OpTensorCreate op_tc(mgr, params); -op_tc.eval() - -kp::Algorithm algo(params, spirv); -kp::OpAlgoCreate op_ac(mgr, algo); -op_ac.eval() - -op_ac.destroy() -op_tc.destroy() - -kp::OpTensorAlgoCreate op_c(mgr, params, algo); -op_c.eval() - -kp::Sequence sq(mgr); - -kp::OpTensorSyncDevice op_tsd(mgr, params); -kp::OpAlgoDispatch op_ad(mgr, algo); -kp::OpTensorSyncLocal op_tsl(mgr, params); - -sq.record({op_tsd, op_ad, op_tsl}) - -for(...) { - sq.eval(); - - tensorA... -} - -###### -####### -####### -####### -####### -###### -// Example not passing mgr -kp::Manager mgr; - -std::shared_ptr op_tc_1{ new kp::OpTensorCreate(params) }; -auto sq_1 = mgr.eval(op_tc_1); // Initialises and stores op as part of new sequence -mgr.eval(op_tc_1); // Fails as this op can only be "initialised" once -mgr.destroy(op_tc_1); -mgr.eval(op_tc_1); // This works as it's a new setup -mgr.eval(params); // Fails as tensors already created -// NOT ALLOED TO DELETE JUST TENSORS ANYMORE - SEE BELOW -mgr.destroy(params); // Sends to inconsistent state as op_tc_1 will still destroy these parameters -mgr.destroy(op_tc_1, recursive=false); // Destroys only operation, which is useful when you need to ensure another operation owns the parameters -auto op_tc_2 = mgr.eval(params); -std::shared_ptr op_tc_2{ new kp::OpTensorCreate(params) }; // fails as tensors already created -op_tc_2.destroy(); // Manager still holds dangling reference so requires explicit termination in manager -mgr.destroy(op_tc_2); -auto op_tc_3 = mgr.eval({ new kp::OpTensorCreate(params) }); - -std::shared_ptr algo{ new kp::Algorithm(params, spirv, kp::Workgroup(), kp::SpecConst(), kp::PushConst()) }; -std::shared_ptr op_ac_1{ new kp::OpAlgoCreate(algo) }; -mgr.eval(op_ac_1); // Initialises and stores op as part of manager -mgr.eval(op_ac_1); // Fails as this op can only be "initialised" once -mgr.destroy(op_ac_1); - -std::shared_ptr op_ac_2 = - mgr.eval({ new kp::OpAlgoCreate(params, { new kp::Algorithm(spirv) }) }); - -std::shared_ptr op_amc{ new kp::OpAlgoMultCreate(params) }; -mgr.eval(op_amc); - -std::shared_ptr algo_mult = op_amc.algorithm() -std::vector> params = op_amc.tensors() - -auto op_tsd = std::make_shared(params); -auto op_ad = std::make_shared(algo); -auto op_ad = std::make_shared(algo); -auto op_tsl = std::make_shared(mgr, params); - -op_params = {op_tsd, op_ad, op_tsl}; - -mgr.record(op_params); -mgr.eval(); // Runs recorded default sequence - -mgr.record(op_params, clear=false); // Non-create ops ok if rerun -mgr.eval(); // Runs twice the recorded paams - -mgr.record("namedSeq", op_params); -mgr.eval("namedSeq"); - -kp::Manager mgrAsync(0, {0, 2}); -mgr.sequence("namedSeq2", 0); // Create named sequence with queue in index 0 -mgr.sequence("namedSeq3", 1); - -mgr.eval_async("namedSeq2", op_params); // Clear, record params and eval -mgr.eval_async("namedSeq3", op_params); // Clear, record params and eval - -mgr.eval_await("namedSeq2"); -mgr.eval_await("namedSeq3"); - -mgr.destroy("namedSeq"); // Destroy named sequence -mgr.destroy({"namedSeq2", "namedSeq3"}); // Destroy multiple named sequences -mgr.destroy("namedSeq"); // Error - - - - -mgr = kp.Manager(0, [0, 2]) - -// Manager does not need to manage seq anymore -sq_1 = kp.Sequence(mgr, 0) - -t1 = kp.Tensor(sq_1, [0, 0, 0]) -t2 = kp.Tensor(sq_1, [0, 1, 2]) - -algo = kp.Algorithm(sq_1) - -op_tc = kp.OpTensorCreate(sq_1, params) -op_tsd = kp.OpTensorSyncDevice(sq_1, params) -op_ac = kp.OpAlgoCreate(sq_1, algo) -op_ad = kp.OpAlgoDispatch(sq_1, algo) - -sq_1.clear() - -op_tc.record() -op_tsd.record() -op_ac.record() -op_ad.record() -op_ad.record() -op_ad.record() - -sq_1.eval() - - -std::shared_ptr mgr = kp::ManagerSP(0, {0, 1}); - -std::shared_ptr sq_2 = kp::SequenceSP(mgr, 1) - -std::shared_ptr t1 = kp::TensorSP(sq_2, {1, 2, 3}); -std::shared_ptr t2 = kp::TensorSP(sq_2, {2, 3, 4}); - -auto params = ... - -std::shared_ptr algo2 = kp::AlgorithmSP(sq_2, params, spirv, workgroup); - -// How do we deal with this? -{ - auto op_1 = kp::OpTensorSyncDevice(sq_2, params) - auto op_2 = kp::OpAlgoDispatch(sq_2, algo) -} - -sq_2.eval() - - -// HEAP ONLY - This would fail - -kp::Manager mgr = kp::Manager(0, {0, 1}); - -kp::Sequence sq_2 = kp::Sequence(mgr, 1) - -kp::Tensor t1 = kp::Tensor(sq_2, {1, 2, 3}); -kp::Tensor t2 = kp::Tensor(sq_2, {2, 3, 4}); - -auto params = ... - -kp::Algorithm algo2 = kp::AlgorithmSP(sq_2, params, spirv, workgroup); - -// How do we deal with this? -{ - auto op_1 = kp::OpTensorSyncDevice(sq_2, params) - auto op_2 = kp::OpAlgoDispatch(sq_2, algo) -} - -sq_2.eval() - - - - - -kp::Manager mgr = kp::Manager(0, {0, 1}); - -kp::Sequence sq_2 = kp::Sequence(mgr, 1) - -kp::Tensor t1 = kp::Tensor(sq_2, {1, 2, 3}); -kp::Tensor t2 = kp::Tensor(sq_2, {2, 3, 4}); - -auto params = ... - -kp::Algorithm* algo2 = new kp::Algorithm(sq_2, params, spirv, workgroup); - -// How do we deal with this? -{ - auto op_1 = kp::OpTensorSyncDevice(sq_2, params) - auto op_2 = kp::OpAlgoDispatch(sq_2, algo) -} - -sq_2.eval() - - - - - - -kp::Manager mgr = kp::Manager; - -auto sq_2 = mgr.sequence() - -{ - // What if we want to use tensor in a different sequence? - auto t1 = sq_2.tensor({1, 2, 3}); - auto t2 = sq_2.tensor({1, 2, 3}); - - auto algo2 = sq_2.algorithm(); - - sq_2.record(kp::OpTensorRebuild({ t1 })) - sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) - sq_2.record(kp::OpTensorSyncDevice(prams)) - sq_2.record(kp::OpAlgoDispatch(prams, algo2)) -} - -sq_2.eval() - - - -kp::Manager mgr = kp::Manager; - -auto t1 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared -auto t2 = mgr.tensor({1, 2, 3}); - -auto algo2 = mgr.algorithm(); - -{ - auto sq_2 = mgr.sequence() - - { - sq_2.record(kp::OpTensorRebuild({ t1 })) // record only supports move operator && - sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) - sq_2.record(kp::OpTensorSyncDevice(prams)) - sq_2.record(kp::OpAlgoDispatch(prams, algo2)) - } - - sq_2.eval() -} - - - -// What about only tensors being init with it - - -{ - kp::Manager mgr = kp::Manager; - - auto t0 = mgr.tensor({0, 0, 0}) - - { - auto t1 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared (refc 1) - - { - auto sq_2 = mgr.sequence() - - { - - auto t2 = mgr.tensor({1, 2, 3}); // Held as weak ptr but passed as shared (refc 1) - auto algo2 = mgr.algorithm(); // Held as weak ptr but passed as shared (refc 1) - - params = {t1, t2} - - sq_2.record(kp::OpTensorRebuild(params, {1, 2, 3, 4})) // Refc is now 2 for 3 for params - sq_2.record(kp::OpAlgoRebuild(params, algo2, spirv)) // refc is now 2 for algo2, 3 for parms - sq_2.record(kp::OpTensorSyncDevice(prams)) // refc for params 4 - sq_2.record(kp::OpAlgoDispatch(prams, algo2)) // refc for params 5, 3 for algo2 - } - - sq_2.eval() // all refcs stil valid - } // seq destroyed so refc for algo2 and t2 drops to 0, gets destroyed, t1 has 1 - } // t1 refc drops to 0, gets destroyed - // refc of t0 is still 1 - - mgr.gc() // Iterates through all tensor, sequence and algo weak_ptr and removes unused - - // can we have something like - mgr.sequence() - .record(kp::OpTensorRebuild(params, {1, 2, 3, 4})) - .record(kp::OpAlgoDispatch(params, algo2)) - .eval(); - -}// refc is destroyed by manager manually, the rest are empty shells so ignored - - - - -kp::Manager mgr = kp::Manager(0, {0, 1}); - -std::shared_ptr t1 = mgr.tensor({1, 2, 3}); -std::shared_ptr t2 = mgr.tensor({1, 2, 3}); - -auto params = ... - -std::shared_ptr algo2 = mgr.algorithm(params, spirv, workgroup); - -sq_2.record(prams) -sq_2.record(algo) - - -// WHY NO MORE DETROY TENSORS: - - * std::shared_ptr op_tc1{ kp::OpTensorCreate(params) }; - * { - * std::shared_ptr op_tc2{ kp::OpTensorCreate(params) }; - * mgr.eval(op_tc2); - * mgr.destroy(params); - * - * mgr.eval(op_tc1); - * - * } // op_tc1 is destroyed and all parameters are freed - - - -// NO LONGER ALLOWED: Mainly as manager now needs to regsiter ops -// If we still want it, then sequence wil have to hold ref to manager -auto sq = mgr.sequence(); - -auto op_tsd = std::make_shared(params); -auto op_ad = std::make_shared(algo); -auto op_tsl = std::make_shared(mgr, params); - -sq.record({op_tsd, op_ad, op_tsl}); // Clear and record -sq.eval(); -sq.record({op_tsd, op_ad, op_tsl}, clear=false); // record on top -sq.eval(); -sq.clear(); // explicitly clear - - - - - -mgr = kp.Manager() - -op_ct = kp.OpTensorCreate(params) -mgr.eval(op_ct) - -algo = kp.Algo(params, spirv) -op_ac = kp.OpAlgoCreate(algo) -mgr.eval(op_ac) # Runs init on operator function (below shows explicit steps) - -op_tsd = kp.OpTensorSyncDevice(params) -op_ad = kp.OpAlgoDispatch(algo) -op_tsl = kp.OpTensorSyncLocal(params) - -sq = mgr.sequence() -sq.record([op_tsd, op_ad, op_tsl]) -sq.eval() -sq.eval() -sq.eval() - -mgr.eval(op_ac) # Would fail as algo is initialised -mgr.destroy(op_ac) # Destroys Op and Algo owned object -mgr.eval(op_ac) # Succeeds with new -mgr.destroy(op_ac) -mgr.init(op_ac) -mgr.eval(op_ac, init=False) - - - - - - - - - - def test_shader_str(): """ diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index b100f17dd..79bc6e1b4 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -318,4 +318,1546 @@ static const unsigned char shaders_glsl_opmult_comp_spv[] = { 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, \ No newline at end of file + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, + 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; +} +} +#endif // define SHADEROP_SHADEROPMULT_HPP + +/* + THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT + + --- + + Copyright 2020 The Institute for Ethical AI & Machine Learning + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SHADEROP_SHADERLOGISTICREGRESSION_HPP +#define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +namespace kp { +namespace shader_data { +static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; +} +} +#endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +#include +#include + +namespace kp { + +/** + * Structured data used in GPU operations. + * + * Tensors are the base building block in Kompute to perform operations across + * GPUs. Each tensor would have a respective Vulkan memory and buffer, which + * would be used to store their respective data. The tensors can be used for GPU + * data storage or transfer. + */ +class Tensor +{ + public: + /** + * Type for tensors created: Device allows memory to be transferred from + * staging buffers. Staging are host memory visible. Storage are device + * visible but are not set up to transfer or receive data (only for shader + * storage). + */ + enum class TensorTypes + { + eDevice = 0, ///< Type is device memory, source and destination + eHost = 1, ///< Type is host memory, source and destination + eStorage = 2, ///< Type is Device memory (only) + }; + + /** + * Default constructor with data provided which would be used to create the + * respective vulkan buffer and memory. + * + * @param data Non-zero-sized vector of data that will be used by the + * tensor + * @param tensorType Type for the tensor which is of type TensorTypes + */ + Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); + + /** + * Destructor which is in charge of freeing vulkan resources unless they + * have been provided externally. + */ + ~Tensor(); + + /** + * Initialiser which calls the initialisation for all the respective tensors + * as well as creates the respective staging tensors. The staging tensors + * would only be created for the tensors of type TensorType::eDevice as + * otherwise there is no need to copy from host memory. + */ + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice); + + /** + * Destroys and frees the GPU resources which include the buffer and memory. + */ + void destroy(); + + bool isInit(); + + /** + * Returns the vector of data currently contained by the Tensor. It is + * important to ensure that there is no out-of-sync data with the GPU + * memory. + * + * @return Reference to vector of elements representing the data in the + * tensor. + */ + std::vector& data(); + /** + * Overrides the subscript operator to expose the underlying data's + * subscript operator which in this case would be its underlying + * vector's. + * + * @param i The index where the element will be returned from. + * @return Returns the element in the position requested. + */ + float& operator[](int index); + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + uint32_t size(); + + /** + * Retrieve the tensor type of the Tensor + * + * @return Tensor type of tensor + */ + TensorTypes tensorType(); + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + void setData(const std::vector& data); + + /** + * Records a copy from the memory of the tensor provided to the current + * thensor. This is intended to pass memory into a processing, to perform + * a staging buffer transfer, or to gather output (between others). + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param copyFromTensor Tensor to copy the data from + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFrom(std::shared_ptr commandBuffer, + std::shared_ptr copyFromTensor, + bool createBarrier); + + /** + * Records a copy from the internal staging memory to the device memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromStagingToDevice( + std::shared_ptr commandBuffer, + bool createBarrier); + + /** + * Records a copy from the internal device memory to the staging memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromDeviceToStaging( + std::shared_ptr commandBuffer, + bool createBarrier); + + /** + * Records the buffer memory barrier into the command buffer which + * ensures that relevant data transfers are carried out correctly. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param srcAccessMask Access flags for source access mask + * @param dstAccessMask Access flags for destination access mask + * @param scrStageMask Pipeline stage flags for source stage mask + * @param dstStageMask Pipeline stage flags for destination stage mask + */ + void recordBufferMemoryBarrier( + std::shared_ptr commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + + /** + * Constructs a vulkan descriptor buffer info which can be used to specify + * and reference the underlying buffer component of the tensor without + * exposing it. + * + * @return Descriptor buffer info with own buffer + */ + vk::DescriptorBufferInfo constructDescriptorBufferInfo(); + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + void mapDataFromHostMemory(); + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + void mapDataIntoHostMemory(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice; + std::shared_ptr mDevice; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mPrimaryBuffer; + bool mFreePrimaryBuffer = false; + std::shared_ptr mStagingBuffer; + bool mFreeStagingBuffer = false; + std::shared_ptr mPrimaryMemory; + bool mFreePrimaryMemory = false; + std::shared_ptr mStagingMemory; + bool mFreeStagingMemory = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mData; + + TensorTypes mTensorType = TensorTypes::eDevice; + + void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer + void createBuffer(std::shared_ptr buffer, + vk::BufferUsageFlags bufferUsageFlags); + void allocateBindMemory(std::shared_ptr buffer, + std::shared_ptr memory, + vk::MemoryPropertyFlags memoryPropertyFlags); + void copyBuffer(std::shared_ptr commandBuffer, + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); + + // Private util functions + vk::BufferUsageFlags getPrimaryBufferUsageFlags(); + vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); + vk::BufferUsageFlags getStagingBufferUsageFlags(); + vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + uint64_t memorySize(); +}; + +} // End namespace kp + +namespace kp { + +/** + Abstraction for compute shaders that are run on top of tensors grouped via + ParameterGroups (which group descriptorsets) +*/ +class Algorithm +{ +public: + + /** + * Default constructor for Algorithm + * + * @param device The Vulkan device to use for creating resources + * @param commandBuffer The vulkan command buffer to bind the pipeline and + * shaders + */ + Algorithm( + std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + /** + * Initialiser for the shader data provided to the algorithm as well as + * tensor parameters that will be used in shader. + * + * @param shaderFileData The bytes in spir-v format of the shader + * @tensorParams The Tensors to be used in the Algorithm / shader for + * @specalizationInstalces The specialization parameters to pass to the function + * processing + */ + void rebuild( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + /** + * Destructor for Algorithm which is responsible for freeing and desroying + * respective pipelines and owned parameter groups. + */ + ~Algorithm(); + + /** + * Records the dispatch function with the provided template parameters or + * alternatively using the size of the tensor by default. + * + * @param x Layout X dispatch value + * @param y Layout Y dispatch value + * @param z Layout Z dispatch value + */ + void recordDispatch(std::shared_ptr commandBuffer); + + bool isInit(); + + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + + const Workgroup& getWorkgroup(); + const Constants& getSpecializationConstants(); + const Constants& getPushConstants(); + const std::vector>& getTensors(); + + void destroy(); + +private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mDevice; + std::vector> mTensors; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mDescriptorSetLayout; + bool mFreeDescriptorSetLayout = false; + std::shared_ptr mDescriptorPool; + bool mFreeDescriptorPool = false; + std::shared_ptr mDescriptorSet; + bool mFreeDescriptorSet = false; + std::shared_ptr mShaderModule; + bool mFreeShaderModule = false; + std::shared_ptr mPipelineLayout; + bool mFreePipelineLayout = false; + std::shared_ptr mPipelineCache; + bool mFreePipelineCache = false; + std::shared_ptr mPipeline; + bool mFreePipeline = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mSpirv; + Constants mSpecializationConstants; + Constants mPushConstants; + Workgroup mWorkgroup; + + bool mIsInit; + + // Create util functions + void createShaderModule(); + void createPipeline(); + + // Parameters + void createParameters(); +}; + +} // End namespace kp + +namespace kp { + +/** + * Base Operation which provides the high level interface that Kompute + * operations implement in order to perform a set of actions in the GPU. + * + * Operations can perform actions on tensors, and optionally can also own an + * Algorithm with respective parameters. kp::Operations with kp::Algorithms + * would inherit from kp::OpBaseAlgo. + */ +class OpBase +{ + public: + + /** + * Default destructor for OpBase class. This OpBase destructor class should + * always be called to destroy and free owned resources unless it is + * intended to destroy the resources in the parent class. + */ + virtual ~OpBase() + { + KP_LOG_DEBUG("Kompute OpBase destructor started"); + } + + /** + * The record function is intended to only send a record command or run + * commands that are expected to record operations that are to be submitted + * as a batch into the GPU. + */ + virtual void record(std::shared_ptr commandBuffer) = 0; + + /** + * Pre eval is called before the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any per-eval setup steps + * required as the computation iteration begins. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are created should be idempotent in case it's called multiple + * times in a row. + */ + virtual void preEval() = 0; + + /** + * Post eval is called after the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any tear-down steps + * required as the computation iteration finishes. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are destroyed should not require a re-init unless explicitly + * provided by the user. + */ + virtual void postEval() = 0; +}; + +} // End namespace kp + +namespace kp { + +/** + * Container of operations that can be sent to GPU as batch + */ +class Sequence: public std::enable_shared_from_this +{ + public: + /** + * Main constructor for sequence which requires core vulkan components to + * generate all dependent resources. + * + * @param physicalDevice Vulkan physical device + * @param device Vulkan logical device + * @param computeQueue Vulkan compute queue + * @param queueIndex Vulkan compute queue index in device + */ + Sequence(std::shared_ptr physicalDevice, + std::shared_ptr device, + std::shared_ptr computeQueue, + uint32_t queueIndex); + /** + * Destructor for sequence which is responsible for cleaning all subsequent + * owned operations. + */ + ~Sequence(); + + /** + */ + std::shared_ptr record(std::shared_ptr op); + + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + */ + template + std::shared_ptr + record(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->record(op); + } + template + std::shared_ptr + record(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->record(op); + } + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + std::shared_ptr eval(); + + std::shared_ptr eval(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + // TODO: Aim to have only a single function with tensors/algorithm + template + std::shared_ptr + eval(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + // TODO: Aim to be able to handle errors when returning without throw except + return this->eval(op); + } + // Needded as otherise can't use initialiser list + template + std::shared_ptr + eval(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->eval(op); + } + + /** + * Eval Async sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. EvalAwait() must + * be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAsync(); + std::shared_ptr evalAsync(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + evalAsync(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->evalAsync(op); + } + // Needed as otherwise it's not possible to use initializer lists + template + std::shared_ptr + evalAsync(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->evalAsync(op); + } + + /** + * Eval Await waits for the fence to finish processing and then once it + * finishes, it runs the postEval of all operations. + * + * @param waitFor Number of milliseconds to wait before timing out. + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); + + /** + * Clear function clears all operations currently recorded and starts recording again. + */ + void clear(); + + /** + * Begins recording commands for commands to be submitted into the command + * buffer. + * + * @return Boolean stating whether execution was successful. + */ + void begin(); + + /** + * Ends the recording and stops recording commands when the record command + * is sent. + * + * @return Boolean stating whether execution was successful. + */ + void end(); + + /** + * Returns true if the sequence is currently in recording activated. + * + * @return Boolean stating if recording ongoing. + */ + bool isRecording(); + + bool isInit(); + + /** + * Returns true if the sequence is currently running - mostly used for async + * workloads. + * + * @return Boolean stating if currently running. + */ + bool isRunning(); + + /** + * Destroys and frees the GPU resources which include the buffer and memory + * and sets the sequence as init=False. + */ + void destroy(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + std::shared_ptr mComputeQueue = nullptr; + uint32_t mQueueIndex = -1; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mCommandPool = nullptr; + bool mFreeCommandPool = false; + std::shared_ptr mCommandBuffer = nullptr; + bool mFreeCommandBuffer = false; + + // -------------- ALWAYS OWNED RESOURCES + vk::Fence mFence; + std::vector> mOperations; + + // State + bool mRecording = false; + bool mIsRunning = false; + + // Create functions + void createCommandPool(); + void createCommandBuffer(); +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncDevice : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncDevice(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncDevice() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Does not perform any postEval commands. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +#define KP_DEFAULT_SESSION "DEFAULT" + +namespace kp { + +/** + Base orchestrator which creates and manages device and child components +*/ +class Manager +{ + public: + /** + Base constructor and default used which creates the base resources + including choosing the device 0 by default. + */ + Manager(); + + /** + * Similar to base constructor but allows the user to provide the device + * they would like to create the resources on. + * + * @param physicalDeviceIndex The index of the physical device to use + * @param manageResources (Optional) Whether to manage the memory of the + * resources created and destroy when the manager is destroyed. + * @param familyQueueIndices (Optional) List of queue indices to add for + * explicit allocation + * @param totalQueues The total number of compute queues to create. + */ + Manager(uint32_t physicalDeviceIndex, + const std::vector& familyQueueIndices = {}); + + /** + * Manager constructor which allows your own vulkan application to integrate + * with the vulkan kompute use. + * + * @param instance Vulkan compute instance to base this application + * @param physicalDevice Vulkan physical device to use for application + * @param device Vulkan logical device to use for all base resources + * @param physicalDeviceIndex Index for vulkan physical device used + */ + Manager(std::shared_ptr instance, + std::shared_ptr physicalDevice, + std::shared_ptr device); + + /** + * Manager destructor which would ensure all owned resources are destroyed + * unless explicitly stated that resources should not be destroyed or freed. + */ + ~Manager(); + + /** + * Get or create a managed Sequence that will be contained by this manager. + * If the named sequence does not currently exist, it would be created and + * initialised. + * + * @param sequenceName The name for the named sequence to be retrieved or + * created + * @param queueIndex The queue to use from the available queues + * @return Shared pointer to the manager owned sequence resource + */ + std::shared_ptr sequence(uint32_t queueIndex = 0); + + /** + * Function that simplifies the common workflow of tensor creation and + * initialization. It will take the constructor parameters for a Tensor + * and will will us it to create a new Tensor and then create it. The + * tensor memory will then be managed and owned by the manager. + * + * @param data The data to initialize the tensor with + * @param tensorType The type of tensor to initialize + * @param syncDataToGPU Whether to sync the data to GPU memory + * @returns Initialized Tensor with memory Syncd to GPU device + */ + std::shared_ptr tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, + bool syncDataToGPU = true); + + std::shared_ptr algorithm( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + void destroy(); + void clear(); + + private: + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mInstance = nullptr; + bool mFreeInstance = false; + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + bool mFreeDevice = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector> mManagedTensors; + std::vector> mManagedSequences; + std::vector> mManagedAlgorithms; + + std::vector mComputeQueueFamilyIndices; + std::vector> mComputeQueues; + + bool mManageResources = false; + +#if DEBUG +#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS + vk::DebugReportCallbackEXT mDebugReportCallback; + vk::DispatchLoaderDynamic mDebugDispatcher; +#endif +#endif + + // Create functions + void createInstance(); + void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); +}; + +} // End namespace kp + +#include + +namespace kp { + +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * By default it enables the user to provide a dynamic number of tensors + * which are then passed as inputs. + */ +class OpAlgoDispatch : public OpBase +{ + public: + + OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpAlgoDispatch() override; + + /** + * This records the commands that are to be sent to the GPU. This includes + * the barriers that ensure the memory has been copied before going in and + * out of the shader, as well as the dispatch operation that sends the + * shader processing to the gpu. This function also records the GPU memory + * copy of the output data for the staging buffer so it can be read by the + * host. + */ + virtual void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Executes after the recorded commands are submitted, and performs a copy + * of the GPU Device memory into the staging buffer so the output data can + * be retrieved. + */ + virtual void postEval() override; + +private: + // -------------- ALWAYS OWNED RESOURCES + std::shared_ptr mAlgorithm; +}; + +} // End namespace kp + +namespace kp { + +/** + * Operation that performs multiplication on two tensors and outpus on third + * tensor. + */ +class OpMult : public OpAlgoDispatch +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + * @param komputeWorkgroup Optional parameter to specify the layout for processing + */ + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(algorithm, true) + { + KP_LOG_DEBUG("Kompute OpMult constructor with params"); + + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } + + std::vector spirv( + (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, + (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + + algorithm->rebuild(tensors, spirv); + } + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpMult() override { + KP_LOG_DEBUG("Kompute OpMult destructor started"); + } +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type +*/ +class OpTensorCopy : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorCopy(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorCopy() override; + + /** + * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Copies the local vectors for all the tensors to sync the data with the gpu. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncLocal : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncLocal(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncLocal() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * For host tensors it performs the map command from the host memory into local memory. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp From 2b09c551e715bc8a983380631f151f7a04ef9268 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 08:48:10 +0000 Subject: [PATCH 12/91] Updated testopshaders --- test/TestOpShadersFromStringAndFile.cpp | 117 +++++++++++++----------- 1 file changed, 63 insertions(+), 54 deletions(-) diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index 60cb8af24..1a1c5c599 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -5,60 +5,69 @@ #include "kompute_test/shaders/shadertest_op_custom_shader.hpp" -//TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) -//{ -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; -// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; -// mgr.rebuild({ tensorA, tensorB }); -// -// std::string shader(R"( -// #version 450 -// -// layout (local_size_x = 1) in; -// -// layout(set = 0, binding = 0) buffer a { float pa[]; }; -// layout(set = 0, binding = 1) buffer b { float pb[]; }; -// -// void main() { -// uint index = gl_GlobalInvocationID.x; -// pb[index] = pa[index]; -// pa[index] = index; -// } -// )"); -// -// mgr.evalOpDefault( -// { tensorA, tensorB }, kp::Shader::compile_source(shader)); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); -// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); -//} -// -//TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) -//{ -// kp::Manager mgr; -// -// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; -// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; -// mgr.rebuild({ tensorA, tensorB }); -// -// mgr.evalOpDefault( -// { tensorA, tensorB }, -// std::vector( -// (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, -// (uint32_t*)(kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv + -// kp::shader_data:: -// test_shaders_glsl_test_op_custom_shader_comp_spv_len))); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); -// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); -//} -// +TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) +{ + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 3, 4, 5 }); + std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + + layout (local_size_x = 1) in; + + layout(set = 0, binding = 0) buffer a { float pa[]; }; + layout(set = 0, binding = 1) buffer b { float pb[]; }; + + void main() { + uint index = gl_GlobalInvocationID.x; + pb[index] = pa[index]; + pa[index] = index; + } + )"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::vector> params = { tensorA, tensorB }; + + mgr.sequence() + ->eval(params) + ->eval(mgr.algorithm(params, spirv)) + ->eval(params); + + + EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); + EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +} + +TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) +{ + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 3, 4, 5 }); + std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + + std::vector spirv = + std::vector( + (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, + (uint32_t*)(kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv + + kp::shader_data:: + test_shaders_glsl_test_op_custom_shader_comp_spv_len)); + + std::vector> params = { tensorA, tensorB }; + + mgr.sequence() + ->eval(params) + ->eval(mgr.algorithm(params, spirv)) + ->eval(params); + + + EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); + EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +} + +// TODO: Add support to read from file for shader //TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) //{ // kp::Manager mgr; From f206c625374465bb3b6fb5b494ff49a7454647b0 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 14:11:31 +0000 Subject: [PATCH 13/91] Added TensorOpCopy tests --- src/Tensor.cpp | 14 +- test/TestOpTensorCopy.cpp | 311 ++++++++++++++++++-------------------- 2 files changed, 157 insertions(+), 168 deletions(-) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index f5341830f..7176fe484 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -80,9 +80,7 @@ bool Tensor::isInit() { return this->mDevice && this->mPrimaryBuffer && - this->mPrimaryMemory && - this->mStagingBuffer && - this->mStagingMemory; + this->mPrimaryMemory; } void @@ -443,14 +441,14 @@ Tensor::destroy() KP_LOG_DEBUG("Kompute Tensor started destroy()"); if (!this->mDevice) { - KP_LOG_ERROR( + KP_LOG_WARN( "Kompute Tensor destructor reached with null Device pointer"); return; } if (this->mFreePrimaryBuffer) { if (!this->mPrimaryBuffer) { - KP_LOG_ERROR("Kompose Tensor expected to destroy primary buffer " + KP_LOG_WARN("Kompose Tensor expected to destroy primary buffer " "but got null buffer"); } else { KP_LOG_DEBUG("Kompose Tensor destroying primary buffer"); @@ -464,7 +462,7 @@ Tensor::destroy() if (this->mFreeStagingBuffer) { if (!this->mStagingBuffer) { - KP_LOG_ERROR("Kompose Tensor expected to destroy staging buffer " + KP_LOG_WARN("Kompose Tensor expected to destroy staging buffer " "but got null buffer"); } else { KP_LOG_DEBUG("Kompose Tensor destroying staging buffer"); @@ -478,7 +476,7 @@ Tensor::destroy() if (this->mFreePrimaryMemory) { if (!this->mPrimaryMemory) { - KP_LOG_ERROR("Kompose Tensor expected to free primary memory but " + KP_LOG_WARN("Kompose Tensor expected to free primary memory but " "got null memory"); } else { KP_LOG_DEBUG("Kompose Tensor freeing primary memory"); @@ -492,7 +490,7 @@ Tensor::destroy() if (this->mFreeStagingMemory) { if (!this->mStagingMemory) { - KP_LOG_ERROR("Kompose Tensor expected to free staging memory but " + KP_LOG_WARN("Kompose Tensor expected to free staging memory but " "got null memory"); } else { KP_LOG_DEBUG("Kompose Tensor freeing staging memory"); diff --git a/test/TestOpTensorCopy.cpp b/test/TestOpTensorCopy.cpp index 449e30da1..dc82a2e50 100644 --- a/test/TestOpTensorCopy.cpp +++ b/test/TestOpTensorCopy.cpp @@ -3,163 +3,154 @@ #include "kompute/Kompute.hpp" -//TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 1, 2, 3 }; -// std::vector testVecB{ 0, 0, 0 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// mgr.rebuild({ tensorA, tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// -// // Making sure the GPU holds the same data -// mgr.evalOpDefault({ tensorB }); -// EXPECT_EQ(tensorA->data(), tensorB->data()); -//} -// -//TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 2, 3, 4 }; -// std::vector testVecB{ 0, 0, 0 }; -// std::vector testVecC{ 0, 0, 0 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// std::shared_ptr tensorC{ new kp::Tensor(testVecC) }; -// -// mgr.rebuild({ tensorA, tensorB, tensorC }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// EXPECT_TRUE(tensorC->isInit()); -// -// mgr.evalOpDefault({ tensorA, tensorB, tensorC }); -// -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// EXPECT_EQ(tensorA->data(), tensorC->data()); -// -// // Making sure the GPU holds the same data -// mgr.evalOpDefault({ tensorB, tensorC }); -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// EXPECT_EQ(tensorA->data(), tensorC->data()); -//} -// -//TEST(TestOpTensorCopy, CopyDeviceToHostTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 3, 4, 5 }; -// std::vector testVecB{ 0, 0, 0 }; -// -// std::shared_ptr tensorA{ new kp::Tensor(testVecA) }; -// std::shared_ptr tensorB{ new kp::Tensor( -// testVecB, kp::Tensor::TensorTypes::eHost) }; -// -// mgr.rebuild({ tensorA, tensorB }, false); -// -// // Only calling sync on device type tensor -// mgr.evalOpDefault({ tensorA }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// -// // Making sure the GPU holds the same data -// mgr.evalOpDefault({ tensorB }); -// EXPECT_EQ(tensorA->data(), tensorB->data()); -//} -// -//TEST(TestOpTensorCopy, CopyHostToDeviceTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 4, 5, 6 }; -// std::vector testVecB{ 0, 0, 0 }; -// -// std::shared_ptr tensorA{ new kp::Tensor( -// testVecA, kp::Tensor::TensorTypes::eHost) }; -// std::shared_ptr tensorB{ new kp::Tensor(testVecB) }; -// -// mgr.rebuild({ tensorA, tensorB }, false); -// -// // Manually copy data into host memory of Tensor -// tensorA->mapDataIntoHostMemory(); -// -// // Only calling sync on device type tensor -// mgr.evalOpDefault({ tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// -// // Making sure the GPU holds the same data -// mgr.evalOpDefault({ tensorB }); -// EXPECT_EQ(tensorA->data(), tensorB->data()); -//} -// -//TEST(TestOpTensorCopy, CopyHostToHostTensor) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 5, 6, 7 }; -// std::vector testVecB{ 0, 0, 0 }; -// -// std::shared_ptr tensorA{ new kp::Tensor( -// testVecA, kp::Tensor::TensorTypes::eHost) }; -// std::shared_ptr tensorB{ new kp::Tensor( -// testVecB, kp::Tensor::TensorTypes::eHost) }; -// -// mgr.rebuild({ tensorA, tensorB }); -// -// EXPECT_TRUE(tensorA->isInit()); -// EXPECT_TRUE(tensorB->isInit()); -// -// mgr.evalOpDefault({ tensorA, tensorB }); -// -// EXPECT_EQ(tensorA->data(), tensorB->data()); -// -// // Making sure the GPU holds the same data -// mgr.evalOpDefault({ tensorB }); -// EXPECT_EQ(tensorA->data(), tensorB->data()); -//} -// -//TEST(TestOpTensorCopy, SingleTensorShouldFail) -//{ -// -// kp::Manager mgr; -// -// std::vector testVecA{ 6, 7, 8 }; -// -// std::shared_ptr tensorA{ new kp::Tensor( -// testVecA, kp::Tensor::TensorTypes::eHost) }; -// -// mgr.rebuild({ tensorA }, false); -// -// EXPECT_TRUE(tensorA->isInit()); -// -// EXPECT_THROW(mgr.evalOpDefault({ tensorA }), -// std::runtime_error); -//} +TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) +{ + + kp::Manager mgr; + + std::vector testVecA{ 1, 2, 3 }; + std::vector testVecB{ 0, 0, 0 }; + + std::shared_ptr tensorA = mgr.tensor(testVecA); + std::shared_ptr tensorB = mgr.tensor(testVecB); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + + mgr.sequence() + ->eval({ tensorA, tensorB }) + ->eval({ tensorA, tensorB }) + ->eval({ tensorA, tensorB }); + + // Making sure the GPU holds the same data + EXPECT_EQ(tensorA->data(), tensorB->data()); +} + +TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) +{ + + kp::Manager mgr; + + std::vector testVecA{ 2, 3, 4 }; + std::vector testVecB{ 0, 0, 0 }; + std::vector testVecC{ 0, 0, 0 }; + + std::shared_ptr tensorA = mgr.tensor(testVecA); + std::shared_ptr tensorB = mgr.tensor(testVecB); + std::shared_ptr tensorC = mgr.tensor(testVecC); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + EXPECT_TRUE(tensorC->isInit()); + + mgr.sequence() + ->eval({tensorA, tensorB, tensorC}) + ->eval({tensorA, tensorB, tensorC }); + + EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->data(), tensorC->data()); + + // Making sure the GPU holds the same data + mgr.sequence() + ->eval({ tensorB, tensorC }); + + EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->data(), tensorC->data()); +} + +TEST(TestOpTensorCopy, CopyDeviceToHostTensor) +{ + + kp::Manager mgr; + + std::vector testVecA{ 3, 4, 5 }; + std::vector testVecB{ 0, 0, 0 }; + + std::shared_ptr tensorA = mgr.tensor(testVecA); + std::shared_ptr tensorB = mgr.tensor( + testVecB, kp::Tensor::TensorTypes::eHost); + + // Only calling sync on device type tensor + mgr.sequence()->eval({ tensorA }); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + + mgr.sequence()->eval({ tensorA, tensorB }); + + EXPECT_EQ(tensorA->data(), tensorB->data()); + + // Making sure the GPU holds the same data + mgr.sequence()->eval({ tensorB }); + EXPECT_EQ(tensorA->data(), tensorB->data()); +} + +TEST(TestOpTensorCopy, CopyHostToDeviceTensor) +{ + + kp::Manager mgr; + + std::vector testVecA{ 4, 5, 6 }; + std::vector testVecB{ 0, 0, 0 }; + + std::shared_ptr tensorA = mgr.tensor( + testVecA, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorB = mgr.tensor(testVecB); + + // Only calling sync on device type tensor + mgr.sequence()->eval({ tensorA, tensorB }); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + + mgr.sequence()->eval({ tensorA, tensorB }); + + EXPECT_EQ(tensorA->data(), tensorB->data()); + + // Making sure the GPU holds the same data + mgr.sequence()->eval({ tensorB }); + EXPECT_EQ(tensorA->data(), tensorB->data()); +} + +TEST(TestOpTensorCopy, CopyHostToHostTensor) +{ + + kp::Manager mgr; + + std::vector testVecA{ 5, 6, 7 }; + std::vector testVecB{ 0, 0, 0 }; + + std::shared_ptr tensorA = mgr.tensor( + testVecA, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorB = mgr.tensor( + testVecB, kp::Tensor::TensorTypes::eHost); + + EXPECT_TRUE(tensorA->isInit()); + EXPECT_TRUE(tensorB->isInit()); + + mgr.sequence() + ->eval({ tensorA }) + ->eval({ tensorA, tensorB }); + + EXPECT_EQ(tensorA->data(), tensorB->data()); + + // Making sure the GPU holds the same data + mgr.sequence()->eval({ tensorB }); + EXPECT_EQ(tensorA->data(), tensorB->data()); +} + +TEST(TestOpTensorCopy, SingleTensorShouldFail) +{ + + kp::Manager mgr; + + std::vector testVecA{ 6, 7, 8 }; + + std::shared_ptr tensorA = mgr.tensor( + testVecA, kp::Tensor::TensorTypes::eHost); + + EXPECT_TRUE(tensorA->isInit()); + + EXPECT_THROW(mgr.sequence()->eval({ tensorA }), + std::runtime_error); +} From 9788c796a8cf56960f0ca1009f38d7ce3766f10f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 14:15:07 +0000 Subject: [PATCH 14/91] Updated sequence and sequence tests --- src/Sequence.cpp | 6 +++--- test/TestSequence.cpp | 50 ++++++++++++------------------------------- 2 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 9424e119d..5a8115c97 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -174,7 +174,7 @@ Sequence::destroy() KP_LOG_DEBUG("Kompute Sequence destroy called"); if (!this->mDevice) { - KP_LOG_ERROR("Kompute Sequence destroy called " + KP_LOG_WARN("Kompute Sequence destroy called " "with null Device pointer"); return; } @@ -182,7 +182,7 @@ Sequence::destroy() if (this->mFreeCommandBuffer) { KP_LOG_INFO("Freeing CommandBuffer"); if (!this->mCommandBuffer) { - KP_LOG_ERROR( + KP_LOG_WARN( "Kompute Sequence destroy called with null " "CommandPool pointer"); return; @@ -199,7 +199,7 @@ Sequence::destroy() if (this->mFreeCommandPool) { KP_LOG_INFO("Destroying CommandPool"); if (this->mCommandPool == nullptr) { - KP_LOG_ERROR( + KP_LOG_WARN( "Kompute Sequence destroy called with null " "CommandPool pointer"); return; diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 8c1ae3751..7a8dd07d1 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -3,40 +3,18 @@ #include "kompute/Kompute.hpp" -//TEST(TestSequence, CmdBufSequenceBeginEnd) -//{ -// kp::Manager mgr; -// -// { -// std::shared_ptr sq = -// mgr.sequence("newSequence"); -// -// EXPECT_TRUE(sq->eval()); -// EXPECT_TRUE(!sq->isRecording()); -// EXPECT_TRUE(sq->begin()); -// EXPECT_TRUE(sq->isRecording()); -// EXPECT_TRUE(!sq->begin()); -// EXPECT_TRUE(sq->isRecording()); -// EXPECT_TRUE(sq->end()); -// EXPECT_TRUE(!sq->isRecording()); -// EXPECT_TRUE(!sq->end()); -// EXPECT_TRUE(!sq->isRecording()); -// EXPECT_TRUE(sq->eval()); -// } -//} -// -//TEST(TestSequence, SequenceDestructorViaManager) -//{ -// std::shared_ptr sq = nullptr; -// -// { -// kp::Manager mgr; -// -// sq = mgr.sequence("newSequence"); -// -// EXPECT_TRUE(sq->isInit()); -// } -// -// EXPECT_FALSE(sq->isInit()); -//} +TEST(TestSequence, SequenceDestructorViaManager) +{ + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr; + + sq = mgr.sequence(); + + EXPECT_TRUE(sq->isInit()); + } + + EXPECT_FALSE(sq->isInit()); +} From 9d206c304d38ed5774f90d593359c93f0fbff180 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 15:14:37 +0000 Subject: [PATCH 15/91] Update cmakelists to align with required setup --- src/CMakeLists.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc36d722c..950f95896 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,8 +151,7 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) # HLSL # glslang includes OGLCompiler, OSDependent, MachineIndependent glslang - SPIRV - glslang-default-resource-limits) + SPIRV) else() find_package(glslang CONFIG REQUIRED) @@ -164,9 +163,8 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) # Not including hlsl support # glslang::HLSL # Adding explicit dependencies to match above - glslang - SPIRV - glslang-default-resource-limits) + glslang::glslang + glslang::SPIRV) endif() endif() From 198fb46eb628051dd3a800b586bfca63ceb71aa2 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 15:28:24 +0000 Subject: [PATCH 16/91] Fixed integration tests fails due to pipeline not freed --- src/Algorithm.cpp | 1 + src/Manager.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index f00bc1090..6215dd090 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -332,6 +332,7 @@ Algorithm::createPipeline() vk::Pipeline pipeline = this->mDevice->createComputePipeline(*this->mPipelineCache, pipelineInfo); this->mPipeline = std::make_shared(pipeline); + this->mFreePipeline = true; #endif // TODO: Update to consistent diff --git a/src/Manager.cpp b/src/Manager.cpp index 9f25e1826..ea152f1a8 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -117,7 +117,6 @@ Manager::destroy() { if (this->mDebugReportCallback) { this->mInstance->destroyDebugReportCallbackEXT( this->mDebugReportCallback, nullptr, this->mDebugDispatcher); - this->mInstance = nullptr; KP_LOG_DEBUG("Kompute Manager Destroyed Debug Report Callback"); } #endif From 4c4d073b908c2cae2feeae9096efc36ea5069dec Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 27 Feb 2021 19:37:31 +0000 Subject: [PATCH 17/91] Python implementation --- python/src/main.cpp | 284 +- python/test/test_kompute.py | 168 +- shaders/glsl/logisticregression.comp.spv | Bin 4816 -> 4816 bytes shaders/glsl/opmult.comp.spv | Bin 1464 -> 1464 bytes single_include/AggregateHeaders.cpp | 12 +- single_include/kompute/Kompute.hpp | 3724 ++++++++--------- src/Algorithm.cpp | 23 +- src/Manager.cpp | 3 +- src/include/kompute/Manager.hpp | 5 +- .../kompute/operations/OpAlgoDispatch.hpp | 2 +- .../shaders/shaderlogisticregression.hpp | 810 ++-- src/include/kompute/shaders/shaderopmult.hpp | 250 +- .../shadertest_logistic_regression.hpp | 810 ++-- .../shaders/shadertest_op_custom_shader.hpp | 190 +- .../shaders/shadertest_workgroup.hpp | 240 +- .../glsl/test_logistic_regression.comp.spv | Bin 4816 -> 4816 bytes .../glsl/test_op_custom_shader.comp.spv | Bin 1096 -> 1096 bytes test/shaders/glsl/test_workgroup.comp.spv | Bin 1396 -> 1396 bytes 18 files changed, 3172 insertions(+), 3349 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 6f99a193b..cb538112b 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -23,8 +23,7 @@ PYBIND11_MODULE(kp, m) { py::module_ np = py::module_::import("numpy"); - - py::enum_(m, "TensorTypes", DOC(kp, Tensor, TensorTypes)) + py::enum_(m, "TensorTypes") .value("device", kp::Tensor::TensorTypes::eDevice, "Tensor holding data in GPU memory.") .value("host", kp::Tensor::TensorTypes::eHost, "Tensor used for CPU visible GPU data.") .value("storage", kp::Tensor::TensorTypes::eStorage, "Tensor with host visible gpu memory.") @@ -53,20 +52,28 @@ PYBIND11_MODULE(kp, m) { py::arg("sources"), py::arg("files") = std::vector(), py::arg("entryPoint") = "main", py::arg("definitions") = std::vector>() ); #endif // KOMPUTE_DISABLE_SHADER_UTILS + py::class_>(m, "OpBase"); + + py::class_>(m, "OpTensorSyncDevice") + .def(py::init>&>()); + + py::class_>(m, "OpTensorSyncLocal") + .def(py::init>&>()); + + py::class_>(m, "OpTensorCopy") + .def(py::init>&>()); + + py::class_>(m, "OpAlgoDispatch") + .def(py::init&, bool>()); + + py::class_>(m, "Algorithm") + .def("get_tensors", &kp::Algorithm::getTensors) + .def("destroy", &kp::Algorithm::destroy) + .def("get_spec_consts", &kp::Algorithm::getSpecializationConstants) + .def("get_push_consts", &kp::Algorithm::getPushConstants) + .def("is_init", &kp::Algorithm::isInit); + py::class_>(m, "Tensor", DOC(kp, Tensor)) - .def(py::init( - [np](const py::array_t data, kp::Tensor::TensorTypes tensor_type) { - const py::array_t flatdata = np.attr("ravel")(data); - const py::buffer_info info = flatdata.request(); - const float* ptr = (float*) info.ptr; - return std::unique_ptr( - new kp::Tensor(std::vector(ptr, ptr+flatdata.size()), tensor_type) - ); - }), - "Construct Tensor with an array as initial data and an optional kp.TensorType (default:device).", - py::arg("data"), - py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice - ) .def("data", &kp::Tensor::data, DOC(kp, Tensor, data)) .def("numpy", [](kp::Tensor& self) { return py::array(self.data().size(), self.data().data()); @@ -108,218 +115,47 @@ PYBIND11_MODULE(kp, m) { .def("map_data_from_host", &kp::Tensor::mapDataFromHostMemory, "Maps data into GPU memory from tensor local data.") .def("map_data_into_host", &kp::Tensor::mapDataIntoHostMemory, "Maps data from GPU memory into tensor local data."); - py::class_>(m, "Sequence") - .def("init", &kp::Sequence::init, DOC(kp, Sequence, init)) - - // record - .def("begin", &kp::Sequence::begin, DOC(kp, Sequence, begin)) - .def("end", &kp::Sequence::end, DOC(kp, Sequence, end)) - - // eval - .def("eval", &kp::Sequence::eval, DOC(kp, Sequence, eval)) - .def("eval_async", &kp::Sequence::evalAsync, DOC(kp, Sequence, evalAsync)) - .def("eval_await", &kp::Sequence::evalAwait, DOC(kp, Sequence, evalAwait)) - - // status - .def("is_running", &kp::Sequence::isRunning, DOC(kp, Sequence, isRunning)) - .def("is_rec", &kp::Sequence::isRecording, DOC(kp, Sequence, isRecording)) - .def("is_init", &kp::Sequence::isInit, DOC(kp, Sequence, isInit)) - - // record - .def("record_tensor_copy", &kp::Sequence::record, DOC(kp, Sequence, record)) - .def("record_tensor_sync_device", &kp::Sequence::record, - "Records operation to sync tensor from local memory to GPU memory") - .def("record_tensor_sync_local", &kp::Sequence::record, - "Records operation to sync tensor(s) from GPU memory to local memory") - .def("record_algo_file", &kp::Sequence::record< - kp::OpAlgoCreate, - const std::string&, - kp::Workgroup, - kp::Constants>, - "Records an operation using a custom shader provided from a shader path", - py::arg("tensors"), py::arg("data"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - .def("record_algo_data", [](kp::Sequence &self, - std::vector> tensors, - py::bytes &bytes, - kp::Workgroup workgroup, - kp::Constants constants) -> bool { - // Bytes have to be converted into std::vector - py::buffer_info info(py::buffer(bytes).request()); - const char *data = reinterpret_cast(info.ptr); - size_t length = static_cast(info.size); - return self.record( - tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); - }, - "Records an operation using a custom shader provided as spirv bytes", - py::arg("tensors"), py::arg("bytes"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ); + .def("record", [](kp::Sequence& self, std::shared_ptr op) { return self.record(op); }) + .def("eval", [](kp::Sequence& self) { return self.eval(); }) + .def("eval", [](kp::Sequence& self, std::shared_ptr op) { return self.eval(op); }) + .def("eval_async", [](kp::Sequence& self) { return self.eval(); }) + .def("eval_async", [](kp::Sequence& self, std::shared_ptr op) { return self.evalAsync(op); }) + .def("eval_await", [](kp::Sequence& self) { return self.evalAwait(); }) + .def("eval_await", [](kp::Sequence& self, uint32_t wait) { return self.evalAwait(wait); }) + .def("is_recording", &kp::Sequence::isRecording) + .def("is_running", &kp::Sequence::isRunning) + .def("is_init", &kp::Sequence::isInit) + .def("clear", &kp::Sequence::clear) + .def("destroy", &kp::Sequence::destroy); - - py::class_(m, "Manager") - .def(py::init(), "Default initializer uses device 0 and first compute compatible GPU queueFamily") - .def(py::init( - [](uint32_t physicalDeviceIndex) { - return std::unique_ptr(new kp::Manager(physicalDeviceIndex)); - }), "Manager initialiser can provide specified device index but will use first compute compatible GPU queueFamily") - .def(py::init( - [](uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices) { - return std::unique_ptr(new kp::Manager(physicalDeviceIndex, familyQueueIndices)); - }), "Manager initialiser can provide specified device and array of GPU queueFamilies to load.") - .def("sequence", &kp::Manager::sequence, - py::arg("name") = "", py::arg("queueIndex") = 0, "Get or create a sequence with specific name and specified index of available queues") - .def("tensor", &kp::Manager::tensor, - py::arg("data"), py::arg("tensorType") = kp::Tensor::TensorTypes::eDevice, py::arg("syncDataToGPU") = true, - "Build and initialise tensor") - .def("rebuild", py::overload_cast>, bool>(&kp::Manager::rebuild), - py::arg("tensors"), py::arg("syncDataToGPU") = true, - "Build and initialise list of tensors") - .def("rebuild", py::overload_cast, bool>(&kp::Manager::rebuild), - py::arg("tensor"), py::arg("syncDataToGPU") = true, - "Build and initialise tensor") - .def("destroy", py::overload_cast>(&kp::Manager::destroy), - py::arg("tensor"), DOC(kp, Manager, destroy)) - .def("destroy", py::overload_cast>>(&kp::Manager::destroy), - py::arg("tensors"), DOC(kp, Manager, destroy, 2)) - .def("destroy", py::overload_cast>>(&kp::Manager::destroy), - py::arg("sequences"), DOC(kp, Manager, destroy, 3)) - .def("destroy", py::overload_cast>(&kp::Manager::destroy), - py::arg("sequence"), DOC(kp, Manager, destroy, 4)) - .def("destroy", py::overload_cast(&kp::Manager::destroy), - py::arg("sequenceName"), DOC(kp, Manager, destroy, 5)) - .def("destroy", py::overload_cast&>(&kp::Manager::destroy), - py::arg("sequenceNames"), DOC(kp, Manager, destroy, 6)) - // temporary backwards compatibility - .def("eval_tensor_create_def",[](kp::Manager& self, std::vector> tensors, bool syncDataToGPU) -> void { - kp_error("IMPORTANT: eval_tensor_create_def is depricated! Please use Manager.rebuild instead as function will be removed soon."); - self.rebuild(tensors, syncDataToGPU); - }, - py::arg("tensors"), py::arg("syncDataToGPU") = true, - "Temporary backwards compatibility for tensor creation function which will be removed in the next version.") - - // Await functions - .def("eval_await", &kp::Manager::evalOpAwait, - py::arg("sequenceName"), py::arg("waitFor") = UINT64_MAX, - "Awaits for asynchronous operation on a named Sequence") - .def("eval_await_def", &kp::Manager::evalOpAwaitDefault, - py::arg("waitFor") = UINT64_MAX, "Awaits for asynchronous operation on the last anonymous Sequence created") - - // eval default - .def("eval_tensor_copy_def", &kp::Manager::evalOpDefault, - "Evaluates operation to copy one tensor to one or many tensors with new anonymous Sequence") - .def("eval_tensor_sync_device_def", &kp::Manager::evalOpDefault, - "Evaluates operation to sync tensor from local memory to GPU memory with new anonymous Sequence") - .def("eval_tensor_sync_local_def", &kp::Manager::evalOpDefault, - "Evaluates operation to sync tensor(s) from GPU memory to local memory with new anonymous Sequence") - .def("eval_algo_file_def", &kp::Manager::evalOpDefault< - kp::OpAlgoCreate, - const std::string&, - kp::Workgroup, - kp::Constants>, - "Evaluates an operation using a custom shader provided from a shader path with new anonymous Sequence", - py::arg("tensors"), py::arg("data"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - .def("eval_algo_data_def", [](kp::Manager &self, - std::vector> tensors, - py::bytes &bytes, - kp::Workgroup workgroup, - kp::Constants constants) { - // Bytes have to be converted into std::vector - py::buffer_info info(py::buffer(bytes).request()); - const char *data = reinterpret_cast(info.ptr); - size_t length = static_cast(info.size); - self.evalOpDefault( - tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); + py::class_>(m, "Manager") + .def(py::init()) + .def(py::init()) + .def(py::init&>()) + .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0) + .def("tensor", [np](kp::Manager& self, + const py::array_t data, + kp::Tensor::TensorTypes tensor_type) { + const py::array_t flatdata = np.attr("ravel")(data); + const py::buffer_info info = flatdata.request(); + const float* ptr = (float*) info.ptr; + return self.tensor(std::vector(ptr, ptr+flatdata.size()), tensor_type); }, - "Evaluates an operation using a custom shader provided as spirv bytes with new anonymous Sequence", - py::arg("tensors"), py::arg("bytes"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - - // eval - .def("eval_tensor_copy", &kp::Manager::evalOp, - "Evaluates operation to copy one tensor to one or many tensors with explicitly named Sequence") - .def("eval_tensor_sync_device", &kp::Manager::evalOp, - "Evaluates operation to sync tensor from local memory to GPU memory with explicitly named Sequence") - .def("eval_tensor_sync_local", &kp::Manager::evalOp, - "Evaluates operation to sync tensor(s) from GPU memory to local memory with explicitly named Sequence") - .def("eval_algo_file", &kp::Manager::evalOp< - kp::OpAlgoCreate, - const std::string&, - kp::Workgroup, - kp::Constants>, - "Evaluates an operation using a custom shader provided from a shader path with explicitly named Sequence", - py::arg("tensors"), py::arg("sequence_name"), py::arg("data"),py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - .def("eval_algo_data", [](kp::Manager &self, - std::vector> tensors, - std::string sequenceName, - py::bytes &bytes, - kp::Workgroup workgroup, - kp::Constants constants) { - // Bytes have to be converted into std::vector - py::buffer_info info(py::buffer(bytes).request()); - const char *data = reinterpret_cast(info.ptr); - size_t length = static_cast(info.size); - self.evalOp( - tensors, sequenceName, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); - }, - "Evaluates an operation using a custom shader provided as spirv bytes with explicitly named Sequence", - py::arg("tensors"), py::arg("sequence_name"), py::arg("bytes"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - - // eval async default - .def("eval_async_tensor_copy_def", &kp::Manager::evalOpAsyncDefault, - "Evaluates asynchronously operation to copy one tensor to one or many tensors with anonymous Sequence") - .def("eval_async_tensor_sync_device_def", &kp::Manager::evalOpAsyncDefault, - "Evaluates asynchronously operation to sync tensor from local memory to GPU memory with anonymous Sequence") - .def("eval_async_tensor_sync_local_def", &kp::Manager::evalOpAsyncDefault, - "Evaluates asynchronously operation to sync tensor(s) from GPU memory to local memory with anonymous Sequence") - .def("eval_async_algo_file_def", &kp::Manager::evalOpAsyncDefault< - kp::OpAlgoCreate, - const std::string&, - kp::Workgroup, - kp::Constants>, - "Evaluates asynchronously an operation using a custom shader provided from a shader path with anonymous Sequence", - py::arg("tensors"), py::arg("data"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - .def("eval_async_algo_data_def", [](kp::Manager &self, - std::vector> tensors, - py::bytes &bytes, - kp::Workgroup workgroup, - kp::Constants constants) { - // Bytes have to be converted into std::vector - py::buffer_info info(py::buffer(bytes).request()); - const char *data = reinterpret_cast(info.ptr); - size_t length = static_cast(info.size); - self.evalOpAsyncDefault( - tensors, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); - }, - "Evaluates asynchronously an operation using a custom shader provided as raw string or spirv bytes with anonymous Sequence", - py::arg("tensors"), py::arg("bytes"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - - // eval async - .def("eval_async_tensor_copy", &kp::Manager::evalOpAsync, - "Evaluates asynchronously operation to copy one tensor to one or many tensors with explicitly named Sequence") - .def("eval_async_tensor_sync_device", &kp::Manager::evalOpAsync, - "Evaluates asynchronously operation to sync tensor from local memory to GPU memory with explicitly named Sequence") - .def("eval_async_tensor_sync_local", &kp::Manager::evalOpAsync, - "Evaluates asynchronously operation to sync tensor(s) from GPU memory to local memory with explicitly named Sequence") - .def("eval_async_algo_file", &kp::Manager::evalOpAsync< - kp::OpAlgoCreate, - const std::string&, - kp::Workgroup, - kp::Constants>, - "Evaluates asynchronously an operation using a custom shader provided from a shader path with explicitly named Sequence", - py::arg("tensors"), py::arg("sequence_name"), py::arg("data"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ) - .def("eval_async_algo_data", [](kp::Manager &self, - std::vector> tensors, - std::string sequenceName, - py::bytes &bytes, - kp::Workgroup workgroup, - kp::Constants constants) { - // Bytes have to be converted into std::vector - py::buffer_info info(py::buffer(bytes).request()); - const char *data = reinterpret_cast(info.ptr); - size_t length = static_cast(info.size); - self.evalOpAsync( - tensors, sequenceName, std::vector((uint32_t*)data, (uint32_t*)(data + length)), workgroup, constants); - }, - "Evaluates asynchronously an operation using a custom shader provided as raw string or spirv bytes with explicitly named Sequence", - py::arg("tensors"), py::arg("sequence_name"), py::arg("bytes"), py::arg("workgroup") = kp::Workgroup(), py::arg("constants") = kp::Constants() ); + "Tensor initialisation function with data and tensor type", + py::arg("data"), py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice) + .def("algorithm", [](kp::Manager& self, + const std::vector>& tensors, + const py::bytes& spirv, + const kp::Workgroup& workgroup = {}, + const kp::Constants& spec_consts = {}, + const kp::Constants& push_consts = {}) { + py::buffer_info info(py::buffer(spirv).request()); + const char *data = reinterpret_cast(info.ptr); + size_t length = static_cast(info.size); + std::vector spirvVec((uint32_t*)data, (uint32_t*)(data + length)); + return self.algorithm(tensors, spirvVec, workgroup, spec_consts, push_consts); + }); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index 2ba4d4868..e923c6393 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -7,25 +7,26 @@ import pyshader as ps DIRNAME = os.path.dirname(os.path.abspath(__file__)) -def test_opalgobase_file(): - """ - Test basic OpMult operation - """ - - tensor_in_a = kp.Tensor([2, 2, 2]) - tensor_in_b = kp.Tensor([1, 2, 3]) - tensor_out = kp.Tensor([0, 0, 0]) - - mgr = kp.Manager() - mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) - - shader_path = os.path.join(DIRNAME, "../../shaders/glsl/opmult.comp.spv") - - mgr.eval_algo_file_def([tensor_in_a, tensor_in_b, tensor_out], shader_path) - - mgr.eval_tensor_sync_local_def([tensor_out]) - - assert tensor_out.data() == [2.0, 4.0, 6.0] +# TODO: Add example with file +#def test_opalgobase_file(): +# """ +# Test basic OpMult operation +# """ +# +# tensor_in_a = kp.Tensor([2, 2, 2]) +# tensor_in_b = kp.Tensor([1, 2, 3]) +# tensor_out = kp.Tensor([0, 0, 0]) +# +# mgr = kp.Manager() +# mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) +# +# shader_path = os.path.join(DIRNAME, "../../shaders/glsl/opmult.comp.spv") +# +# mgr.eval_algo_file_def([tensor_in_a, tensor_in_b, tensor_out], shader_path) +# +# mgr.eval_tensor_sync_local_def([tensor_out]) +# +# assert tensor_out.data() == [2.0, 4.0, 6.0] @@ -48,18 +49,23 @@ void main() } """ - tensor_in_a = kp.Tensor([2, 2, 2]) - tensor_in_b = kp.Tensor([1, 2, 3]) - tensor_out = kp.Tensor([0, 0, 0]) - - mgr = kp.Manager() - mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) - spirv = kp.Shader.compile_source(shader) - mgr.eval_algo_data_def([tensor_in_a, tensor_in_b, tensor_out], spirv) + mgr = kp.Manager() - mgr.eval_tensor_sync_local_def([tensor_out]) + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out = mgr.tensor([0, 0, 0]) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + algo = mgr.algorithm(params, spirv) + + (mgr.sequence() + .record(kp.OpTensorSyncLocal(params)) + .record(kp.OpAlgoDispatch(algo)) + .record(kp.OpTensorSyncDevice(params)) + .eval()) assert tensor_out.data() == [2.0, 4.0, 6.0] @@ -67,36 +73,53 @@ def test_sequence(): """ Test basic OpAlgoBase operation """ - mgr = kp.Manager(0, [2]) - tensor_in_a = kp.Tensor([2, 2, 2]) - tensor_in_b = kp.Tensor([1, 2, 3]) - tensor_out = kp.Tensor([0, 0, 0]) + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs {float valuesLhs[];}; + layout(set = 0, binding = 1) buffer tensorRhs {float valuesRhs[];}; + layout(set = 0, binding = 2) buffer tensorOutput { float valuesOutput[];}; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ - shader_path = os.path.abspath(os.path.join(DIRNAME, "../../shaders/glsl/opmult.comp.spv")) - mgr.eval_async_algo_file_def([tensor_in_a, tensor_in_b, tensor_out], shader_path) + spirv = kp.Shader.compile_source(shader) - mgr.eval_await_def() + mgr = kp.Manager(0) - seq = mgr.sequence("op") - seq.begin() - seq.record_tensor_sync_local([tensor_in_a]) - seq.record_tensor_sync_local([tensor_in_b]) - seq.record_tensor_sync_local([tensor_out]) - seq.end() - seq.eval() + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out = mgr.tensor([0, 0, 0]) - mgr.destroy("op") + params = [tensor_in_a, tensor_in_b, tensor_out] - assert seq.is_init() == False + algo = mgr.algorithm(params, spirv) + + sq = mgr.sequence() + + sq.record(kp.OpTensorSyncLocal(params)) + sq.record(kp.OpAlgoDispatch(algo)) + sq.record(kp.OpTensorSyncDevice(params)) + + sq.eval() + + assert sq.is_init() == True + + sq.destroy() + + assert sq.is_init() == False assert tensor_out.data() == [2.0, 4.0, 6.0] assert np.all(tensor_out.numpy() == [2.0, 4.0, 6.0]) - mgr.destroy(tensor_in_a) - mgr.destroy([tensor_in_b, tensor_out]) + tensor_in_a.destroy() + tensor_in_b.destroy() + tensor_out.destroy() assert tensor_in_a.is_init() == False assert tensor_in_b.is_init() == False @@ -105,10 +128,8 @@ def test_sequence(): def test_workgroup(): mgr = kp.Manager(0) - tensor_a = kp.Tensor(np.zeros([16,8])) - tensor_b = kp.Tensor(np.zeros([16,8])) - - mgr.rebuild([tensor_a, tensor_b]) + tensor_a = mgr.tensor(np.zeros([16,8])) + tensor_b = mgr.tensor(np.zeros([16,8])) @ps.python2shader def compute_shader_wg(gl_idx=("input", "GlobalInvocationId", ps.ivec3), @@ -120,17 +141,15 @@ def test_workgroup(): data1[i] = f32(gl_idx.x) data2[i] = f32(gl_idx.y) - seq = mgr.sequence("new") - seq.begin() - seq.record_algo_data([tensor_a, tensor_b], compute_shader_wg.to_spirv(), workgroup=(16,8,1)) - seq.end() - seq.eval() + algo = mgr.algorithm([tensor_a, tensor_b], compute_shader_wg.to_spirv(), (16,8,1), [], []) - mgr.destroy(seq) + (mgr.sequence() + .record(kp.OpTensorSyncDevice([tensor_a, tensor_b])) + .record(kp.OpAlgoDispatch(algo)) + .record(kp.OpAlgoTensorSyncLocal([tensor_a, tensor_b])) + .eval()) - assert seq.is_init() == False - - mgr.eval_tensor_sync_local_def([tensor_a, tensor_b]) + assert sq.is_init() == False print(tensor_a.numpy()) print(tensor_b.numpy()) @@ -138,32 +157,3 @@ def test_workgroup(): assert np.all(tensor_a.numpy() == np.stack([np.arange(16)]*8, axis=1).ravel()) assert np.all(tensor_b.numpy() == np.stack([np.arange(8)]*16, axis=0).ravel()) - mgr.destroy([tensor_a, tensor_b]) - - assert tensor_a.is_init() == False - assert tensor_b.is_init() == False - - -def test_tensor_rebuild_backwards_compat(): - """ - Test basic OpMult operation - """ - - tensor_in_a = kp.Tensor([2, 2, 2]) - tensor_in_b = kp.Tensor([1, 2, 3]) - tensor_out = kp.Tensor([0, 0, 0]) - - mgr = kp.Manager() - - mgr.eval_tensor_create_def([tensor_in_a, tensor_in_b, tensor_out]) - - shader_path = os.path.abspath(os.path.join(DIRNAME, "../../shaders/glsl/opmult.comp.spv")) - mgr.eval_async_algo_file_def([tensor_in_a, tensor_in_b, tensor_out], shader_path) - mgr.eval_await_def() - - mgr.eval_tensor_sync_local_def([tensor_out]) - - assert tensor_out.data() == [2.0, 4.0, 6.0] - assert np.all(tensor_out.numpy() == [2.0, 4.0, 6.0]) - - diff --git a/shaders/glsl/logisticregression.comp.spv b/shaders/glsl/logisticregression.comp.spv index 2f6883dac6c1f40a2928ccf8e8b37c6fa6d68ac1..f68cb431d7d9e54391a59df577f558b3aa1f12f1 100755 GIT binary patch delta 18 ZcmcbhdO?+wnMs+Qfq{{MV -#include -// VK_NO_PROTOTYPES required before vulkan import but after wrapper.hpp -#undef VK_NO_PROTOTYPES -static const char* KOMPUTE_LOG_TAG = "KomputeLog"; -#endif - -#include - -#include - -// Typedefs to simplify interaction with core types -namespace kp { -typedef std::array Workgroup; -typedef std::vector Constants; -} - -// Must be after vulkan is included -#ifndef KOMPUTE_VK_API_VERSION -#ifndef KOMPUTE_VK_API_MAJOR_VERSION -#define KOMPUTE_VK_API_MAJOR_VERSION 1 -#endif // KOMPUTE_VK_API_MAJOR_VERSION -#ifndef KOMPUTE_VK_API_MINOR_VERSION -#define KOMPUTE_VK_API_MINOR_VERSION 1 -#endif // KOMPUTE_VK_API_MINOR_VERSION -#define KOMPUTE_VK_API_VERSION \ - VK_MAKE_VERSION( \ - KOMPUTE_VK_API_MAJOR_VERSION, KOMPUTE_VK_API_MINOR_VERSION, 0) -#endif // KOMPUTE_VK_API_VERSION - -// SPDLOG_ACTIVE_LEVEL must be defined before spdlog.h import -#ifndef SPDLOG_ACTIVE_LEVEL -#if DEBUG -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG -#else -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO -#endif -#endif - -#if defined(KOMPUTE_BUILD_PYTHON) -#include -namespace py = pybind11; -// from python/src/main.cpp -extern py::object kp_debug, kp_info, kp_warning, kp_error; -#endif - -#ifndef KOMPUTE_LOG_OVERRIDE -#if KOMPUTE_ENABLE_SPDLOG -#include -#define KP_LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) -#define KP_LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) -#define KP_LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) -#define KP_LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) -#else -#include -#if SPDLOG_ACTIVE_LEVEL > 1 -#define KP_LOG_DEBUG(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_DEBUG(...) \ - ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 1 - -#if SPDLOG_ACTIVE_LEVEL > 2 -#define KP_LOG_INFO(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_INFO(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 2 - -#if SPDLOG_ACTIVE_LEVEL > 3 -#define KP_LOG_WARN(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_WARN(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 3 - -#if SPDLOG_ACTIVE_LEVEL > 4 -#define KP_LOG_ERROR(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_ERROR(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 4 -#endif // KOMPUTE_SPDLOG_ENABLED -#endif // KOMPUTE_LOG_OVERRIDE - -#if !defined(KOMPUTE_DISABLE_SHADER_UTILS) || !KOMPUTE_DISABLE_SHADER_UTILS -#include -#include - -#include -#include -#include - -namespace kp { - -// The default resource limit for the GLSL compiler, can be overwritten -// Has been adobted by: -// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp -const TBuiltInResource defaultResource = { -/* .MaxLights = */ 0, -/* .MaxClipPlanes = */ 0, -/* .MaxTextureUnits = */ 0, -/* .MaxTextureCoords = */ 0, -/* .MaxVertexAttribs = */ 64, -/* .MaxVertexUniformComponents = */ 4096, -/* .MaxVaryingFloats = */ 64, -/* .MaxVertexTextureImageUnits = */ 0, -/* .MaxCombinedTextureImageUnits = */ 0, -/* .MaxTextureImageUnits = */ 0, -/* .MaxFragmentUniformComponents = */ 0, -/* .MaxDrawBuffers = */ 0, -/* .MaxVertexUniformVectors = */ 128, -/* .MaxVaryingVectors = */ 8, -/* .MaxFragmentUniformVectors = */ 0, -/* .MaxVertexOutputVectors = */ 16, -/* .MaxFragmentInputVectors = */ 0, -/* .MinProgramTexelOffset = */ -8, -/* .MaxProgramTexelOffset = */ 7, -/* .MaxClipDistances = */ 8, -/* .MaxComputeWorkGroupCountX = */ 65535, -/* .MaxComputeWorkGroupCountY = */ 65535, -/* .MaxComputeWorkGroupCountZ = */ 65535, -/* .MaxComputeWorkGroupSizeX = */ 1024, -/* .MaxComputeWorkGroupSizeY = */ 1024, -/* .MaxComputeWorkGroupSizeZ = */ 64, -/* .MaxComputeUniformComponents = */ 1024, -/* .MaxComputeTextureImageUnits = */ 16, -/* .MaxComputeImageUniforms = */ 8, -/* .MaxComputeAtomicCounters = */ 8, -/* .MaxComputeAtomicCounterBuffers = */ 1, -/* .MaxVaryingComponents = */ 60, -/* .MaxVertexOutputComponents = */ 64, -/* .MaxGeometryInputComponents = */ 64, -/* .MaxGeometryOutputComponents = */ 128, -/* .MaxFragmentInputComponents = */ 0, -/* .MaxImageUnits = */ 0, -/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, -/* .MaxCombinedShaderOutputResources = */ 8, -/* .MaxImageSamples = */ 0, -/* .MaxVertexImageUniforms = */ 0, -/* .MaxTessControlImageUniforms = */ 0, -/* .MaxTessEvaluationImageUniforms = */ 0, -/* .MaxGeometryImageUniforms = */ 0, -/* .MaxFragmentImageUniforms = */ 0, -/* .MaxCombinedImageUniforms = */ 0, -/* .MaxGeometryTextureImageUnits = */ 0, -/* .MaxGeometryOutputVertices = */ 256, -/* .MaxGeometryTotalOutputComponents = */ 1024, -/* .MaxGeometryUniformComponents = */ 1024, -/* .MaxGeometryVaryingComponents = */ 64, -/* .MaxTessControlInputComponents = */ 128, -/* .MaxTessControlOutputComponents = */ 128, -/* .MaxTessControlTextureImageUnits = */ 0, -/* .MaxTessControlUniformComponents = */ 1024, -/* .MaxTessControlTotalOutputComponents = */ 4096, -/* .MaxTessEvaluationInputComponents = */ 128, -/* .MaxTessEvaluationOutputComponents = */ 128, -/* .MaxTessEvaluationTextureImageUnits = */ 16, -/* .MaxTessEvaluationUniformComponents = */ 1024, -/* .MaxTessPatchComponents = */ 120, -/* .MaxPatchVertices = */ 32, -/* .MaxTessGenLevel = */ 64, -/* .MaxViewports = */ 16, -/* .MaxVertexAtomicCounters = */ 0, -/* .MaxTessControlAtomicCounters = */ 0, -/* .MaxTessEvaluationAtomicCounters = */ 0, -/* .MaxGeometryAtomicCounters = */ 0, -/* .MaxFragmentAtomicCounters = */ 0, -/* .MaxCombinedAtomicCounters = */ 8, -/* .MaxAtomicCounterBindings = */ 1, -/* .MaxVertexAtomicCounterBuffers = */ 0, -/* .MaxTessControlAtomicCounterBuffers = */ 0, -/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, -/* .MaxGeometryAtomicCounterBuffers = */ 0, -/* .MaxFragmentAtomicCounterBuffers = */ 0, -/* .MaxCombinedAtomicCounterBuffers = */ 1, -/* .MaxAtomicCounterBufferSize = */ 16384, -/* .MaxTransformFeedbackBuffers = */ 4, -/* .MaxTransformFeedbackInterleavedComponents = */ 64, -/* .MaxCullDistances = */ 8, -/* .MaxCombinedClipAndCullDistances = */ 8, -/* .MaxSamples = */ 4, -/* .maxMeshOutputVerticesNV = */ 256, -/* .maxMeshOutputPrimitivesNV = */ 512, -/* .maxMeshWorkGroupSizeX_NV = */ 32, -/* .maxMeshWorkGroupSizeY_NV = */ 1, -/* .maxMeshWorkGroupSizeZ_NV = */ 1, -/* .maxTaskWorkGroupSizeX_NV = */ 32, -/* .maxTaskWorkGroupSizeY_NV = */ 1, -/* .maxTaskWorkGroupSizeZ_NV = */ 1, -/* .maxMeshViewCountNV = */ 4, -/* .maxDualSourceDrawBuffersEXT = */ 1, - -/* .limits = */ { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, -}}; - -/** - Shader utily class with functions to compile and process glsl files. -*/ -class Shader { -public: - /** - * Compile multiple sources with optional filenames. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. - * - * @param sources A list of raw glsl shaders in string format - * @param files A list of file names respective to each of the sources - * @param entryPoint The function name to use as entry point - * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler - * @return The compiled SPIR-V binary in unsigned int32 format - */ - static std::vector compile_sources( - const std::vector& sources, - const std::vector& files = {}, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - - /** - * Compile a single glslang source from string value. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. - * - * @param source An individual raw glsl shader in string format - * @param entryPoint The function name to use as entry point - * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler - * @return The compiled SPIR-V binary in unsigned int32 format - */ - static std::vector compile_source( - const std::string& source, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - -}; - -} -#endif // DKOMPUTE_DISABLE_SHADER_UTILS - -/* - THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT - - --- - - Copyright 2020 The Institute for Ethical AI & Machine Learning - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef SHADEROP_SHADEROPMULT_HPP -#define SHADEROP_SHADEROPMULT_HPP - -namespace kp { -namespace shader_data { -static const unsigned char shaders_glsl_opmult_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, - 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; -} -} -#endif // define SHADEROP_SHADEROPMULT_HPP - -/* - THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT - - --- - - Copyright 2020 The Institute for Ethical AI & Machine Learning - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef SHADEROP_SHADERLOGISTICREGRESSION_HPP -#define SHADEROP_SHADERLOGISTICREGRESSION_HPP - -namespace kp { -namespace shader_data { -static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; -} -} -#endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP - -#include -#include - -namespace kp { - -/** - * Structured data used in GPU operations. - * - * Tensors are the base building block in Kompute to perform operations across - * GPUs. Each tensor would have a respective Vulkan memory and buffer, which - * would be used to store their respective data. The tensors can be used for GPU - * data storage or transfer. - */ -class Tensor -{ - public: - /** - * Type for tensors created: Device allows memory to be transferred from - * staging buffers. Staging are host memory visible. Storage are device - * visible but are not set up to transfer or receive data (only for shader - * storage). - */ - enum class TensorTypes - { - eDevice = 0, ///< Type is device memory, source and destination - eHost = 1, ///< Type is host memory, source and destination - eStorage = 2, ///< Type is Device memory (only) - }; - - /** - * Default constructor with data provided which would be used to create the - * respective vulkan buffer and memory. - * - * @param data Non-zero-sized vector of data that will be used by the - * tensor - * @param tensorType Type for the tensor which is of type TensorTypes - */ - Tensor(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice); - - /** - * Destructor which is in charge of freeing vulkan resources unless they - * have been provided externally. - */ - ~Tensor(); - - /** - * Initialiser which calls the initialisation for all the respective tensors - * as well as creates the respective staging tensors. The staging tensors - * would only be created for the tensors of type TensorType::eDevice as - * otherwise there is no need to copy from host memory. - */ - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); - - /** - * Destroys and frees the GPU resources which include the buffer and memory. - */ - void destroy(); - - bool isInit(); - - /** - * Returns the vector of data currently contained by the Tensor. It is - * important to ensure that there is no out-of-sync data with the GPU - * memory. - * - * @return Reference to vector of elements representing the data in the - * tensor. - */ - std::vector& data(); - /** - * Overrides the subscript operator to expose the underlying data's - * subscript operator which in this case would be its underlying - * vector's. - * - * @param i The index where the element will be returned from. - * @return Returns the element in the position requested. - */ - float& operator[](int index); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - uint32_t size(); - - /** - * Retrieve the tensor type of the Tensor - * - * @return Tensor type of tensor - */ - TensorTypes tensorType(); - - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setData(const std::vector& data); - - /** - * Records a copy from the memory of the tensor provided to the current - * thensor. This is intended to pass memory into a processing, to perform - * a staging buffer transfer, or to gather output (between others). - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param copyFromTensor Tensor to copy the data from - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFrom(std::shared_ptr commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier); - - /** - * Records a copy from the internal staging memory to the device memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromStagingToDevice( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records a copy from the internal device memory to the staging memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromDeviceToStaging( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records the buffer memory barrier into the command buffer which - * ensures that relevant data transfers are carried out correctly. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param srcAccessMask Access flags for source access mask - * @param dstAccessMask Access flags for destination access mask - * @param scrStageMask Pipeline stage flags for source stage mask - * @param dstStageMask Pipeline stage flags for destination stage mask - */ - void recordBufferMemoryBarrier( - std::shared_ptr commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); - - /** - * Constructs a vulkan descriptor buffer info which can be used to specify - * and reference the underlying buffer component of the tensor without - * exposing it. - * - * @return Descriptor buffer info with own buffer - */ - vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice; - std::shared_ptr mDevice; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mPrimaryBuffer; - bool mFreePrimaryBuffer = false; - std::shared_ptr mStagingBuffer; - bool mFreeStagingBuffer = false; - std::shared_ptr mPrimaryMemory; - bool mFreePrimaryMemory = false; - std::shared_ptr mStagingMemory; - bool mFreeStagingMemory = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - - TensorTypes mTensorType = TensorTypes::eDevice; - - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer - void createBuffer(std::shared_ptr buffer, - vk::BufferUsageFlags bufferUsageFlags); - void allocateBindMemory(std::shared_ptr buffer, - std::shared_ptr memory, - vk::MemoryPropertyFlags memoryPropertyFlags); - void copyBuffer(std::shared_ptr commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); - - // Private util functions - vk::BufferUsageFlags getPrimaryBufferUsageFlags(); - vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); - vk::BufferUsageFlags getStagingBufferUsageFlags(); - vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - uint64_t memorySize(); -}; - -} // End namespace kp - -namespace kp { - -/** - Abstraction for compute shaders that are run on top of tensors grouped via - ParameterGroups (which group descriptorsets) -*/ -class Algorithm -{ -public: - - /** - * Default constructor for Algorithm - * - * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders - */ - Algorithm( - std::shared_ptr device, - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. - * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing - */ - void rebuild( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Destructor for Algorithm which is responsible for freeing and desroying - * respective pipelines and owned parameter groups. - */ - ~Algorithm(); - - /** - * Records the dispatch function with the provided template parameters or - * alternatively using the size of the tensor by default. - * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value - */ - void recordDispatch(std::shared_ptr commandBuffer); - - bool isInit(); - - void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); - - const Workgroup& getWorkgroup(); - const Constants& getSpecializationConstants(); - const Constants& getPushConstants(); - const std::vector>& getTensors(); - - void destroy(); - -private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mDevice; - std::vector> mTensors; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mDescriptorSetLayout; - bool mFreeDescriptorSetLayout = false; - std::shared_ptr mDescriptorPool; - bool mFreeDescriptorPool = false; - std::shared_ptr mDescriptorSet; - bool mFreeDescriptorSet = false; - std::shared_ptr mShaderModule; - bool mFreeShaderModule = false; - std::shared_ptr mPipelineLayout; - bool mFreePipelineLayout = false; - std::shared_ptr mPipelineCache; - bool mFreePipelineCache = false; - std::shared_ptr mPipeline; - bool mFreePipeline = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mSpirv; - Constants mSpecializationConstants; - Constants mPushConstants; - Workgroup mWorkgroup; - - bool mIsInit; - - // Create util functions - void createShaderModule(); - void createPipeline(); - - // Parameters - void createParameters(); -}; - -} // End namespace kp - -namespace kp { - -/** - * Base Operation which provides the high level interface that Kompute - * operations implement in order to perform a set of actions in the GPU. - * - * Operations can perform actions on tensors, and optionally can also own an - * Algorithm with respective parameters. kp::Operations with kp::Algorithms - * would inherit from kp::OpBaseAlgo. - */ -class OpBase -{ - public: - - /** - * Default destructor for OpBase class. This OpBase destructor class should - * always be called to destroy and free owned resources unless it is - * intended to destroy the resources in the parent class. - */ - virtual ~OpBase() - { - KP_LOG_DEBUG("Kompute OpBase destructor started"); - } - - /** - * The record function is intended to only send a record command or run - * commands that are expected to record operations that are to be submitted - * as a batch into the GPU. - */ - virtual void record(std::shared_ptr commandBuffer) = 0; - - /** - * Pre eval is called before the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any per-eval setup steps - * required as the computation iteration begins. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are created should be idempotent in case it's called multiple - * times in a row. - */ - virtual void preEval() = 0; - - /** - * Post eval is called after the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any tear-down steps - * required as the computation iteration finishes. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are destroyed should not require a re-init unless explicitly - * provided by the user. - */ - virtual void postEval() = 0; -}; - -} // End namespace kp - -namespace kp { - -/** - * Container of operations that can be sent to GPU as batch - */ -class Sequence: public std::enable_shared_from_this -{ - public: - /** - * Main constructor for sequence which requires core vulkan components to - * generate all dependent resources. - * - * @param physicalDevice Vulkan physical device - * @param device Vulkan logical device - * @param computeQueue Vulkan compute queue - * @param queueIndex Vulkan compute queue index in device - */ - Sequence(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr computeQueue, - uint32_t queueIndex); - /** - * Destructor for sequence which is responsible for cleaning all subsequent - * owned operations. - */ - ~Sequence(); - - /** - */ - std::shared_ptr record(std::shared_ptr op); - - /** - * Record function for operation to be added to the GPU queue in batch. This - * template requires classes to be derived from the OpBase class. This - * function also requires the Sequence to be recording, otherwise it will - * not be able to add the operation. - * - * @param tensors Vector of tensors to use for the operation - * @param TArgs Template parameters that are used to initialise operation - * which allows for extensible configurations on initialisation. - */ - template - std::shared_ptr - record(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->record(op); - } - template - std::shared_ptr - record(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->record(op); - } - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - std::shared_ptr eval(); - - std::shared_ptr eval(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - // TODO: Aim to have only a single function with tensors/algorithm - template - std::shared_ptr - eval(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - // TODO: Aim to be able to handle errors when returning without throw except - return this->eval(op); - } - // Needded as otherise can't use initialiser list - template - std::shared_ptr - eval(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->eval(op); - } - - /** - * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAsync(); - std::shared_ptr evalAsync(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - template - std::shared_ptr - evalAsync(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->evalAsync(op); - } - // Needed as otherwise it's not possible to use initializer lists - template - std::shared_ptr - evalAsync(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->evalAsync(op); - } - - /** - * Eval Await waits for the fence to finish processing and then once it - * finishes, it runs the postEval of all operations. - * - * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); - - /** - * Clear function clears all operations currently recorded and starts recording again. - */ - void clear(); - - /** - * Begins recording commands for commands to be submitted into the command - * buffer. - * - * @return Boolean stating whether execution was successful. - */ - void begin(); - - /** - * Ends the recording and stops recording commands when the record command - * is sent. - * - * @return Boolean stating whether execution was successful. - */ - void end(); - - /** - * Returns true if the sequence is currently in recording activated. - * - * @return Boolean stating if recording ongoing. - */ - bool isRecording(); - - bool isInit(); - - /** - * Returns true if the sequence is currently running - mostly used for async - * workloads. - * - * @return Boolean stating if currently running. - */ - bool isRunning(); - - /** - * Destroys and frees the GPU resources which include the buffer and memory - * and sets the sequence as init=False. - */ - void destroy(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - std::shared_ptr mComputeQueue = nullptr; - uint32_t mQueueIndex = -1; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mCommandPool = nullptr; - bool mFreeCommandPool = false; - std::shared_ptr mCommandBuffer = nullptr; - bool mFreeCommandBuffer = false; - - // -------------- ALWAYS OWNED RESOURCES - vk::Fence mFence; - std::vector> mOperations; - - // State - bool mRecording = false; - bool mIsRunning = false; - - // Create functions - void createCommandPool(); - void createCommandBuffer(); -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncDevice : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncDevice(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncDevice() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Does not perform any postEval commands. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -#define KP_DEFAULT_SESSION "DEFAULT" - -namespace kp { - -/** - Base orchestrator which creates and manages device and child components -*/ -class Manager -{ - public: - /** - Base constructor and default used which creates the base resources - including choosing the device 0 by default. - */ - Manager(); - - /** - * Similar to base constructor but allows the user to provide the device - * they would like to create the resources on. - * - * @param physicalDeviceIndex The index of the physical device to use - * @param manageResources (Optional) Whether to manage the memory of the - * resources created and destroy when the manager is destroyed. - * @param familyQueueIndices (Optional) List of queue indices to add for - * explicit allocation - * @param totalQueues The total number of compute queues to create. - */ - Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices = {}); - - /** - * Manager constructor which allows your own vulkan application to integrate - * with the vulkan kompute use. - * - * @param instance Vulkan compute instance to base this application - * @param physicalDevice Vulkan physical device to use for application - * @param device Vulkan logical device to use for all base resources - * @param physicalDeviceIndex Index for vulkan physical device used - */ - Manager(std::shared_ptr instance, - std::shared_ptr physicalDevice, - std::shared_ptr device); - - /** - * Manager destructor which would ensure all owned resources are destroyed - * unless explicitly stated that resources should not be destroyed or freed. - */ - ~Manager(); - - /** - * Get or create a managed Sequence that will be contained by this manager. - * If the named sequence does not currently exist, it would be created and - * initialised. - * - * @param sequenceName The name for the named sequence to be retrieved or - * created - * @param queueIndex The queue to use from the available queues - * @return Shared pointer to the manager owned sequence resource - */ - std::shared_ptr sequence(uint32_t queueIndex = 0); - - /** - * Function that simplifies the common workflow of tensor creation and - * initialization. It will take the constructor parameters for a Tensor - * and will will us it to create a new Tensor and then create it. The - * tensor memory will then be managed and owned by the manager. - * - * @param data The data to initialize the tensor with - * @param tensorType The type of tensor to initialize - * @param syncDataToGPU Whether to sync the data to GPU memory - * @returns Initialized Tensor with memory Syncd to GPU device - */ - std::shared_ptr tensor( - const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, - bool syncDataToGPU = true); - - std::shared_ptr algorithm( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - void destroy(); - void clear(); - - private: - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mInstance = nullptr; - bool mFreeInstance = false; - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - bool mFreeDevice = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector> mManagedTensors; - std::vector> mManagedSequences; - std::vector> mManagedAlgorithms; - - std::vector mComputeQueueFamilyIndices; - std::vector> mComputeQueues; - - bool mManageResources = false; - -#if DEBUG -#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS - vk::DebugReportCallbackEXT mDebugReportCallback; - vk::DispatchLoaderDynamic mDebugDispatcher; -#endif -#endif - - // Create functions - void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); -}; - -} // End namespace kp - -#include - -namespace kp { - -/** - * Operation that provides a general abstraction that simplifies the use of - * algorithm and parameter components which can be used with shaders. - * By default it enables the user to provide a dynamic number of tensors - * which are then passed as inputs. - */ -class OpAlgoDispatch : public OpBase -{ - public: - - OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoDispatch() override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - -private: - // -------------- ALWAYS OWNED RESOURCES - std::shared_ptr mAlgorithm; -}; - -} // End namespace kp - -namespace kp { - -/** - * Operation that performs multiplication on two tensors and outpus on third - * tensor. - */ -class OpMult : public OpAlgoDispatch -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm, true) - { - KP_LOG_DEBUG("Kompute OpMult constructor with params"); - - if (tensors.size() != 3) { - throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); - } - - std::vector spirv( - (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, - (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + - kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - - algorithm->rebuild(tensors, spirv); - } - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpMult() override { - KP_LOG_DEBUG("Kompute OpMult destructor started"); - } -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type -*/ -class OpTensorCopy : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorCopy(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorCopy() override; - - /** - * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Copies the local vectors for all the tensors to sync the data with the gpu. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncLocal : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncLocal(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncLocal() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * For host tensors it performs the map command from the host memory into local memory. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp +#pragma once +/* + THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT + + --- + + Copyright 2020 The Institute for Ethical AI & Machine Learning + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SHADEROP_SHADEROPMULT_HPP +#define SHADEROP_SHADEROPMULT_HPP + +namespace kp { +namespace shader_data { +static const unsigned char shaders_glsl_opmult_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, + 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; +} +} +#endif // define SHADEROP_SHADEROPMULT_HPP + +/* + THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT + + --- + + Copyright 2020 The Institute for Ethical AI & Machine Learning + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SHADEROP_SHADERLOGISTICREGRESSION_HPP +#define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +namespace kp { +namespace shader_data { +static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; +} +} +#endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +#if VK_USE_PLATFORM_ANDROID_KHR +#include +#include +// VK_NO_PROTOTYPES required before vulkan import but after wrapper.hpp +#undef VK_NO_PROTOTYPES +static const char* KOMPUTE_LOG_TAG = "KomputeLog"; +#endif + +#include + +#include + +// Typedefs to simplify interaction with core types +namespace kp { +typedef std::array Workgroup; +typedef std::vector Constants; +} + +// Must be after vulkan is included +#ifndef KOMPUTE_VK_API_VERSION +#ifndef KOMPUTE_VK_API_MAJOR_VERSION +#define KOMPUTE_VK_API_MAJOR_VERSION 1 +#endif // KOMPUTE_VK_API_MAJOR_VERSION +#ifndef KOMPUTE_VK_API_MINOR_VERSION +#define KOMPUTE_VK_API_MINOR_VERSION 1 +#endif // KOMPUTE_VK_API_MINOR_VERSION +#define KOMPUTE_VK_API_VERSION \ + VK_MAKE_VERSION( \ + KOMPUTE_VK_API_MAJOR_VERSION, KOMPUTE_VK_API_MINOR_VERSION, 0) +#endif // KOMPUTE_VK_API_VERSION + +// SPDLOG_ACTIVE_LEVEL must be defined before spdlog.h import +#ifndef SPDLOG_ACTIVE_LEVEL +#if DEBUG +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#else +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO +#endif +#endif + +#if defined(KOMPUTE_BUILD_PYTHON) +#include +namespace py = pybind11; +// from python/src/main.cpp +extern py::object kp_debug, kp_info, kp_warning, kp_error; +#endif + +#ifndef KOMPUTE_LOG_OVERRIDE +#if KOMPUTE_ENABLE_SPDLOG +#include +#define KP_LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) +#define KP_LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) +#define KP_LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) +#define KP_LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) +#else +#include +#if SPDLOG_ACTIVE_LEVEL > 1 +#define KP_LOG_DEBUG(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_DEBUG(...) \ + ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 1 + +#if SPDLOG_ACTIVE_LEVEL > 2 +#define KP_LOG_INFO(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_INFO(...) \ + ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 2 + +#if SPDLOG_ACTIVE_LEVEL > 3 +#define KP_LOG_WARN(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_WARN(...) \ + ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 3 + +#if SPDLOG_ACTIVE_LEVEL > 4 +#define KP_LOG_ERROR(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_ERROR(...) \ + ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 4 +#endif // KOMPUTE_SPDLOG_ENABLED +#endif // KOMPUTE_LOG_OVERRIDE + +#if !defined(KOMPUTE_DISABLE_SHADER_UTILS) || !KOMPUTE_DISABLE_SHADER_UTILS +#include +#include + +#include +#include +#include + +namespace kp { + +// The default resource limit for the GLSL compiler, can be overwritten +// Has been adobted by: +// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp +const TBuiltInResource defaultResource = { +/* .MaxLights = */ 0, +/* .MaxClipPlanes = */ 0, +/* .MaxTextureUnits = */ 0, +/* .MaxTextureCoords = */ 0, +/* .MaxVertexAttribs = */ 64, +/* .MaxVertexUniformComponents = */ 4096, +/* .MaxVaryingFloats = */ 64, +/* .MaxVertexTextureImageUnits = */ 0, +/* .MaxCombinedTextureImageUnits = */ 0, +/* .MaxTextureImageUnits = */ 0, +/* .MaxFragmentUniformComponents = */ 0, +/* .MaxDrawBuffers = */ 0, +/* .MaxVertexUniformVectors = */ 128, +/* .MaxVaryingVectors = */ 8, +/* .MaxFragmentUniformVectors = */ 0, +/* .MaxVertexOutputVectors = */ 16, +/* .MaxFragmentInputVectors = */ 0, +/* .MinProgramTexelOffset = */ -8, +/* .MaxProgramTexelOffset = */ 7, +/* .MaxClipDistances = */ 8, +/* .MaxComputeWorkGroupCountX = */ 65535, +/* .MaxComputeWorkGroupCountY = */ 65535, +/* .MaxComputeWorkGroupCountZ = */ 65535, +/* .MaxComputeWorkGroupSizeX = */ 1024, +/* .MaxComputeWorkGroupSizeY = */ 1024, +/* .MaxComputeWorkGroupSizeZ = */ 64, +/* .MaxComputeUniformComponents = */ 1024, +/* .MaxComputeTextureImageUnits = */ 16, +/* .MaxComputeImageUniforms = */ 8, +/* .MaxComputeAtomicCounters = */ 8, +/* .MaxComputeAtomicCounterBuffers = */ 1, +/* .MaxVaryingComponents = */ 60, +/* .MaxVertexOutputComponents = */ 64, +/* .MaxGeometryInputComponents = */ 64, +/* .MaxGeometryOutputComponents = */ 128, +/* .MaxFragmentInputComponents = */ 0, +/* .MaxImageUnits = */ 0, +/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, +/* .MaxCombinedShaderOutputResources = */ 8, +/* .MaxImageSamples = */ 0, +/* .MaxVertexImageUniforms = */ 0, +/* .MaxTessControlImageUniforms = */ 0, +/* .MaxTessEvaluationImageUniforms = */ 0, +/* .MaxGeometryImageUniforms = */ 0, +/* .MaxFragmentImageUniforms = */ 0, +/* .MaxCombinedImageUniforms = */ 0, +/* .MaxGeometryTextureImageUnits = */ 0, +/* .MaxGeometryOutputVertices = */ 256, +/* .MaxGeometryTotalOutputComponents = */ 1024, +/* .MaxGeometryUniformComponents = */ 1024, +/* .MaxGeometryVaryingComponents = */ 64, +/* .MaxTessControlInputComponents = */ 128, +/* .MaxTessControlOutputComponents = */ 128, +/* .MaxTessControlTextureImageUnits = */ 0, +/* .MaxTessControlUniformComponents = */ 1024, +/* .MaxTessControlTotalOutputComponents = */ 4096, +/* .MaxTessEvaluationInputComponents = */ 128, +/* .MaxTessEvaluationOutputComponents = */ 128, +/* .MaxTessEvaluationTextureImageUnits = */ 16, +/* .MaxTessEvaluationUniformComponents = */ 1024, +/* .MaxTessPatchComponents = */ 120, +/* .MaxPatchVertices = */ 32, +/* .MaxTessGenLevel = */ 64, +/* .MaxViewports = */ 16, +/* .MaxVertexAtomicCounters = */ 0, +/* .MaxTessControlAtomicCounters = */ 0, +/* .MaxTessEvaluationAtomicCounters = */ 0, +/* .MaxGeometryAtomicCounters = */ 0, +/* .MaxFragmentAtomicCounters = */ 0, +/* .MaxCombinedAtomicCounters = */ 8, +/* .MaxAtomicCounterBindings = */ 1, +/* .MaxVertexAtomicCounterBuffers = */ 0, +/* .MaxTessControlAtomicCounterBuffers = */ 0, +/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, +/* .MaxGeometryAtomicCounterBuffers = */ 0, +/* .MaxFragmentAtomicCounterBuffers = */ 0, +/* .MaxCombinedAtomicCounterBuffers = */ 1, +/* .MaxAtomicCounterBufferSize = */ 16384, +/* .MaxTransformFeedbackBuffers = */ 4, +/* .MaxTransformFeedbackInterleavedComponents = */ 64, +/* .MaxCullDistances = */ 8, +/* .MaxCombinedClipAndCullDistances = */ 8, +/* .MaxSamples = */ 4, +/* .maxMeshOutputVerticesNV = */ 256, +/* .maxMeshOutputPrimitivesNV = */ 512, +/* .maxMeshWorkGroupSizeX_NV = */ 32, +/* .maxMeshWorkGroupSizeY_NV = */ 1, +/* .maxMeshWorkGroupSizeZ_NV = */ 1, +/* .maxTaskWorkGroupSizeX_NV = */ 32, +/* .maxTaskWorkGroupSizeY_NV = */ 1, +/* .maxTaskWorkGroupSizeZ_NV = */ 1, +/* .maxMeshViewCountNV = */ 4, +/* .maxDualSourceDrawBuffersEXT = */ 1, + +/* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, +}}; + +/** + Shader utily class with functions to compile and process glsl files. +*/ +class Shader { +public: + /** + * Compile multiple sources with optional filenames. Currently this function + * uses the glslang C++ interface which is not thread safe so this funciton + * should not be called from multiple threads concurrently. If you have a + * online shader processing multithreading use-case that can't use offline + * compilation please open an issue. + * + * @param sources A list of raw glsl shaders in string format + * @param files A list of file names respective to each of the sources + * @param entryPoint The function name to use as entry point + * @param definitions List of pairs containing key value definitions + * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @return The compiled SPIR-V binary in unsigned int32 format + */ + static std::vector compile_sources( + const std::vector& sources, + const std::vector& files = {}, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); + + /** + * Compile a single glslang source from string value. Currently this function + * uses the glslang C++ interface which is not thread safe so this funciton + * should not be called from multiple threads concurrently. If you have a + * online shader processing multithreading use-case that can't use offline + * compilation please open an issue. + * + * @param source An individual raw glsl shader in string format + * @param entryPoint The function name to use as entry point + * @param definitions List of pairs containing key value definitions + * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @return The compiled SPIR-V binary in unsigned int32 format + */ + static std::vector compile_source( + const std::string& source, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); + +}; + +} +#endif // DKOMPUTE_DISABLE_SHADER_UTILS + +namespace kp { + +/** + * Structured data used in GPU operations. + * + * Tensors are the base building block in Kompute to perform operations across + * GPUs. Each tensor would have a respective Vulkan memory and buffer, which + * would be used to store their respective data. The tensors can be used for GPU + * data storage or transfer. + */ +class Tensor +{ + public: + /** + * Type for tensors created: Device allows memory to be transferred from + * staging buffers. Staging are host memory visible. Storage are device + * visible but are not set up to transfer or receive data (only for shader + * storage). + */ + enum class TensorTypes + { + eDevice = 0, ///< Type is device memory, source and destination + eHost = 1, ///< Type is host memory, source and destination + eStorage = 2, ///< Type is Device memory (only) + }; + + /** + * Default constructor with data provided which would be used to create the + * respective vulkan buffer and memory. + * + * @param data Non-zero-sized vector of data that will be used by the + * tensor + * @param tensorType Type for the tensor which is of type TensorTypes + */ + Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); + + /** + * Destructor which is in charge of freeing vulkan resources unless they + * have been provided externally. + */ + ~Tensor(); + + /** + * Initialiser which calls the initialisation for all the respective tensors + * as well as creates the respective staging tensors. The staging tensors + * would only be created for the tensors of type TensorType::eDevice as + * otherwise there is no need to copy from host memory. + */ + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice); + + /** + * Destroys and frees the GPU resources which include the buffer and memory. + */ + void destroy(); + + bool isInit(); + + /** + * Returns the vector of data currently contained by the Tensor. It is + * important to ensure that there is no out-of-sync data with the GPU + * memory. + * + * @return Reference to vector of elements representing the data in the + * tensor. + */ + std::vector& data(); + /** + * Overrides the subscript operator to expose the underlying data's + * subscript operator which in this case would be its underlying + * vector's. + * + * @param i The index where the element will be returned from. + * @return Returns the element in the position requested. + */ + float& operator[](int index); + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + uint32_t size(); + + /** + * Retrieve the tensor type of the Tensor + * + * @return Tensor type of tensor + */ + TensorTypes tensorType(); + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + void setData(const std::vector& data); + + /** + * Records a copy from the memory of the tensor provided to the current + * thensor. This is intended to pass memory into a processing, to perform + * a staging buffer transfer, or to gather output (between others). + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param copyFromTensor Tensor to copy the data from + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFrom(std::shared_ptr commandBuffer, + std::shared_ptr copyFromTensor, + bool createBarrier); + + /** + * Records a copy from the internal staging memory to the device memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromStagingToDevice( + std::shared_ptr commandBuffer, + bool createBarrier); + + /** + * Records a copy from the internal device memory to the staging memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromDeviceToStaging( + std::shared_ptr commandBuffer, + bool createBarrier); + + /** + * Records the buffer memory barrier into the command buffer which + * ensures that relevant data transfers are carried out correctly. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param srcAccessMask Access flags for source access mask + * @param dstAccessMask Access flags for destination access mask + * @param scrStageMask Pipeline stage flags for source stage mask + * @param dstStageMask Pipeline stage flags for destination stage mask + */ + void recordBufferMemoryBarrier( + std::shared_ptr commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + + /** + * Constructs a vulkan descriptor buffer info which can be used to specify + * and reference the underlying buffer component of the tensor without + * exposing it. + * + * @return Descriptor buffer info with own buffer + */ + vk::DescriptorBufferInfo constructDescriptorBufferInfo(); + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + void mapDataFromHostMemory(); + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + void mapDataIntoHostMemory(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice; + std::shared_ptr mDevice; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mPrimaryBuffer; + bool mFreePrimaryBuffer = false; + std::shared_ptr mStagingBuffer; + bool mFreeStagingBuffer = false; + std::shared_ptr mPrimaryMemory; + bool mFreePrimaryMemory = false; + std::shared_ptr mStagingMemory; + bool mFreeStagingMemory = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mData; + + TensorTypes mTensorType = TensorTypes::eDevice; + + void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer + void createBuffer(std::shared_ptr buffer, + vk::BufferUsageFlags bufferUsageFlags); + void allocateBindMemory(std::shared_ptr buffer, + std::shared_ptr memory, + vk::MemoryPropertyFlags memoryPropertyFlags); + void copyBuffer(std::shared_ptr commandBuffer, + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); + + // Private util functions + vk::BufferUsageFlags getPrimaryBufferUsageFlags(); + vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); + vk::BufferUsageFlags getStagingBufferUsageFlags(); + vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + uint64_t memorySize(); +}; + +} // End namespace kp + +namespace kp { + +/** + Abstraction for compute shaders that are run on top of tensors grouped via + ParameterGroups (which group descriptorsets) +*/ +class Algorithm +{ +public: + + /** + * Default constructor for Algorithm + * + * @param device The Vulkan device to use for creating resources + * @param commandBuffer The vulkan command buffer to bind the pipeline and + * shaders + */ + Algorithm( + std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + /** + * Initialiser for the shader data provided to the algorithm as well as + * tensor parameters that will be used in shader. + * + * @param shaderFileData The bytes in spir-v format of the shader + * @tensorParams The Tensors to be used in the Algorithm / shader for + * @specalizationInstalces The specialization parameters to pass to the function + * processing + */ + void rebuild( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + /** + * Destructor for Algorithm which is responsible for freeing and desroying + * respective pipelines and owned parameter groups. + */ + ~Algorithm(); + + /** + * Records the dispatch function with the provided template parameters or + * alternatively using the size of the tensor by default. + * + * @param x Layout X dispatch value + * @param y Layout Y dispatch value + * @param z Layout Z dispatch value + */ + void recordDispatch(std::shared_ptr commandBuffer); + + bool isInit(); + + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + + const Workgroup& getWorkgroup(); + const Constants& getSpecializationConstants(); + const Constants& getPushConstants(); + const std::vector>& getTensors(); + + void destroy(); + +private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mDevice; + std::vector> mTensors; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mDescriptorSetLayout; + bool mFreeDescriptorSetLayout = false; + std::shared_ptr mDescriptorPool; + bool mFreeDescriptorPool = false; + std::shared_ptr mDescriptorSet; + bool mFreeDescriptorSet = false; + std::shared_ptr mShaderModule; + bool mFreeShaderModule = false; + std::shared_ptr mPipelineLayout; + bool mFreePipelineLayout = false; + std::shared_ptr mPipelineCache; + bool mFreePipelineCache = false; + std::shared_ptr mPipeline; + bool mFreePipeline = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mSpirv; + Constants mSpecializationConstants; + Constants mPushConstants; + Workgroup mWorkgroup; + + bool mIsInit; + + // Create util functions + void createShaderModule(); + void createPipeline(); + + // Parameters + void createParameters(); +}; + +} // End namespace kp + +namespace kp { + +/** + * Base Operation which provides the high level interface that Kompute + * operations implement in order to perform a set of actions in the GPU. + * + * Operations can perform actions on tensors, and optionally can also own an + * Algorithm with respective parameters. kp::Operations with kp::Algorithms + * would inherit from kp::OpBaseAlgo. + */ +class OpBase +{ + public: + + /** + * Default destructor for OpBase class. This OpBase destructor class should + * always be called to destroy and free owned resources unless it is + * intended to destroy the resources in the parent class. + */ + virtual ~OpBase() + { + KP_LOG_DEBUG("Kompute OpBase destructor started"); + } + + /** + * The record function is intended to only send a record command or run + * commands that are expected to record operations that are to be submitted + * as a batch into the GPU. + */ + virtual void record(std::shared_ptr commandBuffer) = 0; + + /** + * Pre eval is called before the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any per-eval setup steps + * required as the computation iteration begins. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are created should be idempotent in case it's called multiple + * times in a row. + */ + virtual void preEval() = 0; + + /** + * Post eval is called after the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any tear-down steps + * required as the computation iteration finishes. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are destroyed should not require a re-init unless explicitly + * provided by the user. + */ + virtual void postEval() = 0; +}; + +} // End namespace kp + +#include + +namespace kp { + +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * By default it enables the user to provide a dynamic number of tensors + * which are then passed as inputs. + */ +class OpAlgoDispatch : public OpBase +{ + public: + + OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck = false); + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpAlgoDispatch() override; + + /** + * This records the commands that are to be sent to the GPU. This includes + * the barriers that ensure the memory has been copied before going in and + * out of the shader, as well as the dispatch operation that sends the + * shader processing to the gpu. This function also records the GPU memory + * copy of the output data for the staging buffer so it can be read by the + * host. + */ + virtual void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Executes after the recorded commands are submitted, and performs a copy + * of the GPU Device memory into the staging buffer so the output data can + * be retrieved. + */ + virtual void postEval() override; + +private: + // -------------- ALWAYS OWNED RESOURCES + std::shared_ptr mAlgorithm; +}; + +} // End namespace kp + +namespace kp { + +/** + * Operation that performs multiplication on two tensors and outpus on third + * tensor. + */ +class OpMult : public OpAlgoDispatch +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + * @param komputeWorkgroup Optional parameter to specify the layout for processing + */ + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(algorithm, true) + { + KP_LOG_DEBUG("Kompute OpMult constructor with params"); + + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } + + std::vector spirv( + (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, + (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + + algorithm->rebuild(tensors, spirv); + } + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpMult() override { + KP_LOG_DEBUG("Kompute OpMult destructor started"); + } +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type +*/ +class OpTensorCopy : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorCopy(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorCopy() override; + + /** + * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Copies the local vectors for all the tensors to sync the data with the gpu. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncDevice : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncDevice(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncDevice() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Does not perform any postEval commands. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncLocal : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncLocal(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncLocal() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. + */ + void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * For host tensors it performs the map command from the host memory into local memory. + */ + virtual void postEval() override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + * Container of operations that can be sent to GPU as batch + */ +class Sequence: public std::enable_shared_from_this +{ + public: + /** + * Main constructor for sequence which requires core vulkan components to + * generate all dependent resources. + * + * @param physicalDevice Vulkan physical device + * @param device Vulkan logical device + * @param computeQueue Vulkan compute queue + * @param queueIndex Vulkan compute queue index in device + */ + Sequence(std::shared_ptr physicalDevice, + std::shared_ptr device, + std::shared_ptr computeQueue, + uint32_t queueIndex); + /** + * Destructor for sequence which is responsible for cleaning all subsequent + * owned operations. + */ + ~Sequence(); + + /** + */ + std::shared_ptr record(std::shared_ptr op); + + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + */ + template + std::shared_ptr + record(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->record(op); + } + template + std::shared_ptr + record(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->record(op); + } + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + std::shared_ptr eval(); + + std::shared_ptr eval(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + // TODO: Aim to have only a single function with tensors/algorithm + template + std::shared_ptr + eval(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + // TODO: Aim to be able to handle errors when returning without throw except + return this->eval(op); + } + // Needded as otherise can't use initialiser list + template + std::shared_ptr + eval(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->eval(op); + } + + /** + * Eval Async sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. EvalAwait() must + * be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAsync(); + std::shared_ptr evalAsync(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + evalAsync(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->evalAsync(op); + } + // Needed as otherwise it's not possible to use initializer lists + template + std::shared_ptr + evalAsync(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->evalAsync(op); + } + + /** + * Eval Await waits for the fence to finish processing and then once it + * finishes, it runs the postEval of all operations. + * + * @param waitFor Number of milliseconds to wait before timing out. + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); + + /** + * Clear function clears all operations currently recorded and starts recording again. + */ + void clear(); + + /** + * Begins recording commands for commands to be submitted into the command + * buffer. + * + * @return Boolean stating whether execution was successful. + */ + void begin(); + + /** + * Ends the recording and stops recording commands when the record command + * is sent. + * + * @return Boolean stating whether execution was successful. + */ + void end(); + + /** + * Returns true if the sequence is currently in recording activated. + * + * @return Boolean stating if recording ongoing. + */ + bool isRecording(); + + bool isInit(); + + /** + * Returns true if the sequence is currently running - mostly used for async + * workloads. + * + * @return Boolean stating if currently running. + */ + bool isRunning(); + + /** + * Destroys and frees the GPU resources which include the buffer and memory + * and sets the sequence as init=False. + */ + void destroy(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + std::shared_ptr mComputeQueue = nullptr; + uint32_t mQueueIndex = -1; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mCommandPool = nullptr; + bool mFreeCommandPool = false; + std::shared_ptr mCommandBuffer = nullptr; + bool mFreeCommandBuffer = false; + + // -------------- ALWAYS OWNED RESOURCES + vk::Fence mFence; + std::vector> mOperations; + + // State + bool mRecording = false; + bool mIsRunning = false; + + // Create functions + void createCommandPool(); + void createCommandBuffer(); +}; + +} // End namespace kp + +#include +#include + +#define KP_DEFAULT_SESSION "DEFAULT" + +namespace kp { + +/** + Base orchestrator which creates and manages device and child components +*/ +class Manager +{ + public: + /** + Base constructor and default used which creates the base resources + including choosing the device 0 by default. + */ + Manager(); + + /** + * Similar to base constructor but allows the user to provide the device + * they would like to create the resources on. + * + * @param physicalDeviceIndex The index of the physical device to use + * @param manageResources (Optional) Whether to manage the memory of the + * resources created and destroy when the manager is destroyed. + * @param familyQueueIndices (Optional) List of queue indices to add for + * explicit allocation + * @param totalQueues The total number of compute queues to create. + */ + Manager(uint32_t physicalDeviceIndex, + const std::vector& familyQueueIndices = {}); + + /** + * Manager constructor which allows your own vulkan application to integrate + * with the vulkan kompute use. + * + * @param instance Vulkan compute instance to base this application + * @param physicalDevice Vulkan physical device to use for application + * @param device Vulkan logical device to use for all base resources + * @param physicalDeviceIndex Index for vulkan physical device used + */ + Manager(std::shared_ptr instance, + std::shared_ptr physicalDevice, + std::shared_ptr device); + + /** + * Manager destructor which would ensure all owned resources are destroyed + * unless explicitly stated that resources should not be destroyed or freed. + */ + ~Manager(); + + /** + * Get or create a managed Sequence that will be contained by this manager. + * If the named sequence does not currently exist, it would be created and + * initialised. + * + * @param sequenceName The name for the named sequence to be retrieved or + * created + * @param queueIndex The queue to use from the available queues + * @return Shared pointer to the manager owned sequence resource + */ + std::shared_ptr sequence(uint32_t queueIndex = 0); + + /** + * Function that simplifies the common workflow of tensor creation and + * initialization. It will take the constructor parameters for a Tensor + * and will will us it to create a new Tensor and then create it. The + * tensor memory will then be managed and owned by the manager. + * + * @param data The data to initialize the tensor with + * @param tensorType The type of tensor to initialize + * @param syncDataToGPU Whether to sync the data to GPU memory + * @returns Initialized Tensor with memory Syncd to GPU device + */ + std::shared_ptr tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + + std::shared_ptr algorithm( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); + + void destroy(); + void clear(); + + private: + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mInstance = nullptr; + bool mFreeInstance = false; + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + bool mFreeDevice = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector> mManagedTensors; + std::vector> mManagedSequences; + std::vector> mManagedAlgorithms; + + std::vector mComputeQueueFamilyIndices; + std::vector> mComputeQueues; + + bool mManageResources = false; + +#if DEBUG +#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS + vk::DebugReportCallbackEXT mDebugReportCallback; + vk::DispatchLoaderDynamic mDebugDispatcher; +#endif +#endif + + // Create functions + void createInstance(); + void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); +}; + +} // End namespace kp diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 6215dd090..4e532e269 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -126,16 +126,19 @@ Algorithm::destroy() { this->mShaderModule = nullptr; } - if (this->mFreeDescriptorSet) { - KP_LOG_DEBUG("Kompute Algorithm Freeing Descriptor Set"); - if (!this->mDescriptorSet) { - KP_LOG_WARN( - "Kompute Algorithm Error requested to free descriptor set"); - } - this->mDevice->freeDescriptorSets( - *this->mDescriptorPool, 1, this->mDescriptorSet.get()); - this->mDescriptorSet = nullptr; - } + // We don't call freeDescriptorSet as the descriptor pool is not created with + // VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT more at + // (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-vkFreeDescriptorSets-descriptorPool-00312)) + //if (this->mFreeDescriptorSet) { + // KP_LOG_DEBUG("Kompute Algorithm Freeing Descriptor Set"); + // if (!this->mDescriptorSet) { + // KP_LOG_WARN( + // "Kompute Algorithm Error requested to free descriptor set"); + // } + // this->mDevice->freeDescriptorSets( + // *this->mDescriptorPool, 1, this->mDescriptorSet.get()); + // this->mDescriptorSet = nullptr; + //} if (this->mFreeDescriptorSetLayout) { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Set Layout"); diff --git a/src/Manager.cpp b/src/Manager.cpp index ea152f1a8..92ffaea9c 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -346,8 +346,7 @@ Manager::createDevice(const std::vector& familyQueueIndices, uint32_t std::shared_ptr Manager::tensor( const std::vector& data, - Tensor::TensorTypes tensorType, - bool syncDataToGPU) + Tensor::TensorTypes tensorType) { KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 0e97f12fa..f963d5f5a 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -7,8 +7,6 @@ #include "kompute/Sequence.hpp" -#include "kompute/operations/OpTensorSyncDevice.hpp" - #define KP_DEFAULT_SESSION "DEFAULT" namespace kp { @@ -83,8 +81,7 @@ class Manager */ std::shared_ptr tensor( const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice, - bool syncDataToGPU = true); + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); std::shared_ptr algorithm( const std::vector>& tensors = {}, diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index e61c3166d..d80c6bd91 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,7 +17,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoInit = false); + OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck = false); /** * Default destructor, which is in charge of destroying the algorithm diff --git a/src/include/kompute/shaders/shaderlogisticregression.hpp b/src/include/kompute/shaders/shaderlogisticregression.hpp index f456ac1cf..5293a3593 100755 --- a/src/include/kompute/shaders/shaderlogisticregression.hpp +++ b/src/include/kompute/shaders/shaderlogisticregression.hpp @@ -23,411 +23,411 @@ namespace kp { namespace shader_data { -static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; +static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; } } #endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP diff --git a/src/include/kompute/shaders/shaderopmult.hpp b/src/include/kompute/shaders/shaderopmult.hpp index f4015a58f..0954a23dd 100755 --- a/src/include/kompute/shaders/shaderopmult.hpp +++ b/src/include/kompute/shaders/shaderopmult.hpp @@ -23,131 +23,131 @@ namespace kp { namespace shader_data { -static const unsigned char shaders_glsl_opmult_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, - 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; +static const unsigned char shaders_glsl_opmult_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, + 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; } } #endif // define SHADEROP_SHADEROPMULT_HPP diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp index 342861429..18ab913e7 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp @@ -23,411 +23,411 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_logistic_regression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_logistic_regression_comp_spv_len = 4816; +static const unsigned char test_shaders_glsl_test_logistic_regression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_logistic_regression_comp_spv_len = 4816; } } #endif // define SHADEROP_SHADERTEST_LOGISTIC_REGRESSION_HPP diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp index edbf2eed7..e4acc7026 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp @@ -23,101 +23,101 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_op_custom_shader_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x62, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_op_custom_shader_comp_spv_len = 1096; +static const unsigned char test_shaders_glsl_test_op_custom_shader_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x62, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_op_custom_shader_comp_spv_len = 1096; } } #endif // define SHADEROP_SHADERTEST_OP_CUSTOM_SHADER_HPP diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp index 15d623478..4b0bc4572 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp @@ -23,126 +23,126 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_workgroup_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x57, 0x6f, 0x72, 0x6b, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x4e, 0x75, 0x6d, 0x57, 0x6f, - 0x72, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x75, 0x74, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, 0x32, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x74, 0x6f, 0x75, 0x74, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_workgroup_comp_spv_len = 1396; +static const unsigned char test_shaders_glsl_test_workgroup_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x57, 0x6f, 0x72, 0x6b, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x4e, 0x75, 0x6d, 0x57, 0x6f, + 0x72, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x75, 0x74, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, 0x32, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x74, 0x6f, 0x75, 0x74, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_workgroup_comp_spv_len = 1396; } } #endif // define SHADEROP_SHADERTEST_WORKGROUP_HPP diff --git a/test/shaders/glsl/test_logistic_regression.comp.spv b/test/shaders/glsl/test_logistic_regression.comp.spv index 2f6883dac6c1f40a2928ccf8e8b37c6fa6d68ac1..f68cb431d7d9e54391a59df577f558b3aa1f12f1 100755 GIT binary patch delta 18 ZcmcbhdO?+wnMs+Qfq{{MV Date: Sun, 28 Feb 2021 07:57:36 +0000 Subject: [PATCH 18/91] All python tests pass --- python/src/main.cpp | 26 ++- python/test/test_array_multiplication.py | 23 +- python/test/test_kompute.py | 16 +- python/test/test_logistic_regression.py | 34 ++- single_include/AggregateHeaders.cpp | 2 +- single_include/kompute/Kompute.hpp | 200 +++++++++--------- src/Algorithm.cpp | 14 +- src/Manager.cpp | 2 - src/OpAlgoDispatch.cpp | 6 +- .../kompute/operations/OpAlgoDispatch.hpp | 2 +- src/include/kompute/operations/OpMult.hpp | 2 +- 11 files changed, 158 insertions(+), 169 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index cb538112b..36be7ac7a 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -54,17 +54,20 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "OpBase"); - py::class_>(m, "OpTensorSyncDevice") + py::class_>(m, "OpTensorSyncDevice", py::base()) .def(py::init>&>()); - py::class_>(m, "OpTensorSyncLocal") + py::class_>(m, "OpTensorSyncLocal", py::base()) .def(py::init>&>()); - py::class_>(m, "OpTensorCopy") + py::class_>(m, "OpTensorCopy", py::base()) .def(py::init>&>()); - py::class_>(m, "OpAlgoDispatch") - .def(py::init&, bool>()); + py::class_>(m, "OpAlgoDispatch", py::base()) + .def(py::init&>()); + + py::class_>(m, "OpMult", py::base()) + .def(py::init>&,const std::shared_ptr&>()); py::class_>(m, "Algorithm") .def("get_tensors", &kp::Algorithm::getTensors) @@ -112,8 +115,7 @@ PYBIND11_MODULE(kp, m) { .def("__len__", &kp::Tensor::size, "Retrieves the size of the Tensor data as per the local Tensor memory.") .def("tensor_type", &kp::Tensor::tensorType, "Retreves the memory type of the tensor.") .def("is_init", &kp::Tensor::isInit, "Checks whether the tensor GPU memory has been initialised.") - .def("map_data_from_host", &kp::Tensor::mapDataFromHostMemory, "Maps data into GPU memory from tensor local data.") - .def("map_data_into_host", &kp::Tensor::mapDataIntoHostMemory, "Maps data from GPU memory into tensor local data."); + .def("destroy", &kp::Tensor::destroy, "Destroy tensor GPU resources."); py::class_>(m, "Sequence") .def("record", [](kp::Sequence& self, std::shared_ptr op) { return self.record(op); }) @@ -147,15 +149,17 @@ PYBIND11_MODULE(kp, m) { .def("algorithm", [](kp::Manager& self, const std::vector>& tensors, const py::bytes& spirv, - const kp::Workgroup& workgroup = {}, - const kp::Constants& spec_consts = {}, - const kp::Constants& push_consts = {}) { + const kp::Workgroup& workgroup, + const kp::Constants& spec_consts, + const kp::Constants& push_consts) { py::buffer_info info(py::buffer(spirv).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); std::vector spirvVec((uint32_t*)data, (uint32_t*)(data + length)); return self.algorithm(tensors, spirvVec, workgroup, spec_consts, push_consts); - }); + }, + "Algorithm initialisation function", + py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), py::arg("push_consts") = kp::Constants()); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/python/test/test_array_multiplication.py b/python/test/test_array_multiplication.py index bcad405a6..55d764805 100644 --- a/python/test/test_array_multiplication.py +++ b/python/test/test_array_multiplication.py @@ -9,29 +9,26 @@ def test_array_multiplication(): mgr = kp.Manager() # 2. Create Kompute Tensors to hold data - tensor_in_a = kp.Tensor([2, 2, 2]) - tensor_in_b = kp.Tensor([1, 2, 3]) - tensor_out = kp.Tensor([0, 0, 0]) + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out = mgr.tensor([0, 0, 0]) - # 3. Initialise the Kompute Tensors in the GPU - mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) + params = [tensor_in_a, tensor_in_b, tensor_out] # 4. Define the multiplication shader code to run on the GPU @ps.python2shader - def compute_shader_multiply(index=("input", "GlobalInvocationId", ps.ivec3), + def compute_mult(index=("input", "GlobalInvocationId", ps.ivec3), data1=("buffer", 0, ps.Array(ps.f32)), data2=("buffer", 1, ps.Array(ps.f32)), data3=("buffer", 2, ps.Array(ps.f32))): i = index.x data3[i] = data1[i] * data2[i] - # 5. Run shader code against our previously defined tensors - mgr.eval_algo_data_def( - [tensor_in_a, tensor_in_b, tensor_out], - compute_shader_multiply.to_spirv()) - - # 6. Sync tensor data from GPU back to local - mgr.eval_tensor_sync_local_def([tensor_out]) + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, compute_mult.to_spirv()))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) assert tensor_out.data() == [2.0, 4.0, 6.0] assert np.all(tensor_out.numpy() == [2.0, 4.0, 6.0]) diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index e923c6393..ad4b77391 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -7,6 +7,8 @@ import pyshader as ps DIRNAME = os.path.dirname(os.path.abspath(__file__)) +kp_log = logging.getLogger("kp") + # TODO: Add example with file #def test_opalgobase_file(): # """ @@ -62,9 +64,9 @@ void main() algo = mgr.algorithm(params, spirv) (mgr.sequence() - .record(kp.OpTensorSyncLocal(params)) - .record(kp.OpAlgoDispatch(algo)) .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(algo)) + .record(kp.OpTensorSyncLocal(params)) .eval()) assert tensor_out.data() == [2.0, 4.0, 6.0] @@ -102,9 +104,9 @@ def test_sequence(): sq = mgr.sequence() - sq.record(kp.OpTensorSyncLocal(params)) - sq.record(kp.OpAlgoDispatch(algo)) sq.record(kp.OpTensorSyncDevice(params)) + sq.record(kp.OpAlgoDispatch(algo)) + sq.record(kp.OpTensorSyncLocal(params)) sq.eval() @@ -141,16 +143,14 @@ def test_workgroup(): data1[i] = f32(gl_idx.x) data2[i] = f32(gl_idx.y) - algo = mgr.algorithm([tensor_a, tensor_b], compute_shader_wg.to_spirv(), (16,8,1), [], []) + algo = mgr.algorithm([tensor_a, tensor_b], compute_shader_wg.to_spirv(), (16,8,1)) (mgr.sequence() .record(kp.OpTensorSyncDevice([tensor_a, tensor_b])) .record(kp.OpAlgoDispatch(algo)) - .record(kp.OpAlgoTensorSyncLocal([tensor_a, tensor_b])) + .record(kp.OpTensorSyncLocal([tensor_a, tensor_b])) .eval()) - assert sq.is_init() == False - print(tensor_a.numpy()) print(tensor_b.numpy()) diff --git a/python/test/test_logistic_regression.py b/python/test/test_logistic_regression.py index 6783bbc87..4bd0c28fa 100644 --- a/python/test/test_logistic_regression.py +++ b/python/test/test_logistic_regression.py @@ -46,45 +46,39 @@ def test_logistic_regression(): mgr = kp.Manager(0) # First we create input and ouput tensors for shader - tensor_x_i = kp.Tensor([0.0, 1.0, 1.0, 1.0, 1.0]) - tensor_x_j = kp.Tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_x_i = mgr.tensor([0.0, 1.0, 1.0, 1.0, 1.0]) + tensor_x_j = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) - tensor_y = kp.Tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_y = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) - tensor_w_in = kp.Tensor([0.001, 0.001]) - tensor_w_out_i = kp.Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_w_out_j = kp.Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_w_in = mgr.tensor([0.001, 0.001]) + tensor_w_out_i = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_w_out_j = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_b_in = kp.Tensor([0.0]) - tensor_b_out = kp.Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_b_in = mgr.tensor([0.0]) + tensor_b_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_l_out = kp.Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_l_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_m = kp.Tensor([ tensor_y.size() ]) + tensor_m = mgr.tensor([ tensor_y.size() ]) # We store them in an array for easier interaction params = [tensor_x_i, tensor_x_j, tensor_y, tensor_w_in, tensor_w_out_i, tensor_w_out_j, tensor_b_in, tensor_b_out, tensor_l_out, tensor_m] - mgr.rebuild(params) + mgr.sequence().eval(kp.OpTensorSyncDevice(params)) # Create a managed sequence sq = mgr.sequence() - # Clear previous operations and begin recording for new operations - sq.begin() - # Record operation to sync memory from local to GPU memory - sq.record_tensor_sync_device([tensor_w_in, tensor_b_in]) + sq.record(kp.OpTensorSyncDevice([tensor_w_in, tensor_b_in])) # Record operation to execute GPU shader against all our parameters - sq.record_algo_data(params, compute_shader.to_spirv()) + sq.record(kp.OpAlgoDispatch(mgr.algorithm(params, compute_shader.to_spirv()))) # Record operation to sync memory from GPU to local memory - sq.record_tensor_sync_local([tensor_w_out_i, tensor_w_out_j, tensor_b_out, tensor_l_out]) - - # Stop recording operations - sq.end() + sq.record(kp.OpTensorSyncLocal([tensor_w_out_i, tensor_w_out_j, tensor_b_out, tensor_l_out])) ITERATIONS = 100 learning_rate = 0.1 diff --git a/single_include/AggregateHeaders.cpp b/single_include/AggregateHeaders.cpp index 23372873b..25dc04edb 100644 --- a/single_include/AggregateHeaders.cpp +++ b/single_include/AggregateHeaders.cpp @@ -6,10 +6,10 @@ #include "kompute/Tensor.hpp" #include "kompute/Algorithm.hpp" #include "kompute/operations/OpBase.hpp" -#include "kompute/operations/OpMult.hpp" #include "kompute/operations/OpTensorCopy.hpp" #include "kompute/operations/OpTensorSyncDevice.hpp" #include "kompute/operations/OpTensorSyncLocal.hpp" #include "kompute/operations/OpAlgoDispatch.hpp" +#include "kompute/operations/OpMult.hpp" #include "kompute/Sequence.hpp" #include "kompute/Manager.hpp" diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index b1d278081..16c4d6266 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1247,106 +1247,6 @@ class OpBase } // End namespace kp -#include - -namespace kp { - -/** - * Operation that provides a general abstraction that simplifies the use of - * algorithm and parameter components which can be used with shaders. - * By default it enables the user to provide a dynamic number of tensors - * which are then passed as inputs. - */ -class OpAlgoDispatch : public OpBase -{ - public: - - OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck = false); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoDispatch() override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - -private: - // -------------- ALWAYS OWNED RESOURCES - std::shared_ptr mAlgorithm; -}; - -} // End namespace kp - -namespace kp { - -/** - * Operation that performs multiplication on two tensors and outpus on third - * tensor. - */ -class OpMult : public OpAlgoDispatch -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm, true) - { - KP_LOG_DEBUG("Kompute OpMult constructor with params"); - - if (tensors.size() != 3) { - throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); - } - - std::vector spirv( - (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, - (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + - kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - - algorithm->rebuild(tensors, spirv); - } - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpMult() override { - KP_LOG_DEBUG("Kompute OpMult destructor started"); - } -}; - -} // End namespace kp - namespace kp { /** @@ -1484,6 +1384,106 @@ class OpTensorSyncLocal : public OpBase namespace kp { +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * By default it enables the user to provide a dynamic number of tensors + * which are then passed as inputs. + */ +class OpAlgoDispatch : public OpBase +{ + public: + + OpAlgoDispatch(const std::shared_ptr& algorithm); + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpAlgoDispatch() override; + + /** + * This records the commands that are to be sent to the GPU. This includes + * the barriers that ensure the memory has been copied before going in and + * out of the shader, as well as the dispatch operation that sends the + * shader processing to the gpu. This function also records the GPU memory + * copy of the output data for the staging buffer so it can be read by the + * host. + */ + virtual void record(std::shared_ptr commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Executes after the recorded commands are submitted, and performs a copy + * of the GPU Device memory into the staging buffer so the output data can + * be retrieved. + */ + virtual void postEval() override; + +private: + // -------------- ALWAYS OWNED RESOURCES + std::shared_ptr mAlgorithm; +}; + +} // End namespace kp + +#include + +namespace kp { + +/** + * Operation that performs multiplication on two tensors and outpus on third + * tensor. + */ +class OpMult : public OpAlgoDispatch +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + * @param komputeWorkgroup Optional parameter to specify the layout for processing + */ + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(algorithm) + { + KP_LOG_DEBUG("Kompute OpMult constructor with params"); + + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } + + std::vector spirv( + (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, + (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + + algorithm->rebuild(tensors, spirv); + } + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpMult() override { + KP_LOG_DEBUG("Kompute OpMult destructor started"); + } +}; + +} // End namespace kp + +namespace kp { + /** * Container of operations that can be sent to GPU as batch */ diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 4e532e269..f8ee78e3e 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -78,7 +78,7 @@ Algorithm::destroy() { return; } - if (this->mFreePipeline) { + if (this->mFreePipeline && this->mPipeline) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline"); if (!this->mPipeline) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " @@ -90,7 +90,7 @@ Algorithm::destroy() { this->mPipeline = nullptr; } - if (this->mFreePipelineCache) { + if (this->mFreePipelineCache && this->mPipelineCache) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline cache"); if (!this->mPipelineCache) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " @@ -102,7 +102,7 @@ Algorithm::destroy() { this->mPipelineCache = nullptr; } - if (this->mFreePipelineLayout) { + if (this->mFreePipelineLayout && this->mPipelineLayout) { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline layout"); if (!this->mPipelineLayout) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " @@ -114,7 +114,7 @@ Algorithm::destroy() { this->mPipelineLayout = nullptr; } - if (this->mFreeShaderModule) { + if (this->mFreeShaderModule && this->mShaderModule) { KP_LOG_DEBUG("Kompute Algorithm Destroying shader module"); if (!this->mShaderModule) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy shader " @@ -129,7 +129,7 @@ Algorithm::destroy() { // We don't call freeDescriptorSet as the descriptor pool is not created with // VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT more at // (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-vkFreeDescriptorSets-descriptorPool-00312)) - //if (this->mFreeDescriptorSet) { + //if (this->mFreeDescriptorSet && this->mDescriptorSet) { // KP_LOG_DEBUG("Kompute Algorithm Freeing Descriptor Set"); // if (!this->mDescriptorSet) { // KP_LOG_WARN( @@ -140,7 +140,7 @@ Algorithm::destroy() { // this->mDescriptorSet = nullptr; //} - if (this->mFreeDescriptorSetLayout) { + if (this->mFreeDescriptorSetLayout && this->mDescriptorSetLayout) { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Set Layout"); if (!this->mDescriptorSetLayout) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " @@ -152,7 +152,7 @@ Algorithm::destroy() { this->mDescriptorSetLayout = nullptr; } - if (this->mFreeDescriptorPool) { + if (this->mFreeDescriptorPool && this->mDescriptorPool) { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Pool"); if (!this->mDescriptorPool) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " diff --git a/src/Manager.cpp b/src/Manager.cpp index 92ffaea9c..2ee94a2ee 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -4,8 +4,6 @@ #include "kompute/Manager.hpp" -#include "kompute/operations/OpAlgoDispatch.hpp" - namespace kp { #if DEBUG diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 09050fba0..a20900189 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -4,14 +4,10 @@ namespace kp { -OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck) +OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); - if (!skipAlgoCheck && !algorithm->isInit()) { - throw std::runtime_error("Kompute OpAlgoDispatch constructor with non initialised algorithm"); - } - this->mAlgorithm = algorithm; } diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index d80c6bd91..1b5ab1bf0 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,7 +17,7 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::shared_ptr& algorithm, bool skipAlgoCheck = false); + OpAlgoDispatch(const std::shared_ptr& algorithm); /** * Default destructor, which is in charge of destroying the algorithm diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index fea38bdee..992b0e8a0 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -33,7 +33,7 @@ class OpMult : public OpAlgoDispatch * @param komputeWorkgroup Optional parameter to specify the layout for processing */ OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm, true) + : OpAlgoDispatch(algorithm) { KP_LOG_DEBUG("Kompute OpMult constructor with params"); From 7dc1f35206e3e602420d1c294c97a77bed9d8df5 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 28 Feb 2021 13:59:01 +0000 Subject: [PATCH 19/91] Added support for push constants --- shaders/glsl/logisticregression.comp.spv | Bin 4816 -> 4816 bytes shaders/glsl/opmult.comp.spv | Bin 1464 -> 1464 bytes single_include/kompute/Kompute.hpp | 3723 +++++++++-------- src/Algorithm.cpp | 48 +- src/Manager.cpp | 6 +- src/OpAlgoDispatch.cpp | 12 +- src/OpTensorCopy.cpp | 6 +- src/OpTensorSyncDevice.cpp | 6 +- src/OpTensorSyncLocal.cpp | 6 +- src/Sequence.cpp | 6 +- src/Tensor.cpp | 20 +- src/include/kompute/Algorithm.hpp | 14 +- src/include/kompute/Manager.hpp | 3 +- src/include/kompute/Tensor.hpp | 10 +- .../kompute/operations/OpAlgoDispatch.hpp | 10 +- src/include/kompute/operations/OpBase.hpp | 6 +- .../kompute/operations/OpTensorCopy.hpp | 6 +- .../kompute/operations/OpTensorSyncDevice.hpp | 6 +- .../kompute/operations/OpTensorSyncLocal.hpp | 6 +- .../shaders/shaderlogisticregression.hpp | 810 ++-- src/include/kompute/shaders/shaderopmult.hpp | 250 +- test/TestPushConstant.cpp | 47 + .../shadertest_logistic_regression.hpp | 810 ++-- .../shaders/shadertest_op_custom_shader.hpp | 190 +- .../shaders/shadertest_workgroup.hpp | 240 +- .../glsl/test_logistic_regression.comp.spv | Bin 4816 -> 4816 bytes .../glsl/test_op_custom_shader.comp.spv | Bin 1096 -> 1096 bytes test/shaders/glsl/test_workgroup.comp.spv | Bin 1396 -> 1396 bytes 28 files changed, 3151 insertions(+), 3090 deletions(-) create mode 100644 test/TestPushConstant.cpp diff --git a/shaders/glsl/logisticregression.comp.spv b/shaders/glsl/logisticregression.comp.spv index f68cb431d7d9e54391a59df577f558b3aa1f12f1..2f6883dac6c1f40a2928ccf8e8b37c6fa6d68ac1 100755 GIT binary patch delta 18 ZcmcbhdO?+wnMs+Qfq{{MYa{1zApj?i1Hk|Q delta 18 ZcmcbhdO?+wnMs+Qfq{{MV -#include -// VK_NO_PROTOTYPES required before vulkan import but after wrapper.hpp -#undef VK_NO_PROTOTYPES -static const char* KOMPUTE_LOG_TAG = "KomputeLog"; -#endif - -#include - -#include - -// Typedefs to simplify interaction with core types -namespace kp { -typedef std::array Workgroup; -typedef std::vector Constants; -} - -// Must be after vulkan is included -#ifndef KOMPUTE_VK_API_VERSION -#ifndef KOMPUTE_VK_API_MAJOR_VERSION -#define KOMPUTE_VK_API_MAJOR_VERSION 1 -#endif // KOMPUTE_VK_API_MAJOR_VERSION -#ifndef KOMPUTE_VK_API_MINOR_VERSION -#define KOMPUTE_VK_API_MINOR_VERSION 1 -#endif // KOMPUTE_VK_API_MINOR_VERSION -#define KOMPUTE_VK_API_VERSION \ - VK_MAKE_VERSION( \ - KOMPUTE_VK_API_MAJOR_VERSION, KOMPUTE_VK_API_MINOR_VERSION, 0) -#endif // KOMPUTE_VK_API_VERSION - -// SPDLOG_ACTIVE_LEVEL must be defined before spdlog.h import -#ifndef SPDLOG_ACTIVE_LEVEL -#if DEBUG -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG -#else -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO -#endif -#endif - -#if defined(KOMPUTE_BUILD_PYTHON) -#include -namespace py = pybind11; -// from python/src/main.cpp -extern py::object kp_debug, kp_info, kp_warning, kp_error; -#endif - -#ifndef KOMPUTE_LOG_OVERRIDE -#if KOMPUTE_ENABLE_SPDLOG -#include -#define KP_LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) -#define KP_LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) -#define KP_LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) -#define KP_LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) -#else -#include -#if SPDLOG_ACTIVE_LEVEL > 1 -#define KP_LOG_DEBUG(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_DEBUG(...) \ - ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 1 - -#if SPDLOG_ACTIVE_LEVEL > 2 -#define KP_LOG_INFO(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_INFO(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 2 - -#if SPDLOG_ACTIVE_LEVEL > 3 -#define KP_LOG_WARN(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_WARN(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 3 - -#if SPDLOG_ACTIVE_LEVEL > 4 -#define KP_LOG_ERROR(...) -#else -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_ERROR(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) -#elif defined(KOMPUTE_BUILD_PYTHON) -#define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) -#else -#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) -#endif // VK_USE_PLATFORM_ANDROID_KHR -#endif // SPDLOG_ACTIVE_LEVEL > 4 -#endif // KOMPUTE_SPDLOG_ENABLED -#endif // KOMPUTE_LOG_OVERRIDE - -#if !defined(KOMPUTE_DISABLE_SHADER_UTILS) || !KOMPUTE_DISABLE_SHADER_UTILS -#include -#include - -#include -#include -#include - -namespace kp { - -// The default resource limit for the GLSL compiler, can be overwritten -// Has been adobted by: -// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp -const TBuiltInResource defaultResource = { -/* .MaxLights = */ 0, -/* .MaxClipPlanes = */ 0, -/* .MaxTextureUnits = */ 0, -/* .MaxTextureCoords = */ 0, -/* .MaxVertexAttribs = */ 64, -/* .MaxVertexUniformComponents = */ 4096, -/* .MaxVaryingFloats = */ 64, -/* .MaxVertexTextureImageUnits = */ 0, -/* .MaxCombinedTextureImageUnits = */ 0, -/* .MaxTextureImageUnits = */ 0, -/* .MaxFragmentUniformComponents = */ 0, -/* .MaxDrawBuffers = */ 0, -/* .MaxVertexUniformVectors = */ 128, -/* .MaxVaryingVectors = */ 8, -/* .MaxFragmentUniformVectors = */ 0, -/* .MaxVertexOutputVectors = */ 16, -/* .MaxFragmentInputVectors = */ 0, -/* .MinProgramTexelOffset = */ -8, -/* .MaxProgramTexelOffset = */ 7, -/* .MaxClipDistances = */ 8, -/* .MaxComputeWorkGroupCountX = */ 65535, -/* .MaxComputeWorkGroupCountY = */ 65535, -/* .MaxComputeWorkGroupCountZ = */ 65535, -/* .MaxComputeWorkGroupSizeX = */ 1024, -/* .MaxComputeWorkGroupSizeY = */ 1024, -/* .MaxComputeWorkGroupSizeZ = */ 64, -/* .MaxComputeUniformComponents = */ 1024, -/* .MaxComputeTextureImageUnits = */ 16, -/* .MaxComputeImageUniforms = */ 8, -/* .MaxComputeAtomicCounters = */ 8, -/* .MaxComputeAtomicCounterBuffers = */ 1, -/* .MaxVaryingComponents = */ 60, -/* .MaxVertexOutputComponents = */ 64, -/* .MaxGeometryInputComponents = */ 64, -/* .MaxGeometryOutputComponents = */ 128, -/* .MaxFragmentInputComponents = */ 0, -/* .MaxImageUnits = */ 0, -/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, -/* .MaxCombinedShaderOutputResources = */ 8, -/* .MaxImageSamples = */ 0, -/* .MaxVertexImageUniforms = */ 0, -/* .MaxTessControlImageUniforms = */ 0, -/* .MaxTessEvaluationImageUniforms = */ 0, -/* .MaxGeometryImageUniforms = */ 0, -/* .MaxFragmentImageUniforms = */ 0, -/* .MaxCombinedImageUniforms = */ 0, -/* .MaxGeometryTextureImageUnits = */ 0, -/* .MaxGeometryOutputVertices = */ 256, -/* .MaxGeometryTotalOutputComponents = */ 1024, -/* .MaxGeometryUniformComponents = */ 1024, -/* .MaxGeometryVaryingComponents = */ 64, -/* .MaxTessControlInputComponents = */ 128, -/* .MaxTessControlOutputComponents = */ 128, -/* .MaxTessControlTextureImageUnits = */ 0, -/* .MaxTessControlUniformComponents = */ 1024, -/* .MaxTessControlTotalOutputComponents = */ 4096, -/* .MaxTessEvaluationInputComponents = */ 128, -/* .MaxTessEvaluationOutputComponents = */ 128, -/* .MaxTessEvaluationTextureImageUnits = */ 16, -/* .MaxTessEvaluationUniformComponents = */ 1024, -/* .MaxTessPatchComponents = */ 120, -/* .MaxPatchVertices = */ 32, -/* .MaxTessGenLevel = */ 64, -/* .MaxViewports = */ 16, -/* .MaxVertexAtomicCounters = */ 0, -/* .MaxTessControlAtomicCounters = */ 0, -/* .MaxTessEvaluationAtomicCounters = */ 0, -/* .MaxGeometryAtomicCounters = */ 0, -/* .MaxFragmentAtomicCounters = */ 0, -/* .MaxCombinedAtomicCounters = */ 8, -/* .MaxAtomicCounterBindings = */ 1, -/* .MaxVertexAtomicCounterBuffers = */ 0, -/* .MaxTessControlAtomicCounterBuffers = */ 0, -/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, -/* .MaxGeometryAtomicCounterBuffers = */ 0, -/* .MaxFragmentAtomicCounterBuffers = */ 0, -/* .MaxCombinedAtomicCounterBuffers = */ 1, -/* .MaxAtomicCounterBufferSize = */ 16384, -/* .MaxTransformFeedbackBuffers = */ 4, -/* .MaxTransformFeedbackInterleavedComponents = */ 64, -/* .MaxCullDistances = */ 8, -/* .MaxCombinedClipAndCullDistances = */ 8, -/* .MaxSamples = */ 4, -/* .maxMeshOutputVerticesNV = */ 256, -/* .maxMeshOutputPrimitivesNV = */ 512, -/* .maxMeshWorkGroupSizeX_NV = */ 32, -/* .maxMeshWorkGroupSizeY_NV = */ 1, -/* .maxMeshWorkGroupSizeZ_NV = */ 1, -/* .maxTaskWorkGroupSizeX_NV = */ 32, -/* .maxTaskWorkGroupSizeY_NV = */ 1, -/* .maxTaskWorkGroupSizeZ_NV = */ 1, -/* .maxMeshViewCountNV = */ 4, -/* .maxDualSourceDrawBuffersEXT = */ 1, - -/* .limits = */ { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, -}}; - -/** - Shader utily class with functions to compile and process glsl files. -*/ -class Shader { -public: - /** - * Compile multiple sources with optional filenames. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. - * - * @param sources A list of raw glsl shaders in string format - * @param files A list of file names respective to each of the sources - * @param entryPoint The function name to use as entry point - * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler - * @return The compiled SPIR-V binary in unsigned int32 format - */ - static std::vector compile_sources( - const std::vector& sources, - const std::vector& files = {}, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - - /** - * Compile a single glslang source from string value. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. - * - * @param source An individual raw glsl shader in string format - * @param entryPoint The function name to use as entry point - * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler - * @return The compiled SPIR-V binary in unsigned int32 format - */ - static std::vector compile_source( - const std::string& source, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - -}; - -} -#endif // DKOMPUTE_DISABLE_SHADER_UTILS - -namespace kp { - -/** - * Structured data used in GPU operations. - * - * Tensors are the base building block in Kompute to perform operations across - * GPUs. Each tensor would have a respective Vulkan memory and buffer, which - * would be used to store their respective data. The tensors can be used for GPU - * data storage or transfer. - */ -class Tensor -{ - public: - /** - * Type for tensors created: Device allows memory to be transferred from - * staging buffers. Staging are host memory visible. Storage are device - * visible but are not set up to transfer or receive data (only for shader - * storage). - */ - enum class TensorTypes - { - eDevice = 0, ///< Type is device memory, source and destination - eHost = 1, ///< Type is host memory, source and destination - eStorage = 2, ///< Type is Device memory (only) - }; - - /** - * Default constructor with data provided which would be used to create the - * respective vulkan buffer and memory. - * - * @param data Non-zero-sized vector of data that will be used by the - * tensor - * @param tensorType Type for the tensor which is of type TensorTypes - */ - Tensor(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice); - - /** - * Destructor which is in charge of freeing vulkan resources unless they - * have been provided externally. - */ - ~Tensor(); - - /** - * Initialiser which calls the initialisation for all the respective tensors - * as well as creates the respective staging tensors. The staging tensors - * would only be created for the tensors of type TensorType::eDevice as - * otherwise there is no need to copy from host memory. - */ - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); - - /** - * Destroys and frees the GPU resources which include the buffer and memory. - */ - void destroy(); - - bool isInit(); - - /** - * Returns the vector of data currently contained by the Tensor. It is - * important to ensure that there is no out-of-sync data with the GPU - * memory. - * - * @return Reference to vector of elements representing the data in the - * tensor. - */ - std::vector& data(); - /** - * Overrides the subscript operator to expose the underlying data's - * subscript operator which in this case would be its underlying - * vector's. - * - * @param i The index where the element will be returned from. - * @return Returns the element in the position requested. - */ - float& operator[](int index); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - uint32_t size(); - - /** - * Retrieve the tensor type of the Tensor - * - * @return Tensor type of tensor - */ - TensorTypes tensorType(); - - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setData(const std::vector& data); - - /** - * Records a copy from the memory of the tensor provided to the current - * thensor. This is intended to pass memory into a processing, to perform - * a staging buffer transfer, or to gather output (between others). - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param copyFromTensor Tensor to copy the data from - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFrom(std::shared_ptr commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier); - - /** - * Records a copy from the internal staging memory to the device memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromStagingToDevice( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records a copy from the internal device memory to the staging memory - * using an optional barrier to wait for the operation. This function would - * only be relevant for kp::Tensors of type eDevice. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. - */ - void recordCopyFromDeviceToStaging( - std::shared_ptr commandBuffer, - bool createBarrier); - - /** - * Records the buffer memory barrier into the command buffer which - * ensures that relevant data transfers are carried out correctly. - * - * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param srcAccessMask Access flags for source access mask - * @param dstAccessMask Access flags for destination access mask - * @param scrStageMask Pipeline stage flags for source stage mask - * @param dstStageMask Pipeline stage flags for destination stage mask - */ - void recordBufferMemoryBarrier( - std::shared_ptr commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); - - /** - * Constructs a vulkan descriptor buffer info which can be used to specify - * and reference the underlying buffer component of the tensor without - * exposing it. - * - * @return Descriptor buffer info with own buffer - */ - vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice; - std::shared_ptr mDevice; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mPrimaryBuffer; - bool mFreePrimaryBuffer = false; - std::shared_ptr mStagingBuffer; - bool mFreeStagingBuffer = false; - std::shared_ptr mPrimaryMemory; - bool mFreePrimaryMemory = false; - std::shared_ptr mStagingMemory; - bool mFreeStagingMemory = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - - TensorTypes mTensorType = TensorTypes::eDevice; - - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer - void createBuffer(std::shared_ptr buffer, - vk::BufferUsageFlags bufferUsageFlags); - void allocateBindMemory(std::shared_ptr buffer, - std::shared_ptr memory, - vk::MemoryPropertyFlags memoryPropertyFlags); - void copyBuffer(std::shared_ptr commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); - - // Private util functions - vk::BufferUsageFlags getPrimaryBufferUsageFlags(); - vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); - vk::BufferUsageFlags getStagingBufferUsageFlags(); - vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - uint64_t memorySize(); -}; - -} // End namespace kp - -namespace kp { - -/** - Abstraction for compute shaders that are run on top of tensors grouped via - ParameterGroups (which group descriptorsets) -*/ -class Algorithm -{ -public: - - /** - * Default constructor for Algorithm - * - * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders - */ - Algorithm( - std::shared_ptr device, - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. - * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing - */ - void rebuild( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - /** - * Destructor for Algorithm which is responsible for freeing and desroying - * respective pipelines and owned parameter groups. - */ - ~Algorithm(); - - /** - * Records the dispatch function with the provided template parameters or - * alternatively using the size of the tensor by default. - * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value - */ - void recordDispatch(std::shared_ptr commandBuffer); - - bool isInit(); - - void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); - - const Workgroup& getWorkgroup(); - const Constants& getSpecializationConstants(); - const Constants& getPushConstants(); - const std::vector>& getTensors(); - - void destroy(); - -private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mDevice; - std::vector> mTensors; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mDescriptorSetLayout; - bool mFreeDescriptorSetLayout = false; - std::shared_ptr mDescriptorPool; - bool mFreeDescriptorPool = false; - std::shared_ptr mDescriptorSet; - bool mFreeDescriptorSet = false; - std::shared_ptr mShaderModule; - bool mFreeShaderModule = false; - std::shared_ptr mPipelineLayout; - bool mFreePipelineLayout = false; - std::shared_ptr mPipelineCache; - bool mFreePipelineCache = false; - std::shared_ptr mPipeline; - bool mFreePipeline = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector mSpirv; - Constants mSpecializationConstants; - Constants mPushConstants; - Workgroup mWorkgroup; - - bool mIsInit; - - // Create util functions - void createShaderModule(); - void createPipeline(); - - // Parameters - void createParameters(); -}; - -} // End namespace kp - -namespace kp { - -/** - * Base Operation which provides the high level interface that Kompute - * operations implement in order to perform a set of actions in the GPU. - * - * Operations can perform actions on tensors, and optionally can also own an - * Algorithm with respective parameters. kp::Operations with kp::Algorithms - * would inherit from kp::OpBaseAlgo. - */ -class OpBase -{ - public: - - /** - * Default destructor for OpBase class. This OpBase destructor class should - * always be called to destroy and free owned resources unless it is - * intended to destroy the resources in the parent class. - */ - virtual ~OpBase() - { - KP_LOG_DEBUG("Kompute OpBase destructor started"); - } - - /** - * The record function is intended to only send a record command or run - * commands that are expected to record operations that are to be submitted - * as a batch into the GPU. - */ - virtual void record(std::shared_ptr commandBuffer) = 0; - - /** - * Pre eval is called before the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any per-eval setup steps - * required as the computation iteration begins. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are created should be idempotent in case it's called multiple - * times in a row. - */ - virtual void preEval() = 0; - - /** - * Post eval is called after the Sequence has called eval and submitted the commands to - * the GPU for processing, and can be used to perform any tear-down steps - * required as the computation iteration finishes. It's worth noting that - * there are situations where eval can be called multiple times, so the - * resources that are destroyed should not require a re-init unless explicitly - * provided by the user. - */ - virtual void postEval() = 0; -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type -*/ -class OpTensorCopy : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorCopy(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorCopy() override; - - /** - * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Copies the local vectors for all the tensors to sync the data with the gpu. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncDevice : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncDevice(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncDevice() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Does not perform any postEval commands. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -namespace kp { - -/** - Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. -*/ -class OpTensorSyncLocal : public OpBase -{ - public: - /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - */ - OpTensorSyncLocal(const std::vector>& tensors); - - /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. - */ - ~OpTensorSyncLocal() override; - - /** - * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. - */ - void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * For host tensors it performs the map command from the host memory into local memory. - */ - virtual void postEval() override; - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector> mTensors; -}; - -} // End namespace kp - -namespace kp { - -/** - * Operation that provides a general abstraction that simplifies the use of - * algorithm and parameter components which can be used with shaders. - * By default it enables the user to provide a dynamic number of tensors - * which are then passed as inputs. - */ -class OpAlgoDispatch : public OpBase -{ - public: - - OpAlgoDispatch(const std::shared_ptr& algorithm); - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpAlgoDispatch() override; - - /** - * This records the commands that are to be sent to the GPU. This includes - * the barriers that ensure the memory has been copied before going in and - * out of the shader, as well as the dispatch operation that sends the - * shader processing to the gpu. This function also records the GPU memory - * copy of the output data for the staging buffer so it can be read by the - * host. - */ - virtual void record(std::shared_ptr commandBuffer) override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. - */ - virtual void postEval() override; - -private: - // -------------- ALWAYS OWNED RESOURCES - std::shared_ptr mAlgorithm; -}; - -} // End namespace kp - -#include - -namespace kp { - -/** - * Operation that performs multiplication on two tensors and outpus on third - * tensor. - */ -class OpMult : public OpAlgoDispatch -{ - public: - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing - */ - OpMult(std::vector> tensors, std::shared_ptr algorithm) - : OpAlgoDispatch(algorithm) - { - KP_LOG_DEBUG("Kompute OpMult constructor with params"); - - if (tensors.size() != 3) { - throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); - } - - std::vector spirv( - (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, - (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + - kp::shader_data::shaders_glsl_opmult_comp_spv_len)); - - algorithm->rebuild(tensors, spirv); - } - - /** - * Default destructor, which is in charge of destroying the algorithm - * components but does not destroy the underlying tensors - */ - virtual ~OpMult() override { - KP_LOG_DEBUG("Kompute OpMult destructor started"); - } -}; - -} // End namespace kp - -namespace kp { - -/** - * Container of operations that can be sent to GPU as batch - */ -class Sequence: public std::enable_shared_from_this -{ - public: - /** - * Main constructor for sequence which requires core vulkan components to - * generate all dependent resources. - * - * @param physicalDevice Vulkan physical device - * @param device Vulkan logical device - * @param computeQueue Vulkan compute queue - * @param queueIndex Vulkan compute queue index in device - */ - Sequence(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr computeQueue, - uint32_t queueIndex); - /** - * Destructor for sequence which is responsible for cleaning all subsequent - * owned operations. - */ - ~Sequence(); - - /** - */ - std::shared_ptr record(std::shared_ptr op); - - /** - * Record function for operation to be added to the GPU queue in batch. This - * template requires classes to be derived from the OpBase class. This - * function also requires the Sequence to be recording, otherwise it will - * not be able to add the operation. - * - * @param tensors Vector of tensors to use for the operation - * @param TArgs Template parameters that are used to initialise operation - * which allows for extensible configurations on initialisation. - */ - template - std::shared_ptr - record(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->record(op); - } - template - std::shared_ptr - record(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->record(op); - } - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - std::shared_ptr eval(); - - std::shared_ptr eval(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - // TODO: Aim to have only a single function with tensors/algorithm - template - std::shared_ptr - eval(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - // TODO: Aim to be able to handle errors when returning without throw except - return this->eval(op); - } - // Needded as otherise can't use initialiser list - template - std::shared_ptr - eval(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->eval(op); - } - - /** - * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. - * - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAsync(); - std::shared_ptr evalAsync(std::shared_ptr op); - - /** - * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. - * - * @return shared_ptr of the Sequence class itself - */ - template - std::shared_ptr - evalAsync(std::vector> tensors, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; - - return this->evalAsync(op); - } - // Needed as otherwise it's not possible to use initializer lists - template - std::shared_ptr - evalAsync(std::shared_ptr algorithm, TArgs&&... params) - { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; - - return this->evalAsync(op); - } - - /** - * Eval Await waits for the fence to finish processing and then once it - * finishes, it runs the postEval of all operations. - * - * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. - */ - std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); - - /** - * Clear function clears all operations currently recorded and starts recording again. - */ - void clear(); - - /** - * Begins recording commands for commands to be submitted into the command - * buffer. - * - * @return Boolean stating whether execution was successful. - */ - void begin(); - - /** - * Ends the recording and stops recording commands when the record command - * is sent. - * - * @return Boolean stating whether execution was successful. - */ - void end(); - - /** - * Returns true if the sequence is currently in recording activated. - * - * @return Boolean stating if recording ongoing. - */ - bool isRecording(); - - bool isInit(); - - /** - * Returns true if the sequence is currently running - mostly used for async - * workloads. - * - * @return Boolean stating if currently running. - */ - bool isRunning(); - - /** - * Destroys and frees the GPU resources which include the buffer and memory - * and sets the sequence as init=False. - */ - void destroy(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - std::shared_ptr mComputeQueue = nullptr; - uint32_t mQueueIndex = -1; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mCommandPool = nullptr; - bool mFreeCommandPool = false; - std::shared_ptr mCommandBuffer = nullptr; - bool mFreeCommandBuffer = false; - - // -------------- ALWAYS OWNED RESOURCES - vk::Fence mFence; - std::vector> mOperations; - - // State - bool mRecording = false; - bool mIsRunning = false; - - // Create functions - void createCommandPool(); - void createCommandBuffer(); -}; - -} // End namespace kp - -#include -#include - -#define KP_DEFAULT_SESSION "DEFAULT" - -namespace kp { - -/** - Base orchestrator which creates and manages device and child components -*/ -class Manager -{ - public: - /** - Base constructor and default used which creates the base resources - including choosing the device 0 by default. - */ - Manager(); - - /** - * Similar to base constructor but allows the user to provide the device - * they would like to create the resources on. - * - * @param physicalDeviceIndex The index of the physical device to use - * @param manageResources (Optional) Whether to manage the memory of the - * resources created and destroy when the manager is destroyed. - * @param familyQueueIndices (Optional) List of queue indices to add for - * explicit allocation - * @param totalQueues The total number of compute queues to create. - */ - Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices = {}); - - /** - * Manager constructor which allows your own vulkan application to integrate - * with the vulkan kompute use. - * - * @param instance Vulkan compute instance to base this application - * @param physicalDevice Vulkan physical device to use for application - * @param device Vulkan logical device to use for all base resources - * @param physicalDeviceIndex Index for vulkan physical device used - */ - Manager(std::shared_ptr instance, - std::shared_ptr physicalDevice, - std::shared_ptr device); - - /** - * Manager destructor which would ensure all owned resources are destroyed - * unless explicitly stated that resources should not be destroyed or freed. - */ - ~Manager(); - - /** - * Get or create a managed Sequence that will be contained by this manager. - * If the named sequence does not currently exist, it would be created and - * initialised. - * - * @param sequenceName The name for the named sequence to be retrieved or - * created - * @param queueIndex The queue to use from the available queues - * @return Shared pointer to the manager owned sequence resource - */ - std::shared_ptr sequence(uint32_t queueIndex = 0); - - /** - * Function that simplifies the common workflow of tensor creation and - * initialization. It will take the constructor parameters for a Tensor - * and will will us it to create a new Tensor and then create it. The - * tensor memory will then be managed and owned by the manager. - * - * @param data The data to initialize the tensor with - * @param tensorType The type of tensor to initialize - * @param syncDataToGPU Whether to sync the data to GPU memory - * @returns Initialized Tensor with memory Syncd to GPU device - */ - std::shared_ptr tensor( - const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); - - std::shared_ptr algorithm( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); - - void destroy(); - void clear(); - - private: - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mInstance = nullptr; - bool mFreeInstance = false; - std::shared_ptr mPhysicalDevice = nullptr; - std::shared_ptr mDevice = nullptr; - bool mFreeDevice = false; - - // -------------- ALWAYS OWNED RESOURCES - std::vector> mManagedTensors; - std::vector> mManagedSequences; - std::vector> mManagedAlgorithms; - - std::vector mComputeQueueFamilyIndices; - std::vector> mComputeQueues; - - bool mManageResources = false; - -#if DEBUG -#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS - vk::DebugReportCallbackEXT mDebugReportCallback; - vk::DispatchLoaderDynamic mDebugDispatcher; -#endif -#endif - - // Create functions - void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); -}; - -} // End namespace kp +#pragma once +/* + THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT + + --- + + Copyright 2020 The Institute for Ethical AI & Machine Learning + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SHADEROP_SHADEROPMULT_HPP +#define SHADEROP_SHADEROPMULT_HPP + +namespace kp { +namespace shader_data { +static const unsigned char shaders_glsl_opmult_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, + 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; +} +} +#endif // define SHADEROP_SHADEROPMULT_HPP + +/* + THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT + + --- + + Copyright 2020 The Institute for Ethical AI & Machine Learning + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SHADEROP_SHADERLOGISTICREGRESSION_HPP +#define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +namespace kp { +namespace shader_data { +static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; +} +} +#endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP + +#if VK_USE_PLATFORM_ANDROID_KHR +#include +#include +// VK_NO_PROTOTYPES required before vulkan import but after wrapper.hpp +#undef VK_NO_PROTOTYPES +static const char* KOMPUTE_LOG_TAG = "KomputeLog"; +#endif + +#include + +#include + +// Typedefs to simplify interaction with core types +namespace kp { +typedef std::array Workgroup; +typedef std::vector Constants; +} + +// Must be after vulkan is included +#ifndef KOMPUTE_VK_API_VERSION +#ifndef KOMPUTE_VK_API_MAJOR_VERSION +#define KOMPUTE_VK_API_MAJOR_VERSION 1 +#endif // KOMPUTE_VK_API_MAJOR_VERSION +#ifndef KOMPUTE_VK_API_MINOR_VERSION +#define KOMPUTE_VK_API_MINOR_VERSION 1 +#endif // KOMPUTE_VK_API_MINOR_VERSION +#define KOMPUTE_VK_API_VERSION \ + VK_MAKE_VERSION( \ + KOMPUTE_VK_API_MAJOR_VERSION, KOMPUTE_VK_API_MINOR_VERSION, 0) +#endif // KOMPUTE_VK_API_VERSION + +// SPDLOG_ACTIVE_LEVEL must be defined before spdlog.h import +#ifndef SPDLOG_ACTIVE_LEVEL +#if DEBUG +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#else +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO +#endif +#endif + +#if defined(KOMPUTE_BUILD_PYTHON) +#include +namespace py = pybind11; +// from python/src/main.cpp +extern py::object kp_debug, kp_info, kp_warning, kp_error; +#endif + +#ifndef KOMPUTE_LOG_OVERRIDE +#if KOMPUTE_ENABLE_SPDLOG +#include +#define KP_LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) +#define KP_LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) +#define KP_LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) +#define KP_LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) +#else +#include +#if SPDLOG_ACTIVE_LEVEL > 1 +#define KP_LOG_DEBUG(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_DEBUG(...) \ + ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 1 + +#if SPDLOG_ACTIVE_LEVEL > 2 +#define KP_LOG_INFO(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_INFO(...) \ + ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 2 + +#if SPDLOG_ACTIVE_LEVEL > 3 +#define KP_LOG_WARN(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_WARN(...) \ + ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 3 + +#if SPDLOG_ACTIVE_LEVEL > 4 +#define KP_LOG_ERROR(...) +#else +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#define KP_LOG_ERROR(...) \ + ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#elif defined(KOMPUTE_BUILD_PYTHON) +#define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) +#else +#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#endif // VK_USE_PLATFORM_ANDROID_KHR +#endif // SPDLOG_ACTIVE_LEVEL > 4 +#endif // KOMPUTE_SPDLOG_ENABLED +#endif // KOMPUTE_LOG_OVERRIDE + +#if !defined(KOMPUTE_DISABLE_SHADER_UTILS) || !KOMPUTE_DISABLE_SHADER_UTILS +#include +#include + +#include +#include +#include + +namespace kp { + +// The default resource limit for the GLSL compiler, can be overwritten +// Has been adobted by: +// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp +const TBuiltInResource defaultResource = { +/* .MaxLights = */ 0, +/* .MaxClipPlanes = */ 0, +/* .MaxTextureUnits = */ 0, +/* .MaxTextureCoords = */ 0, +/* .MaxVertexAttribs = */ 64, +/* .MaxVertexUniformComponents = */ 4096, +/* .MaxVaryingFloats = */ 64, +/* .MaxVertexTextureImageUnits = */ 0, +/* .MaxCombinedTextureImageUnits = */ 0, +/* .MaxTextureImageUnits = */ 0, +/* .MaxFragmentUniformComponents = */ 0, +/* .MaxDrawBuffers = */ 0, +/* .MaxVertexUniformVectors = */ 128, +/* .MaxVaryingVectors = */ 8, +/* .MaxFragmentUniformVectors = */ 0, +/* .MaxVertexOutputVectors = */ 16, +/* .MaxFragmentInputVectors = */ 0, +/* .MinProgramTexelOffset = */ -8, +/* .MaxProgramTexelOffset = */ 7, +/* .MaxClipDistances = */ 8, +/* .MaxComputeWorkGroupCountX = */ 65535, +/* .MaxComputeWorkGroupCountY = */ 65535, +/* .MaxComputeWorkGroupCountZ = */ 65535, +/* .MaxComputeWorkGroupSizeX = */ 1024, +/* .MaxComputeWorkGroupSizeY = */ 1024, +/* .MaxComputeWorkGroupSizeZ = */ 64, +/* .MaxComputeUniformComponents = */ 1024, +/* .MaxComputeTextureImageUnits = */ 16, +/* .MaxComputeImageUniforms = */ 8, +/* .MaxComputeAtomicCounters = */ 8, +/* .MaxComputeAtomicCounterBuffers = */ 1, +/* .MaxVaryingComponents = */ 60, +/* .MaxVertexOutputComponents = */ 64, +/* .MaxGeometryInputComponents = */ 64, +/* .MaxGeometryOutputComponents = */ 128, +/* .MaxFragmentInputComponents = */ 0, +/* .MaxImageUnits = */ 0, +/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, +/* .MaxCombinedShaderOutputResources = */ 8, +/* .MaxImageSamples = */ 0, +/* .MaxVertexImageUniforms = */ 0, +/* .MaxTessControlImageUniforms = */ 0, +/* .MaxTessEvaluationImageUniforms = */ 0, +/* .MaxGeometryImageUniforms = */ 0, +/* .MaxFragmentImageUniforms = */ 0, +/* .MaxCombinedImageUniforms = */ 0, +/* .MaxGeometryTextureImageUnits = */ 0, +/* .MaxGeometryOutputVertices = */ 256, +/* .MaxGeometryTotalOutputComponents = */ 1024, +/* .MaxGeometryUniformComponents = */ 1024, +/* .MaxGeometryVaryingComponents = */ 64, +/* .MaxTessControlInputComponents = */ 128, +/* .MaxTessControlOutputComponents = */ 128, +/* .MaxTessControlTextureImageUnits = */ 0, +/* .MaxTessControlUniformComponents = */ 1024, +/* .MaxTessControlTotalOutputComponents = */ 4096, +/* .MaxTessEvaluationInputComponents = */ 128, +/* .MaxTessEvaluationOutputComponents = */ 128, +/* .MaxTessEvaluationTextureImageUnits = */ 16, +/* .MaxTessEvaluationUniformComponents = */ 1024, +/* .MaxTessPatchComponents = */ 120, +/* .MaxPatchVertices = */ 32, +/* .MaxTessGenLevel = */ 64, +/* .MaxViewports = */ 16, +/* .MaxVertexAtomicCounters = */ 0, +/* .MaxTessControlAtomicCounters = */ 0, +/* .MaxTessEvaluationAtomicCounters = */ 0, +/* .MaxGeometryAtomicCounters = */ 0, +/* .MaxFragmentAtomicCounters = */ 0, +/* .MaxCombinedAtomicCounters = */ 8, +/* .MaxAtomicCounterBindings = */ 1, +/* .MaxVertexAtomicCounterBuffers = */ 0, +/* .MaxTessControlAtomicCounterBuffers = */ 0, +/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, +/* .MaxGeometryAtomicCounterBuffers = */ 0, +/* .MaxFragmentAtomicCounterBuffers = */ 0, +/* .MaxCombinedAtomicCounterBuffers = */ 1, +/* .MaxAtomicCounterBufferSize = */ 16384, +/* .MaxTransformFeedbackBuffers = */ 4, +/* .MaxTransformFeedbackInterleavedComponents = */ 64, +/* .MaxCullDistances = */ 8, +/* .MaxCombinedClipAndCullDistances = */ 8, +/* .MaxSamples = */ 4, +/* .maxMeshOutputVerticesNV = */ 256, +/* .maxMeshOutputPrimitivesNV = */ 512, +/* .maxMeshWorkGroupSizeX_NV = */ 32, +/* .maxMeshWorkGroupSizeY_NV = */ 1, +/* .maxMeshWorkGroupSizeZ_NV = */ 1, +/* .maxTaskWorkGroupSizeX_NV = */ 32, +/* .maxTaskWorkGroupSizeY_NV = */ 1, +/* .maxTaskWorkGroupSizeZ_NV = */ 1, +/* .maxMeshViewCountNV = */ 4, +/* .maxDualSourceDrawBuffersEXT = */ 1, + +/* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, +}}; + +/** + Shader utily class with functions to compile and process glsl files. +*/ +class Shader { +public: + /** + * Compile multiple sources with optional filenames. Currently this function + * uses the glslang C++ interface which is not thread safe so this funciton + * should not be called from multiple threads concurrently. If you have a + * online shader processing multithreading use-case that can't use offline + * compilation please open an issue. + * + * @param sources A list of raw glsl shaders in string format + * @param files A list of file names respective to each of the sources + * @param entryPoint The function name to use as entry point + * @param definitions List of pairs containing key value definitions + * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @return The compiled SPIR-V binary in unsigned int32 format + */ + static std::vector compile_sources( + const std::vector& sources, + const std::vector& files = {}, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); + + /** + * Compile a single glslang source from string value. Currently this function + * uses the glslang C++ interface which is not thread safe so this funciton + * should not be called from multiple threads concurrently. If you have a + * online shader processing multithreading use-case that can't use offline + * compilation please open an issue. + * + * @param source An individual raw glsl shader in string format + * @param entryPoint The function name to use as entry point + * @param definitions List of pairs containing key value definitions + * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @return The compiled SPIR-V binary in unsigned int32 format + */ + static std::vector compile_source( + const std::string& source, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); + +}; + +} +#endif // DKOMPUTE_DISABLE_SHADER_UTILS + +namespace kp { + +/** + * Structured data used in GPU operations. + * + * Tensors are the base building block in Kompute to perform operations across + * GPUs. Each tensor would have a respective Vulkan memory and buffer, which + * would be used to store their respective data. The tensors can be used for GPU + * data storage or transfer. + */ +class Tensor +{ + public: + /** + * Type for tensors created: Device allows memory to be transferred from + * staging buffers. Staging are host memory visible. Storage are device + * visible but are not set up to transfer or receive data (only for shader + * storage). + */ + enum class TensorTypes + { + eDevice = 0, ///< Type is device memory, source and destination + eHost = 1, ///< Type is host memory, source and destination + eStorage = 2, ///< Type is Device memory (only) + }; + + /** + * Default constructor with data provided which would be used to create the + * respective vulkan buffer and memory. + * + * @param data Non-zero-sized vector of data that will be used by the + * tensor + * @param tensorType Type for the tensor which is of type TensorTypes + */ + Tensor(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); + + /** + * Destructor which is in charge of freeing vulkan resources unless they + * have been provided externally. + */ + ~Tensor(); + + /** + * Initialiser which calls the initialisation for all the respective tensors + * as well as creates the respective staging tensors. The staging tensors + * would only be created for the tensors of type TensorType::eDevice as + * otherwise there is no need to copy from host memory. + */ + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice); + + /** + * Destroys and frees the GPU resources which include the buffer and memory. + */ + void destroy(); + + bool isInit(); + + /** + * Returns the vector of data currently contained by the Tensor. It is + * important to ensure that there is no out-of-sync data with the GPU + * memory. + * + * @return Reference to vector of elements representing the data in the + * tensor. + */ + std::vector& data(); + /** + * Overrides the subscript operator to expose the underlying data's + * subscript operator which in this case would be its underlying + * vector's. + * + * @param i The index where the element will be returned from. + * @return Returns the element in the position requested. + */ + float& operator[](int index); + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + uint32_t size(); + + /** + * Retrieve the tensor type of the Tensor + * + * @return Tensor type of tensor + */ + TensorTypes tensorType(); + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + void setData(const std::vector& data); + + /** + * Records a copy from the memory of the tensor provided to the current + * thensor. This is intended to pass memory into a processing, to perform + * a staging buffer transfer, or to gather output (between others). + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param copyFromTensor Tensor to copy the data from + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFrom(const vk::CommandBuffer& commandBuffer, + std::shared_ptr copyFromTensor, + bool createBarrier); + + /** + * Records a copy from the internal staging memory to the device memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromStagingToDevice( + const vk::CommandBuffer& commandBuffer, + bool createBarrier); + + /** + * Records a copy from the internal device memory to the staging memory + * using an optional barrier to wait for the operation. This function would + * only be relevant for kp::Tensors of type eDevice. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param createBarrier Whether to create a barrier that ensures the data is + * copied before further operations. Default is true. + */ + void recordCopyFromDeviceToStaging( + const vk::CommandBuffer& commandBuffer, + bool createBarrier); + + /** + * Records the buffer memory barrier into the command buffer which + * ensures that relevant data transfers are carried out correctly. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param srcAccessMask Access flags for source access mask + * @param dstAccessMask Access flags for destination access mask + * @param scrStageMask Pipeline stage flags for source stage mask + * @param dstStageMask Pipeline stage flags for destination stage mask + */ + void recordBufferMemoryBarrier( + const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + + /** + * Constructs a vulkan descriptor buffer info which can be used to specify + * and reference the underlying buffer component of the tensor without + * exposing it. + * + * @return Descriptor buffer info with own buffer + */ + vk::DescriptorBufferInfo constructDescriptorBufferInfo(); + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + void mapDataFromHostMemory(); + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + void mapDataIntoHostMemory(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice; + std::shared_ptr mDevice; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mPrimaryBuffer; + bool mFreePrimaryBuffer = false; + std::shared_ptr mStagingBuffer; + bool mFreeStagingBuffer = false; + std::shared_ptr mPrimaryMemory; + bool mFreePrimaryMemory = false; + std::shared_ptr mStagingMemory; + bool mFreeStagingMemory = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mData; + + TensorTypes mTensorType = TensorTypes::eDevice; + + void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer + void createBuffer(std::shared_ptr buffer, + vk::BufferUsageFlags bufferUsageFlags); + void allocateBindMemory(std::shared_ptr buffer, + std::shared_ptr memory, + vk::MemoryPropertyFlags memoryPropertyFlags); + void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); + + // Private util functions + vk::BufferUsageFlags getPrimaryBufferUsageFlags(); + vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); + vk::BufferUsageFlags getStagingBufferUsageFlags(); + vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + uint64_t memorySize(); +}; + +} // End namespace kp + +namespace kp { + +/** + Abstraction for compute shaders that are run on top of tensors grouped via + ParameterGroups (which group descriptorsets) +*/ +class Algorithm +{ +public: + + /** + * Default constructor for Algorithm + * + * @param device The Vulkan device to use for creating resources + * @param commandBuffer The vulkan command buffer to bind the pipeline and + * shaders + */ + Algorithm( + std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); + + /** + * Initialiser for the shader data provided to the algorithm as well as + * tensor parameters that will be used in shader. + * + * @param shaderFileData The bytes in spir-v format of the shader + * @tensorParams The Tensors to be used in the Algorithm / shader for + * @specalizationInstalces The specialization parameters to pass to the function + * processing + */ + void rebuild( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); + + /** + * Destructor for Algorithm which is responsible for freeing and desroying + * respective pipelines and owned parameter groups. + */ + ~Algorithm(); + + /** + * Records the dispatch function with the provided template parameters or + * alternatively using the size of the tensor by default. + * + * @param x Layout X dispatch value + * @param y Layout Y dispatch value + * @param z Layout Z dispatch value + */ + void recordDispatch(const vk::CommandBuffer& commandBuffer); + + void bindCore(const vk::CommandBuffer& commandBuffer); + + void bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants); + + bool isInit(); + + void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + + const Workgroup& getWorkgroup(); + const Constants& getSpecializationConstants(); + const std::vector>& getTensors(); + + void destroy(); + +private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mDevice; + std::vector> mTensors; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mDescriptorSetLayout; + bool mFreeDescriptorSetLayout = false; + std::shared_ptr mDescriptorPool; + bool mFreeDescriptorPool = false; + std::shared_ptr mDescriptorSet; + bool mFreeDescriptorSet = false; + std::shared_ptr mShaderModule; + bool mFreeShaderModule = false; + std::shared_ptr mPipelineLayout; + bool mFreePipelineLayout = false; + std::shared_ptr mPipelineCache; + bool mFreePipelineCache = false; + std::shared_ptr mPipeline; + bool mFreePipeline = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector mSpirv; + Constants mSpecializationConstants; + Workgroup mWorkgroup; + + bool mIsInit; + + // Create util functions + void createShaderModule(); + void createPipeline(); + + // Parameters + void createParameters(); +}; + +} // End namespace kp + +namespace kp { + +/** + * Base Operation which provides the high level interface that Kompute + * operations implement in order to perform a set of actions in the GPU. + * + * Operations can perform actions on tensors, and optionally can also own an + * Algorithm with respective parameters. kp::Operations with kp::Algorithms + * would inherit from kp::OpBaseAlgo. + */ +class OpBase +{ + public: + + /** + * Default destructor for OpBase class. This OpBase destructor class should + * always be called to destroy and free owned resources unless it is + * intended to destroy the resources in the parent class. + */ + virtual ~OpBase() + { + KP_LOG_DEBUG("Kompute OpBase destructor started"); + } + + /** + * The record function is intended to only send a record command or run + * commands that are expected to record operations that are to be submitted + * as a batch into the GPU. + */ + virtual void record(const vk::CommandBuffer& commandBuffer) = 0; + + /** + * Pre eval is called before the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any per-eval setup steps + * required as the computation iteration begins. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are created should be idempotent in case it's called multiple + * times in a row. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) = 0; + + /** + * Post eval is called after the Sequence has called eval and submitted the commands to + * the GPU for processing, and can be used to perform any tear-down steps + * required as the computation iteration finishes. It's worth noting that + * there are situations where eval can be called multiple times, so the + * resources that are destroyed should not require a re-init unless explicitly + * provided by the user. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) = 0; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type +*/ +class OpTensorCopy : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorCopy(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorCopy() override; + + /** + * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. + */ + void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * Copies the local vectors for all the tensors to sync the data with the gpu. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncDevice : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncDevice(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncDevice() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. + */ + void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any postEval commands. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. +*/ +class OpTensorSyncLocal : public OpBase +{ + public: + /** + * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + */ + OpTensorSyncLocal(const std::vector>& tensors); + + /** + * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + */ + ~OpTensorSyncLocal() override; + + /** + * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. + */ + void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * For host tensors it performs the map command from the host memory into local memory. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * By default it enables the user to provide a dynamic number of tensors + * which are then passed as inputs. + */ +class OpAlgoDispatch : public OpBase +{ + public: + + OpAlgoDispatch(const std::shared_ptr& algorithm, + const kp::Constants& pushConstants = {}); + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpAlgoDispatch() override; + + /** + * This records the commands that are to be sent to the GPU. This includes + * the barriers that ensure the memory has been copied before going in and + * out of the shader, as well as the dispatch operation that sends the + * shader processing to the gpu. This function also records the GPU memory + * copy of the output data for the staging buffer so it can be read by the + * host. + */ + virtual void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * Executes after the recorded commands are submitted, and performs a copy + * of the GPU Device memory into the staging buffer so the output data can + * be retrieved. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + +private: + // -------------- ALWAYS OWNED RESOURCES + std::shared_ptr mAlgorithm; + Constants mPushConstants; +}; + +} // End namespace kp + +#include + +namespace kp { + +/** + * Operation that performs multiplication on two tensors and outpus on third + * tensor. + */ +class OpMult : public OpAlgoDispatch +{ + public: + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that are to be used in this operation + * @param komputeWorkgroup Optional parameter to specify the layout for processing + */ + OpMult(std::vector> tensors, std::shared_ptr algorithm) + : OpAlgoDispatch(algorithm) + { + KP_LOG_DEBUG("Kompute OpMult constructor with params"); + + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } + + std::vector spirv( + (uint32_t*)shader_data::shaders_glsl_opmult_comp_spv, + (uint32_t*)(shader_data::shaders_glsl_opmult_comp_spv + + kp::shader_data::shaders_glsl_opmult_comp_spv_len)); + + algorithm->rebuild(tensors, spirv); + } + + /** + * Default destructor, which is in charge of destroying the algorithm + * components but does not destroy the underlying tensors + */ + virtual ~OpMult() override { + KP_LOG_DEBUG("Kompute OpMult destructor started"); + } +}; + +} // End namespace kp + +namespace kp { + +/** + * Container of operations that can be sent to GPU as batch + */ +class Sequence: public std::enable_shared_from_this +{ + public: + /** + * Main constructor for sequence which requires core vulkan components to + * generate all dependent resources. + * + * @param physicalDevice Vulkan physical device + * @param device Vulkan logical device + * @param computeQueue Vulkan compute queue + * @param queueIndex Vulkan compute queue index in device + */ + Sequence(std::shared_ptr physicalDevice, + std::shared_ptr device, + std::shared_ptr computeQueue, + uint32_t queueIndex); + /** + * Destructor for sequence which is responsible for cleaning all subsequent + * owned operations. + */ + ~Sequence(); + + /** + */ + std::shared_ptr record(std::shared_ptr op); + + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + */ + template + std::shared_ptr + record(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->record(op); + } + template + std::shared_ptr + record(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->record(op); + } + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + std::shared_ptr eval(); + + std::shared_ptr eval(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + // TODO: Aim to have only a single function with tensors/algorithm + template + std::shared_ptr + eval(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + // TODO: Aim to be able to handle errors when returning without throw except + return this->eval(op); + } + // Needded as otherise can't use initialiser list + template + std::shared_ptr + eval(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->eval(op); + } + + /** + * Eval Async sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. EvalAwait() must + * be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAsync(); + std::shared_ptr evalAsync(std::shared_ptr op); + + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @return shared_ptr of the Sequence class itself + */ + template + std::shared_ptr + evalAsync(std::vector> tensors, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(tensors, std::forward(params)...) }; + + return this->evalAsync(op); + } + // Needed as otherwise it's not possible to use initializer lists + template + std::shared_ptr + evalAsync(std::shared_ptr algorithm, TArgs&&... params) + { + KP_LOG_DEBUG("Kompute Sequence record function started"); + + static_assert(std::is_base_of::value, + "Kompute Sequence record(...) template only valid with " + "OpBase derived classes"); + + KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); + std::shared_ptr op{ + new T(algorithm, std::forward(params)...) }; + + return this->evalAsync(op); + } + + /** + * Eval Await waits for the fence to finish processing and then once it + * finishes, it runs the postEval of all operations. + * + * @param waitFor Number of milliseconds to wait before timing out. + * @return Boolean stating whether execution was successful. + */ + std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); + + /** + * Clear function clears all operations currently recorded and starts recording again. + */ + void clear(); + + /** + * Begins recording commands for commands to be submitted into the command + * buffer. + * + * @return Boolean stating whether execution was successful. + */ + void begin(); + + /** + * Ends the recording and stops recording commands when the record command + * is sent. + * + * @return Boolean stating whether execution was successful. + */ + void end(); + + /** + * Returns true if the sequence is currently in recording activated. + * + * @return Boolean stating if recording ongoing. + */ + bool isRecording(); + + bool isInit(); + + /** + * Returns true if the sequence is currently running - mostly used for async + * workloads. + * + * @return Boolean stating if currently running. + */ + bool isRunning(); + + /** + * Destroys and frees the GPU resources which include the buffer and memory + * and sets the sequence as init=False. + */ + void destroy(); + + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + std::shared_ptr mComputeQueue = nullptr; + uint32_t mQueueIndex = -1; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mCommandPool = nullptr; + bool mFreeCommandPool = false; + std::shared_ptr mCommandBuffer = nullptr; + bool mFreeCommandBuffer = false; + + // -------------- ALWAYS OWNED RESOURCES + vk::Fence mFence; + std::vector> mOperations; + + // State + bool mRecording = false; + bool mIsRunning = false; + + // Create functions + void createCommandPool(); + void createCommandBuffer(); +}; + +} // End namespace kp + +#include +#include + +#define KP_DEFAULT_SESSION "DEFAULT" + +namespace kp { + +/** + Base orchestrator which creates and manages device and child components +*/ +class Manager +{ + public: + /** + Base constructor and default used which creates the base resources + including choosing the device 0 by default. + */ + Manager(); + + /** + * Similar to base constructor but allows the user to provide the device + * they would like to create the resources on. + * + * @param physicalDeviceIndex The index of the physical device to use + * @param manageResources (Optional) Whether to manage the memory of the + * resources created and destroy when the manager is destroyed. + * @param familyQueueIndices (Optional) List of queue indices to add for + * explicit allocation + * @param totalQueues The total number of compute queues to create. + */ + Manager(uint32_t physicalDeviceIndex, + const std::vector& familyQueueIndices = {}); + + /** + * Manager constructor which allows your own vulkan application to integrate + * with the vulkan kompute use. + * + * @param instance Vulkan compute instance to base this application + * @param physicalDevice Vulkan physical device to use for application + * @param device Vulkan logical device to use for all base resources + * @param physicalDeviceIndex Index for vulkan physical device used + */ + Manager(std::shared_ptr instance, + std::shared_ptr physicalDevice, + std::shared_ptr device); + + /** + * Manager destructor which would ensure all owned resources are destroyed + * unless explicitly stated that resources should not be destroyed or freed. + */ + ~Manager(); + + /** + * Get or create a managed Sequence that will be contained by this manager. + * If the named sequence does not currently exist, it would be created and + * initialised. + * + * @param sequenceName The name for the named sequence to be retrieved or + * created + * @param queueIndex The queue to use from the available queues + * @return Shared pointer to the manager owned sequence resource + */ + std::shared_ptr sequence(uint32_t queueIndex = 0); + + /** + * Function that simplifies the common workflow of tensor creation and + * initialization. It will take the constructor parameters for a Tensor + * and will will us it to create a new Tensor and then create it. The + * tensor memory will then be managed and owned by the manager. + * + * @param data The data to initialize the tensor with + * @param tensorType The type of tensor to initialize + * @param syncDataToGPU Whether to sync the data to GPU memory + * @returns Initialized Tensor with memory Syncd to GPU device + */ + std::shared_ptr tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + + std::shared_ptr algorithm( + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); + + void destroy(); + void clear(); + + private: + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mInstance = nullptr; + bool mFreeInstance = false; + std::shared_ptr mPhysicalDevice = nullptr; + std::shared_ptr mDevice = nullptr; + bool mFreeDevice = false; + + // -------------- ALWAYS OWNED RESOURCES + std::vector> mManagedTensors; + std::vector> mManagedSequences; + std::vector> mManagedAlgorithms; + + std::vector mComputeQueueFamilyIndices; + std::vector> mComputeQueues; + + bool mManageResources = false; + +#if DEBUG +#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS + vk::DebugReportCallbackEXT mDebugReportCallback; + vk::DispatchLoaderDynamic mDebugDispatcher; +#endif +#endif + + // Create functions + void createInstance(); + void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); +}; + +} // End namespace kp diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index f8ee78e3e..174f78d9b 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -2,6 +2,8 @@ #include "kompute/Algorithm.hpp" +#include "fmt/ranges.h" + namespace kp { Algorithm::Algorithm( @@ -9,8 +11,7 @@ Algorithm::Algorithm( const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants, - const Constants& pushConstants) + const Constants& specializationConstants) { KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); @@ -18,7 +19,7 @@ Algorithm::Algorithm( if (tensors.size() && spirv.size()) { KP_LOG_INFO("Kompute Algorithm initialising with tensor size: {} and spirv size: {}", tensors.size(), spirv.size()); - this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); + this->rebuild(tensors, spirv, workgroup, specializationConstants); } else { KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or spirv so not rebuilding vulkan components"); @@ -37,15 +38,13 @@ Algorithm::rebuild( const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants, - const Constants& pushConstants) + const Constants& specializationConstants) { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); this->mTensors = tensors; this->mSpirv = spirv; this->mSpecializationConstants = specializationConstants; - this->mPushConstants = pushConstants; this->setWorkgroup(workgroup, this->mTensors.size() ? this->mTensors[0]->size() : 1); // Descriptor pool is created first so if available then destroy all before rebuild @@ -347,27 +346,43 @@ Algorithm::createPipeline() } void -Algorithm::recordDispatch(std::shared_ptr commandBuffer) +Algorithm::bindCore(const vk::CommandBuffer& commandBuffer) { - KP_LOG_DEBUG("Kompute Algorithm calling record dispatch"); + KP_LOG_DEBUG("Kompute Algorithm binding pipeline"); - commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, + commandBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, *this->mPipeline); - KP_LOG_DEBUG("Kompute Algorithm pipeline bound"); + KP_LOG_DEBUG("Kompute Algorithm binding descriptor sets"); - commandBuffer->bindDescriptorSets(vk::PipelineBindPoint::eCompute, + commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *this->mPipelineLayout, 0, // First set *this->mDescriptorSet, nullptr // Dispatcher ); +} - KP_LOG_DEBUG("Kompute Algorithm descriptor sets bound"); +void +Algorithm::bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants) +{ + if (pushConstants.size()) { + KP_LOG_DEBUG("Kompute Algorithm binding push constants size: {}", pushConstants.size()); - commandBuffer->dispatch(this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); + commandBuffer.pushConstants(*this->mPipelineLayout, + vk::ShaderStageFlagBits::eCompute, + 0, + pushConstants.size() * sizeof(float), + pushConstants.data()); + } +} - KP_LOG_DEBUG("Kompute Algorithm dispatch success"); +void +Algorithm::recordDispatch(const vk::CommandBuffer& commandBuffer) +{ + KP_LOG_DEBUG("Kompute Algorithm recording dispatch"); + + commandBuffer.dispatch(this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); } void @@ -405,11 +420,6 @@ Algorithm::getSpecializationConstants() { return this->mSpecializationConstants; } -const Constants& -Algorithm::getPushConstants() { - return this->mPushConstants; -} - const std::vector>& Algorithm::getTensors() { return this->mTensors; diff --git a/src/Manager.cpp b/src/Manager.cpp index 2ee94a2ee..bb109aace 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -363,8 +363,7 @@ Manager::algorithm( const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants, - const Constants& pushConstants) { + const Constants& specializationConstants) { KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); @@ -374,8 +373,7 @@ Manager::algorithm( tensors, spirv, workgroup, - specializationConstants, - pushConstants)}; + specializationConstants)}; if (this->mManageResources) { this->mManagedAlgorithms.push_back(algorithm); diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index a20900189..b8e49f144 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -4,11 +4,13 @@ namespace kp { -OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm) +OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm, + const kp::Constants& pushConstants) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); this->mAlgorithm = algorithm; + this->mPushConstants = pushConstants; } OpAlgoDispatch::~OpAlgoDispatch() @@ -17,7 +19,7 @@ OpAlgoDispatch::~OpAlgoDispatch() } void -OpAlgoDispatch::record(std::shared_ptr commandBuffer) +OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpAlgoDispatch record called"); @@ -31,17 +33,19 @@ OpAlgoDispatch::record(std::shared_ptr commandBuffer) vk::PipelineStageFlagBits::eComputeShader); } + this->mAlgorithm->bindCore(commandBuffer); + this->mAlgorithm->bindPush(commandBuffer, this->mPushConstants); this->mAlgorithm->recordDispatch(commandBuffer); } void -OpAlgoDispatch::preEval() +OpAlgoDispatch::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpAlgoDispatch preEval called"); } void -OpAlgoDispatch::postEval() +OpAlgoDispatch::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpAlgoDispatch postSubmit called"); } diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 87f0a3b20..6950a4cd2 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -21,7 +21,7 @@ OpTensorCopy::~OpTensorCopy() } void -OpTensorCopy::record(std::shared_ptr commandBuffer) +OpTensorCopy::record(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy record called"); @@ -33,13 +33,13 @@ OpTensorCopy::record(std::shared_ptr commandBuffer) } void -OpTensorCopy::preEval() +OpTensorCopy::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy preEval called"); } void -OpTensorCopy::postEval() +OpTensorCopy::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy postEval called"); diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index a73224297..5fdd7b74c 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -24,7 +24,7 @@ OpTensorSyncDevice::~OpTensorSyncDevice() } void -OpTensorSyncDevice::record(std::shared_ptr commandBuffer) +OpTensorSyncDevice::record(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice record called"); @@ -37,7 +37,7 @@ OpTensorSyncDevice::record(std::shared_ptr commandBuffer) } void -OpTensorSyncDevice::preEval() +OpTensorSyncDevice::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice preEval called"); @@ -50,7 +50,7 @@ OpTensorSyncDevice::preEval() } void -OpTensorSyncDevice::postEval() +OpTensorSyncDevice::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice postEval called"); } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 3cd022bf2..6add3fa20 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -24,7 +24,7 @@ OpTensorSyncLocal::~OpTensorSyncLocal() } void -OpTensorSyncLocal::record(std::shared_ptr commandBuffer) +OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal record called"); @@ -37,13 +37,13 @@ OpTensorSyncLocal::record(std::shared_ptr commandBuffer) } void -OpTensorSyncLocal::preEval() +OpTensorSyncLocal::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal preEval called"); } void -OpTensorSyncLocal::postEval() +OpTensorSyncLocal::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncLocal postEval called"); diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 5a8115c97..077681fac 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -96,7 +96,7 @@ Sequence::evalAsync() this->mIsRunning = true; for (size_t i = 0; i < this->mOperations.size(); i++) { - this->mOperations[i]->preEval(); + this->mOperations[i]->preEval(*this->mCommandBuffer); } vk::SubmitInfo submitInfo( @@ -142,7 +142,7 @@ Sequence::evalAwait(uint64_t waitFor) } for (size_t i = 0; i < this->mOperations.size(); i++) { - this->mOperations[i]->postEval(); + this->mOperations[i]->postEval(*this->mCommandBuffer); } return shared_from_this(); @@ -241,7 +241,7 @@ Sequence::record(std::shared_ptr op) KP_LOG_DEBUG( "Kompute Sequence running record on OpBase derived class instance"); - op->record(this->mCommandBuffer); + op->record(*this->mCommandBuffer); this->mOperations.push_back(op); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 7176fe484..dd645708e 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -94,7 +94,7 @@ Tensor::setData(const std::vector& data) } void -Tensor::recordCopyFrom(std::shared_ptr commandBuffer, +Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, std::shared_ptr copyFromTensor, bool createBarrier) { @@ -104,7 +104,7 @@ Tensor::recordCopyFrom(std::shared_ptr commandBuffer, KP_LOG_DEBUG("Kompute Tensor recordCopyFrom data size {}.", bufferSize); - this->copyBuffer(commandBuffer, + this->recordCopyBuffer(commandBuffer, copyFromTensor->mPrimaryBuffer, this->mPrimaryBuffer, bufferSize, @@ -114,7 +114,7 @@ Tensor::recordCopyFrom(std::shared_ptr commandBuffer, void Tensor::recordCopyFromStagingToDevice( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, bool createBarrier) { vk::DeviceSize bufferSize(this->memorySize()); @@ -122,7 +122,7 @@ Tensor::recordCopyFromStagingToDevice( KP_LOG_DEBUG("Kompute Tensor copying data size {}.", bufferSize); - this->copyBuffer(commandBuffer, + this->recordCopyBuffer(commandBuffer, this->mStagingBuffer, this->mPrimaryBuffer, bufferSize, @@ -132,7 +132,7 @@ Tensor::recordCopyFromStagingToDevice( void Tensor::recordCopyFromDeviceToStaging( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, bool createBarrier) { vk::DeviceSize bufferSize(this->memorySize()); @@ -140,7 +140,7 @@ Tensor::recordCopyFromDeviceToStaging( KP_LOG_DEBUG("Kompute Tensor copying data size {}.", bufferSize); - this->copyBuffer(commandBuffer, + this->recordCopyBuffer(commandBuffer, this->mPrimaryBuffer, this->mStagingBuffer, bufferSize, @@ -149,7 +149,7 @@ Tensor::recordCopyFromDeviceToStaging( } void -Tensor::copyBuffer(std::shared_ptr commandBuffer, +Tensor::recordCopyBuffer(const vk::CommandBuffer& commandBuffer, std::shared_ptr bufferFrom, std::shared_ptr bufferTo, vk::DeviceSize bufferSize, @@ -157,7 +157,7 @@ Tensor::copyBuffer(std::shared_ptr commandBuffer, bool createBarrier) { - commandBuffer->copyBuffer(*bufferFrom, *bufferTo, copyRegion); + commandBuffer.copyBuffer(*bufferFrom, *bufferTo, copyRegion); if (createBarrier) { // Buffer to ensure wait until data is copied to staging buffer @@ -171,7 +171,7 @@ Tensor::copyBuffer(std::shared_ptr commandBuffer, void Tensor::recordBufferMemoryBarrier( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, vk::AccessFlagBits srcAccessMask, vk::AccessFlagBits dstAccessMask, vk::PipelineStageFlagBits srcStageMask, @@ -189,7 +189,7 @@ Tensor::recordBufferMemoryBarrier( bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - commandBuffer->pipelineBarrier(srcStageMask, + commandBuffer.pipelineBarrier(srcStageMask, dstStageMask, vk::DependencyFlags(), nullptr, diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index 8b37f3e9a..b80f3946c 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -26,8 +26,7 @@ public: const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); + const Constants& specializationConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -42,8 +41,7 @@ public: const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); + const Constants& specializationConstants = {}); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -59,7 +57,11 @@ public: * @param y Layout Y dispatch value * @param z Layout Z dispatch value */ - void recordDispatch(std::shared_ptr commandBuffer); + void recordDispatch(const vk::CommandBuffer& commandBuffer); + + void bindCore(const vk::CommandBuffer& commandBuffer); + + void bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants); bool isInit(); @@ -67,7 +69,6 @@ public: const Workgroup& getWorkgroup(); const Constants& getSpecializationConstants(); - const Constants& getPushConstants(); const std::vector>& getTensors(); void destroy(); @@ -96,7 +97,6 @@ private: // -------------- ALWAYS OWNED RESOURCES std::vector mSpirv; Constants mSpecializationConstants; - Constants mPushConstants; Workgroup mWorkgroup; bool mIsInit; diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index f963d5f5a..e651cf2bb 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -87,8 +87,7 @@ class Manager const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}, - const Constants& pushConstants = {}); + const Constants& specializationConstants = {}); void destroy(); void clear(); diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index c3521b7a8..513525f73 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -112,7 +112,7 @@ class Tensor * @param createBarrier Whether to create a barrier that ensures the data is * copied before further operations. Default is true. */ - void recordCopyFrom(std::shared_ptr commandBuffer, + void recordCopyFrom(const vk::CommandBuffer& commandBuffer, std::shared_ptr copyFromTensor, bool createBarrier); @@ -126,7 +126,7 @@ class Tensor * copied before further operations. Default is true. */ void recordCopyFromStagingToDevice( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, bool createBarrier); /** @@ -139,7 +139,7 @@ class Tensor * copied before further operations. Default is true. */ void recordCopyFromDeviceToStaging( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, bool createBarrier); /** @@ -153,7 +153,7 @@ class Tensor * @param dstStageMask Pipeline stage flags for destination stage mask */ void recordBufferMemoryBarrier( - std::shared_ptr commandBuffer, + const vk::CommandBuffer& commandBuffer, vk::AccessFlagBits srcAccessMask, vk::AccessFlagBits dstAccessMask, vk::PipelineStageFlagBits srcStageMask, @@ -204,7 +204,7 @@ class Tensor void allocateBindMemory(std::shared_ptr buffer, std::shared_ptr memory, vk::MemoryPropertyFlags memoryPropertyFlags); - void copyBuffer(std::shared_ptr commandBuffer, + void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, std::shared_ptr bufferFrom, std::shared_ptr bufferTo, vk::DeviceSize bufferSize, diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index 1b5ab1bf0..6975f2793 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,7 +17,8 @@ class OpAlgoDispatch : public OpBase { public: - OpAlgoDispatch(const std::shared_ptr& algorithm); + OpAlgoDispatch(const std::shared_ptr& algorithm, + const kp::Constants& pushConstants = {}); /** * Default destructor, which is in charge of destroying the algorithm @@ -33,23 +34,24 @@ class OpAlgoDispatch : public OpBase * copy of the output data for the staging buffer so it can be read by the * host. */ - virtual void record(std::shared_ptr commandBuffer) override; + virtual void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. */ - virtual void preEval() override; + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Executes after the recorded commands are submitted, and performs a copy * of the GPU Device memory into the staging buffer so the output data can * be retrieved. */ - virtual void postEval() override; + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; private: // -------------- ALWAYS OWNED RESOURCES std::shared_ptr mAlgorithm; + Constants mPushConstants; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpBase.hpp b/src/include/kompute/operations/OpBase.hpp index fd628cf02..ba1e892d5 100644 --- a/src/include/kompute/operations/OpBase.hpp +++ b/src/include/kompute/operations/OpBase.hpp @@ -34,7 +34,7 @@ class OpBase * commands that are expected to record operations that are to be submitted * as a batch into the GPU. */ - virtual void record(std::shared_ptr commandBuffer) = 0; + virtual void record(const vk::CommandBuffer& commandBuffer) = 0; /** * Pre eval is called before the Sequence has called eval and submitted the commands to @@ -44,7 +44,7 @@ class OpBase * resources that are created should be idempotent in case it's called multiple * times in a row. */ - virtual void preEval() = 0; + virtual void preEval(const vk::CommandBuffer& commandBuffer) = 0; /** * Post eval is called after the Sequence has called eval and submitted the commands to @@ -54,7 +54,7 @@ class OpBase * resources that are destroyed should not require a re-init unless explicitly * provided by the user. */ - virtual void postEval() = 0; + virtual void postEval(const vk::CommandBuffer& commandBuffer) = 0; }; } // End namespace kp diff --git a/src/include/kompute/operations/OpTensorCopy.hpp b/src/include/kompute/operations/OpTensorCopy.hpp index 01fad8334..3d202031f 100644 --- a/src/include/kompute/operations/OpTensorCopy.hpp +++ b/src/include/kompute/operations/OpTensorCopy.hpp @@ -32,17 +32,17 @@ class OpTensorCopy : public OpBase /** * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. */ - void record(std::shared_ptr commandBuffer) override; + void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. */ - virtual void preEval() override; + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Copies the local vectors for all the tensors to sync the data with the gpu. */ - virtual void postEval() override; + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; private: // -------------- ALWAYS OWNED RESOURCES diff --git a/src/include/kompute/operations/OpTensorSyncDevice.hpp b/src/include/kompute/operations/OpTensorSyncDevice.hpp index 8addce188..cbb8ec40e 100644 --- a/src/include/kompute/operations/OpTensorSyncDevice.hpp +++ b/src/include/kompute/operations/OpTensorSyncDevice.hpp @@ -31,17 +31,17 @@ class OpTensorSyncDevice : public OpBase /** * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. */ - void record(std::shared_ptr commandBuffer) override; + void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. */ - virtual void preEval() override; + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any postEval commands. */ - virtual void postEval() override; + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; private: // -------------- ALWAYS OWNED RESOURCES diff --git a/src/include/kompute/operations/OpTensorSyncLocal.hpp b/src/include/kompute/operations/OpTensorSyncLocal.hpp index 7df8ccdd7..276f38137 100644 --- a/src/include/kompute/operations/OpTensorSyncLocal.hpp +++ b/src/include/kompute/operations/OpTensorSyncLocal.hpp @@ -32,17 +32,17 @@ class OpTensorSyncLocal : public OpBase /** * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. */ - void record(std::shared_ptr commandBuffer) override; + void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. */ - virtual void preEval() override; + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * For host tensors it performs the map command from the host memory into local memory. */ - virtual void postEval() override; + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; private: diff --git a/src/include/kompute/shaders/shaderlogisticregression.hpp b/src/include/kompute/shaders/shaderlogisticregression.hpp index 5293a3593..f456ac1cf 100755 --- a/src/include/kompute/shaders/shaderlogisticregression.hpp +++ b/src/include/kompute/shaders/shaderlogisticregression.hpp @@ -23,411 +23,411 @@ namespace kp { namespace shader_data { -static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; +static const unsigned char shaders_glsl_logisticregression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_logisticregression_comp_spv_len = 4816; } } #endif // define SHADEROP_SHADERLOGISTICREGRESSION_HPP diff --git a/src/include/kompute/shaders/shaderopmult.hpp b/src/include/kompute/shaders/shaderopmult.hpp index 0954a23dd..f4015a58f 100755 --- a/src/include/kompute/shaders/shaderopmult.hpp +++ b/src/include/kompute/shaders/shaderopmult.hpp @@ -23,131 +23,131 @@ namespace kp { namespace shader_data { -static const unsigned char shaders_glsl_opmult_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, - 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; +static const unsigned char shaders_glsl_opmult_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x4c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x68, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x4c, 0x45, 0x4e, 0x5f, 0x4c, 0x48, 0x53, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, 0x52, 0x48, 0x53, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x4e, 0x5f, + 0x4f, 0x55, 0x54, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int shaders_glsl_opmult_comp_spv_len = 1464; } } #endif // define SHADEROP_SHADEROPMULT_HPP diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp new file mode 100644 index 000000000..78ba57c00 --- /dev/null +++ b/test/TestPushConstant.cpp @@ -0,0 +1,47 @@ +#include "gtest/gtest.h" + +#include "kompute/Kompute.hpp" + +#include "fmt/ranges.h" + +TEST(TestPushConstants, TestTwoConstants) +{ + { + std::string shader(R"( + #version 450 + layout(push_constant) uniform PushConstants { + float x; + float y; + float z; + } pcs; + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr; + + std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + + std::shared_ptr algo = + mgr.algorithm({tensor}, spirv, kp::Workgroup({1})); + + sq = mgr.sequence() + ->record({tensor}) + ->record(algo, kp::Constants{0.1, 0.2, 0.3}) + ->record(algo, kp::Constants{0.3, 0.2, 0.1}) + ->record({tensor}) + ->eval(); + + EXPECT_EQ(tensor->data(), kp::Constants({0.4, 0.4, 0.4})); + } + } +} diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp index 18ab913e7..342861429 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_logistic_regression.hpp @@ -23,411 +23,411 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_logistic_regression_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, - 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, - 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, - 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, - 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, - 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, - 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, - 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_logistic_regression_comp_spv_len = 4816; +static const unsigned char test_shaders_glsl_test_logistic_regression_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6d, 0x6f, 0x69, 0x64, 0x28, + 0x66, 0x31, 0x3b, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x76, 0x66, + 0x32, 0x3b, 0x76, 0x66, 0x32, 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x73, 0x73, 0x28, 0x66, 0x31, + 0x3b, 0x66, 0x31, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x69, 0x64, 0x78, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x77, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x62, 0x77, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x62, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x62, 0x62, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x78, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x62, 0x78, 0x69, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x69, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x78, 0x6a, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x6a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x79, 0x43, 0x75, 0x72, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x62, 0x79, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x79, 0x48, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x64, 0x5a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x64, 0x57, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x64, 0x42, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x69, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x62, 0x77, 0x6f, 0x75, 0x74, 0x6a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x75, 0x74, + 0x6a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x62, 0x62, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x62, 0x6c, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x15, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x6b, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x73, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xac, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_logistic_regression_comp_spv_len = 4816; } } #endif // define SHADEROP_SHADERTEST_LOGISTIC_REGRESSION_HPP diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp index e4acc7026..edbf2eed7 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_op_custom_shader.hpp @@ -23,101 +23,101 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_op_custom_shader_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x62, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_op_custom_shader_comp_spv_len = 1096; +static const unsigned char test_shaders_glsl_test_op_custom_shader_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x62, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_op_custom_shader_comp_spv_len = 1096; } } #endif // define SHADEROP_SHADERTEST_OP_CUSTOM_SHADER_HPP diff --git a/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp b/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp index 4b0bc4572..15d623478 100755 --- a/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp +++ b/test/compiled_shaders_include/kompute_test/shaders/shadertest_workgroup.hpp @@ -23,126 +23,126 @@ namespace kp { namespace shader_data { -static const unsigned char test_shaders_glsl_test_workgroup_comp_spv[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x57, 0x6f, 0x72, 0x6b, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x4e, 0x75, 0x6d, 0x57, 0x6f, - 0x72, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x75, 0x74, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, 0x32, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x74, 0x6f, 0x75, 0x74, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x3b, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 -}; -static const unsigned int test_shaders_glsl_test_workgroup_comp_spv_len = 1396; +static const unsigned char test_shaders_glsl_test_workgroup_comp_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x57, 0x6f, 0x72, 0x6b, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x4e, 0x75, 0x6d, 0x57, 0x6f, + 0x72, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x75, 0x74, 0x78, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x75, 0x74, 0x32, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x74, 0x6f, 0x75, 0x74, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int test_shaders_glsl_test_workgroup_comp_spv_len = 1396; } } #endif // define SHADEROP_SHADERTEST_WORKGROUP_HPP diff --git a/test/shaders/glsl/test_logistic_regression.comp.spv b/test/shaders/glsl/test_logistic_regression.comp.spv index f68cb431d7d9e54391a59df577f558b3aa1f12f1..2f6883dac6c1f40a2928ccf8e8b37c6fa6d68ac1 100755 GIT binary patch delta 18 ZcmcbhdO?+wnMs+Qfq{{MYa{1zApj?i1Hk|Q delta 18 ZcmcbhdO?+wnMs+Qfq{{MV Date: Sun, 28 Feb 2021 14:53:46 +0000 Subject: [PATCH 20/91] Updated python to align with current configuration --- python/src/main.cpp | 16 ++++---- python/test/test_array_multiplication.py | 4 +- python/test/test_kompute.py | 47 ++++++++++++++++++++---- src/Algorithm.cpp | 2 - 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 36be7ac7a..8aac68c98 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -4,6 +4,8 @@ #include +#include "fmt/ranges.h" + #include "docstrings.hpp" namespace py = pybind11; @@ -64,7 +66,8 @@ PYBIND11_MODULE(kp, m) { .def(py::init>&>()); py::class_>(m, "OpAlgoDispatch", py::base()) - .def(py::init&>()); + .def(py::init&,const kp::Constants&>(), + py::arg("algorithm"), py::arg("push_consts") = kp::Constants()); py::class_>(m, "OpMult", py::base()) .def(py::init>&,const std::shared_ptr&>()); @@ -73,12 +76,10 @@ PYBIND11_MODULE(kp, m) { .def("get_tensors", &kp::Algorithm::getTensors) .def("destroy", &kp::Algorithm::destroy) .def("get_spec_consts", &kp::Algorithm::getSpecializationConstants) - .def("get_push_consts", &kp::Algorithm::getPushConstants) .def("is_init", &kp::Algorithm::isInit); py::class_>(m, "Tensor", DOC(kp, Tensor)) - .def("data", &kp::Tensor::data, DOC(kp, Tensor, data)) - .def("numpy", [](kp::Tensor& self) { + .def("data", [](kp::Tensor& self) { return py::array(self.data().size(), self.data().data()); }, "Returns stored data as a new numpy array.") .def("__getitem__", [](kp::Tensor &self, size_t index) -> float { return self.data()[index]; }, @@ -150,16 +151,15 @@ PYBIND11_MODULE(kp, m) { const std::vector>& tensors, const py::bytes& spirv, const kp::Workgroup& workgroup, - const kp::Constants& spec_consts, - const kp::Constants& push_consts) { + const kp::Constants& spec_consts) { py::buffer_info info(py::buffer(spirv).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); std::vector spirvVec((uint32_t*)data, (uint32_t*)(data + length)); - return self.algorithm(tensors, spirvVec, workgroup, spec_consts, push_consts); + return self.algorithm(tensors, spirvVec, workgroup, spec_consts); }, "Algorithm initialisation function", - py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), py::arg("push_consts") = kp::Constants()); + py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants()); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/python/test/test_array_multiplication.py b/python/test/test_array_multiplication.py index 55d764805..0dab581c6 100644 --- a/python/test/test_array_multiplication.py +++ b/python/test/test_array_multiplication.py @@ -30,5 +30,5 @@ def test_array_multiplication(): .record(kp.OpTensorSyncLocal([tensor_out])) .eval()) - assert tensor_out.data() == [2.0, 4.0, 6.0] - assert np.all(tensor_out.numpy() == [2.0, 4.0, 6.0]) + assert tensor_out.data().tolist() == [2.0, 4.0, 6.0] + assert np.all(tensor_out.data() == [2.0, 4.0, 6.0]) diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index ad4b77391..4514e2dd2 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -69,7 +69,7 @@ void main() .record(kp.OpTensorSyncLocal(params)) .eval()) - assert tensor_out.data() == [2.0, 4.0, 6.0] + assert tensor_out.data().tolist() == [2.0, 4.0, 6.0] def test_sequence(): """ @@ -116,8 +116,8 @@ def test_sequence(): assert sq.is_init() == False - assert tensor_out.data() == [2.0, 4.0, 6.0] - assert np.all(tensor_out.numpy() == [2.0, 4.0, 6.0]) + assert tensor_out.data().tolist() == [2.0, 4.0, 6.0] + assert np.all(tensor_out.data() == [2.0, 4.0, 6.0]) tensor_in_a.destroy() tensor_in_b.destroy() @@ -127,6 +127,39 @@ def test_sequence(): assert tensor_in_b.is_init() == False assert tensor_out.is_init() == False +def test_pushconsts(): + + spirv = kp.Shader.compile_source(""" + #version 450 + layout(push_constant) uniform PushConstants { + float x; + float y; + float z; + } pcs; + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; + } + """) + + mgr = kp.Manager() + + tensor = mgr.tensor([0, 0, 0]) + + algo = mgr.algorithm([tensor], spirv, (1, 1, 1)) + + (mgr.sequence() + .record(kp.OpTensorSyncDevice([tensor])) + .record(kp.OpAlgoDispatch(algo, [0.1, 0.2, 0.3])) + .record(kp.OpAlgoDispatch(algo, [0.3, 0.2, 0.1])) + .record(kp.OpTensorSyncLocal([tensor])) + .eval()) + + assert np.all(tensor.data() == np.array([0.4, 0.4, 0.4], dtype=np.float32)) + def test_workgroup(): mgr = kp.Manager(0) @@ -151,9 +184,9 @@ def test_workgroup(): .record(kp.OpTensorSyncLocal([tensor_a, tensor_b])) .eval()) - print(tensor_a.numpy()) - print(tensor_b.numpy()) + print(tensor_a.data()) + print(tensor_b.data()) - assert np.all(tensor_a.numpy() == np.stack([np.arange(16)]*8, axis=1).ravel()) - assert np.all(tensor_b.numpy() == np.stack([np.arange(8)]*16, axis=0).ravel()) + assert np.all(tensor_a.data() == np.stack([np.arange(16)]*8, axis=1).ravel()) + assert np.all(tensor_b.data() == np.stack([np.arange(8)]*16, axis=0).ravel()) diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 174f78d9b..cfae65643 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -2,8 +2,6 @@ #include "kompute/Algorithm.hpp" -#include "fmt/ranges.h" - namespace kp { Algorithm::Algorithm( From ddb77702eefc473f9b1ec9c239abc745092154b4 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 28 Feb 2021 15:53:09 +0000 Subject: [PATCH 21/91] Updated examples in readme --- README.md | 140 +++++++++++++++++++++------- python/test/test_kompute.py | 57 +++++++++++ test/TestMultipleAlgoExecutions.cpp | 60 ++++++++++++ 3 files changed, 221 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 8d8e13947..03caa4c96 100644 --- a/README.md +++ b/README.md @@ -56,35 +56,65 @@ int main() { // 2. Create and initialise Kompute Tensors through manager auto tensorInA = mgr.tensor({ 2., 2., 2. }); auto tensorInB = mgr.tensor({ 1., 2., 3. }); - auto tensorOut = mgr.tensor({ 0., 0., 0. }); + auto tensorOutA = mgr.tensor({ 0., 0., 0. }); + auto tensorOutB = mgr.tensor({ 0., 0., 0. }); - // 3. Specify "multiply shader" code (can also be raw string, spir-v bytes or file path) - std::string shaderString = (R"( + std::vector> params = {tensorInA, tensorInB, tensorOutA, tensorOutB}; + + // 3. Create algorithm based on shader (supports buffers & push/spec constants) + 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 bina { float tina[]; }; - layout(set = 0, binding = 1) buffer binb { float tinb[]; }; - layout(set = 0, binding = 2) buffer bout { float tout[]; }; + 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 { float out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { float 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; - tout[index] = tina[index] * tinb[index]; + out_a[index] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; } )"); - // 3. Run operation with string shader synchronously - mgr.evalOpDefault( - { tensorInA, tensorInB, tensorOut }, - kp::Shader::compile_source(shaderString)); + kp::Workgroup workgroup({3, 1, 1}); + kp::Constants specConsts({ 2 }); - // 4. Map results back from GPU memory to print the results - mgr.evalOpDefault({ tensorInA, tensorInB, tensorOut }); + auto algorithm = mgr.algorithm(params, kp::Shader::compile_source(shader), workgroup, specConsts); - // Prints the output which is Output: { 2, 4, 6 } - for (const float& elem : tensorOut->data()) std::cout << elem << " "; + kp::Constants pushConstsA({ 2.0 }); + kp::Constants pushConstsB({ 3.0 }); + + // 4. Run operation synchronously using sequence + mgr.sequence() + ->record(params) + ->record(algorithm, pushConstsA) + ->record(algorithm, pushConstsB) + ->eval(); + + // 5. Sync results from the GPU asynchronously + sq = mgr.sequence() + sq->evalAsync(params); + + // ... Do other work asynchronously whilst GPU finishes + + sq->evalAwait(); + + // Prints the first output which is: { 4, 8, 12 } + for (const float& elem : tensorOutA->data()) std::cout << elem << " "; + // Prints the second output which is: { 10, 10, 10 } + for (const float& elem : tensorOutB->data()) std::cout << elem << " "; } ``` @@ -94,34 +124,72 @@ int main() { The [Python package](https://kompute.cc/overview/python-package.html) provides a [high level interactive interface](https://kompute.cc/overview/python-reference.html) that enables for experimentation whilst ensuring high performance and fast development workflows. ```python + # 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) -mgr = Manager() +mgr = kp.Manager() -# 2. Create and initialise Kompute Tensors (can be initialized with List[] or np.Array) -tensor_in_a = Tensor([2, 2, 2]) -tensor_in_b = Tensor([1, 2, 3]) -tensor_out = Tensor([0, 0, 0]) +# 2. Create and initialise Kompute Tensors through manager +tensor_in_a = mgr.tensor([2, 2, 2]) +tensor_in_b = mgr.tensor([1, 2, 3]) +tensor_out_a = mgr.tensor([0, 0, 0]) +tensor_out_b = mgr.tensor([0, 0, 0]) -mgr.eval_tensor_create_def([tensor_in_a, tensor_in_b, tensor_out]) +params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] -# 3. Specify "multiply shader" code (can also be raw string, spir-v bytes or file path) -@python2shader -def compute_shader_multiply(index=("input", "GlobalInvocationId", ivec3), - data1=("buffer", 0, Array(f32)), - data2=("buffer", 1, Array(f32)), - data3=("buffer", 2, Array(f32))): - i = index.x - data3[i] = data1[i] * data2[i] +# 3. Create algorithm based on shader (supports buffers & push/spec constants) +shader = """ + #version 450 -# 4. Run multiplication operation synchronously -mgr.eval_algo_data_def( - [tensor_in_a, tensor_in_b, tensor_out], compute_shader_multiply.to_spirv()) + layout (local_size_x = 1) in; -# 5. Map results back from GPU memory to print the results -mgr.eval_tensor_sync_local_def([tensor_out]) + // 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 { float out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { float 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] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; + } +""" + +workgroup = (3, 1, 1) +spec_consts = [2] +push_consts_a = [2] +push_consts_b = [3] + +algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) + +# 4. Run operation synchronously using sequence +(mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(algo, push_consts_a)) + .record(kp.OpAlgoDispatch(algo, push_consts_b)) + .eval()) + +# 5. Sync results from the GPU asynchronously +sq = mgr.sequence() +sq.eval_async(kp.OpTensorSyncLocal(params)) + +# ... Do other work asynchronously whilst GPU finishes + +sq.eval_await() + +# Prints the first output which is: { 4, 8, 12 } +print(tensor_out_a) +# Prints the first output which is: { 10, 10, 10 } +print(tensor_out_b) -# Prints [2.0, 4.0, 6.0] -print(tensor_out.data()) ``` ### Interactive Notebooks & Hands on Videos diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index 4514e2dd2..865f72d92 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -30,6 +30,63 @@ kp_log = logging.getLogger("kp") # # assert tensor_out.data() == [2.0, 4.0, 6.0] +def test_end_to_end(): + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out_a = mgr.tensor([0, 0, 0]) + tensor_out_b = mgr.tensor([0, 0, 0]) + + params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] + + shader = """ + #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 { float out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { float 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] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; + } + """ + + workgroup = (3, 1, 1) + spec_consts = [2] + push_consts_a = [2] + push_consts_b = [3] + + algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(algo, push_consts_a)) + .record(kp.OpAlgoDispatch(algo, push_consts_b)) + .eval()) + + sq = mgr.sequence() + sq.eval_async(kp.OpTensorSyncLocal(params)) + + sq.eval_await() + + assert tensor_out_a.data().tolist() == [4, 8, 12] + assert tensor_out_b.data().tolist() == [10, 10, 10] def test_shader_str(): diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 0ddbbffc8..8be2e6d82 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -3,6 +3,66 @@ #include "kompute/Kompute.hpp" +TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) { + + kp::Manager mgr; + + auto tensorInA = mgr.tensor({ 2., 2., 2. }); + auto tensorInB = mgr.tensor({ 1., 2., 3. }); + auto tensorOutA = mgr.tensor({ 0., 0., 0. }); + auto tensorOutB = mgr.tensor({ 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 { float out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { float 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] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; + } + )"); + + std::vector> params = {tensorInA, tensorInB, tensorOutA, tensorOutB}; + + kp::Workgroup workgroup({3, 1, 1}); + kp::Constants specConsts({ 2 }); + kp::Constants pushConstsA({ 2.0 }); + kp::Constants pushConstsB({ 3.0 }); + + auto algorithm = mgr.algorithm(params, kp::Shader::compile_source(shader), workgroup, specConsts); + + // 3. Run operation with string shader synchronously + mgr.sequence() + ->record(params) + ->record(algorithm, pushConstsA) + ->record(algorithm, pushConstsB) + ->eval(); + + auto sq = mgr.sequence(); + sq->evalAsync(params); + + sq->evalAwait(); + + EXPECT_EQ(tensorOutA->data(), std::vector({ 4, 8, 12 })); + EXPECT_EQ(tensorOutB->data(), std::vector({ 10, 10, 10 })); +} + TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) { From 75315db943eac2548b313f30cf5448cf9070bd74 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 28 Feb 2021 15:58:22 +0000 Subject: [PATCH 22/91] Updated ref architecture --- README.md | 2 +- docs/images/kompute-vulkan-architecture.jpg | Bin 268146 -> 219220 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03caa4c96..657cfc4a1 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ The core architecture of Kompute includes the following: * [Kompute Sequence](https://kompute.cc/overview/reference.html#sequence) - Container of operations that can be sent to GPU as batch * [Kompute Operation (Base)](https://kompute.cc/overview/reference.html#algorithm) - Base class from which all operations inherit * [Kompute Tensor](https://kompute.cc/overview/reference.html#tensor) - Tensor structured data used in GPU operations -* [Kompute Algorithm](https://kompute.cc/overview/reference.html#algorithm) - Abstraction for (shader) code executed in the GPU +* [Kompute Algorithm](https://kompute.cc/overview/reference.html#algorithm) - Abstraction for (shader) logic executed in the GPU To see a full breakdown you can read further in the [C++ Class Reference](https://kompute.cc/overview/reference.html). diff --git a/docs/images/kompute-vulkan-architecture.jpg b/docs/images/kompute-vulkan-architecture.jpg index 56013996b7e1c3576fc38af3c8ab6c921999c308..c3043a4bf2420ad3617cfa33f8a5fd3f88bee0bb 100755 GIT binary patch literal 219220 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%0Di|Ti_bY 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

*K{q6!)YgZxTzqD5A<^QNpQr-CxBpv0U! z+Q-K4sTH+nEZhzw_N{3OdeoY(tzn3L4wdeuu{9@x{M0k5KjcG1oqVy6R4@LE_;7? zAE#3Yvh0}<~Je)H3aZka?#9%LT+w6c^+R5((d%oD?8^RoV@Bm2JfA^@{^IL4q(4v))#O0YbA zY$CEv_rN~7F~;|G0n~^yP_L5U`Q7AnD*kM1-+XD7GX_nrG)JK}~fez#Okad^)Q7+Og>p zfMV2z0S1bK;Lrd6{d2HCS5&dw)G} zfDP`0Bkp>J{?`tfV?JDl(Ng{bAzVwJ4}6(Zu>Vf5kNJ9r(DQXpVsS0!aD7`K(Zn|Z z6##r6ktRr1xw{J&E8&g+K;k)Ab*YHYH2MYfb_13_2b~wM!8ihe`NJ-VlMU?ZB_F}A zuF?Q@_0w3hilq3tQ}_q?{$%W{ZKlORkkk~Q9#6)AaPmvQcqj?Lp~sb05u|YeNisXn zF_wvI%)j+oiCCs7vc=+<+5j8;U^_H(5vX#W5rQDImb0)8id|6Y3vlwR%(3rw??0h; z;Tqk!Mf{=@0V+9;W+n$ynm>l*t_UM&fqo%YQd!}G^S4HWEL(+CjjfpdsuVkj44{W) zGSPLr!Mcj&*1%Z`7?3uAk$O%hwF5S8&B))}$qnaY=F*V!XeJy{9w#q=;i-bTgtr3h z%)Rc(8)E{1)xP;#4bY(X1#>PE-=}QR=M8=e6|@@DYlg+c>Mggm?`Y@IU%b_Z1XWZr6Ce`gaznDnXv$@^imV386{<5%e7#E zEGPXFbyG6pllo@WEF2)-(Bp=;Uvg=_^GJC~5G;F^;HaYUPu-^5f%t&Bci(GZdq+_+ zmwGhb%noueBrYn-?VXms=Mn39Z%z1Ew6wG&gCvEU%W3RzpxX@+Eb?6y&`hQQ_2h?- zG20Vh@7enWbT)sgDzjVl^S@lxd9YGfsxj+O40ru>&oRmkN+%M);J>hh=2Ia)-B0jW zshSN@Lp9-VM`x9lC)CPXWKn>5J!`g%Mlqp}XRH;%Uc%RomIDHO272nV%qb?bsyX)n zIXDK+)CZ5j3jEdlEsU^LHoVL+pk*Y)nXy%faLl7WX9queL18pRCC;fmriAqetNZ?G zbcX~zV9f_&cKw$Vo@9r3QbmYT_CnBS*Osehb|H63+X z+RaOEn8Xh1ay{{zqw+ZDxBe_b3w-4O2z@jHBQz#PaFn@f*}M4F`H8HY<8^P{a6}O*e2|U>E5w)hkOY&z=*GYzyMRyvP1s}Y*Ez64TI1;ChvEh z0Ng^MF6kYf0@SzNll}@SJuOZ>rc9T64&e_<8G+sOz-SB_pt2^MVW^Z@_Egmip&^g1 z#LFqJ`B=410qYmRN(=4~v?dmVRb^Uv>STm}hTLIP!gCbMUBqa`G~!p?bOY9Si|B{3 zXBlY#X<6ZAz>)70k3UI945(2M@Bn~ksZy7fV(d;_hm}eoWOk?q{H=cYT^ZP;j(I{e zbU;Hh0o2*4AL9bd?(W14F{#I6UoXjkcJ}=agp(JCDqtI6x!0~itE%nFGI`0^mSwWk zHoVj}GZ0(${(KMmVVv=Oop_^dudbQz1m`h9O+U;7Z11S;-zYDdLVTEtxeX}Dt@gL) zgncUy_6|3?`2<#5K4Ay#ov;AZH8DP5ava76umNsHhn2!*cm7t;WlR%rTVdq>pJnz> zXGXA(c)HRQZ(904U2;@29 z-_o1!=NVO#HJ?nQQ+d&Nz@}dRRKUS>Dp`s`h2^(e_~%1r?DaDGw|tHcd7+ZQhu#E! zvShdesSWAK@j2e@nstQouas+(U{6okk!{BmqKQCy-w=uSyCdWDL*;<%cQvYLpbU%; zF5yT>HMy!a70wftQZe1vRN(5;Yv)NUtU`9@VtNPdz_{4mB)G1tR6%QZk4ACka|Mjq zmyf#KX~DJa6h{UcX!<_5by><2`yVmTR!TQjanhIX5)j2WSg39F-6dHgobnL*7I%9^ zn6CO@KIw?GEt$Z~6Qq~@psltuVl2ERpL=3JdqCET%f*wLklE0jaa>4<@l6<>rfrex zwze7TD-)@{R?f_|hKp)!F>eDE`i9gTe93HcPTJs6U*REoqWMKEb6o$oxihI|EXm2$dZoc*Q<@3y zt}gZ&KIVN&NMPjqUS9whVz)~Q+9%s@TMwB~5egvQRx-1z1kllwA3Db@HZrpAdnYPp z5RF%dTSt};LBT)_+kWqv(HH$P!&D^32UY;(bZ-e;IOuTRX}sDRcBmn&Gn4whYg5)5 z^?MJ_4_z94*n2A*VLMHmegmhUI$Tj)6`lfGiURs6jp_x7{xg6)oc2NWC#_PVsE6<~E5%F}d zAt$Zom+7N`vrBqY3VU48xm{D7?jZ;y7o&?o&kZ;BAl07&6hy7`G`_fCU0~5ryJf=Z zU4H9MbjV67{#wuQ<_c`%l+L6zM1-6maRajAAs|pt4ylg8e3}=iFs0XlIiK|N4-5jq zp8G>J`2WY=cgJJhw*M1Rsg#lxS4DK&LbfZFl_aufQD%1b_(VyWArztPb&+w|Tp^om z%HBlwzE;0e^?Yyc?{kl5exLjI_~U-vT|Vph9OwBt&ttrg;|<)ATF{>XwUhRl)YmGJ$K&W;-Np9eUP2C1tCFeZNq_!}emR>2)Dgjx*6IT%7dWcIebQHbXSB4A-ZouZ zs3J+QcGa^d5xAB%9{a$`XJ5myY9Yi0P0NjCt!2HgxT5}e3*Q?%H97sNuVdE5EWY)Q$7s#KJW@@!c9P8e@+y!X4ts#li;m$9G~fK?Tf2pg;%ThC!!0 zi}rp_jq0Z~mHjgHa=o<*^;)D!CtXWC9>|;YRM`&CLKW7U)6S1Rr+)4}Gs|f)aK%tY zrL%K?FAXW?!NHUI#cY}Tr@HgQ!*BM~-YG+3=v(BT9yRpcYgM0q{2BFwkA1x5X%thm z_trJt-skluw8uHlYBk;-PfJ#%ZWg*hDiptF50piUZRK&%OWNdG63vuCc38NI42gUe zl|<0_>)YRYIsN|j$Gi?eBxtpY; z+xMw*yaD2l9?#EOh8b%QMGM~G<`zt~(QaDlUdf+Xe>xq$I*9G=(~0$A{g%Ccw}`?% zjqt{#fs_!!vC0JwR=u%n9q@iV+#$1SwyA8uo}t$Y69KRd8fW+P^_ZQ;d&5rKK0^b0 zZQTTWbOk_G;=d(P|JuGB2TZ7K@m-su?a^OV{vz7;X<%2|M*LP(=(oJy-DAHV5%{aR z*_l!Q*VOm_HBaeJoSh`B#3}=a!MPn@=8b>QIc{2|e*V=Fn?o*oexf1*Px)%i5fcF? z0xY@1i9NZcEfLlLQ|+B~udfbhrQM}X6ET@N!3 zz<3+iupmb(Aq35bf}p|_d|1U4wsIs7>^7PpxS{EdBj!NOhozQLWn%&$gMeiWs4#d1 zf=bNpr-38%3bkGjB4n_ImmA)APa9A&&4#CJfhtlv;8Ul~0U`kj`&kyrh0^doh8p_G zbpVzV0yxGUkA@HPE|TMj$H9};uxx-{&r2m4YPr;l-!$DWl<%+$SAkf7Nb@|XN3{j7 zy6wpTK4>%oL+@_@56;|BlK?N2b^}Q1m5*EM*e(=R#hRX#%SneAU>$6geQ$sCIp9?_ zzXz%FaQvp;LdHWpr7DQVN|0G%#=&Cpvrt@KU&@Bo1n1!>{8mBv_9p{BZ-2}akiz4_ zEun^WNU^Ml%XrTdgX<08$TpGm$UeE1f3Y5bAnY-a*4XygxE+I3ME>(gfN;^z@@$RD z!;hmSAlbda0^jje>765Vf7>{Ku-gB~TC93*wLfDWnmNmz^8(%cz1 zu!pBwq+3$g+~g-^a|Zkg3NPwvEwF%V(0?-ke+7?48i)nq@St+QbLa_aPJH1fL!6(| zR=?I#PpDH*dU-YT+M=3=1y&-1e}&5G{bPRljKhv-Re_&HR!h5%{5`j$J&1Nj-1gof{uYmO(csI|o&Y)XpBaUB|NXDi#UCz9@S?1C9IPtx_U1Cz ze;d%>W!xYT5&!C8z$G~Wv?g2?7}aZ**tXW8K~ea@YfW2l8( z{^reL1CZ(L{T7kkah;n|GGuZaiy!Su&O8e??>8AxGZ_rCHnY!m`vzX}2)+J9Mfl?S<4>%{{Gl=*44UTnu}9zO?xkieyAR&^uqa*VzIiw z%G2ILH1NTe7hM1#!3OeIIDcVH+;H|c&Nx{s7WlGQ@-}1uOfNVUYJ+MEJhA7(O`y%*;I?%ucIat6S69ANv-S1+2;#-$5){2-X;1 z{SfcY1ubwYEXKVg%~}>PPUqTj6@2^I6B1usn|glhAwthwV7&G|72EEcY{{4WCM#NI z&hj$Ty?Xa`QLi}ANAy&ysIoX z514=mX%%XPb%Z^A0yH^5B6XIpTKUM(z;EiHwjZA;ISrC&_P`s`f!<{5hFHI``?%?M zMS1a6W}4m`GyIV)-?bTphz7U5Z1^zIA~|+56Nd+YE1lQ^@lv&HvB59*;127VOQOEN zeRg1`%9n>2I`V$p3^ovSDSAZy9=<$*THoA)r(E3fip9;o5FkObTz(`wH-^r~(xJnJoPZ~#@i#I@sI#yg%6g`m}?%%}IjuE?s+F!oY5L1`qsZT~Ia0TymlKEiyqxfU&yK>iI&pkJsw&R@EW5 zs_ObGzY>rz_%4B^F-PDp>*Il)BqVuew99xYUrY3I_*a6SJce+}_ zHl3R{Bk;$w+TB|&y^p>`rT-qr$t}y|Q75`b-XT$5x}o4qS4UCxL4v+KCRZEkP3Roy zgNye9tb}hM)hJi9EPRGnWnd~BX=aH7I)havIG=SAGO#q0akg>sJPQSBjUhTzHADff#>OmkS2bpBOy62k)4f>?!qmL&8?&K_%2zv!SPRK?sGj~4~ zmklTBS0TG9TO%Sv*2@&|T$?i>(PxA&kVe)ae#*XwIm`&v@g~g9bN+_STS>ZEL#LM- zA7lw!lQM6yuHacaMvK~4a$7<$77JHr%{iuyR2?@WxyhPFA0JA#w8yqqoA$PCN^G{M zNG4>_)3=$2Fmk$U)s=7mrME)nH7vBCFP{6<&>e<{31%&Xz%k(7!C?qUx3+N^$Xo1f z;>Al`hyl5jm7}Vu87~1gDN+GF6ocNErjqjRA0RW_iOc&3ae99(zWAT=H^m(_WQ96A z3e0Hx51Fij@2Ic|B)0y3QlQ;y|G4gUqE*^Sqvef92 zK{TbUF4*RRiWLBy4%N~vx2?jwFmoSN+n)q5O?p#iUEY}dx)=d99L_njWz88dA1FyQ>=Daa$)wz{tsHE()vr(Ffu>XfR%Vgu8O7-Rge3l0n zX=W2IH#Z%z6Y;2~<`$Ev<&PBb80mb|N%!7Ce{TL}zMRQfq<5`MoD_SsW|+-BgJSlk zU{dOl*d`WB_zWJla>->7jYnmqzt7nS=HTBq)XDEgsv&#Nu*j&DY2wp}{G4B+bYFt4 z_okDsw5WREdpxVB-oZZ8BcG+3bqm#d1Pz|a8YAe0Ge*W_tVpyD5R#4y)qcub7)Y^Y z7F9s_UnMl{8Z^qwH&Y)!%sqc@na;%^@q$N)M{SKzildRv+s+c${*F5xE3*PhWVF(E zR7Rg%8P+u+I#MM?Wu<)bm8VP<*HgVB>?)Q6rycIDHuaT&1Fp2X?vJFi++!BeVbv2E z1y>4dW4dGR=WE5d_wkoW(yWtfvnu?7Pcjc9(|a|h2Vm{(GG}q z;dFpHZEP}twK`>C4vkUhyq{)ygd;CBc_4D)$tTND`WGTxFH81#Ra@sWSQmF+pR9=R z6Anm%d#~oU-|BoJYe(1P)EI2ubLZ)LMl~zxz~S?RH8+d~#&u>lK0)PdX{%UH;JDFr z;n})HFC(-7Wjsc_t!q^Bp)$qb#pUxaX&wZc7NHN@R+E#83dk+yrHs(Q$R!dgP0W&~ z^V+qwLMi{f5!R6tW{JPZ5v zImg*o%j!MQ1LiiqSt<;ds`@I&X_lv)caej~TkA0|8Mud5%ZUZYoKLDi<=B3d!iF_l zuilI#)IB+NIm5A1sfF`?DWghROD+b}^>Xk=p`q5D$D+6McvA*?AABkq4Jqhk*$4$e z{(Xr4FU-{rSsh{ZGfGIcg4}>JZ_3)UsI#<>9GDx5*76yMsVIu?KB4)h`^HR||2eJj zECvamYlg6Mf+A9#r@}dc)}IKbDH=yK(rSCcjtX@o6k+80#?(i zMUCG`-VAAF^vMvZ(oU{<&Nqc+oy;06fLZg*X{7ywW40$(nZlStzOXqbAK8K*#UTJ( zU|TVsO&3Ul>UF?px)gB4-H*Ew4>s<&mN{T$oEh2|Ks(SYF65wWk0&iY!*X~>t)AVM zfzL;Q)5@1c*`y^RUp>hk*C}7T8WkkvTtInPVCSp zAE$)2Ol?i0QrzT6LSp|ko&MwMhE$Josap67W5W)K_sID^e%1TAYvwrj!v{yqRk$9I zeyC*mc?68v5u7MgE`6Qebm`*CG z9YZc#X0RvN68^azj`ZVd>1ce2t^{ zwqP>@a?n+#b(LODG22bN-potp1&{y!@+NC&!DnW0Wq&bL(z z5{(Op!Jl{(vvNQWgNL?S?HYrm;YENTM}aKy(+mUrQ5_e&hXAxb)e3f{3=nR-7>UBH zn7TctLmS5E)~rJy2hIqrGp{!+xIN(@FB=0o*luW2KrKH(^P3=;Z1w^~0MtNy^|kyU z4$TKuuw_Cm%=360tr)n+?bR=k!#dZJFtq&|5H#3+zrw%`NxOM8aX*NH#7Se%6s_a- zSb!$zt#Kc3qY`1eQv9dt;!WvB;MsAns;qWO;#NMFk}so-G?=p0_p15Z2l3}Q)|ESY zH6j<{jmTZ(@3G^Y9dnKW8~)F|h~A1GGqE-pCKGX~)ujn(>=&On}> zc4MaJm)gIk2XN1nFSlqBz7@2px~)CuvWP2?OPP)LKR|S1K;l&NYxIGZSdYZ^qVrG| zGnkrH^W(5@ZMB&OSEx&|V8yA z-$&vfQlXwhSV`p$K1`%!KF@TwcT<5(z%Yz3fPA6*vPMxex|j=%^988vH;4I|2F9RY zzvrHRv&3JIo9!jM@dnV4qqhv_?eX#PyRXIR3s@Aw1#o+To5BPpl`*~{ z67Lxf1%IP;5LcLoWA}e0crnY+Fel}(raicFyciqujgf)V5`91i4;JGsJ_Z}e3Ai~5 z*oxLKWn)Ir-6k+0Y1lX2Pad% zHSPI=Ye&?HTFsPF&ESjkmH_Q+1qw#>5#8>3c0Z-%=gpSkKCo-bh1G(1#A?7lN^!DV zMkPt`pAr<@lH(7TQT=|il)tevf~rLhKBEDT0u}u&X;*XXVpi?-Rxwm~uS(fnlU zYki<6n}FCxCgHu0vfH@wz0jdoi?N^9+gDLjJ)BQ)u}KxM?;{ck5LS`i2uvt(7Fe?jdz0LRV6W~Xa}3hvyR!c%w)%!&p-^2- zz0>CiMU&_=3zI%1Z@Ypwo)4HjmTFMf7}x?{;SE(6gv{ zHtP7&MW(#*S85w7Yv!oN)kLArlPDJ4nLH(%ij@NtEC@r!_~~xNse3U!R3{d%C{f-$ zNH_891U1WP2}YCJ>isok3(@%k9(Fq7BJBOzj{cdkflUe##~iaQK3IugyiP8b6%h@P zFX;Dc@S|})&9-K8zJwF6mr<>uV(xk`jxOtm7dJp$cxmil|CpE)ViFIHl`Y#>)@Zs- z#I4DtiOdRsw%ff8F0`!LS}Al<_E~HXltZo32~XNpg19b&rtYB0v!(=7du~$Q9_JG$r;EHb}zN??7rhCb9E+gF@%y1*J19@OENB190t z7V&mriy?78ij2$}SAi2q|X`B`_}G_M&SMPI1f9>o-c z6%uV5N3CWx`wt;wEG|83DtuBO*5gMdI{NBn!9p~WrDC#;qCucW z)9ejZM9g&Az}@~=qjR=?lli)=97b0b`MteMZof7WK4$8Rkh8Wnvd`y^H<`@*5KL-S z{(O>7f|RX(`AN35+gAcy%ra!{;5wj&)7#`Kndg|BfDTkSHoxqty{bnvfA52JJHko# zGxFf_cCMayR_~)(W}IL3`&3cpeCx%AYxU{MK+`&OCwv~F8k>5 zT1+pG-^z@x(}|}ehb|A(hkJ6Ca5$OSt*M*GmlhNtxi2dF)n2&zL8e-YN~q>~oPtA- z9I|_29(^=3#~&kthb>%R*odvwo_V{eMwcttB^QFU8jf2g`QS9EqD@-9G0!Vyu4+sajJLEbMP18%u z4>c1@825Udu%_`YsFGAx>56K(VkjpeX@`F9(8A;R)9rKmZ4sTl@2&Nkj(5*n3ttR+ zeuzC-1{32^K|yfp;7~+svXGPBYW{d~CR3SS92D&xtEq6dDc)Azj3Yjv(3tt0F7MP; zUQYK%J`p}_$8N$kfYs{)p)uYdk4tIsOCv>>Xf$OBcb#3gbrYDhx>*(f=;pecDf{DD ztba$w?+<6n4zTg6ES?CBZ8+&1_0eNYr~U%(9e09G@(l%7e&7i304N~XO-D>Sg^>4O zK2KBf&5aLP6=r3N0)dzHO1+h1u=R;8D!5;q|BZGDKjQwKO3BA(5*ij;=*AJ(;5NUx zAoybdRoik68hi=-j4k3I$m$0x5hdjZux+>uXQKB9VwPFHGdh*g0!IYIdQj(~?{r7_ zp&wLAzbnZ%nkHQN4_+c=H4(7j&p$>@F~jE@fnNh#h~6;6dm7-eb$_8e!aV%JrIeiE zutQGx&UXMDO=|0;KM0^&6}9KFEIZ_ccD@Cmmdc#_L8lZOTR8X&jcs9Bwks46HR<*J zpn#H;v_|q&h-#tF``Iz%Qc@R+FzWhL^`-->R0=>x3CTfA zFsXhcyyu0%jeXEYk(y4En4frw0>D$HoIu7IpjHbW!5YA%ntRUp}h(;=A+aspwBE?jv zq|SDLJ>TI>Z9y)`v*LvvbZTgou1`ux>d$sTQ;g<-iU_QkzH!?HWy=^@JAWZLU1z&I z=G#vPj2|vEbjqrn+ur%Y3;`baiUSudcwFb-a|FIK|a-u|?PZnLvjF&JNf|NH}^r z2F+n6mE9CI<`8~Nv%iM+Lw%Jl{m0W6X9}EemLP88Ay5ze&S0w{M6=7uD|fm2^V6dj zr+ep|TM=@hF8x&Q1CK)~Bgai+mO%|Co(KjY)@-1}D`o?}Kn>=NGA^1C?*=b}W1r!W zToYK3F>H&?aov7n#T^$LJpmUh%!7~`6GKn&U>giX}#tvK`^Aoz&mOtYbL2C11uM*x8R7JD03DkGOC-ER4 z2FI!}?2v8EF>rGQzX95+OU$M~0+V>D-_(was5BN0xk8(#);ds;K!uD~eVTspe%mAy z`yXYxnCdkFB?Ki%8gA}*V;yto=rEcipV? zsRXk4tm}g*wOo(oPSW(xYV!AbBq>q6N}|*4FTq@}(NEgn8`=lv>&gdEVDX2DmbVv{ zZ~ymhDCJKYN_jc~+u4mU1G63EV*`pD*aDBMS6S1_(aAoRzVU9s`JrwVjZMq7O;=^1 zMGLYtnY!50N(_-S*kI%X@kA^eia3uruwyZUkCHG1%N7I38Jqz-13ZcWCh6k6MgqR> zi;*xaiNZqMj?2DtDa3pTB>&AD{veQ0n3VQz)g9jPtaJExtsq`_46JJp>hM3<9+H;h zDyK57b{>Lm0n1=ze->B-fgyI_J3qtC&DP-+wSr$9gTLj2(8M1>H1|B@1^dQHk%FCC zK(qDjJo1qij@xq&TA+ddppQ@n^bu~9bFl3Pz8r$#JubjE4#o^if2VqYghj=D*2%XY z*rB5D)E3{B$DH-%Gmo)Dn{>jbI7F^rdfwE&(+a0XN-TjT*_nbvN}EkK zAOLO^^9G0w>P~>@+5ywZWX)UznU@o=4epg+CSLd_vHm9pS0}mfk3)0RyJMF4w4m-+ zC^W5NOIKjEc5KS!w2ytJ4n?jvC#I_u=NMVl^b5{jmnkPD4T=u^ptZ5q1|7XB-fj^= zVO*C&R(CY0F!WhSF^u!LfnQAZ+ai1^9qusbeu(){;gevLra4$+cJ^)IPj*Mcm|Zk( zm{VhVpPP&Nk(7&hq?a1J&n{<(F^}zeiKJJO4*0~`I;>u?M_2av1JFH}XZh|$Mxn$N z%62ut`x)+LpxI*QLIxcb^PdfiT*}gb5zHDKMe=1#Gp$g3$=-8FtZL{CNPmbHCN8`#q zXHh?&D`w^6PpFC4((2OHWm77XYHTuX#j=W?-k@MOb+M1y+VAcBfd9Ke}m>J873#Dm+C23{C=_*B7H5o$JtixK0?1zE7vUQJj72SxJg?R6h1@Htdc{M2KUZaId$xR&#i)ydSodu`^d>yw#|c@iddW(+RC8`V@5P^)Nq zf7K3XjLL4Sp$Ra7`NIZI>)gE3!Lm@vzP@GzBFb{ChxU-Q@ju!M*?3Mz$NW_>+b#{;A+z5;;~0vIaKd z0DksHX96MSf)w62PLQTLAiU5&5agCpm6Dv>K3HEEEECO3H$)$)A;D2gcacTZ zKvaz`@pI4Ppb;`R`?-qEFSDyUif04ZjDjUK;z@)anUbB4DyV;0Nz~3CV`k};no*ek z@w3{&Fn6}cxG(nlCT9Ye`YxSx-MH0D-W0vB`FQMHoO{Fg0>T~hv|q0ij~X{fMpGLlq$O})L-t*DXEqCKlSMX(yzJ&E*{kw-R1)P^Y z8^HTeM?B@O6<%;5zNPo-t$fefyFmsb`i;iNd8T*+ct1<_qcY)3dXDp%tK)dc<7txX z4FP9MZ+@~gD5pGCr%%G_ahHDOEd%N&2Ir4?(cJhXL_qZJb7YM!y*vzaUFTwrDQ8)A zwVV@Wo8{(2t(r}K7EwOJTK9N+W?bHaWIEdXi_&L@{4|G&9vR=Gcfw8jqz*(?9^6kV z*u)$cXR0(q>&qE+e+ti)U8rOKGfMS2X^$(GYL%{!C4PynKU&UY_mDr z1fC<13W6*IL!)mp?7tFRD>lD8ay;l`qx+|%OJQH$#tgpAe|97Z##2YMC&%!u78yPB zB7B&+75O4p<~HVfYjpqehP#8*9$BJqYY7+dP{72Gvj!pgxa`bA*)&@6>oVg`V1LFdbLJ9`&Tr3sSF9^#4u!m6Zt}GX_zVOd)864-v7mUMb*lt~>9eji5v`>zNbl(KN_t z6FXn2){KGUEVXL4lneqqBtoJ;QAUl#5a|wV)Lnmia#0$T=}c77^O{++-{euhQ(I|8 zN>j_k&^dEl4bGAG8N?8l%yUxW*YV()On&_=8amgjt4-MF&6$fIHgRif);>PNO`{!J z@l1grC_3)UTkPe7%hSSCKKF}7BC`@j>-dwN8@wD2vM**8B+d?1qDnhBDnv7roa$Ws zT279)QS|bNufNOtBJtoJ@i>Eon$+GtdQ}&?NB*8MVHYT_6w}A|ly(O--w7NQa-HQ&f0!mqv!80WnBgys2{mRf^b%7HIZhdox)?89s~q%{}Dt3E^~i-ndw z>LOorj9wjn&Uf!@RGF!-qCT80SeG&QZjGF>IwwVj{{q!ItE!tk#ZN~2JRCOJ zj=QUm5^&cY*k5gih<%cwVmb3#>$~L3pYPB31|D<7Tz&f9!8tGHV4j~A%}uh)-FfuT z;xy7ESuWL4Oq8D_C{pvg&cyd~c?)lKtVZ!p-TGC`n!*Gs~5%lME-NBbyeBBBgura6fpBxY-?nhfjT35-~ zO3zFWXmv+_8u9sbO9_#ubfKh3h1yMN|1T?*zJ<68mUks$j-Psp)K`*E-EN zs;|z{G+%R3OHou=D=dS2AdHDJo*N#;eeS zjx0ezz5Mz1`3#(klu-jwi31vy28lY-Y`Q(Ah36Yn(@Ngs*pzZRWi+U?r6Wr@ z-9(hVn{!_gGP!(mAdr}jKUzyaJw#1#*fTC{y15z9jECkHhOe4ILF3LYgSuRmn7yrb zGVyIpDk!J)_y9o~^M0Pf2M6~zHx}x?WMt&)mF|Rc!p+N4EYNCCGq{pYta-(A*R~M{ zADOEAiO!?q@hKy}ky1||#GruQ#!vW1OKISp_|dNvyj~wM6P_rw<3unWvOI3MuKJXt z=dPa!#c6eeyJW?5FYYV{kX#$smlx4JEEdtn##m>w$Mm%gJe&U+hv8k&ikA9 zGDnxq6RCs6sxJFYk`i0F1U#~GC*l zYiWY8)}EW7Im6&qsEGrFnmm5O^Mkk%yo0gt2Q^0}2HFaTAp`NmNuayL_Up_lrvNp_ z9IV$49>nlFw^Ur+xBi`WL#MO5qG&9v&6L(ZKgzVRMZq%p#o0isN`l3rx5Rcd<)dv$ zZF8>tv7g++Lq7}3 z$Y*gdyk{4DgCb^B7bXe25oq)GnBZYh8{BH!cpVCi^ z^)dVlFz=rL!s8!tNxrv=U^WXsT1{c4zEbz;XOcFJ*c@p1R2yn?4PFaM9bgO3O!Ss9 zura8jsPOK`iYbu&`jz7W8unyyApHX}(GRbJBp8S!GvYxTK#2sX`3n>TqR&EiKXPtm zpYRli*zk}6HgH1`WTHtxvqN|mfO4H155HBkmHgU};mh2_1unq>Q8E*)8Wvx(30sH@ z)A8JW`d=NHUCr;#y4^2sceVN3R$8VV?I{XUR<+oAXD_g6d4=M8kD1?aHZ0bx$Tscf zPuSu-p1kmXQ4IQD`OF=xdw>bHe-azO3w8VHq0e9K34Gr;eNP?OZ?E4sc8ZE5m?_mT zCmFKO0 zTVOtbvPZQ67M9j;EG&&bI9?ofybowdwe4Lqe7+Hy-2TCof?C)iB(OuazruaxZxk#) zxLvluirYloYDl&H4bo!2F~_|4!IUzO-610Am$>}}bSS^Eu>9b5`Qu8KG$SK3=d2cy zqb^Bi{Ls;N{k*Jbv~!56acquxsYbRe8?fomZE+6&BSe{mF*~qcAzkp%4=^{HKIg7uX_ z>{A_j%A~v?@eeFQxTs99f64hqsZynkPDHlAVk=y$dKTeT7DF{*P@Zz0@kqBHMv zj?;DD9Ey{3_nduJSck-ctx(5R0=kE_Y?b&HXRoD>{&X{<$VL`7k!xJ7q90=uSP2NR zX7B>7oLtz_3*^j0mH8A0hgEVw$)a;n=9gIrgSNz9$q-m`2T4$t8(#_DK7^)nGgT10 zS~sdY667^l-l+R<`)i#0VjJ^6lSO*X9)-gR0C=35w4_s6kJRqW`0+vgTW7{!`966r z_#$+iWUVp2|1a#8J|F`kYHX_ZN8m?*G4+8SLue-=hb% z2OHf0yHK`_FTM=ep?v0Ne({Hu?96pEd_?bM9&96PRZ8It4y-ZP-+U#wO#*o0O?1spG>P>yMdQ6jQ2tw~hZ-R;!T+<;YrFF7j>qrJ0dnz0h3Z3$ z714}^t@C`CPl@^aG)6w<&fTb_Ikm!H?g(NT6Coq>`xN3e)OE}GvKdJu4O@{OU*z?% zH@!H;`E@GK)yr~ZbALF0(E6b+=*fIQ3$)ff90{_lAPz!{d7)IF-Q(ygY1|MkD|a0& zK2tcIJUmr>EZD*M#aDuyb?j4NZ`>oguLP0_&Ip4I%ZeVgM_o zHH4lA?ojM#W&+y;>S@f9wh^$W*HCCqVYE_Nn&?-8`yih9aOLTs51t;bu+^lzOnGQ` zK>qmyva8d7y-s&0{qCy$;J@ob%cSitfkwz5m+?hj>rlgJNky)AmD%bm`v>-u51Q0g z-WfV9Wmde`BgH=?Nvf|-w;w0kWTF(R+TkEX{>$y)<|6bo-Bn`oC*+I-yM3^%#YU)A z1M_IaFH;jCk#@1c>{JS3gYf(-pDLb;GB) zV0gknJga}tByR60s9NXQVmVA@)8cHH<1L<3>Cb>#bm3Sc7*>T2*^DNu0Tu8D0LqZ2 ztauj?_&W5Z6@y#8E`c^k0RBP1EM|oYAbJVFX_TtTu?^W6&-p?%dw^Dz01o@YLBMBd z@$*9wijv`X*Y^(EWX#&)Jr_YGJoOerVcRn`V|{@jWM$(z+vQW-{!FCg37zmhD!2OL z?efKKf2NA54|_3J0VgKZVY`4*+n<`+d0fL@&q`;V_1Cp>E*sDu?9ylmgW9O!!p4m)KD^t1mduT|w>N4`7qp0yYgqdIY_9ROT!{-a%{w|AW0{r5lCvHSm0e}tl3>--9U#~Q*Bve~~a+5W50 z6nk#~b^xsXIqd)G``BOPV|%u+j`i(=R-4h?dOJP_V=UJ=`b9@S?R`Z_Q^UWzYE11Y z!c|%arcwA-SR=C5ycmlsE@+ld>H<~56`%CbAnmvRE5R2tJMYxaHo@_L%BtM){1CAd zKFgUKT5MpK1*t#}IL;rokf(;qT!q7shX&+-Uhf$FW(3mp^<)q&BgYe;dnK+Oe zApT0Q#^Al;d| zM!tL3ze5A^QiHVl@|AqklOn%g2QZFR89hB5S#atC^W=w1o6GhFubgQ??^LgB)C$P( z9{Wo0v8(ls($&u=L#l5ivCzn=O#kK>^Xq}x5cqdUy*nHJH5$+;B0=B^-}FwOfiZsQ z$CUPFJ!b1BYRgR{iLf`ib0>lFYz|l=gCzctiQv@y-rP*7oj+221@j5fida8i{gHT1 zwEHw@Gh~R`>g<(?+0?PBsJB+Swro0aBtd{A+PkP#mvZ}YY3$huK!CP412sRc)-l=F zfKiQE%IP=GJOKItaLKr|Qube0X z9SWX96&j#Uvqf_Tk5^n30zJ+&q55XK*b=sF7jMXc0zovH4m4OVo+LhuOH zU)N--KyAN#$E!};&cS*>cdyXs8A(UAZR&w-e_6NdZk2zFs-6yEQs!OH8dy89$@8XJ zI0&&!p2?dDkPFRqeir;nIs8Vf&rc_QeJ4>d?6`**=V`V(Xd#>?NUU6VZu-4uYzlPG zWN;a02~@Bb)=TSv{Ysz(%Kpl4&Ytey+iLHVfXhmPnpaUc?D1`9GPKdR=ujD_>TSD1 zdaW8Xet>s@+S>)i|DUc-OZ_}I{nUww_ap(+$q2D{k=I3>zzTm|@jC@Dd;Wut6Yq#7 z?Tjsrr!D3szAK#On~&j;?=paguEIIs{>hL{;RxHjgg=4fa8y?uOsjJyI_0u~j< zu!Jpw0K6xg!>|L^%Mvy71T0`_-BHqVQ+OtsQ3pCuI12%Eg}l_2{Kwh=5-(yK9OVz77)@qX(n7YNv`_JI}o zoe?O6TGu>>?~GY`jj#7!<^}5z3~*x|g+Pq6d8>aMe2W(B%vS=yvJ!)3>o<fZHad?PYNOzi7#!UO9X-%ll z>tyAz>yxeAUkQ=}ajn?Ys{ydpqhAS%;Gwd*605$qz^C(D{bUHo0i+HZ4RE2@fOTCQa}!$;ZTlR5;PfH-711-E_JpUt6a9< zqSkR*v=HlI1#@P{FrWLyz^Z{=Ol=KJgir?WHD&nAXs~1N80om3L#sO@3G2zhE5knB zz#D=sYsc&i+&LLt5XT|Y=FJ<>-OHOV!T!BtYQncqM#{NdMO+$uTmlzLK4AoQ=--oB z`kTn4e^QB$GJB;}(aCa#J3o1l!>d(rfK=%^tCve&DTo11fMr5A7>p%J33KU5hO;AQ zdR4O~4YY+xF$++2sj$yx=B%jNvim-C<`-A0C3h}tGtci_0nc63YuY%4uPKLEMgZ6S zGHgRR90nk$cr0u&4o`Wu<{PLLMT#|RPm3&DT=PmCJex4p$rvqrjfZQEf0&|x_}L># z@1bK$lOEB}yNrCDe{v_oKE%L?%bK@tI1X(SF>u?>Go{{^`6^}0rQI)(85GotG&yDc zFhEICm0rXrOL{Gu!!5I%bWNOW6QaT!y#QZZe8IOQ+B#sz1Kx59=N9l5w*ep3jgsX0 z{?_oU1--fr)Us&JcDapzsezU0G`kc{js70ZKDj)XZk}>#rK(z`n%6@FLM$FNqCP++ z&<~YxeNzc)o7BHhNoqz^N{?w?lh~s&Q>MUU<5#nVc~q6+_v~XVh{GsQVFx|Qw?+aF zMndG5a{OZK{+5wQ1~;et%gwij`CLaCb#s!5gBl{G=2ZUEgmb4E?OR_N@mHv!93%0P zVruvju==$eay;bn(&7xlQMSV&N8CZ6qP0@l&xB2fp?64clj8pTI@sww7w<@Ct=lub8B!&7F!y7`b?aubv++_3mHBbPFK&*RS3gAd$|(Sd7wO;zuruwF>= zbue#R_S%fNQ_i4yPL$;0r}4^8Cb&*)N{1TFW!LS$ZxH?Z&oo{Qz;6Az$>eYx6W(t0 zd~cPd2}fnE^4+&tX$+)%vn=m5Es*RB9~;g^4>FjSHkE`ey=HvOQ=8hWU|4;aT$Gmm z1mCaUJypczc_9ULm&($Zac|?(xH@|4f`frlG^AN6$%JK^kR8M-`c2a5bARY(QcA3^ zaqSKA7%I!PD&15-8R!hcJ33%SwN$aZjjm1rJj4aRTmM0MyqjBVv^hdB|Ng*`(K3iC z<=#V0E)C-GnHyb7i0hmmWL@iTgs92nNUGvnvq2Nv2GDy2H7dR4ACARuITD>C&DpVb4DYqFu@NoC6U)4mX*R6UQ8CEF?QiQVU zC5Y@=I1NWjuA-#zsehr_z8jML+ZtLqLjLeEn-0_r_*hybEwG$l37#R=an>+2INp{8?}U|pd#D-$z}v>9wmT|ekplets&<6)-vZ*RD{o*NIX~9Zmc7e!Bme}*~Y@1_hyLw@yys&$cf#6O0(y>|7uK0sT=+8hr z3VJX0klP4b)6P9icC;%Iy~ogwQMj?lJIH5b<)JHcr}5UPh!qctrj8?j!=xs51;h#v zP-9Xmz8F4(r~Z6*aed7$I-_XdMHf2kMN8%P4#$qyp8m-2AjXR-oou*k6iKJ}DOzGRXWLKvR?O_|4c)VIAD{b3 z_+Zar;a~-7Vfaka<~&|Aupbq^3aVVj^PeCSvdwT>m5V(eBc+QdF1w#~^voE_ek0i< zgCDlqCptFv+?3nusuwo}TL?5!qrDfF#T8_d zs=(*z;^=&^;z(N2!3VBYhaSkBY`5wC&BL(k>!czAy9&j0sm5ehkbDK!*=8@go)bdDEj)d4}jLqt> z6T#YO^jU?2Gnyxm3<<-SC;S&e6nyHZ#Z;%SI31Mqk)KE{WOvAu(6#MQc7}V*tsvws z7P=I$n6Z>aqc0{L%6k(XSP36#cZfCZ5p0{y$ z|L@hq398cDBvXgFiMBp{k)grOlYH~&DQM-G4%M$6(T)8|VCU|)88JAI;zu)dg9W1g z`vPGlE{;OxEqZ&Iue#}*bIBE8^m;V-sdA**XrK2@%?u`qO@{_PdEJKecb>0Q>!zLX zNSc~;V3SQ2>(Kwa{|sML!?UXgpY11l7~nL+?{;7UR5!XabPhYJ4ql@Yw<#3qZS-TE zr7j<{4Q9Kn(@ugMo|Tq6zjClsAa^Z8VmOSB!r8=IM1E->m&b^=^8e3^@G|pPEj~(D{ zUrj>@Ty8T$@``m1gfMPK_zDksU#5K!57s7L83_WSj!s#rxdA8>OTNLx<)vpUQa-@j z#UaWz{K*a>+H8=l-f$r0qrlRJyi3F-aQ4(p2=pG+w} zYHDie%9&C%ymK&o8XEO7r<{qf#YWbLi`Z0ggQe10D7t%=RyE;J-xvD4^Gts4f9egk za`9M2?8`N`twfi_33wa4Ez9ai5>POAv00n7PN`?^B4vn+E>Z3h zXY97)pcq*bV^KI-8dQ)|gzUVsAY%L6aH(8Lc#JKR_>Kd}8(i!dTdeEO-JE44ix+#Y zZ)fyA@y1Xnb^Va-GLeR`fTzXlMW%>IlL+w#fw$JO%j4?j@~^)?vP5*!^ult$evtqz zB1$gxvNbTmgc&MqX!n=}c|C8co`8gfd8v}F1Eq~!$rFk^K?%fe6KAEU*H3y(nV*5guKT*Vo#OjX(NNE=MNx3@8mu&?qUch_ zqjI9{I|zd+^oQ(9CGUTP%p_E{karpMgGq<@4tm2KIVXBX#kG5q4m8JN^0eSP=)tDw z`7w|oG2G}B4v8v{8ZXdihN$5X|?s1Pz|FxA0}oX4JJ*p`L|s9Vy%7C1Z~g)PRDlY>GUwasyB3iY1` zwei#_Yp*AVcHGku?7IDS-Uf_Tg-o4Oe_iOt`E1-arx98e)h}d8*ct20%;XT7%3K&m z^pr!Af(nb0=@|=ID~=|F!8)Eb`I|Lw(H(Tzj`(M*Xg4CQMwHZQjwA*rE zIq)8*)4@Q7?yLJdfJG_)tX*48Y;To~jk< z(zI1gX;_N7H5_Nb^F0Zx=Cr0%@yyM)zS0d_Rn5O%7R#&NRP%Fxm_Q_~X`+UU)jj2E zQsJ0fYT|hYm`Tgv;e}vJtBb)kRh@(iriXh?X}aJqLXF4V87KbRSl@&|`eoR_aN-=sqF7 z!L`~9iI@b-J&AWI%G1+lIPcX>E-jVw?b(qsvV!JtCv#@&-8NYEQdRTEbdP+S?A9`73;DkptCH2vPX%kz6{50z*tW%->ki z@96-6@t(*V3mciM*@?4bTs3If=15(Q1c92#5AGbP&m+KLrl9(N^A@7%q3K;M*q#v_ z>7O6`VaU#LAJ*~iEpK=Qf-f~Z{<^<`pwP?VNYL(2$r1Da{Sn$%18nZAKcNo6=b#dS zpN~p)6k40WlB_vB44Jh(e;nd`vjl*)sq~)Il}qDy(6HmH(*$tErQA@dRcv3Kzie~N z{GI(FPym`M43&0P8mYNAlT;>e&XYxfa!%pY?zMGgX@F_G{ybEXCPlDK-&Sw)p7}td z<8j&|m32pddm?@*6y4B?ojLFdIoO?HA(FkB(K&Pt%^Jy1n~WcAQ^* z!B98#ZT}$Eh;MV2bURh`0HB=c=`Wv_^FohiffX9v{Zgwm%hMY-k~PPbfGjA4&3ZYB z-XuyKpR<_Z>J@&5SN0ta3{EX7Hn~Aut#vK6Sl1F=v7(Jog5a7@JNWG6f>H#-6`!Y+ zi)5l>t0HbVE4wK?PlI{NFjo?syg3SvZ!2C}?UTZE;PEUV*;d56u_pR$f=EeIsN0#b zpQ}=*mv&$?Jbaatl%tmX<&L0Xj#aaM0JQhKf!t#I9K}0<)VGhH`vzfXlqgz1Vb>SS z>m=aR*4}U((wS=QZ+yaB(RG3QRm^e1j^ftVrl8E%s;j%EsKJn@C%9DRc^_92lrS*X zX_@RS1;nWw$j24}vjq2Iv>VPn1ZL)i5t9zuai7EGJblDr{ z_Zk_(p+-WBeB2@fl;QkIImd#wXU9RPs}PYt*QB_f{4t?2w=LgXU2z)zvLP$^Q4cUc zbf|7_+-xaQ5wL&RkICsR{oD6uSPC*|cyK{kb9o{D9puwNZBz|RlV;%E$%6c>7)rmb zpg%qxfp-J~<^2hu3jk@JBBWigE2zs6;K3@!5$C z?5Agxvvj_(ovp6sC?(w3MH`|q$?+q7E2S8rw)(gg7p2h5Ml8wenex&}xTLEgp{k#d^X|RsE^snFQK8$n^3GMLrszaOg^rbu*?sjuWpFzu{thE7A`a z_MoXqFVd8-{sz<5zw_e$(0Pxi;o8`wT{XdX$qlxs;-&>rVlL354$;HAYHXs5fFj(= zY){ep>^?T|Wt2V!HZCFSUpkn@E7s3bYpbWZH#7kvCPL`z@2;f3iL3k(bNPF-`+jvL zDa;Mt)pBz=Ua_29i8cvi3|7I%b({(bf`xR#v?73orGygtjWDPjS`TJCZc4RKe3j5o zvy`Kxg2YdCl}`ikCu~7UU`zpQn~UT0OI_-{$|_(G1DG-t*sgX=By8F4r1yk}UyYop zvmM(|5AF(xR+Urf+i&Rqdgahx*5qyjL#3fuv|?5IKj5)X}##Y zsTr`)0elA^z{UzKWBJ2}M^1i`w_gh0ni5T)F~7Qon!n?XqK(R)i zYQlLhF94Kj764ioHhW$E^xXNV{}*mk zKpx5RAFfDI0B)as2T*z^D!Pm@LGH7ubAP-%Htb&b{u%|X2))# z%aK$5zt{4w)h}7Di_<>G1D4vM`S8b4u(@Yh_rs{h`X_*|^{;$$S|bTm$yLlvw=I-l zs;>DdM}pAnKz^0jJTO4zJttQe)6^pyc{41;Z*JuDs`sWr2d)&e!cmFV{sz|ye8-!T zU{Ut*F+fvRA^Ty$|0Wb|(z;RK{Hf0OENcTiNm!toGqSZq%b{R-JHn$9hQ%FVVUsCy zQ-MeHYz=xcMr8#)7y|DW+8)0Ic&H*gz-qCkcm*_PFdTYyPB?hveulf{nnHbfsDH5$ z`Tq*>UeLb;1|2#0;*bH=Jpx^Y&7!VuiIj=nZ=hTO0CQnt)Ol8aB+7&B>PY_27l>9k z?Vcflkq3PiSv1(i+UZ{Y9W*HM9n_qE09bWt0G-ZT8T`{lTOS2cUVzQD`!#FhU?01D z=KnQTX!1K~74YrPs?7d$$xn?sZ@l#TCvSDZh69bZB;jbf20fKR;=XE^7+~ zho*MX_~GyIn?N-!qs+BblV|szXaQW%Mrc2iY_m@u za>L_0XgmLC9ehi4LX8mSCDMuT(mW3HhF-XU1^K5dEzbA{ZP{!D)lD_viSG`>)n8qB zxAWeaA=sZ;=-Ic_q}A)$zE@x;h%vz5sdcZ?C*d-EO}23uxJr0VG`cTZg26Rs{yATX*KUR1L8?Q-Y1)YnH<%dlCL@L32a6DYremE16mdXko9N`EkRtIhq|@uj9(B z#wxP;Rb9*J&x3@@+&;+Z>t|^%V;-i#P;=J|Ex#ej(fKwaRTZWv?=rGla|YImrhD`e z3M>L;o#mY({`Zm9P1)g|dSuScu{cXl!nM4I`d|_>bZk)SM=m>v*e*Vjyo~7$5$16r zYNDbd3oM-3z~r4BLAg2>)DVN)7egaPv9ro3sk~sOUB{myfyt~ z2@mX3adqn`4pgglZ>_;!9CMl<`@Bf@PAab!v8+kNx*xZcx}Oyr+o>fBEMFybD9fFF z@i={jO#n2bKRK)Hb1HC_qrT;4q!p_-X0yA5yl%eFbrLbuZM!Oi3kww_*?#33r7yIT zd)liumi-wiQl2eSBy*J{R78~xPpW`av0Gnfn3J-t;<@^gbO(c<^b6s7TdUT?_3C;z zfRG|l*+BeRY6aTOvwu;Unx6E&Wic&Y?$xR5e7Kq%49F-IBe=?;gD6?*MlV%2cf3_A ze~H#$<8W=jJ#TRA43)kFWM$-`0A@5j@JjP8t)qga;{qFzTevNExYkXUsuUT6z#f#g zbA-AKR+QE2!5%)*$2)i=CG+8csRcXnAWVQ1=(XM4TM~E|YT|S}=qOnJYzz6>kbrG^ zfa4oYjt600P*nqIDP48-W4iuEml5`8gqOWvKEJR(l}GAOri2BwWW+Ace%j*263zMV$wEW)}oHtMb8Y)hu{wM&FoyjQrE_>!Jd3VgcsPzQf9 zBQp)Gf{auNs#aA2gCsu5msyU&ICk`Kjfuk+w8p8U*I%jlZdP_gC#Si@^|nL9ZFYv3 zfF+QCN_C2l#cbjkL9C3&>;VA6Men^S-Uqif_)YXzLK5W~Bjgl038EMFNAdAyme@(x zaEvSkkD72dFP~R_~1*2(u>FFrrwz$cy&Z%LC5~6DvYK`&n|{GM`1R zj5`WfBElQ&BJVLVFtvc@kZX+CwrFlHBl5Z;2>HA4l8ue)?9Az#qkhzam1AO^;!_S5 zXCvvHsHRPn*4?gAe)XfBDtB*WdYk$TPoj8=A~EO2v4BSxo1Xgfq=&bASDITdql!D1 zyGXA?c1w>+9jUnMjE|8pcMfG4GZ8O!p(U91zR+B|^7Zbg03c6jxIUB68)Cz&C$DYpNQ zzU#lo|9h?9L5?cGM0EnR3BSe?`!9|Y%3%HTdl|rjz|s0QoS}cqS^7Wr`!7nZ&PQEt zo+t?boLjxJ%#CbC=@*2{59I-{Meg5P;jaCM0=NLh*x#@FPxryEy9e177<{jwKEQ-_rU1 zyt3B(GO2)n-}{dDdjg^TK|c~=6|I8FQK!W zn?LE5$WsSgLY2u$E5UxtTI|xlCiGk>jdhlUV_L+O^Y(SUYg$dZASBrjV@Y-%rCyMX+Da* z5eLN9MQ@If=Y2lypKlUZsj1P{)xst(^0&4w8hoQP;Lhn=^vJOrGoR`-&pT0#Xgck! zg!@b-GVkdSx3F4|H>dW;!=wbJZtD`l#b913mWFGNoqV*17*$+sAs%?aLa*v6v&rH( z`mfbi)z(+BOSqGu7~b>KfKsEbwPfcI7aA;#ubNA^$ESBj-k$EXwR7rs!NxKr+Rc|U z78yi;w`f^w$Qed)drzv#ncZzxKBYZ*B{*u@J5RhuE;NXtfl`wf6yze)nNQ%zq<=o5 z-^H)HedTgF5EHVG173Wdhq}uI#a$;WP9SUSJGf?CA;-uR&n^{6 zZR+8nkjn8a@g=+CTm$sp%03ghXXTssv^4#-aiC|gjMOD6&WJM@V~Pv}fg(Skdo*n7 zDq*js-$6;PR5`Yk+^I5R%N6>jp>_mQO*0sG1{il4`1Mw8o}FS4qHUR zi)eheL{#sa#!jFc=hPe(>E|s3G1Tr}ISbo_9F(Xp>IWT$ODR{SzZhRZb=MJ{RJOdK zVsNmdOmVp$`(==Rxav|ADJqVneh(zg-ex&sa$oYDg7>>x7)I=~?Y7HwE05x1*qJFQ zA1m|wh&6a23Q1QLF)Z~E@3RgEq16Ke zJNJu>BeBVGbUke3Ou@W0^%t$`P!rQ!^BS}LP5T*|_lA~9;ix3B!@1XH>(Z+qjU9?j_OVRheRH@jTWuzleZ|dDIn4%r~M<`~sfSPxZ4_?-D zp2y|b=GkkJmTn6wSEGR}pX^i9JbfiROz2o8ELq1dV)gU+c~4O+qF>J{Tj2Vkjk|<|In(=Jy@crbR*4k=B$a zVNu!b5#=49z)q#Bz%s(8mvKb6k?fOXlnuw$W$c;hnZj=+H%s6`KjM3Z$!(G1XL-K; z`k?s@;Q!xo^Af>C2FA|mU*k>-PE1Y@DlpDA4#1aB`QugZHH| zjkAi*-zuMwEOV$)JDJcbYE8h9?0ZjZwlREzcE71^btf`o?NbL20tJu-Y3UxtCmuCi zX)eJd+jN!N_3sq-FP#U5W}0cKxWJuP65YzX_UAC+GOmhM@vE^_4xY=@fv87cx4i7< zuO2a#D9enq5LpTqHROktIl_({WK&aPYkfr&2P_f!*>lO3O+eJo=H87*$=)TWVEs@b zJSAwuspI1At!aYF^j9fu_U0#QyfsA@QC2YhK?5V0IthAg1m?qnQ>iwxiTBOqJVlw< znh;^Z4vvikBs&6U=4+iQeFps}T3msMtQlu{Z0QMnuJOYL770UISdGg%!!wWQS_;(^ zIXO#g>1`|Ph3cC3E}gd-?%wgZL2*qXz8ZmoC|yn*eb%KjxiYuy+JEoGv@lC(k@gVD zT9+lBH7%dfpFL^*-j!mPi6gcto8%Q1AEodfhOA{XCX#UX3lz`I1EDZmsM%{Xf;dBl zzvZ<#3$^AOG}3F5;3lZvza$5z5Oabf7hKJOT@=do&Tch z%YQ{F<{zR=8u3MP-W3>%@{MoM(EI5?2jatHix&W_(V7YWEEXx@Pfqi)UzC2he2Dk2 zURc0Jv0FiDw~Gr5?i@*bLlqDcy@TeoS>* zGA4F~zRt91pn?S0A@7?ULMg?u5~ocXp;+;zEEhkXxW}o5#PH_E>u!T++{zM2mb9X* zwFy`J78A!J1oNJj&|HHF8b_&}zL`)IlSs7ytJRPy&6v}fQB4ffg}PiG`c=%aPuY2&^p%ldkM3Yn7_1H#fLj%Erp-BL&ETc+ zo)oP4``%5@rDf1=Z;+d>zKwW^>Gc((gD@rkH1hc{t5MAuBwPgMjYF%h=4xmGd=Bd zY?W;hcM=W~mp#`Jm>kc{S12{-8GkjSvyt!$KE--!8Z%EwtD93%{PHFot80r>^Bfvl zY~@Amynw@~?X5L)+W?{SSW3_@y2*jB{|WC}ald$bJy9%^M4_dYcU)+Ax1&>Zbd>ab z@TPxcj`3z1Z}sq{VvZS4$!qSF4D9cq?Ae>D_wL_*tx?J2qxxk4k4s-0?dyg`03Z(c zj;rh(_!i-|G8oXTsf`Nr1UMuN{!a6Ng{Dq2o_&G=S5hPnAjIw88HR2G?osn56<5qk zUVs|@b?(2urvCkN$N#gn?RUz=KM3VPXNu?529H2T{P3>d%7RbUbiRGJ0xA(y~rOyU2xO49(iM& zNb7n$uTRt&niDX&cr=2ad41f#*cbGmg=EV0UJQn?OGvk_Aav6c1^S_m?lc^pU{Pml0-mA%Z2RcF{Y85ikaV7qRiC&~_=0h{V3rme8!cRXMZaK*gN zp*}&v62TZg#e1E>*^}3Aq^)GZosA}K0V83Ci~WYoH?#-)eTWY{a~Yp);x}ppC?ONU zTh0YkijGFNKf^*KTWJvuG^B$)>|G^{E}NU*K{@(|kETUSO3u@G!Pv8KOL#GFw55Po z&AI0WUhZTZB$+Rrqr&G{`~h~2I|+%7qks4=`KAcPxYy&t-cM8CW_2^+?@LPvq_2B_ za`_Z5$(i~wscXQ#--DZg&cI?d!mU9mOvi0_Mpp?T>3V~#RS?W`qC#zY>m4aJ9vf|# zWav05SLg{(d6v9w6ucMyd;a+Ia1PDFmg`^$c9I4!Zv4Usqd{Qac6&~HzaE2;yx z9&enzjK0OAc6iBxl8RdAQS8q@b%xKP=v+k@8tX?4V}uJI?9&=MD-X%}Z62X3ZDvSi zUCM=Hj9z$GZd2g&Ma9@}C@9Y~xrE4Q@6;@#B)DC{n`LLI54E)Wk!Uand2|Q<;q6Si zQR?UfowBcU#;#vV9f1f6vs5>9?FR3hMsAn#avO20U^dZsk0U~%1^^=KM;UFTAB|i_ z-Y>5mDZM0~$HA0>H$NzEsx~jVYj6K%ZFZeQx{(mww)aV!4W$FqC>uazd5{C1m_?0v zKvjM|g0tzXgImDSL=|V+KPZI`Aq+))e*Y~%4?_mvF8G)K`&}LQAHiRw&TH#*tWI3s zYvO?@&2~21{ubxwcK@v|9RV@cK8ZfO4dum+cZm+(%ev424+a7e(7urs0B*_9MS2_2o)+sS1W3q`d~gEJVyhB#Zmvcwh}nQ zve~F%3JJPAwV*z| z`9t*h_%lG{!Vw3AiK#D{TtfI?HPu81{L)s@pDx#~Qta_Zxc`+Wr^pxnPganhHopsD z;gd3T^Ail;8Aep(lan}$EU8OKVA0ZRZ5jaa`5*q(@T|Y*^Zt&N{exm<#XwMObcOyo zQsD}xNU351CD{$$1jGV(GV+hO0jmw`pUp7(XKU~8hTK1hA@^JSc}+_|G0PZww2*#I zu9jm`b{QT6L{5Z&*Q~`9H!Oh70{%Y>mh#Wq&fl4K*Fy%!j!2z0-Lz&Vb$YqhuS*(F zVi~<1z>!)#jYRpX0huJ8P+v;O#b}31G=tLFd_YW_M+nu`6=9w{y!AEB(w7Cli78<1q=*>A2rjJbIp zPTK-pkyYr}sae}DC9eju5KA)P-zAlQ5ZdWaSJ9fT%&LG}ZJESE3>+{Zti z6#eDqbn>+53z+|UJ7oU{(Ei8r|kT7#e`t>8^Qdt=r`s*JN+V4Z=x26zvt(7cO zOS<-R6+=FlHnBSpa@B0<;zx%i1?bi>&;nM$Uy;VDJQ9}FCS}R0sV2tP;bc&L^yNw2 zrvLiWVe4;Xe`97b|E)3oTMhhIp5uO_KUAF%v)ed4Ux!pSGX=o4$)8DzaC}1Qv}q5)3di z@F83y51r0bcPxRgxbH`YU;i)s{mVPFuSJ&u*^k&ztu6q6gwmO?ck?^w02|=+e}tlf zbR`^BPs0ng0Nc>mj6hLGFKL1L&Z)ml?bspja8AVIkHm6jlYK5cJVG-DcN?xn-7>Ht z9n=DCGnDEbz_4>q%ekhX(MOPSE!S%-)Wo>A2XxWg-RS~3JpFQ1f{AwViI zzIA}ihICest4oS4)yK84HQ|W$fd_!>31&)vT)ts&$hvPvSh-R67s?G!YZq-w49i&} z391jr!(1;nE@G@GFvA=sjfSMB5ouVUsmuy%FNJJfZM~N@ZwSjt>$UUKFy9&7uaGA6 zrTGNIv*?B(s{|s$iVsUfMc*X!XeY^cRDG6ikhQ+grymz+V<*;$=`W=X)@{ofs0e|L zTxw=+Bt|1Nbt`3_IHC#ROD2*l^e;CsXtJZag&c>g&Hga`lKN;k99N?^A*)W*L~skWWG-Tdvg7KkyqryK?e?v^vVIzi zg2eQoa?&*pOFb4@tc0;>)~EG@iOu7xOmEXu39n`q7GNIEg%$(txyf}lAq!SQ5)0IK z!InB^#AHH73iS!!oHm2?WTs%Um-|=*&vgo5il3jj7ZJ~J#mXY7qKyjSkV1VRTZC|< zy>QBs2cs{BQnyj+YsX>4!EvpB4#{# zByCyDEoL#f1QLGTIuiah^Vdk9Af8C~LhptiNV#F6bXFO!Ww&vV+6dtRp-iM(KsGmeV#;)>@freYG>#+@S7sdItZiv%H zyMKLOk`pu-d4WuPMXQ%uDn&D@R1Y|2%Qm0Zqo8G+79n$u;>ZW0^F)!#ds^YLGOTlc zoc87%;%px->`I>nS|M8m6%^scr-_}j-)3<>As^rU@W>9 z=D-d!=ci5C5~&uHWx}u}Hm~8Hs z9@xcpvzI{7r3bb~nrnL4$9&)NFPK91vxK&iBWtuH>O5wpqHQ8Vknx9&Y5N*kzMCw$ zlU(+E9G7q7p71|tK6W6uZQyG$5hA2Z6q~tOmec5Hp(TXH{JEEW8xR6=r4L&|`#L%c zY&*1dJR88e5y>HKqe4cR=Dd-XV3$oU#p2jAwf&xijcGD<2__OgdR8ekVJ34*<8VD5 zbx9DQ7>E`%zsjonZi()C0LY8OD43 zLN0_UF6yQ)m#WlMW@tz5$$xH=%P~6=&g}MMKL~x`Ja_wT)xFp-*A5dmeOUE*bx=li z0j+tF#qo>>Li_m^h*BHPrG2|Df)i%8G4WcLQk77Vn}LeK7|V&s{e>(}c19rS@>`w4 z%e)mU+|+%}K5aLD9VA9c3BrsxSvdVzzNPwtd$}=Ip61%e?ds?ILy>69R@?Mxab;Iv zy(~dM2zpe=+%a4K)Pdzq=N2{G{)J76AEd9mE2Ke5lu2-3W(|wx^9n>6Mn;iLeO#dz zYq^@@<&N*3YmM-NGTB_6p(yK)bD2_{)P&PRxwH|2n{ka#r1HR>a}5F8GgH=6*x4b) z*C_lX@hm#4kV|BF5zJNIyrL$_t=a|QW7%J28)U8SOA{|WNJ+h8-!eDcT#c%Rt`P&< z%?aDlErw!YUYHh9i8p0V_@+>75YN`}z7*v^+kv|hM?fN_&oj5x9FvXoyH)u@`~?;s zqDekhK58ts%&&rG?)X2JZzSpl#enDucwc`EAnc#ibxX3TI?GFx9d|->TrHhDv0U>d zoT|7!TpN$~@`*dTX|&2ATQHAScpAh#zc_*C+$Cc?-x!i4S9VR3)M1!tuq(^1C(ME4 z9C40<5FXY|Dr!`xu@2eZZ;rQtPnmB}ZQI_D`8Kb_K^mi~2J*GD_xD#^Z|43AvDS5J zkV_gr`@|XQ8OomlW;R-F4{mgL;&~7e7#c9tB=LG3#rf4)?sS{EOmpc&?c14|=OJ^S z+JL8g7fT{)mv&8<$>D9lD?oQQDli@(N`NoqH?Dp?%&JZ6n*vmC#C`v|%@qRbustIy z72Oocc z2DtnDODu{-LMHvqF`qD3RP*kE1e_^t{PM+dLb8WBQHg!NMIy$`FjKbM^(ZV;yWU#H z0Qy`7lFel2RMexYiThP!Mirx0r8I`N%oN{(YEcWOJUFhYKGiZF*8`NBBoOc)3l z{Km{D%8yM4g1ww#1hb}5V$A;h&p(tU^ta5h3z3!awku#G6qp@10>H-=4I0#HWxb+?(hW z%7;)Av*i8-g!zfha5rfJ8_$gNyEQyEa)GxG50_;qBDGwxvur}T6bROKbIt4hX5LJw zBt58#?E%j;teU|%IQ=IG!!s|%Dw>1Zc|AUOVhcY+e!`cxW_EvC6t<(7$gI#MVxwXd zB{M!dp>Tk=d43Vm8nS$(KnBOVWmz8>araT+WY!_l zNzKU=8@;tvf~5#gD-^%vhBo(!#A8Z{1 zFhPs-VQEbFKG#V&%y1$7PF>~ev@t^DUlRJ+KGc}Z4*|l*+QV?S-=-qJ`dX@bN#)9T zg3Zx~b>#C{H*;!gV)U681sw{-yuhGdPoF#GZJIT^JB}+B0Ne5`L9~L}rISZmXyeMsz7X}=?_vDa)?~AADs?%Mzne&bP`)d*i zYZb>bO<&QXjbV}8KPiUdowjd~iG;fsP9e1Ay)EtS=^9;zd60KWO|07)UP+2|^DmO* zI0I{wSVM)tIaB)ZB@k)%W@mg>p(%Rih)hn+Q`0D7j-l7e~E^ z8WR8`wEKyvO*qc*-LG4j_8e57d0tpgry|3U3MGCU6TD`nCPyEQG>|t?Df1(y<`^H; z1XB=`RCWg(f&0VUPHQ(^gn< zJLf$e8hJ9kGxkG2DMkf~(S$V4ueK2M%Ic;!?#5=H;xnROCyhq+Qsg`nyiG;;e(s#C zx_C=A#$sigKv6mq4+l_IZ7@a6quxN6*xJDRtboX0EjXz}_S=r#;KC0rF(7@JqX^&F zZlh@KHVRTTvK&f;Y?`BuoR;OOAQv5j9|{Lp}~4dy)DfYGt$m(#G{6E%vlRP z`n@YyiF1(+z!VD((~1NlbpUA;U$syPA(fi#85 zW+ihmmhbTyvXpjdKC7yt(Z+aO({vGG*;^@aOGq{i=4$9o;;bO|oLqAG(%tGOGxw(G zw3(O+ABhfIQ46o?j(hrSONqwz1BFfZpo0_|$T1D9SrcgMi|l9rMwb>&IM$f8E5T)t zA*DPSh?bnl^k`Pj2f8H4GpuWS4o;dK(zAA6Xp;R7YUkB5A9a?0>~z6^&GRt}vM6uQ zX#41e#C;XTw>8VO39BJ7i|u+0^&?)QuHNQF=4v&He516(0D)G(%@h;6{_)*!hW9y* zAH3f8lX97UuOzQ?sKxZQ0(oNx>YTAIpb$K+QEElkR6AB;tw{TUrr4q|21AA4Higk$ z2UPbZUB&jUwk3+FZDYs)UHs7X3cH>z>QsCJwAlTKC!LNJfg3X`GBGo5 zId0Uw@rrFE2++2b6W{&0?Y^nCh$Z1(id-+Hw6$ zx&#+ETc8sZtMpUWg z^s&AeKEBF;?(jqmIA32ItPMG*{7Be-TBO|+)IPMHzwOw*Vm=90h@ z$oB<(IZ1|pHNxQ#kJJ(`*G*kxyp%v=mOPaF5*9*a@@7AWZ%rZLi?7RabDSfS4pl^F zj-sA*u3Kcgz^DjyHR>x5%K~X>ncDp8ux@K}QVhPY9NWx2y$4UMu6uu8ZQaZp8+abbICNsvy=LAi@1$_7 zY)EDF&4`g%W3t!O{nLvIxqLCowsXXG&-~4?bn!OV1A7YlOz)KgUO(B`z+2Z2J4woP zhaPx(-|Bg6V^Z%N;K=n!oR{+0SM)xUw1~--D|pFqfiN3dI=iBm8N%52moALkA6<9X z(~r)f=V&AS%M~*vDLT(t(>NKw=-3*gUvKf4!^iO6j6{|NmwE{GWh zf$WkR&A1${G!a-RDyGoUOVwgC0a#Fp#S`xF9OsOeTd;e}n(yhu{pCIzOOXM25$UP& zxZ|=@^e4arv-aO`9$dNVub!lhsd7`QzZ}|@7$^f~t zEEjgMHi)2fTZ#Q|w)~S=;PkG_8`mUkJnkkhmk8OHT99c>6h9g(rH|P=^Wf5=EwdP3 zkWcYK;(GOGGy_~}n;rIu$MoK!rRl3@{w}lUvzL(?nT;DK?E;XX_qOWoWDryUy6aF$ z>__f5BYAH>+`wZ{>RF9)mG%Wvud988z#t3Ombye_OUWCgfN+9 zT#1v$>S-UwaSI<+9tgRuQ1+;{Y1B)?ey%9AUs9U&(|D=Xis$o4<4F9TX0zKRek~qO z=#7G0?0-puwRwwa(R^z`7qP2h9-B$wMfLoph z*3=w%gS!qBOeC0>81uVn=dNiSr?Kx1d~&5M2@0-CNqPU0CXjsXLoLHT!l9iI8 z)5vy|OA(iVOKN#B(c|0VE*B;)xEv$%=@W@(X*otk=J1q(dh4r_MQv!}lbR%B zcHR?hO#ha-v3exgyY3Mv}K4hT}h)t2C}GGD>qN znNDwSrnSZSkX2g;sYOlswazNmkA&gb+dj}w?RdyA#d}Bk6_y^T2*^q4>0<~5Z_1&h zvalN*-Nyag!h2w}X5!)VSoe8mYH9DL8!1hoOTdyA`*p6WEsKJMxXH1T#&|2Sa_>il z6m?ca#4F2=T}s$e`?GOVy2#BhpzrUL^j9%Nb3Qdz4=cn#w_de3aB!ozB{L1yh3iKl zDp3u9`Z#A(4G^W+a1lx_ccHnn4V^%Aa8yNu8#9){WZ)ptrCZP=xa}3$*fJGy{g~5* zbk#>u^^YdwLK2>6I{gI92GYq=E>G`V->sp3VnP|`RN6UzT;Nk*S~hCz;}Qp0od zBBWGFhW3WO8FtiAIr_Dhz{ z?>uiUE{~MO+&8JHPSP}`xixDZ{@jYwzYc-c%wHyx86V_Hkq~Q?-?13#e#9^%L;N&^ zwDcKe_>-uFYxSnhObH{!&VurIVt&Vz$}b_e7j5?Ae5)BLVGkqgz5Y z4VpKf-n@UvT_z#A2?XRk0Sx?oer!_Oz1*f^DNhr3mWV7TTk9Qb7USABj(oRiquV`V zr08LHJeCX#E&5A0;JOKA&e;_mMn#?Ogkfg#Ef7^2M^^V(mIi}WU-mSn`plT&8MRB% z`L~;3(a{6_JL|3}hNiJ2Vb<1V134x)nuwK0Z_Bj!+xxCdqZygOu|Zh$6mPP}n#UHP z1d&@!yssePhk^~SU0qFC>WeF^;X!to5q0a`EqvT-qe>N!CNTNz0p$@iZ{Bua2q8JL zW3#lk{ju|s7GEZ}t3n5Eu z_E1poLVb&b6D1v22va=4zrg6_7^lV7l-{)6pGc&{^o1)-${Xrq6{xvfe}i zytiE-Rwaw5SSdz^Ql%Nc=_*bj;-jO9Q^Q={`^;!>)xevV;qKsos&JoGxrsN(8}U6l z<{olu35>{Uo9lrrjA$n{3r}utRyP@jbi+;#P*C-g6#7DMXxWf=XOb8HFgG>kv<6%U zp*IEJ^zh=8vFEyxQ&ku7gp5>!Rh4J%zDh+G}jIC6zY z8`!B@2m{t2dxxpn$^dC1GJ!Y={4M} zk5VX~F+gy(g%9sC?zF@v7!p*Ebn)q+b$D_UWR}>{Lb1V7+}=R0P2kNYEsNZ*gd)@5 zbI)g)*BlQG&R16kZtM?hn>{~hX1C7E|uUCk)7 zo|#bK>>bUwZ%klM(P9*2`)l`RDzDNPrg`K==DwNpxJIwv%I`mpbxaZwi~Nv(#y2?g zuqiNvj>__E7AfQ7TV>fZ3(V1db1Wi5^H3tzf_(Fd5zQI1@$pTToa{uL+vPSa;V zxXuhTVrjp~;oRp|*S?^(wagYPH+W0<8A%q>ca&_pMCXNd&y53gx2aiAT9ASsAJ>)E z?Eh)+tHY}7vc5r7R0I?WX#^A`mF^=dARt}RDw2n8se>S(q=105bc3|yp*y6zySop^ zZ#&lnIx|oBzRz6GydQsf$%cFPUVE?jt+k|kwf0}xK;sQW@pd9(mERhXl1)8*Y8^vd zyi3K8#f`VbCM(u4gDeo=rUz=qjG<#G`ro=fJv48in5`QAw=-3 zooQC;*LoD{w`eX3{EBj+7qr5c2vDpku>DlX1kpEK()+P}JZ2DQMx|TEn&h9yl{)_} zq149z;f05#{_^gP_nTubzidWV+{IK6XjdT`n0j^LWO&J#>L=s#>Xs$Wob<;8K65*{FLtbg1M!L*c+t89&Hw9s|rFE7-|R)6^gKSU~$ zMM0|h{p%Debhl?wEYM!P1U!?aK18DdklvFH@UF?ensmPxBB=Ym=HsB)g=bMu@S3uk zoOCJ{+Nt=2;o1|e*1MAs!hL}a=8xff1lEdjZC%1enYMkklLtBtdn#7cqO^CEGKwCQJ=%; zL7CuGrkQJ?i>cW{x2&0j@&S^rvYkNXtmiQ5u}<0-Y=^u$z=Sv*x|$5X`f7TGt|0^{pP53tD&jSm;}I$U%0SGoT$mbl1aoprPgqy0FsIdq**$f;1#bX&s{UM#l!x3;Wqa zQcJB)aR14SiDMCI_y*2Gu-)={S;G5wY6ew;H#gkPL8!~SpQ7&F$F)I03mfdtB(8;D zRU~EUU2P1nE_|0!yOI~&qCS8^to~p^96S8_G{}oPUz`}CouMD7f0$4_tkPEU!sjjF z9EA(_*((>Q-U$mSb|SbF=l5hsR77}(n_M_Q7Z(T5N;n7L@}<`>iE^P6&L#yf1+8RF zh8;gp9eL@54-}^Dd5m?b5g8QIjpSIMv2Jqnv0fw`W;0&SP$H+Rlf%V@VXcDHFx#^ihMM}hIk+l_KU=h zmoZTLM-%d&^@92Nan12Vwa$6GS$&?bD9@3djY$RJjV{5lS0db?dfYqJ%ecnJ3gzou z!uN?&FQt`wmst^CPkP3chCheC_#bxL#T6|X4Hcj`2YC1##?2_H$!Ii<(fvlrwDueEx56X7s0}u_Gnp#9RXdMf zU@k&M71kLW(4jiFH-lG7KQ&&W~^rj zabl>F{s@_pk-nqNxDi?vnh3n0d)e4XaaOUw%Kez!6T#jOI3zhvyD+fWN;IQFy2Qj*GB)&Yar)SK($CWZ=q%x4OkOK;X z#j!qc(PhbWX68#kYB_hSHM`@Du*9$Hb{i><%xVR1cAj(LG&I{HTfT=LVHGCglJ`Eh zi>B1`aMJ#Tue|4x^yf&*_spYLAI&_t9AQ>(s3arAmT_zc?^Xo5z233umt^p{1)|Eb z;;u|>pHkh+9m1aix@h_Uh6={a zH9roPW{n{L5pfFr#Mn1w1MwY<*YoTH4Gkv~nRM5*Q zI15Uj@~dJ7Fg|(HGVqXL2?Vw1*)eYE3y^-vl6o^;)Rp;>bx;&~nIm`>ztP`VpnWTg zo<(OKo%_Q*H(;-k^{QV2aLx9A}v1qcjQ!Y6>*dlc>;5brUDo2_TD%V zEe)2ZC61{s5!Lp@Lk+SyBQM`+Ch}%t;oc%odJwi$N{U(k8lG>7v1DJA!4R6o$bPQi zC1$sz=nYTic9sIc>%$eUuC3O2aziFdCYv`O#yOhi3=ON)ldRAW=6M}?3<}@%>91S< zih^aaqGscQxR@k9&X2=yJtXr|aqw1H)8^rH4XLSV@AA=|3*@)e%sNXBZ{O4zOPYXf z5%-Qk>Hs}L{8m;OJ-nHzvjfCm5{PaV-oi1@MKoO;-$6Ol4J(@@6#zWEQemK8uZ{Ji zFuc}x!G{AYD$0p;51(?Kb-pt5NojUG3R>P{8O~@TX-_IkE-)}a7--KV_DOsCiY*?w59g-MfNJ&<%*2na}iaSVX)Ysa!@{!8_ zRe&vFLj3$;gq?_6rv%Rm5hj(KuY%t7>jO>8ic>CT75va%+N7JU_B(LEo(SPJb*&td z+2Dsb6f0Hh)%sM0)A+5>PZu6S4ym%*?rv_HwQ)~=VnFbxh)iVd3$b716JhTg5-UsR z_>>Ugsk7y<#?_qC_aRjx<7fNSeWoV;?=9V&WnT%wKBbH2+@>z8BOtoW?krU7Dm;LY*nuz z`+JbSmygMDjF+$E++KJG{i2s(RGP7`ipZE(fF_#Hd-X9-_y+t=)G>l*uoe{IqVocr@?h;<^#t?691+53T3*j zTL_aw|K?R7xqb6Lh*w5;2Z3_bbHAdP0wuIcP{;)QfWYu8$}ps*H^Fd^OLya27))xq zsH}t`s2%%kh(kg~)(u;2j6UdP68o)Mwb($>v^x?>mZm)9F56@K_u0y>i<>iUFRkU( z9rf9lTI9Oa8)HeSq3$b|6}mtBRMrEY>7^2m9%|);N2NLb4JhXyD&9xCgJx}xtx?*O zfsGUdBBHuBP;~jI;Dv*Y)a7E32gj!gc?LwQijK5@7T)85s&1>=5b=bcG zNE4Ln)WS_&Qr%XqO0r#yd}YA|-t(dx zs7;S4LHKB|w+uvgcVCm+nEGEgR*SfU>P)SBJ*M>bW&B{pL#Cu;y^ev0&wLE0g%u^? zEMl4Q)4JB2<}asvcW~2LKS-U?-`y=hlPWFGDa&TT?WVNUpJs_@hTG+E%pSa?tTrRT)bt9MCRvt|FVH5S3ok zx*gP5eeUdwEk#3z8wm@AJdX?L8K^|v^`3eQJh&(p!Xznbm}wCeKgV7oBv6Rw-wt^l zOnQHDNx0XFk$ffKW#5&-m&_yqdx1E0M^C(UjZrJO+Sw$=%kJhK5!=%gbv{&u&h+k=4zUJZR?b(JM?OTeO_Q6u;$CU-rQy=LQv8X+vOas( z&#aR%3XRdlAQPCj`ChG(%{6xUk*%9Gg|}|zKfB?>k@jrDkmNEPJj*~#;6DhsGL#a*u|JS+Otfa@Jr-N$#~#=bqdOCB*aWhE(^-iEKy z>*m&Lx3>*i-UTBuek4&l^SCnF$Bt7-_=9V*>lr@B1rhI>&63&^rk0fyz0;`CP{n*I zQe@-K-k~_OO!XV*XHh4cZGw6=GBITa@2U&MYv!@d3yQK3=ndte#8^Au_08`BtXZu# zl!R{#d;1`Np)LlNJl32sAPRlSwn_0kHA!FkO=|r4dNdL&)&To5p4Pl$9t@GSo5DDN+QMDqv9f--2_pXjCspRVKv0l{a@^1K2e&MDK3nAV` z4FxrsjwPQc;>)MUNcNte{i8N?lnyq!6j>FEL#cWiC%1 z`zj34HZ4{l5+tp$M9wOG>=4R_@8oJGXC%Ng{kFLqORpu7a*F2VL@m}VupaDo-Cyi9 zx#5bxh39HKS`)w;7)3`|ikkGe@5*t(Fb#HuS=t#pnA?HC-qKf-I}T{2;r%njo*BHt0WsAhxS>X zFkv{A_iA(0{zC1&a-2`9j%Thi){UT~|qTdN&_5&JL>h6Z;fWH8N?z#*S84wp1opfp@_izIHb>G)jBB3-nz0=9Uh+M zg3+8kAph*1G|KWvEYUSpE6wWY&2yTEE1XClgU|Ggm(v^|kS%t-SkK(|zXqrNcYl|{ zIBu0g!hx;up4dn_^T|@C3Y?hJqw1Tnu=$^y3~2wP0Zga)_`jl1?nED3AoKhBZcMBl z5TpbDhv=QmFbKwhQzAR3a3rpbN~qgk)2Fl5eATL)Jb3lVvd^&loa~oeUeh3l#oVMMC&ON4ovh z$d1q8p%VZB=^W~TwV>lT&I}NW;{)V@-&Yi`j3(L~Y{R2pYe{e=S_}I~cnax!=yAM7 z{uPBlWY?$u7i73|s_Kf)-`tF^@O#*lo}D_PX3A2*3lvd2pE5l?FAAA`6vC^SL9E3WFEF#*(djFNfy7l5#3_* zo)*RM*?^xpeN61@fNSZ4{eZG`Zv3mjQ73+Po^;`J!N%E;i&|zDxWNstVlJ6JxXay0 z7W=~y5shyR(g{lcEhiiMXHMJCuWfYT6We%CZnm@?{U@oIDO)7hjsaa8NS!?5Pr89L zN*@pFs|K#4Nf~0?(jz(nUw*@tK=7YEll81Atu~H{QbzcJc3G4w=Ukq`rh2+$gji@~ zTEK2zuKFb}^Bng02=#@%E2`qApBiR*8D5-ujTLVL;4f(2Md~fTLVSh)*@^!C>*(hX z$ba&_j*byzEbxG0R$ZT#KX|`G67~hqAmhj|*u1mg!bExDhc(3HP!itXoo$q2-h@g+ zgSMy#*a;`>14K7uo$)J5U4HUMxW2$Q{pLEXeHJ`duS}3^uqp^T0tq6N0+ZX6O1CtC zKD0#di;RPC7_GpLer5~-japBL2n(uzqqrs0O^t6?TJQyg(*87b-IoP@wywl(x)d1L z$ekE~D>h>6kd8=QWaZ{?-}hbnecz&r=kwjbzZl(5bS`poT7lMAqSU~x}7=|hvR;E2W!Cr`Cr{6if8}Q2l&q#nd%boLIB$27N`vAK#Cx~DWn4* z0a4)kL#lFLHXe z7lgwdL6EN(bh1KxB%G#pc;mewm2&i(J~Bx6>NT-0iA;~gs_e4X%9g+;kxL&M&F& z?esD{c#h;%m7g|VG(%CT*Nj66>C7)p2QMKM4A?+M8Q@Fx$+wQlr(soo`=V@q2S~q7 z$b`@b^FLz2(s~P}M~BjPb1qUEt~?t6j3Is%<1Kgo{$$5Z?&oixdm7hz+=8p;6nL=7MjEj#u#n|JP} z6tm68dUk))U417w|6eNCmPR4f&Bay4^9y}>Ed!6G9RgZ-kI#`wW_(2v1cY7z!c!9w zBZbra4z~eD?15d0+-X7Fkplvr(*Z4rdTdf?P%~LA z^LM}fzR40Hk``{Q{)Fkpv2A=!#5v84xW*p%JOh0H5`0QYFD}%B!5+6XQWfda407jdrsgIk)`-8 zqiyVSLV}RCnrYyC@GGYdu8nm;mUdv<=xOXYytf^|1td>&4lf*YQkv)eZE`&0Tb$3O13(D~hx*NtD!f7Mj$b$l>eMonaX0hs( zWl@X}o~leYqh$sMu*6Ege*iTi7H4>UE&pC47fJNTm=Xh)R=n2gDcYJ{@l?Iy#6ZlW zbj3abFRXfhcZCb0gzut;oacP8@j9jH-SDFEbM-mjFj?v~hnGNYS8pCTNoPJ`=5KF; z-hXv)PHBA(@k$9*izouA$#hiw(9s_jP37!%NG2rZ%!r7|9cYW zb#P@L*&;QV_3vOMD+>{TFy68G03lQw+WI= zW5pKT+;=-0MV(g~u&|5}I239fdOIckq@G7O^KNkE*%g$n&t_pElS&@>t%}6NV;5r! z60V1}skdFj-%9K=3}DT3d&-~c_eOvyGaKZ}y{=_%kI5=*Z_nE>GuYclw=i=@T9Eo2 zO(NG$t+vepQVNOQVv?$%83x>KH+da-6~d+Saz67&?4*v|)=~8*Dn%nCd+tGPynO!A z>ba-;&5At~xgBv`Gew=$WxcUE{=4^ApFgh%YOo@#prR7^Xil$rzpVFWr{zYLzPX^& zqU?M>g4!+lHkOQ}V?J_|I2rvLEu^0Z~yBtJRMVIH}c!}hEoV9s1P2_o2l7U(&@y@stf$Q)*=PFXLd z?@rAT+<4W&vh53NZ)sn3O8UNLtp-`PBav$J2M3Oh5miC9`;RsR%o8H0ES#N;J*c>9 z#YV?^J7Lp|vL=TkR6uq9dP&@osOhkmxFj?y6EhwEYTdcYP|-0$#T)X8y$s^#P^_kP z+_R==!bL^8_>Kh*$DZWnl;s!s@b$kAq@WnjdDk#-Io_KLOXPasKCuG+p-vWmX&-_A zqLNafonz2O;k%pd=d?nVSsKnvMWF9yS_UU|U$u9^9k_h7-tGnAf`Z$N9W_?X=K^f^aMpq;ORrMXCQ zy7Br*C1FE+_)N(rA`WQ?(@&T?fbXAQsrTj^B9-hP(3n3=C$bEA_rT6S*z%H-6>VD* zrn4Y7$&07GNp4xX-45jOni`F>YHG?47_yu>St+Q;O}Y8g0z9gfLIO2MZh6372nX@! zK?aL+N7^P2@rN?Bo^lRj;v~^3cn?kW?Bn= zUTe}{`{?XP zQeKkoF59WcgK^?tj^Gbr`X-8a3?ED+XcQeoKL)1Uavr0{*h5P6< zAqUk(6wAA{A{t4|aU6U)aZ+vYhw{0uSF-1(>zS)NXy@b?(jP~g`Wy6ZV08|yJ_4o= z+Z;!SRQv^jT|^{u$rWU2wNgAc zY*q3|gUU$^-bjr-$=8oMm4}2ZH&HI}h{BD#qNEz^!QFB(4|ZkYQ1dMt1}TNcty~Mf zf)d)Pd^@gSOj7y47~eD+vomsVc+syS;ZG1&WXdQuB5ba(?2y z;4t&xs%!n@W|QD!m@4maTdftcB&&16WNIi z)Io{U*}63tb3@y|ii4QuTc=O}7$60b%HJVA0Zs>SF~8&A52|r>BXf;&Us3$v_^^H_ zlVdzgn$L%%Mz}JM!APXhA-J4l>wyMYTI|KfPi!v6>LRasKl3Ql*xZ319DV|9JdkWA z7YZ~t?<_{I0vN%^uv!fBUJsN^}Aute^*)(^JF5a zGYJ8YTYHZO5)JYVwe(#{MP9~qmerxm1nHCxSttC4q2fDP=#2I9xEnsR1l!sH01)T) zZ}6$_O1(k2YTVvq%)o*horaA#4?{Bfoh;-f$4>m}LPT|U$*B5puH>pTk`J9fdnYGi z*=fd)6e^nrg~J|$c+)+=a7>oo{dh5P?MfE?hR<=I8$t$11{H4|)QxbSM3@%Z2cuIV z2Z|wx^JTJW!}_~6Huzg3DuR5y=^7WQy;&dDJPo4OmgMzpOv`sHJ|uz~aq{Wb;Il4} z(e*5^`1`pVrd#&s+kUhb_sEx_oOncTLCC~)QxL^FY?qi9^DdQ(J%+fDpy3VoM_STn z`S0H*ulzD@Cd(1|qDxJz*Lqew{%YIo&6Wn1Guibm=cUlQyNtFoc)rvb88SY*`(W3$ z#WZ!H$xkb`*T8Vm9*3Bd+k1_1)`Owz{2kV-tic6u1p}Ru;V{XFhhuw4H zU4y-oJ?Tb1WnHXN7u~EMtFGZ+uf8_MZy;}&F=NuKSpm<7@giZfFT)lDh`UP_rcxa4 zy%T4Q=h(a*>TVRcoD!EnY;|1HwAa8 zbxj)&UmW%sVZX|Yn>3Po(!FB1Lr@xg`>D8>kd}cgyHt>`-3mGtDSCpjx_&T0AAZ+p z|Bza%!O_Ze7@oCTnY@1xAC^c0lRCjpM^@iDC0!$kclQ-Vs%A5dUY%pC(h0lI(t>aU zZ)GQ^*$zv$*v4f0eNd4Oib+$T+aS+V#bB@aeQiGO$yT`O#aZFX2ah(gQESPyj~&)V7KYRA+sSlZB)EB>69&p9QM8z9<=lKw)A z!kQ4prJr!#^t_!?Opj6*!_Dr=Ztd&>`+4bj8;MJyGLq&o8}GRJbjwsy7xs`wZ|Pwg zqQ59-3Yj?ffY5+#_Wyd5>M{@GA=J7?s}oQ|ZZ4kpaSRl>8E~c;qtt zD-re2_&fS{>w5^VUf53iVV|a&hX;IG12~Cg!g`DK5X>`&AG$|~EM_ccXr8GBQloR*mNzllC{-heIV{!R9_UnrALcTM#^7e((MA7lkR7{@CP#;`>232*bAIMCiuqlJhM7z>xjBV}VGD)BqPSuitajOG3@`b@ zbpK5qJgL98{7)6X?^C zQx;#!OZSF~vi(I{_*;qar@s3^JD6Ee#C!%g4~%(1mLZ%-PxruKN2~%*+C&O)GoUdw zBZW!jj{9NT*MPGm&>B-=r689fhZi%%2xZ4s0g1cZ|0V3_U;lO)c7><@IBfHd0zq1^ zma~9J5-VCfNgpPiQ zsaA6Z19EWWibVQ>BwF{|kxl@e2#-S;^qu?^#65!`9gN^>O~BX%GNb1#D2=s^r?3^vEd5&X04;Zd-Iqh1!%wPi7D!RH--F@s~Y#+ zqR|l)sC&W(@nKtg=*P$8Qz9qUx+m-8(vT0>!4UgHurZK);S#sBy>@@viM?Qho!C{C z?-u5}&V$hZm->q$zLCd9CwN}}4HbM%1>7o#FBKg+=j}l!pa}UQ5W^Qy<@u5oSUl8 zUVSHo;t(>mq=}Dsxbe9!Z-s7anUj@Rodmwxl0wAh{PFq41|g6M(VK(F`19dej!qX{2Z)b(&U&rthnOi-KXcLx~DR9 zuMZT+M}EnZ5z_l}qximm4w`|D(BDHIWE3SoKHHmcImKiT5SQSM-=i2g@$GphIPkGs z1u%1sPT2gdOaLY)+xUu73<;9!`{u#l4OtqLIzHRe0Gk?x?1^k9eaXrZ1*WT?2G9O0 zru5X7@_)~k@~>`u5)Yq%RZ^{LqM*b`n`Dusu||fCK?>bVy_+AosA1Qzc9;>jI)RcW zVNwcl7QoJZl@x^NvF7Lj)+G5ZVKsxM} zWLRHb^x?&{cR-LVg{}I{$nSuti3C>Ihr8fz6l6Ci-{12)?m(n$(=P{rVZHs|M8jUI z0;xO!D8>)v;H^lBOFWBZ;(~b{29La4zy;q(_m|l6k38D&K*M& z{i=|fDLxV%Ng#fG;3e6v;}{=O;QlfnV|<0d^`dD7 z3aN9&;iddzpq!P#1S*p=DnXr`yUayJLI5A34eNdYUXY65xFdRGERhHDY(7Z?zt^g< z%oEsv=FS!1=YKO93FOOUcc6B8#VEpp{ro8Wy(0Op((yy!%ze=Sk^^770UYARWm;VO z%V%fsJWllFx14qO21EkD>9(K%T|Pf)IdCqfqxPd59Yc38er9UkzYqrddz;+;l0KvV zz9~w_KWczQLKf@QH>1k5iAL4M0@A~S^}W!X@77PLpJU~&5Be6Qc-^3s_8-hTNp8?8p~*27-owS2r!ImKNg zKIO_b`2J{jWk24fBbh@BYRB3eYTn3)vk_?afoH5?Wj>QrqnA%%Vg++1v@NJK?-k@$k3zi%5z zkx|4=dfNholssp4VCTeC?nwXft^Ex~q0a3%~k>x10vn_>=QTpD0p^Lgj6>Kc9S+0oj(^Lsi`SLSyoy#n_ zl<5V#Vwo#?^EK_IbS3m#LW9=se7YnHivo8x*zlcbdKg2*RLQYN@|+&OYv@#qO=Wec zta#wg#rx!1zgA$V3b39xBPyYLLtDV6|7AXtV@b1jCqq^4(|TurHGVjhxTP<<)Q2zU z){c*L@iMAeFmKVb&5d^M@ksS$KFCC}R&Jg?LA4SmJ)ggh0__IkRc;l@wC^;wn51GE z@UtU6M&%tGfweMsgB5Y^L}*Upkr9;kDLv+JqPTyV<5NS9Drdk8xv~h4v$q=t@A@}u z-70JMDU|>rTaclX;#_&T35!Z!DuO#7&_rHl5xc5Zj$pltxFO7mC5Z*iC|%hiV97^h zZStnTrk{7gMnH%OLA?AYfCw9EL2QWAi6#OKe^=r*AYsanz&-iEUEu6rQ~x>m=IvmVJZYSC_`qK=dM%vPkg#Az+9 zFioCs3U`V#cvS{<^Q=7He&9|OphThc2xnQ+ZT58)W%b9`?)qE@uErH=x-*sh+&g5R zlalkEv zFNO(+*gGa37l34G@?2gh%c$4vjI9l$OHokP&1uUEki{Tx7)%r7oQxKFndTr-6!IXu zNU1A>%ILl-#)w+n@WB0s^L@mGk^>k_750`SZ}K}NYogn)`dbKexcb8JT%gjcs@*Y24oPJdnVh;nlCNZz-J-py0N zuh~kgES5nmi0Y$t18HuZPWGga(!KB+Da@!4s}yCSB<6_V73@J3_#WWg=&%@-L1Z0(Ipr%%MYo z6k@Yu#GC7~9a6tjnN)c`Dy(YBRkX-PMDNaX{j*ImuZ~J2J4fUAmv~l5Nz3UB#HTQs z_;nOIjqY^l<&4l3EZAJ}%(-4Um4c9@Ed!b5XJAbgBUv*@Yko1$Eyi14QDoN!m!ui zOTA?-B-lBP_5F=~z9??8_nLzGGVXf`cZ?yj;%^>oj}HZN%&E-@p$4Zfsn;|{^) zzARaUbvaND3YVFrJkh)W{3L93n0Boqe=E}{A~ia1 zxlnsclI!+Xq$}U}>Xa`}PwMT_Nl`{$PRaFwTS0kz4W|zHCZ<2h3`L1Ga1FyS7HbAW6 zsZcERj=uQ?hlg{r!AmR5W{icQsHA)`7Y);GNzR!L%$YT|iSFC!5EO=}yoaW%X$rvr z7CtEmSGJ}^ZcixOl%im{K-Fy8%(vH*@z`-}8lT&%{qo{q*+)N>zUQHxyMv4Dh1r|2 z-VZ1oW5jcDYMus6W8EpU5UCd+j#{j)An%Ju^sxvy_q~;tKVAhUj z=tQorA7&j&8k|q0q1V+i=vb|cb5=H@)I2G4YYy^K%o2TRSVoCaz0un+-JE>gC9fl| zFDxlCxjj`dd3g8&N8ACm(c5cg{W14Qa>?RnGYm9{yJt3B_FYAKj2N|#(EG#a)9@q3 z4#M}&cQ?$qfvMb;#7VdN*-t~rM zEgcp$sqG6R8Sl@o3HAn5X06DMZfNIix#Fs)QNwtaP2+uS&_J?xO)(I%D^xeJ@;GVs z*i|H4d}LayWs49ybnkOrTLegfl^g3G_>Fz~iqiU)US$L_4h!Ih?LQFdFJ(0DEAjAX z3UG+t->+fBzgseMG_ba?rY72%PrT0t!(NC}@RH2!(faUYWC7=1?}iA|CUx=fbAJ?c zJxNKqKv#`2AxM7f94vYp-k_EiJgRsAdz`?G@K!N7%7x8S7Np^89$e2G+tUFIu#k}= zNF!bN&34Fol6%DP=7^Wym~(swQscKke=6IAjyO(x?sKg(Bw@%zm#aX?}Z&&g4X(@-DQpZ`mby22?2INbQ?h`~^!w&Kgym-0OZ0r1p^XK+v7dwk3eq2>?m}cM3 z7ey`~)=Cv*=)~*wZGoHl5`5^w!K8PC=d`kQFm8NEHu>N@gvC!UJXLkRcTci>y>?hd zz11zL|4gf~F=g;r4$GR{yL&fWu@2gxiLqX&?IJNt)?rq~%CrQ{_WC?}r0a=+@PsX|EO2r@mUlDJSeav-x4 zkRD9K*@AUnSFSs(NnK0TcT!bCn78MN;cFSX*r3fGo7~H*jXFAWAJ2S3QrN1js*~4n z9$JQ68#d6XVsjb9dEwRc0jI)y&YdQS82TkSb}aH+;uj2(Qm3m$r6onB8~7=tJKe$9 z&oG8m4lpj8><#JLG-spHP?+99=T3u;N8Ya+5(?{5DfE-?Wb_G8BgT0aHS9)A?bXsI zD8w67PnC#8pA#Di&w?YyM)8s5Ynz&8je_GQ49en6@m(4sX)vgI+m~@Q(V0AZ1&dAE zv#8BanVs^}j6OO>*Kr>14ca*7bP}6THP^K>My!rRS-=x{6>D)BIgojU)1W;YM2pGQ zeb|vTF*oDtVAGM*X9A9pGj}OOP*N_(`!p%Mp`tjvs%*Z0)k<7lZQny0S~8+Q=`&a0 z)$jzLs`xGuzQZ%$sel22VI@@|`Wvg}DHDwMcehreD0{Wai`<*U#WiyW4la^^xUtE)TQaI#k15#?7Dq$?|I>Y#;U%)83MMAM^phi*SC{5>DfWoJ|2SZPox$+%)bb_^#N;sOb}4 zJs}8iH`+go@Ay~f(?9fg=4505Ibzq2Ec%L~vb1!<%T0pZ4FQ)U3XhZYq|JC-^?R@W z)9?SG2KYa;7H8Mt!#$9tV{qr6%^0lut~8Ujk!$Nf3oN58-{MsNEWGGHS}*fK_1Gpe za5d|LEU7zwM-2;c>+q08Ejdg54k5ghZ}-N3JW}0hpMSnJk0LkuzCF0z}Sa}Ha4I2XO+6A_5P$e%6=m_ z;>Z2_{{SperB|FEndxP8uEsZj^4a?u1bI`jUSb!>+lFT&e2f=)T$TiDw+7M=GZGhO zqCd7Z0$*zef{;OGh19xBezRbyw&C4q-^*WTth6ZgAd%zj_D+=&N zKJjhS(YcN|A>EU0lArvRewjen=im2FlJ!abwEL3_db;?3JEx~d@09RPUL_}~w4JW2aBf-yi4G2ltfS_&3bnDYJFTYMy*VPg%`VR`Z)cc*GTOhyXva*zic(>H zN5H~*Q8oY71UEj^NLaqDkva5z5Ea#Pi&m4qo0>k6xXAymj4G#k>OY^J`gBiD_vG{p boSuQxGjMtaPS3#U88|%ye>nrFUkCml23FrQ literal 268146 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

19AJ(Kg%K7G=!z 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 Date: Sun, 28 Feb 2021 16:02:37 +0000 Subject: [PATCH 23/91] Reformat --- src/Algorithm.cpp | 141 +++++++------ src/Manager.cpp | 89 ++++---- src/OpAlgoDispatch.cpp | 5 +- src/OpTensorSyncDevice.cpp | 4 +- src/OpTensorSyncLocal.cpp | 4 +- src/Sequence.cpp | 42 ++-- src/Shader.cpp | 77 ++++--- src/Tensor.cpp | 101 +++++---- src/include/kompute/Algorithm.hpp | 32 ++- src/include/kompute/Core.hpp | 52 +++-- src/include/kompute/Manager.hpp | 11 +- src/include/kompute/Sequence.hpp | 56 ++--- src/include/kompute/Shader.hpp | 259 ++++++++++++------------ src/include/kompute/Tensor.hpp | 33 ++- test/TestAsyncOperations.cpp | 6 +- test/TestDestroy.cpp | 32 +-- test/TestLogisticRegression.cpp | 80 ++++---- test/TestManager.cpp | 30 +-- test/TestMultipleAlgoExecutions.cpp | 96 ++++----- test/TestOpShadersFromStringAndFile.cpp | 32 +-- test/TestOpTensorCopy.cpp | 37 ++-- test/TestPushConstant.cpp | 16 +- test/TestSequence.cpp | 1 - test/TestSpecializationConstant.cpp | 16 +- test/TestTensor.cpp | 1 - test/TestWorkgroup.cpp | 38 +++- 26 files changed, 667 insertions(+), 624 deletions(-) diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index cfae65643..c58c5a228 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -4,23 +4,25 @@ namespace kp { -Algorithm::Algorithm( - std::shared_ptr device, - const std::vector>& tensors, - const std::vector& spirv, - const Workgroup& workgroup, - const Constants& specializationConstants) +Algorithm::Algorithm(std::shared_ptr device, + const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants) { KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); this->mDevice = device; if (tensors.size() && spirv.size()) { - KP_LOG_INFO("Kompute Algorithm initialising with tensor size: {} and spirv size: {}", tensors.size(), spirv.size()); + KP_LOG_INFO("Kompute Algorithm initialising with tensor size: {} and " + "spirv size: {}", + tensors.size(), + spirv.size()); this->rebuild(tensors, spirv, workgroup, specializationConstants); - } - else { - KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or spirv so not rebuilding vulkan components"); + } else { + KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or " + "spirv so not rebuilding vulkan components"); } } @@ -32,20 +34,21 @@ Algorithm::~Algorithm() } void -Algorithm::rebuild( - const std::vector>& tensors, - const std::vector& spirv, - const Workgroup& workgroup, - const Constants& specializationConstants) +Algorithm::rebuild(const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants) { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); this->mTensors = tensors; this->mSpirv = spirv; this->mSpecializationConstants = specializationConstants; - this->setWorkgroup(workgroup, this->mTensors.size() ? this->mTensors[0]->size() : 1); + this->setWorkgroup(workgroup, + this->mTensors.size() ? this->mTensors[0]->size() : 1); - // Descriptor pool is created first so if available then destroy all before rebuild + // Descriptor pool is created first so if available then destroy all before + // rebuild if (this->isInit()) { this->destroy(); } @@ -56,22 +59,20 @@ Algorithm::rebuild( } bool -Algorithm::isInit() { - return this->mPipeline && - this->mPipelineCache && - this->mPipelineLayout && - this->mDescriptorPool && - this->mDescriptorSet && - this->mDescriptorSetLayout && - this->mShaderModule; +Algorithm::isInit() +{ + return this->mPipeline && this->mPipelineCache && this->mPipelineLayout && + this->mDescriptorPool && this->mDescriptorSet && + this->mDescriptorSetLayout && this->mShaderModule; } void -Algorithm::destroy() { +Algorithm::destroy() +{ if (!this->mDevice) { - KP_LOG_WARN( - "Kompute Algorithm destroy function reached with null Device pointer"); + KP_LOG_WARN("Kompute Algorithm destroy function reached with null " + "Device pointer"); return; } @@ -79,7 +80,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline"); if (!this->mPipeline) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " - "pipeline but it is null"); + "pipeline but it is null"); } this->mDevice->destroy( *this->mPipeline, @@ -91,7 +92,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline cache"); if (!this->mPipelineCache) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " - "pipeline cache but it is null"); + "pipeline cache but it is null"); } this->mDevice->destroy( *this->mPipelineCache, @@ -103,7 +104,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying pipeline layout"); if (!this->mPipelineLayout) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " - "pipeline layout but it is null"); + "pipeline layout but it is null"); } this->mDevice->destroy( *this->mPipelineLayout, @@ -115,7 +116,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying shader module"); if (!this->mShaderModule) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy shader " - "module but it is null"); + "module but it is null"); } this->mDevice->destroy( *this->mShaderModule, @@ -123,10 +124,10 @@ Algorithm::destroy() { this->mShaderModule = nullptr; } - // We don't call freeDescriptorSet as the descriptor pool is not created with - // VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT more at + // We don't call freeDescriptorSet as the descriptor pool is not created + // with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT more at // (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-vkFreeDescriptorSets-descriptorPool-00312)) - //if (this->mFreeDescriptorSet && this->mDescriptorSet) { + // if (this->mFreeDescriptorSet && this->mDescriptorSet) { // KP_LOG_DEBUG("Kompute Algorithm Freeing Descriptor Set"); // if (!this->mDescriptorSet) { // KP_LOG_WARN( @@ -141,7 +142,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Set Layout"); if (!this->mDescriptorSetLayout) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " - "descriptor set layout but it is null"); + "descriptor set layout but it is null"); } this->mDevice->destroy( *this->mDescriptorSetLayout, @@ -153,7 +154,7 @@ Algorithm::destroy() { KP_LOG_DEBUG("Kompute Algorithm Destroying Descriptor Pool"); if (!this->mDescriptorPool) { KP_LOG_WARN("Kompute Algorithm Error requested to destroy " - "descriptor pool but it is null"); + "descriptor pool but it is null"); } this->mDevice->destroy( *this->mDescriptorPool, @@ -246,10 +247,10 @@ Algorithm::createShaderModule() { KP_LOG_DEBUG("Kompute Algorithm createShaderModule started"); - vk::ShaderModuleCreateInfo shaderModuleInfo( - vk::ShaderModuleCreateFlags(), - sizeof(uint32_t) * this->mSpirv.size(), - this->mSpirv.data()); + vk::ShaderModuleCreateInfo shaderModuleInfo(vk::ShaderModuleCreateFlags(), + sizeof(uint32_t) * + this->mSpirv.size(), + this->mSpirv.data()); KP_LOG_DEBUG("Kompute Algorithm Creating shader module. ShaderFileSize: {}", this->mSpirv.size()); @@ -281,14 +282,14 @@ Algorithm::createPipeline() for (uint32_t i = 0; i < this->mSpecializationConstants.size(); i++) { vk::SpecializationMapEntry specializationEntry( - static_cast(i), - static_cast(sizeof(float) * i), - sizeof(float)); + static_cast(i), + static_cast(sizeof(float) * i), + sizeof(float)); specializationEntries.push_back(specializationEntry); } - // This passes ownership of the memory so we remove ownership from + // This passes ownership of the memory so we remove ownership from // specialization container by using "transferDataOwnership" vk::SpecializationInfo specializationInfo( static_cast(specializationEntries.size()), @@ -338,7 +339,8 @@ Algorithm::createPipeline() // TODO: Update to consistent // this->mPipeline = std::make_shared(); // this->mDevice->createComputePipelines( - // *this->mPipelineCache, 1, &pipelineInfo, nullptr, this->mPipeline.get()); + // *this->mPipelineCache, 1, &pipelineInfo, nullptr, + // this->mPipeline.get()); KP_LOG_DEBUG("Kompute Algorithm Create Pipeline Success"); } @@ -349,29 +351,31 @@ Algorithm::bindCore(const vk::CommandBuffer& commandBuffer) KP_LOG_DEBUG("Kompute Algorithm binding pipeline"); commandBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, - *this->mPipeline); + *this->mPipeline); KP_LOG_DEBUG("Kompute Algorithm binding descriptor sets"); commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, - *this->mPipelineLayout, - 0, // First set - *this->mDescriptorSet, - nullptr // Dispatcher + *this->mPipelineLayout, + 0, // First set + *this->mDescriptorSet, + nullptr // Dispatcher ); } void -Algorithm::bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants) +Algorithm::bindPush(const vk::CommandBuffer& commandBuffer, + const Constants& pushConstants) { if (pushConstants.size()) { - KP_LOG_DEBUG("Kompute Algorithm binding push constants size: {}", pushConstants.size()); + KP_LOG_DEBUG("Kompute Algorithm binding push constants size: {}", + pushConstants.size()); commandBuffer.pushConstants(*this->mPipelineLayout, - vk::ShaderStageFlagBits::eCompute, - 0, - pushConstants.size() * sizeof(float), - pushConstants.data()); + vk::ShaderStageFlagBits::eCompute, + 0, + pushConstants.size() * sizeof(float), + pushConstants.data()); } } @@ -380,11 +384,13 @@ Algorithm::recordDispatch(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute Algorithm recording dispatch"); - commandBuffer.dispatch(this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); + commandBuffer.dispatch( + this->mWorkgroup[0], this->mWorkgroup[1], this->mWorkgroup[2]); } void -Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { +Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) +{ KP_LOG_INFO("Kompute OpAlgoCreate setting dispatch size"); @@ -393,11 +399,9 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { if (workgroup[0] > 0) { // If at least the x value is provided we use mainly the parameters // provided - this->mWorkgroup = { - workgroup[0], - workgroup[1] > 0 ? workgroup[1] : 1, - workgroup[2] > 0 ? workgroup[2] : 1 - }; + this->mWorkgroup = { workgroup[0], + workgroup[1] > 0 ? workgroup[1] : 1, + workgroup[2] > 0 ? workgroup[2] : 1 }; } else { this->mWorkgroup = { minSize, 1, 1 }; } @@ -409,17 +413,20 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) { } const Workgroup& -Algorithm::getWorkgroup() { +Algorithm::getWorkgroup() +{ return this->mWorkgroup; } const Constants& -Algorithm::getSpecializationConstants() { +Algorithm::getSpecializationConstants() +{ return this->mSpecializationConstants; } const std::vector>& -Algorithm::getTensors() { +Algorithm::getTensors() +{ return this->mTensors; } diff --git a/src/Manager.cpp b/src/Manager.cpp index bb109aace..38f67de0d 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -55,7 +55,8 @@ Manager::~Manager() } void -Manager::destroy() { +Manager::destroy() +{ KP_LOG_DEBUG("Kompute Manager destroy() started"); @@ -78,7 +79,8 @@ Manager::destroy() { if (this->mManageResources && this->mManagedAlgorithms.size()) { KP_LOG_DEBUG("Kompute Manager explicitly freeing algorithms"); - for (const std::weak_ptr& weakAlgorithm : this->mManagedAlgorithms) { + for (const std::weak_ptr& weakAlgorithm : + this->mManagedAlgorithms) { if (std::shared_ptr algorithm = weakAlgorithm.lock()) { algorithm->destroy(); } @@ -214,31 +216,31 @@ Manager::createInstance() } void -Manager::clear() { +Manager::clear() +{ if (this->mManageResources) { this->mManagedTensors.erase( - std::remove_if( - begin(this->mManagedTensors), - end(this->mManagedTensors), - [](std::weak_ptr t) {return t.expired();}), - end(this->mManagedTensors)); + std::remove_if(begin(this->mManagedTensors), + end(this->mManagedTensors), + [](std::weak_ptr t) { return t.expired(); }), + end(this->mManagedTensors)); this->mManagedAlgorithms.erase( - std::remove_if( - begin(this->mManagedAlgorithms), - end(this->mManagedAlgorithms), - [](std::weak_ptr t) {return t.expired();}), - end(this->mManagedAlgorithms)); + std::remove_if( + begin(this->mManagedAlgorithms), + end(this->mManagedAlgorithms), + [](std::weak_ptr t) { return t.expired(); }), + end(this->mManagedAlgorithms)); this->mManagedSequences.erase( - std::remove_if( - begin(this->mManagedSequences), - end(this->mManagedSequences), - [](std::weak_ptr t) {return t.expired();}), - end(this->mManagedSequences)); + std::remove_if(begin(this->mManagedSequences), + end(this->mManagedSequences), + [](std::weak_ptr t) { return t.expired(); }), + end(this->mManagedSequences)); } } void -Manager::createDevice(const std::vector& familyQueueIndices, uint32_t physicalDeviceIndex) +Manager::createDevice(const std::vector& familyQueueIndices, + uint32_t physicalDeviceIndex) { KP_LOG_DEBUG("Kompute Manager creating Device"); @@ -256,8 +258,7 @@ Manager::createDevice(const std::vector& familyQueueIndices, uint32_t std::vector physicalDevices = this->mInstance->enumeratePhysicalDevices(); - vk::PhysicalDevice physicalDevice = - physicalDevices[physicalDeviceIndex]; + vk::PhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; this->mPhysicalDevice = std::make_shared(physicalDevice); @@ -342,16 +343,14 @@ Manager::createDevice(const std::vector& familyQueueIndices, uint32_t } std::shared_ptr -Manager::tensor( - const std::vector& data, - Tensor::TensorTypes tensorType) +Manager::tensor(const std::vector& data, Tensor::TensorTypes tensorType) { KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - std::shared_ptr tensor{ - new kp::Tensor(this->mPhysicalDevice, this->mDevice, data, tensorType) }; + std::shared_ptr tensor{ new kp::Tensor( + this->mPhysicalDevice, this->mDevice, data, tensorType) }; - if (this->mManageResources) { + if (this->mManageResources) { this->mManagedTensors.push_back(tensor); } @@ -359,23 +358,18 @@ Manager::tensor( } std::shared_ptr -Manager::algorithm( - const std::vector>& tensors, - const std::vector& spirv, - const Workgroup& workgroup, - const Constants& specializationConstants) { +Manager::algorithm(const std::vector>& tensors, + const std::vector& spirv, + const Workgroup& workgroup, + const Constants& specializationConstants) +{ KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); - std::shared_ptr algorithm{ - new kp::Algorithm( - this->mDevice, - tensors, - spirv, - workgroup, - specializationConstants)}; + std::shared_ptr algorithm{ new kp::Algorithm( + this->mDevice, tensors, spirv, workgroup, specializationConstants) }; - if (this->mManageResources) { + if (this->mManageResources) { this->mManagedAlgorithms.push_back(algorithm); } @@ -385,16 +379,15 @@ Manager::algorithm( std::shared_ptr Manager::sequence(uint32_t queueIndex) { - KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", - queueIndex); + KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", queueIndex); - std::shared_ptr sq{ - new kp::Sequence(this->mPhysicalDevice, - this->mDevice, - this->mComputeQueues[queueIndex], - this->mComputeQueueFamilyIndices[queueIndex]) }; + std::shared_ptr sq{ new kp::Sequence( + this->mPhysicalDevice, + this->mDevice, + this->mComputeQueues[queueIndex], + this->mComputeQueueFamilyIndices[queueIndex]) }; - if (this->mManageResources) { + if (this->mManageResources) { this->mManagedSequences.push_back(sq); } diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index b8e49f144..4a30751fb 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -5,7 +5,7 @@ namespace kp { OpAlgoDispatch::OpAlgoDispatch(const std::shared_ptr& algorithm, - const kp::Constants& pushConstants) + const kp::Constants& pushConstants) { KP_LOG_DEBUG("Kompute OpAlgoDispatch constructor"); @@ -24,7 +24,8 @@ OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) KP_LOG_DEBUG("Kompute OpAlgoDispatch record called"); // Barrier to ensure the data is finished writing to buffer memory - for (const std::shared_ptr& tensor : this->mAlgorithm->getTensors()) { + for (const std::shared_ptr& tensor : + this->mAlgorithm->getTensors()) { tensor->recordBufferMemoryBarrier( commandBuffer, vk::AccessFlagBits::eHostWrite, diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index 5fdd7b74c..85cefde77 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -30,8 +30,8 @@ OpTensorSyncDevice::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordCopyFromStagingToDevice( - commandBuffer, false); + this->mTensors[i]->recordCopyFromStagingToDevice(commandBuffer, + false); } } } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 6add3fa20..092490d15 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -30,8 +30,8 @@ OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordCopyFromDeviceToStaging( - commandBuffer, true); + this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer, + true); } } } diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 077681fac..68ff082ce 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -37,7 +37,8 @@ Sequence::begin() } if (this->isRunning()) { - throw std::runtime_error("Kompute Sequence begin called when sequence still running"); + throw std::runtime_error( + "Kompute Sequence begin called when sequence still running"); } KP_LOG_INFO("Kompute Sequence command now started recording"); @@ -53,8 +54,7 @@ Sequence::end() if (!this->isRecording()) { KP_LOG_WARN("Kompute Sequence end called when not recording"); return; - } - else { + } else { KP_LOG_INFO("Kompute Sequence command recording END"); this->mCommandBuffer->end(); this->mRecording = false; @@ -62,7 +62,8 @@ Sequence::end() } void -Sequence::clear() { +Sequence::clear() +{ KP_LOG_DEBUG("Kompute Sequence calling clear"); this->end(); } @@ -76,7 +77,8 @@ Sequence::eval() } std::shared_ptr -Sequence::eval(std::shared_ptr op) { +Sequence::eval(std::shared_ptr op) +{ this->clear(); return this->record(op)->eval(); } @@ -89,8 +91,9 @@ Sequence::evalAsync() } if (this->mIsRunning) { - throw std::runtime_error("Kompute Sequence evalAsync called when an eval async was " - "called without successful wait"); + throw std::runtime_error( + "Kompute Sequence evalAsync called when an eval async was " + "called without successful wait"); } this->mIsRunning = true; @@ -137,7 +140,8 @@ Sequence::evalAwait(uint64_t waitFor) this->mIsRunning = false; if (result == vk::Result::eTimeout) { - KP_LOG_WARN("Kompute Sequence evalAwait reached timeout of {}", waitFor); + KP_LOG_WARN("Kompute Sequence evalAwait reached timeout of {}", + waitFor); return shared_from_this(); } @@ -161,11 +165,10 @@ Sequence::isRecording() } bool -Sequence::isInit() { - return this->mDevice && - this->mCommandPool && - this->mCommandBuffer && - this->mComputeQueue; +Sequence::isInit() +{ + return this->mDevice && this->mCommandPool && this->mCommandBuffer && + this->mComputeQueue; } void @@ -175,16 +178,15 @@ Sequence::destroy() if (!this->mDevice) { KP_LOG_WARN("Kompute Sequence destroy called " - "with null Device pointer"); + "with null Device pointer"); return; } if (this->mFreeCommandBuffer) { KP_LOG_INFO("Freeing CommandBuffer"); if (!this->mCommandBuffer) { - KP_LOG_WARN( - "Kompute Sequence destroy called with null " - "CommandPool pointer"); + KP_LOG_WARN("Kompute Sequence destroy called with null " + "CommandPool pointer"); return; } this->mDevice->freeCommandBuffers( @@ -199,9 +201,8 @@ Sequence::destroy() if (this->mFreeCommandPool) { KP_LOG_INFO("Destroying CommandPool"); if (this->mCommandPool == nullptr) { - KP_LOG_WARN( - "Kompute Sequence destroy called with null " - "CommandPool pointer"); + KP_LOG_WARN("Kompute Sequence destroy called with null " + "CommandPool pointer"); return; } this->mDevice->destroy( @@ -228,7 +229,6 @@ Sequence::destroy() if (this->mComputeQueue) { this->mComputeQueue = nullptr; } - } std::shared_ptr diff --git a/src/Shader.cpp b/src/Shader.cpp index cdcd66e74..428b5a667 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -5,11 +5,13 @@ namespace kp { std::vector -Shader::compile_sources(const std::vector& sources, - const std::vector& files, - const std::string& entryPoint, - std::vector> definitions, - const TBuiltInResource& resources) { +Shader::compile_sources( + const std::vector& sources, + const std::vector& files, + const std::string& entryPoint, + std::vector> definitions, + const TBuiltInResource& resources) +{ // Initialize glslang library. glslang::InitializeProcess(); @@ -18,27 +20,32 @@ Shader::compile_sources(const std::vector& sources, const EShLanguage language = EShLangCompute; glslang::TShader shader(language); - std::vector filesCStr(files.size()), sourcesCStr(sources.size()); - for (size_t i = 0; i < sources.size(); i++) sourcesCStr[i] = sources[i].c_str(); + std::vector filesCStr(files.size()), + sourcesCStr(sources.size()); + for (size_t i = 0; i < sources.size(); i++) + sourcesCStr[i] = sources[i].c_str(); if (files.size() > 1) { assert(files.size() == sources.size()); - for (size_t i = 0; i < files.size(); i++) filesCStr[i] = files[i].c_str(); - shader.setStringsWithLengthsAndNames(sourcesCStr.data(), nullptr, filesCStr.data(), filesCStr.size()); - } - else { - filesCStr = {""}; - shader.setStringsWithLengthsAndNames(sourcesCStr.data(), nullptr, filesCStr.data(), sourcesCStr.size()); + for (size_t i = 0; i < files.size(); i++) + filesCStr[i] = files[i].c_str(); + shader.setStringsWithLengthsAndNames( + sourcesCStr.data(), nullptr, filesCStr.data(), filesCStr.size()); + } else { + filesCStr = { "" }; + shader.setStringsWithLengthsAndNames( + sourcesCStr.data(), nullptr, filesCStr.data(), sourcesCStr.size()); } shader.setEntryPoint(entryPoint.c_str()); shader.setSourceEntryPoint(entryPoint.c_str()); std::string info_log = ""; - const EShMessages messages = static_cast(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); - if (!shader.parse(&resources, 100, false, messages)) - { - info_log = std::string(shader.getInfoLog()) + "\n" + std::string(shader.getInfoDebugLog()); + const EShMessages messages = static_cast( + EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); + if (!shader.parse(&resources, 100, false, messages)) { + info_log = std::string(shader.getInfoLog()) + "\n" + + std::string(shader.getInfoDebugLog()); KP_LOG_ERROR("Kompute Shader Error: {}", info_log); throw std::runtime_error(info_log); } @@ -47,24 +54,23 @@ Shader::compile_sources(const std::vector& sources, glslang::TProgram program; program.addShader(&shader); // Link program. - if (!program.link(messages)) - { - info_log = std::string(program.getInfoLog()) + "\n" + std::string(program.getInfoDebugLog()); + if (!program.link(messages)) { + info_log = std::string(program.getInfoLog()) + "\n" + + std::string(program.getInfoDebugLog()); KP_LOG_ERROR("Kompute Shader Error: {}", info_log); throw std::runtime_error(info_log); } // Save any info log that was generated. - if (shader.getInfoLog()) - { - info_log += std::string(shader.getInfoLog()) + "\n" + std::string(shader.getInfoDebugLog()) + "\n"; + if (shader.getInfoLog()) { + info_log += std::string(shader.getInfoLog()) + "\n" + + std::string(shader.getInfoDebugLog()) + "\n"; KP_LOG_INFO("Kompute Shader Information: {}", info_log); } - glslang::TIntermediate *intermediate = program.getIntermediate(language); + glslang::TIntermediate* intermediate = program.getIntermediate(language); // Translate to SPIRV. - if (!intermediate) - { + if (!intermediate) { info_log += "Failed to get shared intermediate code.\n"; KP_LOG_ERROR("Kompute Shader Error: {}", info_log); throw std::runtime_error(info_log); @@ -74,8 +80,7 @@ Shader::compile_sources(const std::vector& sources, std::vector spirv; glslang::GlslangToSpv(*intermediate, spirv, &logger); - if (shader.getInfoLog()) - { + if (shader.getInfoLog()) { info_log += logger.getAllMessages() + "\n"; KP_LOG_DEBUG("Kompute Shader all result messages: {}", info_log); } @@ -87,11 +92,17 @@ Shader::compile_sources(const std::vector& sources, } std::vector -Shader::compile_source(const std::string& source, - const std::string& entryPoint, - std::vector> definitions, - const TBuiltInResource& resource) { - return compile_sources({source}, std::vector({}), entryPoint, definitions, resource); +Shader::compile_source( + const std::string& source, + const std::string& entryPoint, + std::vector> definitions, + const TBuiltInResource& resource) +{ + return compile_sources({ source }, + std::vector({}), + entryPoint, + definitions, + resource); } } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index dd645708e..f584c07bd 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -4,9 +4,9 @@ namespace kp { Tensor::Tensor(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType) + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType) { KP_LOG_DEBUG("Kompute Tensor constructor data length: {}, and type: {}", data.size(), @@ -29,17 +29,16 @@ Tensor::~Tensor() } void -Tensor::rebuild(const std::vector& data, - TensorTypes tensorType) +Tensor::rebuild(const std::vector& data, TensorTypes tensorType) { - KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", - data.size()); + KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", data.size()); this->mData = data; this->mTensorType = tensorType; if (this->mPrimaryBuffer || this->mPrimaryMemory) { - KP_LOG_DEBUG("Kompute Tensor destroying existing resources before rebuild"); + KP_LOG_DEBUG( + "Kompute Tensor destroying existing resources before rebuild"); this->destroy(); } @@ -77,10 +76,9 @@ Tensor::tensorType() } bool -Tensor::isInit() { - return this->mDevice && - this->mPrimaryBuffer && - this->mPrimaryMemory; +Tensor::isInit() +{ + return this->mDevice && this->mPrimaryBuffer && this->mPrimaryMemory; } void @@ -105,17 +103,16 @@ Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, KP_LOG_DEBUG("Kompute Tensor recordCopyFrom data size {}.", bufferSize); this->recordCopyBuffer(commandBuffer, - copyFromTensor->mPrimaryBuffer, - this->mPrimaryBuffer, - bufferSize, - copyRegion, - createBarrier); + copyFromTensor->mPrimaryBuffer, + this->mPrimaryBuffer, + bufferSize, + copyRegion, + createBarrier); } void -Tensor::recordCopyFromStagingToDevice( - const vk::CommandBuffer& commandBuffer, - bool createBarrier) +Tensor::recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, + bool createBarrier) { vk::DeviceSize bufferSize(this->memorySize()); vk::BufferCopy copyRegion(0, 0, bufferSize); @@ -123,17 +120,16 @@ Tensor::recordCopyFromStagingToDevice( KP_LOG_DEBUG("Kompute Tensor copying data size {}.", bufferSize); this->recordCopyBuffer(commandBuffer, - this->mStagingBuffer, - this->mPrimaryBuffer, - bufferSize, - copyRegion, - createBarrier); + this->mStagingBuffer, + this->mPrimaryBuffer, + bufferSize, + copyRegion, + createBarrier); } void -Tensor::recordCopyFromDeviceToStaging( - const vk::CommandBuffer& commandBuffer, - bool createBarrier) +Tensor::recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, + bool createBarrier) { vk::DeviceSize bufferSize(this->memorySize()); vk::BufferCopy copyRegion(0, 0, bufferSize); @@ -141,20 +137,20 @@ Tensor::recordCopyFromDeviceToStaging( KP_LOG_DEBUG("Kompute Tensor copying data size {}.", bufferSize); this->recordCopyBuffer(commandBuffer, - this->mPrimaryBuffer, - this->mStagingBuffer, - bufferSize, - copyRegion, - createBarrier); + this->mPrimaryBuffer, + this->mStagingBuffer, + bufferSize, + copyRegion, + createBarrier); } void Tensor::recordCopyBuffer(const vk::CommandBuffer& commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier) + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier) { commandBuffer.copyBuffer(*bufferFrom, *bufferTo, copyRegion); @@ -170,12 +166,11 @@ Tensor::recordCopyBuffer(const vk::CommandBuffer& commandBuffer, } void -Tensor::recordBufferMemoryBarrier( - const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask) +Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask) { KP_LOG_DEBUG("Kompute Tensor recording buffer memory barrier"); @@ -190,11 +185,11 @@ Tensor::recordBufferMemoryBarrier( bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; commandBuffer.pipelineBarrier(srcStageMask, - dstStageMask, - vk::DependencyFlags(), - nullptr, - bufferMemoryBarrier, - nullptr); + dstStageMask, + vk::DependencyFlags(), + nullptr, + bufferMemoryBarrier, + nullptr); } vk::DescriptorBufferInfo @@ -449,7 +444,7 @@ Tensor::destroy() if (this->mFreePrimaryBuffer) { if (!this->mPrimaryBuffer) { KP_LOG_WARN("Kompose Tensor expected to destroy primary buffer " - "but got null buffer"); + "but got null buffer"); } else { KP_LOG_DEBUG("Kompose Tensor destroying primary buffer"); this->mDevice->destroy( @@ -463,7 +458,7 @@ Tensor::destroy() if (this->mFreeStagingBuffer) { if (!this->mStagingBuffer) { KP_LOG_WARN("Kompose Tensor expected to destroy staging buffer " - "but got null buffer"); + "but got null buffer"); } else { KP_LOG_DEBUG("Kompose Tensor destroying staging buffer"); this->mDevice->destroy( @@ -477,7 +472,7 @@ Tensor::destroy() if (this->mFreePrimaryMemory) { if (!this->mPrimaryMemory) { KP_LOG_WARN("Kompose Tensor expected to free primary memory but " - "got null memory"); + "got null memory"); } else { KP_LOG_DEBUG("Kompose Tensor freeing primary memory"); this->mDevice->freeMemory( @@ -491,7 +486,7 @@ Tensor::destroy() if (this->mFreeStagingMemory) { if (!this->mStagingMemory) { KP_LOG_WARN("Kompose Tensor expected to free staging memory but " - "got null memory"); + "got null memory"); } else { KP_LOG_DEBUG("Kompose Tensor freeing staging memory"); this->mDevice->freeMemory( diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index b80f3946c..e5fd1287e 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -12,8 +12,7 @@ namespace kp { */ class Algorithm { -public: - + public: /** * Default constructor for Algorithm * @@ -21,12 +20,11 @@ public: * @param commandBuffer The vulkan command buffer to bind the pipeline and * shaders */ - Algorithm( - std::shared_ptr device, - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + Algorithm(std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -34,14 +32,13 @@ public: * * @param shaderFileData The bytes in spir-v format of the shader * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing + * @specalizationInstalces The specialization parameters to pass to the + * function processing */ - void rebuild( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + void rebuild(const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -61,7 +58,8 @@ public: void bindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants); + void bindPush(const vk::CommandBuffer& commandBuffer, + const Constants& pushConstants); bool isInit(); @@ -73,7 +71,7 @@ public: void destroy(); -private: + private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mDevice; std::vector> mTensors; diff --git a/src/include/kompute/Core.hpp b/src/include/kompute/Core.hpp index 6da52953f..b50bf081d 100644 --- a/src/include/kompute/Core.hpp +++ b/src/include/kompute/Core.hpp @@ -60,12 +60,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_DEBUG(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_DEBUG(...) \ - ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_DEBUG(...) \ + ((void)__android_log_print( \ + ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_DEBUG(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 1 @@ -73,12 +80,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_INFO(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_INFO(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_INFO(...) \ + ((void)__android_log_print( \ + ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_INFO(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 2 @@ -86,12 +100,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_WARN(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_WARN(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_WARN(...) \ + ((void)__android_log_print( \ + ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_WARN(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 3 @@ -99,12 +120,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_ERROR(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_ERROR(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_ERROR(...) \ + ((void)__android_log_print( \ + ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_ERROR(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 4 #endif // KOMPUTE_SPDLOG_ENABLED diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index e651cf2bb..61212abf2 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -84,10 +84,10 @@ class Manager Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); std::shared_ptr algorithm( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); void destroy(); void clear(); @@ -119,7 +119,8 @@ class Manager // Create functions void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); + void createDevice(const std::vector& familyQueueIndices = {}, + uint32_t hysicalDeviceIndex = 0); }; } // End namespace kp diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index fa3b399e8..29c6a0c3b 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -9,7 +9,7 @@ namespace kp { /** * Container of operations that can be sent to GPU as batch */ -class Sequence: public std::enable_shared_from_this +class Sequence : public std::enable_shared_from_this { public: /** @@ -46,8 +46,9 @@ class Sequence: public std::enable_shared_from_this * which allows for extensible configurations on initialisation. */ template - std::shared_ptr - record(std::vector> tensors, TArgs&&... params) + std::shared_ptr record( + std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -56,14 +57,13 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); } template - std::shared_ptr - record(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr record(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -72,8 +72,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->record(op); } @@ -96,8 +96,8 @@ class Sequence: public std::enable_shared_from_this */ // TODO: Aim to have only a single function with tensors/algorithm template - std::shared_ptr - eval(std::vector> tensors, TArgs&&... params) + std::shared_ptr eval(std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -106,16 +106,16 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - // TODO: Aim to be able to handle errors when returning without throw except + // TODO: Aim to be able to handle errors when returning without throw + // except return this->eval(op); } // Needded as otherise can't use initialiser list template - std::shared_ptr - eval(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr eval(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -124,8 +124,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->eval(op); } @@ -147,8 +147,9 @@ class Sequence: public std::enable_shared_from_this * @return shared_ptr of the Sequence class itself */ template - std::shared_ptr - evalAsync(std::vector> tensors, TArgs&&... params) + std::shared_ptr evalAsync( + std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -157,15 +158,14 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->evalAsync(op); } // Needed as otherwise it's not possible to use initializer lists template - std::shared_ptr - evalAsync(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr evalAsync(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -174,8 +174,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->evalAsync(op); } @@ -190,7 +190,8 @@ class Sequence: public std::enable_shared_from_this std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** - * Clear function clears all operations currently recorded and starts recording again. + * Clear function clears all operations currently recorded and starts + * recording again. */ void clear(); @@ -217,7 +218,6 @@ class Sequence: public std::enable_shared_from_this */ bool isRecording(); - bool isInit(); /** diff --git a/src/include/kompute/Shader.hpp b/src/include/kompute/Shader.hpp index 8c9a14c83..2d0e43741 100644 --- a/src/include/kompute/Shader.hpp +++ b/src/include/kompute/Shader.hpp @@ -4,9 +4,9 @@ #include #include +#include #include #include -#include #include "kompute/Core.hpp" @@ -16,161 +16,162 @@ namespace kp { // Has been adobted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp const TBuiltInResource defaultResource = { -/* .MaxLights = */ 0, -/* .MaxClipPlanes = */ 0, -/* .MaxTextureUnits = */ 0, -/* .MaxTextureCoords = */ 0, -/* .MaxVertexAttribs = */ 64, -/* .MaxVertexUniformComponents = */ 4096, -/* .MaxVaryingFloats = */ 64, -/* .MaxVertexTextureImageUnits = */ 0, -/* .MaxCombinedTextureImageUnits = */ 0, -/* .MaxTextureImageUnits = */ 0, -/* .MaxFragmentUniformComponents = */ 0, -/* .MaxDrawBuffers = */ 0, -/* .MaxVertexUniformVectors = */ 128, -/* .MaxVaryingVectors = */ 8, -/* .MaxFragmentUniformVectors = */ 0, -/* .MaxVertexOutputVectors = */ 16, -/* .MaxFragmentInputVectors = */ 0, -/* .MinProgramTexelOffset = */ -8, -/* .MaxProgramTexelOffset = */ 7, -/* .MaxClipDistances = */ 8, -/* .MaxComputeWorkGroupCountX = */ 65535, -/* .MaxComputeWorkGroupCountY = */ 65535, -/* .MaxComputeWorkGroupCountZ = */ 65535, -/* .MaxComputeWorkGroupSizeX = */ 1024, -/* .MaxComputeWorkGroupSizeY = */ 1024, -/* .MaxComputeWorkGroupSizeZ = */ 64, -/* .MaxComputeUniformComponents = */ 1024, -/* .MaxComputeTextureImageUnits = */ 16, -/* .MaxComputeImageUniforms = */ 8, -/* .MaxComputeAtomicCounters = */ 8, -/* .MaxComputeAtomicCounterBuffers = */ 1, -/* .MaxVaryingComponents = */ 60, -/* .MaxVertexOutputComponents = */ 64, -/* .MaxGeometryInputComponents = */ 64, -/* .MaxGeometryOutputComponents = */ 128, -/* .MaxFragmentInputComponents = */ 0, -/* .MaxImageUnits = */ 0, -/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, -/* .MaxCombinedShaderOutputResources = */ 8, -/* .MaxImageSamples = */ 0, -/* .MaxVertexImageUniforms = */ 0, -/* .MaxTessControlImageUniforms = */ 0, -/* .MaxTessEvaluationImageUniforms = */ 0, -/* .MaxGeometryImageUniforms = */ 0, -/* .MaxFragmentImageUniforms = */ 0, -/* .MaxCombinedImageUniforms = */ 0, -/* .MaxGeometryTextureImageUnits = */ 0, -/* .MaxGeometryOutputVertices = */ 256, -/* .MaxGeometryTotalOutputComponents = */ 1024, -/* .MaxGeometryUniformComponents = */ 1024, -/* .MaxGeometryVaryingComponents = */ 64, -/* .MaxTessControlInputComponents = */ 128, -/* .MaxTessControlOutputComponents = */ 128, -/* .MaxTessControlTextureImageUnits = */ 0, -/* .MaxTessControlUniformComponents = */ 1024, -/* .MaxTessControlTotalOutputComponents = */ 4096, -/* .MaxTessEvaluationInputComponents = */ 128, -/* .MaxTessEvaluationOutputComponents = */ 128, -/* .MaxTessEvaluationTextureImageUnits = */ 16, -/* .MaxTessEvaluationUniformComponents = */ 1024, -/* .MaxTessPatchComponents = */ 120, -/* .MaxPatchVertices = */ 32, -/* .MaxTessGenLevel = */ 64, -/* .MaxViewports = */ 16, -/* .MaxVertexAtomicCounters = */ 0, -/* .MaxTessControlAtomicCounters = */ 0, -/* .MaxTessEvaluationAtomicCounters = */ 0, -/* .MaxGeometryAtomicCounters = */ 0, -/* .MaxFragmentAtomicCounters = */ 0, -/* .MaxCombinedAtomicCounters = */ 8, -/* .MaxAtomicCounterBindings = */ 1, -/* .MaxVertexAtomicCounterBuffers = */ 0, -/* .MaxTessControlAtomicCounterBuffers = */ 0, -/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, -/* .MaxGeometryAtomicCounterBuffers = */ 0, -/* .MaxFragmentAtomicCounterBuffers = */ 0, -/* .MaxCombinedAtomicCounterBuffers = */ 1, -/* .MaxAtomicCounterBufferSize = */ 16384, -/* .MaxTransformFeedbackBuffers = */ 4, -/* .MaxTransformFeedbackInterleavedComponents = */ 64, -/* .MaxCullDistances = */ 8, -/* .MaxCombinedClipAndCullDistances = */ 8, -/* .MaxSamples = */ 4, -/* .maxMeshOutputVerticesNV = */ 256, -/* .maxMeshOutputPrimitivesNV = */ 512, -/* .maxMeshWorkGroupSizeX_NV = */ 32, -/* .maxMeshWorkGroupSizeY_NV = */ 1, -/* .maxMeshWorkGroupSizeZ_NV = */ 1, -/* .maxTaskWorkGroupSizeX_NV = */ 32, -/* .maxTaskWorkGroupSizeY_NV = */ 1, -/* .maxTaskWorkGroupSizeZ_NV = */ 1, -/* .maxMeshViewCountNV = */ 4, -/* .maxDualSourceDrawBuffersEXT = */ 1, + /* .MaxLights = */ 0, + /* .MaxClipPlanes = */ 0, + /* .MaxTextureUnits = */ 0, + /* .MaxTextureCoords = */ 0, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 0, + /* .MaxCombinedTextureImageUnits = */ 0, + /* .MaxTextureImageUnits = */ 0, + /* .MaxFragmentUniformComponents = */ 0, + /* .MaxDrawBuffers = */ 0, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 0, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 0, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 0, + /* .MaxImageUnits = */ 0, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 0, + /* .MaxCombinedImageUniforms = */ 0, + /* .MaxGeometryTextureImageUnits = */ 0, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 0, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 0, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 0, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ + { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; -/* .limits = */ { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, -}}; - /** Shader utily class with functions to compile and process glsl files. */ -class Shader { -public: +class Shader +{ + public: /** * Compile multiple sources with optional filenames. Currently this function * uses the glslang C++ interface which is not thread safe so this funciton * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline + * online shader processing multithreading use-case that can't use offline * compilation please open an issue. * * @param sources A list of raw glsl shaders in string format * @param files A list of file names respective to each of the sources * @param entryPoint The function name to use as entry point * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @param resourcesLimit A list that contains the resource limits for the + * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ static std::vector compile_sources( - const std::vector& sources, - const std::vector& files = {}, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const std::vector& sources, + const std::vector& files = {}, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); /** - * Compile a single glslang source from string value. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. + * Compile a single glslang source from string value. Currently this + * function uses the glslang C++ interface which is not thread safe so this + * funciton should not be called from multiple threads concurrently. If you + * have a online shader processing multithreading use-case that can't use + * offline compilation please open an issue. * * @param source An individual raw glsl shader in string format * @param entryPoint The function name to use as entry point * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @param resourcesLimit A list that contains the resource limits for the + * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ static std::vector compile_source( - const std::string& source, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - + const std::string& source, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); }; - - } #endif // DKOMPUTE_DISABLE_SHADER_UTILS - diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 513525f73..7b24f3de7 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -54,7 +54,7 @@ class Tensor * otherwise there is no need to copy from host memory. */ void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); + TensorTypes tensorType = TensorTypes::eDevice); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -125,9 +125,8 @@ class Tensor * @param createBarrier Whether to create a barrier that ensures the data is * copied before further operations. Default is true. */ - void recordCopyFromStagingToDevice( - const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, + bool createBarrier); /** * Records a copy from the internal device memory to the staging memory @@ -138,9 +137,8 @@ class Tensor * @param createBarrier Whether to create a barrier that ensures the data is * copied before further operations. Default is true. */ - void recordCopyFromDeviceToStaging( - const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, + bool createBarrier); /** * Records the buffer memory barrier into the command buffer which @@ -152,12 +150,11 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordBufferMemoryBarrier( - const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -205,11 +202,11 @@ class Tensor std::shared_ptr memory, vk::MemoryPropertyFlags memoryPropertyFlags); void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); // Private util functions vk::BufferUsageFlags getPrimaryBufferUsageFlags(); diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index da9f8d887..b1919ce52 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -84,7 +84,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) for (uint32_t i = 0; i < numParallel; i++) { inputsAsyncB.push_back(mgr.tensor(data)); - algosAsync.push_back(mgr.algorithm({inputsAsyncB[i]}, spirv)); + algosAsync.push_back(mgr.algorithm({ inputsAsyncB[i] }, spirv)); } std::vector> sqs; @@ -160,8 +160,8 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) sq1->eval({ tensorA, tensorB }); - std::shared_ptr algo1 = mgr.algorithm({tensorA}, spirv); - std::shared_ptr algo2 = mgr.algorithm({tensorB}, spirv); + std::shared_ptr algo1 = mgr.algorithm({ tensorA }, spirv); + std::shared_ptr algo2 = mgr.algorithm({ tensorB }, spirv); sq1->evalAsync(algo1); sq2->evalAsync(algo2); diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index cf753267e..fee3854c4 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -27,12 +27,12 @@ TEST(TestDestroy, TestDestroyTensorSingle) tensorA = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = - mgr.algorithm({ tensorA }, spirv); + mgr.algorithm({ tensorA }, spirv); mgr.sequence() - ->record(algo) - ->eval() - ->eval(algo->getTensors()); + ->record(algo) + ->eval() + ->eval(algo->getTensors()); tensorA->destroy(); EXPECT_FALSE(tensorA->isInit()); @@ -68,14 +68,14 @@ TEST(TestDestroy, TestDestroyTensorVector) tensorA = mgr.tensor({ 1, 1, 1 }); tensorB = mgr.tensor({ 1, 1, 1 }); - std::shared_ptr algo = - mgr.algorithm({tensorA, tensorB}, spirv); + std::shared_ptr algo = + mgr.algorithm({ tensorA, tensorB }, spirv); mgr.sequence() - ->record(algo->getTensors()) - ->record(algo) - ->record(algo->getTensors()) - ->eval(); + ->record(algo->getTensors()) + ->record(algo) + ->record(algo->getTensors()) + ->eval(); tensorA->destroy(); tensorB->destroy(); @@ -109,12 +109,13 @@ TEST(TestDestroy, TestDestroySequenceSingle) { kp::Manager mgr; - tensorA = mgr.tensor({0, 0, 0}); + tensorA = mgr.tensor({ 0, 0, 0 }); - sq = mgr.sequence() - ->record({tensorA}) - ->record(mgr.algorithm({tensorA}, spirv)) - ->record({tensorA}) + sq = + mgr.sequence() + ->record({ tensorA }) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record({ tensorA }) ->eval(); sq->destroy(); @@ -124,4 +125,3 @@ TEST(TestDestroy, TestDestroySequenceSingle) } EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); } - diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index f7ad9eda1..980273246 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -29,24 +29,27 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; mgr.sequence()->eval(params); std::vector spirv = std::vector( - (uint32_t*)kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv, - (uint32_t*)(kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv + - kp::shader_data::test_shaders_glsl_test_logistic_regression_comp_spv_len)); + (uint32_t*)kp::shader_data:: + test_shaders_glsl_test_logistic_regression_comp_spv, + (uint32_t*)(kp::shader_data:: + test_shaders_glsl_test_logistic_regression_comp_spv + + kp::shader_data:: + test_shaders_glsl_test_logistic_regression_comp_spv_len)); - std::shared_ptr algorithm = - mgr.algorithm(params, spirv, kp::Workgroup({5}), kp::Constants({5.0})); + std::shared_ptr algorithm = mgr.algorithm( + params, spirv, kp::Workgroup({ 5 }), kp::Constants({ 5.0 })); std::shared_ptr sq = - mgr.sequence() - ->record({ wIn, bIn }) - ->record(algorithm) - ->record({ wOutI, wOutJ, bOut, lOut }); + mgr.sequence() + ->record({ wIn, bIn }) + ->record(algorithm) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -90,37 +93,38 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr wIn = mgr.tensor( - { 0.001, 0.001 }, kp::Tensor::TensorTypes::eHost); + std::shared_ptr wIn = + mgr.tensor({ 0.001, 0.001 }, kp::Tensor::TensorTypes::eHost); std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr bIn = mgr.tensor( - { 0 }, - kp::Tensor::TensorTypes::eHost); + std::shared_ptr bIn = + mgr.tensor({ 0 }, kp::Tensor::TensorTypes::eHost); std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; mgr.sequence()->record(params)->eval(); std::vector spirv = std::vector( - (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, - (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + - kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); + (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, + (uint32_t*)(kp::shader_data:: + shaders_glsl_logisticregression_comp_spv + + kp::shader_data:: + shaders_glsl_logisticregression_comp_spv_len)); std::shared_ptr algorithm = - mgr.algorithm(params, spirv, kp::Workgroup(), kp::Constants({5.0})); + mgr.algorithm(params, spirv, kp::Workgroup(), kp::Constants({ 5.0 })); std::shared_ptr sq = - mgr.sequence() - ->record({ wIn, bIn }) - ->record(algorithm) - ->record({ wOutI, wOutJ, bOut, lOut }); + mgr.sequence() + ->record({ wIn, bIn }) + ->record(algorithm) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -136,18 +140,18 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) bIn->mapDataIntoHostMemory(); } - // Based on the inputs the outputs should be at least: - // * wi < 0.01 - // * wj > 1.0 - // * b < 0 - // TODO: Add EXPECT_DOUBLE_EQ instead - EXPECT_LT(wIn->data()[0], 0.01); - EXPECT_GT(wIn->data()[1], 1.0); - EXPECT_LT(bIn->data()[0], 0.0); + // Based on the inputs the outputs should be at least: + // * wi < 0.01 + // * wj > 1.0 + // * b < 0 + // TODO: Add EXPECT_DOUBLE_EQ instead + EXPECT_LT(wIn->data()[0], 0.01); + EXPECT_GT(wIn->data()[1], 1.0); + EXPECT_LT(bIn->data()[0], 0.0); - KP_LOG_WARN("Result wIn i: {}, wIn j: {}, bIn: {}", - wIn->data()[0], - wIn->data()[1], - bIn->data()[0]); + KP_LOG_WARN("Result wIn i: {}, wIn j: {}, bIn: {}", + wIn->data()[0], + wIn->data()[1], + bIn->data()[0]); } } diff --git a/test/TestManager.cpp b/test/TestManager.cpp index f87e81159..ce055ff8c 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -11,13 +11,14 @@ TEST(TestManager, EndToEndOpMultEvalFlow) std::shared_ptr tensorRHS = mgr.tensor({ 2, 4, 6 }); std::shared_ptr tensorOutput = mgr.tensor({ 0, 0, 0 }); - std::vector> params = - { tensorLHS, tensorRHS, tensorOutput }; + std::vector> params = { tensorLHS, + tensorRHS, + tensorOutput }; mgr.sequence() - ->eval(params) - ->eval(params, mgr.algorithm()) - ->eval(params); + ->eval(params) + ->eval(params, mgr.algorithm()) + ->eval(params); EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } @@ -30,14 +31,15 @@ TEST(TestManager, EndToEndOpMultSeqFlow) std::shared_ptr tensorRHS = mgr.tensor({ 2, 4, 6 }); std::shared_ptr tensorOutput = mgr.tensor({ 0, 0, 0 }); - std::vector> params = - { tensorLHS, tensorRHS, tensorOutput }; + std::vector> params = { tensorLHS, + tensorRHS, + tensorOutput }; mgr.sequence() - ->record(params) - ->record(params, mgr.algorithm()) - ->record(params) - ->eval(); + ->record(params) + ->record(params, mgr.algorithm()) + ->record(params) + ->eval(); EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } @@ -50,8 +52,9 @@ TEST(TestManager, TestMultipleSequences) std::shared_ptr tensorRHS = mgr.tensor({ 2, 4, 6 }); std::shared_ptr tensorOutput = mgr.tensor({ 0, 0, 0 }); - std::vector> params = - { tensorLHS, tensorRHS, tensorOutput }; + std::vector> params = { tensorLHS, + tensorRHS, + tensorOutput }; mgr.sequence()->eval(params); mgr.sequence()->eval(params, mgr.algorithm()); @@ -59,4 +62,3 @@ TEST(TestManager, TestMultipleSequences) EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); } - diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 8be2e6d82..e050e02ea 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -3,9 +3,10 @@ #include "kompute/Kompute.hpp" -TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) { +TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) +{ - kp::Manager mgr; + kp::Manager mgr; auto tensorInA = mgr.tensor({ 2., 2., 2. }); auto tensorInB = mgr.tensor({ 1., 2., 3. }); @@ -38,21 +39,24 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) { } )"); - std::vector> params = {tensorInA, tensorInB, tensorOutA, tensorOutB}; + std::vector> params = { + tensorInA, tensorInB, tensorOutA, tensorOutB + }; - kp::Workgroup workgroup({3, 1, 1}); + kp::Workgroup workgroup({ 3, 1, 1 }); kp::Constants specConsts({ 2 }); kp::Constants pushConstsA({ 2.0 }); kp::Constants pushConstsB({ 3.0 }); - auto algorithm = mgr.algorithm(params, kp::Shader::compile_source(shader), workgroup, specConsts); + auto algorithm = mgr.algorithm( + params, kp::Shader::compile_source(shader), workgroup, specConsts); // 3. Run operation with string shader synchronously mgr.sequence() - ->record(params) - ->record(algorithm, pushConstsA) - ->record(algorithm, pushConstsB) - ->eval(); + ->record(params) + ->record(algorithm, pushConstsA) + ->record(algorithm, pushConstsB) + ->eval(); auto sq = mgr.sequence(); sq->evalAsync(params); @@ -83,12 +87,12 @@ TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) { mgr.sequence() - ->record({ tensorA }) - ->record(mgr.algorithm({tensorA}, spirv)) - ->record(mgr.algorithm({tensorA}, spirv)) - ->record(mgr.algorithm({tensorA}, spirv)) - ->record({ tensorA }) - ->eval(); + ->record({ tensorA }) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record({ tensorA }) + ->eval(); } EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); @@ -111,29 +115,20 @@ TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) std::vector spirv = kp::Shader::compile_source(shader); - std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + std::shared_ptr algorithm = + mgr.algorithm({ tensorA }, spirv); std::shared_ptr sq = mgr.sequence(); - mgr.sequence() - ->record({ tensorA }) - ->eval(); + mgr.sequence()->record({ tensorA })->eval(); - mgr.sequence() - ->record(algorithm) - ->eval(); + mgr.sequence()->record(algorithm)->eval(); - mgr.sequence() - ->record(algorithm) - ->eval(); + mgr.sequence()->record(algorithm)->eval(); - mgr.sequence() - ->record(algorithm) - ->eval(); + mgr.sequence()->record(algorithm)->eval(); - mgr.sequence() - ->record({ tensorA }) - ->eval(); + mgr.sequence()->record({ tensorA })->eval(); EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); } @@ -156,23 +151,20 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) std::vector spirv = kp::Shader::compile_source(shader); - std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + std::shared_ptr algorithm = + mgr.algorithm({ tensorA }, spirv); std::shared_ptr sq = mgr.sequence(); sq->record({ tensorA })->eval(); - sq->record(algorithm) - ->eval(); + sq->record(algorithm)->eval(); - sq->record(algorithm) - ->eval(); + sq->record(algorithm)->eval(); - sq->record(algorithm) - ->eval(); + sq->record(algorithm)->eval(); - sq->record({ tensorA }) - ->eval(); + sq->record({ tensorA })->eval(); EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); } @@ -194,24 +186,20 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) std::vector spirv = kp::Shader::compile_source(shader); - std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + std::shared_ptr algorithm = + mgr.algorithm({ tensorA }, spirv); std::shared_ptr sq = mgr.sequence(); sq->record({ tensorA })->eval(); - sq->record(algorithm) - ->eval() - ->eval() - ->eval(); + sq->record(algorithm)->eval()->eval()->eval(); - sq->record({ tensorA }) - ->eval(); + sq->record({ tensorA })->eval(); EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); } - TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) { std::shared_ptr tensorA = nullptr; @@ -234,22 +222,18 @@ TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) std::vector spirv = kp::Shader::compile_source(shader); - std::shared_ptr algorithm = mgr.algorithm({tensorA}, spirv); + std::shared_ptr algorithm = + mgr.algorithm({ tensorA }, spirv); sq = mgr.sequence(); sq->record({ tensorA })->eval(); - sq->record(algorithm) - ->eval() - ->eval() - ->eval(); + sq->record(algorithm)->eval()->eval()->eval(); - sq->record({ tensorA }) - ->eval(); + sq->record({ tensorA })->eval(); } } EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); } - diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index 1a1c5c599..3e6856a21 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -32,10 +32,9 @@ TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) std::vector> params = { tensorA, tensorB }; mgr.sequence() - ->eval(params) - ->eval(mgr.algorithm(params, spirv)) - ->eval(params); - + ->eval(params) + ->eval(mgr.algorithm(params, spirv)) + ->eval(params); EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); @@ -48,27 +47,27 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) std::shared_ptr tensorA = mgr.tensor({ 3, 4, 5 }); std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); - std::vector spirv = - std::vector( - (uint32_t*)kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, - (uint32_t*)(kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv + - kp::shader_data:: - test_shaders_glsl_test_op_custom_shader_comp_spv_len)); + std::vector spirv = std::vector( + (uint32_t*) + kp::shader_data::test_shaders_glsl_test_op_custom_shader_comp_spv, + (uint32_t*)(kp::shader_data:: + test_shaders_glsl_test_op_custom_shader_comp_spv + + kp::shader_data:: + test_shaders_glsl_test_op_custom_shader_comp_spv_len)); std::vector> params = { tensorA, tensorB }; mgr.sequence() - ->eval(params) - ->eval(mgr.algorithm(params, spirv)) - ->eval(params); - + ->eval(params) + ->eval(mgr.algorithm(params, spirv)) + ->eval(params); EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); } // TODO: Add support to read from file for shader -//TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) +// TEST(TestOpAlgoCreate, ShaderCompiledDataFromFile) //{ // kp::Manager mgr; // @@ -77,7 +76,8 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) // mgr.rebuild({ tensorA, tensorB }); // // mgr.evalOpDefault( -// { tensorA, tensorB }, "test/shaders/glsl/test_op_custom_shader.comp.spv"); +// { tensorA, tensorB }, +// "test/shaders/glsl/test_op_custom_shader.comp.spv"); // // mgr.evalOpDefault({ tensorA, tensorB }); // diff --git a/test/TestOpTensorCopy.cpp b/test/TestOpTensorCopy.cpp index dc82a2e50..85e0b545b 100644 --- a/test/TestOpTensorCopy.cpp +++ b/test/TestOpTensorCopy.cpp @@ -18,9 +18,9 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) EXPECT_TRUE(tensorB->isInit()); mgr.sequence() - ->eval({ tensorA, tensorB }) - ->eval({ tensorA, tensorB }) - ->eval({ tensorA, tensorB }); + ->eval({ tensorA, tensorB }) + ->eval({ tensorA, tensorB }) + ->eval({ tensorA, tensorB }); // Making sure the GPU holds the same data EXPECT_EQ(tensorA->data(), tensorB->data()); @@ -44,15 +44,14 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) EXPECT_TRUE(tensorC->isInit()); mgr.sequence() - ->eval({tensorA, tensorB, tensorC}) - ->eval({tensorA, tensorB, tensorC }); + ->eval({ tensorA, tensorB, tensorC }) + ->eval({ tensorA, tensorB, tensorC }); EXPECT_EQ(tensorA->data(), tensorB->data()); EXPECT_EQ(tensorA->data(), tensorC->data()); // Making sure the GPU holds the same data - mgr.sequence() - ->eval({ tensorB, tensorC }); + mgr.sequence()->eval({ tensorB, tensorC }); EXPECT_EQ(tensorA->data(), tensorB->data()); EXPECT_EQ(tensorA->data(), tensorC->data()); @@ -67,8 +66,8 @@ TEST(TestOpTensorCopy, CopyDeviceToHostTensor) std::vector testVecB{ 0, 0, 0 }; std::shared_ptr tensorA = mgr.tensor(testVecA); - std::shared_ptr tensorB = mgr.tensor( - testVecB, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorB = + mgr.tensor(testVecB, kp::Tensor::TensorTypes::eHost); // Only calling sync on device type tensor mgr.sequence()->eval({ tensorA }); @@ -93,8 +92,8 @@ TEST(TestOpTensorCopy, CopyHostToDeviceTensor) std::vector testVecA{ 4, 5, 6 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = mgr.tensor( - testVecA, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorA = + mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); std::shared_ptr tensorB = mgr.tensor(testVecB); // Only calling sync on device type tensor @@ -120,17 +119,17 @@ TEST(TestOpTensorCopy, CopyHostToHostTensor) std::vector testVecA{ 5, 6, 7 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = mgr.tensor( - testVecA, kp::Tensor::TensorTypes::eHost); - std::shared_ptr tensorB = mgr.tensor( - testVecB, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorA = + mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorB = + mgr.tensor(testVecB, kp::Tensor::TensorTypes::eHost); EXPECT_TRUE(tensorA->isInit()); EXPECT_TRUE(tensorB->isInit()); mgr.sequence() - ->eval({ tensorA }) - ->eval({ tensorA, tensorB }); + ->eval({ tensorA }) + ->eval({ tensorA, tensorB }); EXPECT_EQ(tensorA->data(), tensorB->data()); @@ -146,8 +145,8 @@ TEST(TestOpTensorCopy, SingleTensorShouldFail) std::vector testVecA{ 6, 7, 8 }; - std::shared_ptr tensorA = mgr.tensor( - testVecA, kp::Tensor::TensorTypes::eHost); + std::shared_ptr tensorA = + mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); EXPECT_TRUE(tensorA->isInit()); diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index 78ba57c00..ae8cf4a32 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -32,16 +32,18 @@ TEST(TestPushConstants, TestTwoConstants) std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = - mgr.algorithm({tensor}, spirv, kp::Workgroup({1})); + mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 })); sq = mgr.sequence() - ->record({tensor}) - ->record(algo, kp::Constants{0.1, 0.2, 0.3}) - ->record(algo, kp::Constants{0.3, 0.2, 0.1}) - ->record({tensor}) - ->eval(); + ->record({ tensor }) + ->record(algo, + kp::Constants{ 0.1, 0.2, 0.3 }) + ->record(algo, + kp::Constants{ 0.3, 0.2, 0.1 }) + ->record({ tensor }) + ->eval(); - EXPECT_EQ(tensor->data(), kp::Constants({0.4, 0.4, 0.4})); + EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); } } } diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 7a8dd07d1..4d0233694 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -17,4 +17,3 @@ TEST(TestSequence, SequenceDestructorViaManager) EXPECT_FALSE(sq->isInit()); } - diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index d1e4cdc9e..e66f9d52e 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -28,17 +28,19 @@ TEST(TestSpecializationConstants, TestTwoConstants) std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); - std::vector> params = {tensorA, tensorB}; + std::vector> params = { tensorA, + tensorB }; - kp::Constants spec = kp::Constants({5.0, 0.3}); + kp::Constants spec = kp::Constants({ 5.0, 0.3 }); - std::shared_ptr algo = mgr.algorithm(params, spirv, {}, spec); + std::shared_ptr algo = + mgr.algorithm(params, spirv, {}, spec); sq = mgr.sequence() - ->record(params) - ->record(algo) - ->record(params) - ->eval(); + ->record(params) + ->record(algo) + ->record(params) + ->eval(); EXPECT_EQ(tensorA->data(), std::vector({ 5, 5, 5 })); EXPECT_EQ(tensorB->data(), std::vector({ 0.3, 0.3, 0.3 })); diff --git a/test/TestTensor.cpp b/test/TestTensor.cpp index 76ecbe60d..d33367722 100644 --- a/test/TestTensor.cpp +++ b/test/TestTensor.cpp @@ -11,4 +11,3 @@ TEST(TestTensor, ConstructorData) EXPECT_EQ(tensor->size(), vec.size()); EXPECT_EQ(tensor->data(), vec); } - diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 5da0a4c54..3eb9147a1 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -18,16 +18,21 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) tensorA = mgr.tensor(std::vector(16 * 8)); tensorB = mgr.tensor(std::vector(16 * 8)); - std::vector> params = {tensorA, tensorB}; + std::vector> params = { tensorA, + tensorB }; std::vector spirv( - (uint32_t*)kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv, - (uint32_t*)(kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv + - kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv_len)); + (uint32_t*) + kp::shader_data::test_shaders_glsl_test_workgroup_comp_spv, + (uint32_t*)(kp::shader_data:: + test_shaders_glsl_test_workgroup_comp_spv + + kp::shader_data:: + test_shaders_glsl_test_workgroup_comp_spv_len)); - kp::Workgroup workgroup = {16, 8, 1}; + kp::Workgroup workgroup = { 16, 8, 1 }; - std::shared_ptr algorithm = mgr.algorithm(params, spirv, workgroup); + std::shared_ptr algorithm = + mgr.algorithm(params, spirv, workgroup); sq = mgr.sequence(); sq->record(params); @@ -37,11 +42,26 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) } } - std::vector expectedA = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}; + std::vector expectedA = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15 + }; - std::vector expectedB = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; + std::vector expectedB = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, + 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 + }; EXPECT_EQ(tensorA->data(), expectedA); EXPECT_EQ(tensorB->data(), expectedB); } - From 4fddf74ca7092edfc9fe5e46b45a225eec1109f2 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 28 Feb 2021 17:07:17 +0000 Subject: [PATCH 24/91] Updated examples --- .../app/src/main/cpp/KomputeModelML.cpp | 95 ++-- .../app/src/main/cpp/KomputeModelML.hpp | 5 +- examples/array_multiplication/src/Main.cpp | 11 +- .../kompute_summator/KomputeSummatorNode.cpp | 13 +- .../kompute_model_ml/KomputeModelMLNode.cpp | 83 ++-- .../gdnative_shared/src/KomputeModelML.cpp | 101 ++-- .../gdnative_shared/src/KomputeModelML.hpp | 4 +- examples/logistic_regression/src/Main.cpp | 45 +- single_include/kompute/Kompute.hpp | 439 ++++++++++-------- src/include/kompute/Core.hpp | 16 +- src/include/kompute/operations/OpBase.hpp | 1 - 11 files changed, 408 insertions(+), 405 deletions(-) diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp index f1884760a..647cd5236 100755 --- a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp +++ b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp @@ -20,61 +20,62 @@ void KomputeModelML::train(std::vector yData, std::vector xIData, uint32_t ITERATIONS = 100; float learningRate = 0.1; - std::shared_ptr xI{ new kp::Tensor(xIData) }; - std::shared_ptr xJ{ new kp::Tensor(xJData) }; - - std::shared_ptr y{ new kp::Tensor(yData) }; - - std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; - std::shared_ptr wOutI{ new kp::Tensor(zerosData) }; - std::shared_ptr wOutJ{ new kp::Tensor(zerosData) }; - - std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; - std::shared_ptr bOut{ new kp::Tensor(zerosData) }; - - std::shared_ptr lOut{ new kp::Tensor(zerosData) }; - - std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; - { kp::Manager mgr; - { - mgr.rebuild(params); + std::shared_ptr xI = mgr.tensor(xIData); + std::shared_ptr xJ = mgr.tensor(xJData); - std::shared_ptr sq = mgr.sequence(); + std::shared_ptr y = mgr.tensor(yData); - // Record op algo base - sq->begin(); + std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr wOutI = mgr.tensor(zerosData); + std::shared_ptr wOutJ = mgr.tensor(zerosData); - sq->record({ wIn, bIn }); + std::shared_ptr bIn = mgr.tensor({ 0 }); + std::shared_ptr bOut = mgr.tensor(zerosData); - // Newer versions of Android are able to use shaderc to read raw string - sq->record( - params, kp::Shader::compile_source(LR_SHADER)); + std::shared_ptr lOut = mgr.tensor(zerosData); - sq->record({ wOutI, wOutJ, bOut, lOut }); + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; - sq->end(); + std::vector spirv( + (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, + (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - // Iterate across all expected iterations - for (size_t i = 0; i < ITERATIONS; i++) { + std::shared_ptr algo = + mgr.algorithm(params, spirv, kp::Workgroup({ 5 }), kp::Constants({ 5.0 })); - sq->eval(); + mgr.sequence()->eval(params); - for (size_t j = 0; j < bOut->size(); j++) { - wIn->data()[0] -= learningRate * wOutI->data()[j]; - wIn->data()[1] -= learningRate * wOutJ->data()[j]; - bIn->data()[0] -= learningRate * bOut->data()[j]; - } + std::shared_ptr sq = mgr.sequence() + ->record({ wIn, bIn }) + ->record(algo) + ->record({ wOutI, wOutJ, bOut, lOut }); + + // Iterate across all expected iterations + for (size_t i = 0; i < ITERATIONS; i++) { + + sq->eval(); + + for (size_t j = 0; j < bOut->size(); j++) { + wIn->data()[0] -= learningRate * wOutI->data()[j]; + wIn->data()[1] -= learningRate * wOutJ->data()[j]; + bIn->data()[0] -= learningRate * bOut->data()[j]; } } - } - this->mWeights = kp::Tensor(wIn->data()); - this->mBias = kp::Tensor(bIn->data()); + KP_LOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); + KP_LOG_INFO("{}", wIn->data()[0]); + KP_LOG_INFO("{}", wIn->data()[1]); + KP_LOG_INFO("{}", bIn->data()[0]); + + this->mWeights = wIn; + this->mBias = bIn; + } } std::vector KomputeModelML::predict(std::vector xI, std::vector xJ) { @@ -88,9 +89,9 @@ std::vector KomputeModelML::predict(std::vector xI, std::vectormWeights.data()[0] - + xJVal * this->mWeights.data()[1] - + this->mBias.data()[0]); + float result = (xIVal * this->mWeights->data()[0] + + xJVal * this->mWeights->data()[1] + + this->mBias->data()[0]); // Instead of using sigmoid we'll just return full numbers float var = result > 0 ? 1 : 0; @@ -103,13 +104,13 @@ std::vector KomputeModelML::predict(std::vector xI, std::vector KomputeModelML::get_params() { std::vector retVector; - if(this->mWeights.size() + this->mBias.size() == 0) { + if(this->mWeights->size() + this->mBias->size() == 0) { return retVector; } - retVector.push_back(this->mWeights.data()[0]); - retVector.push_back(this->mWeights.data()[1]); - retVector.push_back(this->mBias.data()[0]); + retVector.push_back(this->mWeights->data()[0]); + retVector.push_back(this->mWeights->data()[1]); + retVector.push_back(this->mBias->data()[0]); retVector.push_back(99.0); return retVector; diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp index 335f05805..093edbafc 100755 --- a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp +++ b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "kompute/Kompute.hpp" @@ -20,8 +21,8 @@ public: std::vector get_params(); private: - kp::Tensor mWeights; - kp::Tensor mBias; + std::shared_ptr mWeights; + std::shared_ptr mBias; }; diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index 8ec611e15..fd823bca8 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -37,11 +37,14 @@ int main() } )"); - mgr.evalOpDefault( - { tensorInA, tensorInB, tensorOut }, - kp::Shader::compile_source(shader)); + std::vector> params = { tensorInA, tensorInB, tensorOut }; - mgr.evalOpDefault({tensorOut}); + std::shared_ptr algo = mgr.algorithm(params, kp::Shader::compile_source(shader)); + + mgr.sequence() + ->record(params) + ->record(algo) + ->record(params); // prints "Output { 0 4 12 }" std::cout<< "Output: { "; diff --git a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp index 2e9f1bc00..f50c56d5c 100644 --- a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp +++ b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp @@ -31,7 +31,7 @@ void KomputeSummatorNode::_init() { std::cout << "CALLING INIT" << std::endl; this->mPrimaryTensor = this->mManager.tensor({ 0.0 }); this->mSecondaryTensor = this->mManager.tensor({ 0.0 }); - this->mSequence = this->mManager.sequence("AdditionSeq"); + this->mSequence = this->mManager.sequence(); // We now record the steps in the sequence if (std::shared_ptr sq = this->mSequence) @@ -51,7 +51,11 @@ void KomputeSummatorNode::_init() { } )"); - sq->begin(); + std::shared_ptr algo = + mgr.algorithm( + { this->mPrimaryTensor, this->mSecondaryTensor }, + kp::Shader::compile_source(shader)); + // First we ensure secondary tensor loads to GPU // No need to sync the primary tensor as it should not be changed @@ -59,15 +63,12 @@ void KomputeSummatorNode::_init() { { this->mSecondaryTensor }); // Then we run the operation with both tensors - sq->record( - { this->mPrimaryTensor, this->mSecondaryTensor }, - kp::Shader::compile_source(shader)); + sq->record(algo) // We map the result back to local sq->record( { this->mPrimaryTensor }); - sq->end(); } else { throw std::runtime_error("Sequence pointer no longer available"); diff --git a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp index 57490a8d4..081315a4b 100644 --- a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp +++ b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp @@ -29,54 +29,41 @@ void KomputeModelMLNode::train(Array yArr, Array xIArr, Array xJArr) { uint32_t ITERATIONS = 100; float learningRate = 0.1; - std::shared_ptr xI{ new kp::Tensor(xIData) }; - std::shared_ptr xJ{ new kp::Tensor(xJData) }; - - std::shared_ptr y{ new kp::Tensor(yData) }; - - std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; - std::shared_ptr wOutI{ new kp::Tensor(zerosData) }; - std::shared_ptr wOutJ{ new kp::Tensor(zerosData) }; - - std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; - std::shared_ptr bOut{ new kp::Tensor(zerosData) }; - - std::shared_ptr lOut{ new kp::Tensor(zerosData) }; - - std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; - { kp::Manager mgr; - mgr.rebuild(params); + std::shared_ptr xI = mgr.tensor(xIData); + std::shared_ptr xJ = mgr.tensor(xJData); + + std::shared_ptr y = mgr.tensor(yData); + + std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr wOutI = mgr.tensor(zerosData); + std::shared_ptr wOutJ = mgr.tensor(zerosData); + + std::shared_ptr bIn = mgr.tensor({ 0 }); + std::shared_ptr bOut = mgr.tensor(zerosData); + + std::shared_ptr lOut = mgr.tensor(zerosData); + + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; { - std::shared_ptr sq = mgr.sequence(); + std::vector spirv( + (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, + (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - // Record op algo base - sq->begin(); + std::shared_ptr algo = mgr.algorithm(params, spirv); - sq->record({ wIn, bIn }); + mgr.sequence()->eval(params); -#ifdef KOMPUTE_ANDROID_SHADER_FROM_STRING - // Newer versions of Android are able to use shaderc to read raw string - sq->record( - params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); -#else - // Older versions of Android require the SPIRV binary directly - sq->record( - params, std::vector( - kp::shader_data::shaders_glsl_logisticregression_comp_spv, - kp::shader_data::shaders_glsl_logisticregression_comp_spv - + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len - )); -#endif - - sq->record({ wOutI, wOutJ, bOut, lOut }); - - sq->end(); + std::shared_ptr sq = mgr.sequence() + ->record({ wIn, bIn }) + ->record(algo) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -90,15 +77,15 @@ void KomputeModelMLNode::train(Array yArr, Array xIArr, Array xJArr) { } } } + + KP_LOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); + KP_LOG_INFO(wIn->data()[0]); + KP_LOG_INFO(wIn->data()[1]); + KP_LOG_INFO(bIn->data()[0]); + + this->mWeights = kp::Tensor(wIn->data()); + this->mBias = kp::Tensor(bIn->data()); } - - KP_LOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); - KP_LOG_INFO(wIn->data()[0]); - KP_LOG_INFO(wIn->data()[1]); - KP_LOG_INFO(bIn->data()[0]); - - this->mWeights = kp::Tensor(wIn->data()); - this->mBias = kp::Tensor(bIn->data()); } Array KomputeModelMLNode::predict(Array xI, Array xJ) { diff --git a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp index 1a01febd0..1222fe867 100644 --- a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp +++ b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp @@ -33,54 +33,41 @@ void KomputeModelML::train(Array yArr, Array xIArr, Array xJArr) { uint32_t ITERATIONS = 100; float learningRate = 0.1; - std::shared_ptr xI{ new kp::Tensor(xIData) }; - std::shared_ptr xJ{ new kp::Tensor(xJData) }; - - std::shared_ptr y{ new kp::Tensor(yData) }; - - std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; - std::shared_ptr wOutI{ new kp::Tensor(zerosData) }; - std::shared_ptr wOutJ{ new kp::Tensor(zerosData) }; - - std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; - std::shared_ptr bOut{ new kp::Tensor(zerosData) }; - - std::shared_ptr lOut{ new kp::Tensor(zerosData) }; - - std::vector> params = { xI, xJ, y, - wIn, wOutI, wOutJ, - bIn, bOut, lOut }; - { kp::Manager mgr; + std::shared_ptr xI = mgr.tensor(xIData); + std::shared_ptr xJ = mgr.tensor(xJData); + + std::shared_ptr y = mgr.tensor(yData); + + std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr wOutI = mgr.tensor(zerosData); + std::shared_ptr wOutJ = mgr.tensor(zerosData); + + std::shared_ptr bIn = mgr.tensor({ 0 }); + std::shared_ptr bOut = mgr.tensor(zerosData); + + std::shared_ptr lOut = mgr.tensor(zerosData); + + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; + { - mgr.rebuild(params); + std::vector spirv( + (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, + (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - std::shared_ptr sq = mgr.sequence(); + std::shared_ptr algo = mgr.algorithm(params, spirv); - // Record op algo base - sq->begin(); + mgr.sequence()->eval(params); - sq->record({ wIn, bIn }); - -#ifdef KOMPUTE_ANDROID_SHADER_FROM_STRING - // Newer versions of Android are able to use shaderc to read raw string - sq->record( - params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); -#else - // Older versions of Android require the SPIRV binary directly - sq->record( - params, std::vector( - kp::shader_data::shaders_glsl_logisticregression_comp_spv, - kp::shader_data::shaders_glsl_logisticregression_comp_spv - + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len - )); -#endif - - sq->record({ wOutI, wOutJ, bOut, lOut }); - - sq->end(); + std::shared_ptr sq = mgr.sequence() + ->record({ wIn, bIn }) + ->record(algo) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { @@ -94,15 +81,15 @@ void KomputeModelML::train(Array yArr, Array xIArr, Array xJArr) { } } } + + KP_LOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); + KP_LOG_INFO(wIn->data()[0]); + KP_LOG_INFO(wIn->data()[1]); + KP_LOG_INFO(bIn->data()[0]); + + this->mWeights = wIn; + this->mBias = bIn; } - - KP_LOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); - KP_LOG_INFO(wIn->data()[0]); - KP_LOG_INFO(wIn->data()[1]); - KP_LOG_INFO(bIn->data()[0]); - - this->mWeights = kp::Tensor(wIn->data()); - this->mBias = kp::Tensor(bIn->data()); } Array KomputeModelML::predict(Array xI, Array xJ) { @@ -116,9 +103,9 @@ Array KomputeModelML::predict(Array xI, Array xJ) { for (size_t i = 0; i < xI.size(); i++) { float xIVal = xI[i]; float xJVal = xJ[i]; - float result = (xIVal * this->mWeights.data()[0] - + xJVal * this->mWeights.data()[1] - + this->mBias.data()[0]); + float result = (xIVal * this->mWeights->data()[0] + + xJVal * this->mWeights->data()[1] + + this->mBias->data()[0]); // Instead of using sigmoid we'll just return full numbers Variant var = result > 0 ? 1 : 0; @@ -131,15 +118,15 @@ Array KomputeModelML::predict(Array xI, Array xJ) { Array KomputeModelML::get_params() { Array retArray; - KP_LOG_INFO(this->mWeights.size() + this->mBias.size()); + KP_LOG_INFO(this->mWeights->size() + this->mBias->size()); - if(this->mWeights.size() + this->mBias.size() == 0) { + if(this->mWeights->size() + this->mBias->size() == 0) { return retArray; } - retArray.push_back(this->mWeights.data()[0]); - retArray.push_back(this->mWeights.data()[1]); - retArray.push_back(this->mBias.data()[0]); + retArray.push_back(this->mWeights->data()[0]); + retArray.push_back(this->mWeights->data()[1]); + retArray.push_back(this->mBias->data()[0]); retArray.push_back(99.0); return retArray; diff --git a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.hpp b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.hpp index 1f87fbb69..69bab4f19 100644 --- a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.hpp +++ b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.hpp @@ -28,8 +28,8 @@ public: static void _register_methods(); private: - kp::Tensor mWeights; - kp::Tensor mBias; + std::shared_ptr mWeights; + std::shared_ptr mBias; }; static std::string LR_SHADER = R"( diff --git a/examples/logistic_regression/src/Main.cpp b/examples/logistic_regression/src/Main.cpp index 769699ca7..c435575e2 100755 --- a/examples/logistic_regression/src/Main.cpp +++ b/examples/logistic_regression/src/Main.cpp @@ -15,44 +15,39 @@ int main() uint32_t ITERATIONS = 100; float learningRate = 0.1; - std::shared_ptr xI{ new kp::Tensor({ 0, 1, 1, 1, 1 }) }; - std::shared_ptr xJ{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; + kp::Manager mgr; - std::shared_ptr y{ new kp::Tensor({ 0, 0, 0, 1, 1 }) }; + std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; - std::shared_ptr wOutI{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; - std::shared_ptr wOutJ{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; + std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; - std::shared_ptr bOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; + std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr lOut{ new kp::Tensor({ 0, 0, 0, 0, 0 }) }; + std::shared_ptr bIn = mgr.tensor({ 0 }); + std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + + std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, bIn, bOut, lOut }; - kp::Manager mgr; - - mgr.rebuild(params); - - std::shared_ptr sq = mgr.sequence(); - - // Record op algo base - sq->begin(); - - sq->record({ wIn, bIn }); - - sq->record( - params, std::vector( + std::vector spirv( (uint32_t*)kp::shader_data::shaders_glsl_logisticregression_comp_spv, (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv - + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len))); + + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - sq->record({ wOutI, wOutJ, bOut, lOut }); + std::shared_ptr algo = mgr.algorithm(params, spirv); - sq->end(); + mgr.sequence()->eval(params); + + std::shared_ptr sq = mgr.sequence() + ->record({ wIn, bIn }) + ->record(algo) + ->record({ wOutI, wOutJ, bOut, lOut }); // Iterate across all expected iterations for (size_t i = 0; i < ITERATIONS; i++) { diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 0bf66d593..52d574ad3 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -647,12 +647,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_DEBUG(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_DEBUG(...) \ - ((void)__android_log_print(ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_DEBUG(...) \ + ((void)__android_log_write( \ + ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_DEBUG(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_DEBUG(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 1 @@ -660,12 +667,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_INFO(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_INFO(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_INFO(...) \ + ((void)__android_log_write( \ + ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_INFO(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_INFO(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 2 @@ -673,12 +687,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_WARN(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_WARN(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_WARN(...) \ + ((void)__android_log_write( \ + ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_WARN(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_WARN(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 3 @@ -686,12 +707,19 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #define KP_LOG_ERROR(...) #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) -#define KP_LOG_ERROR(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) +#define KP_LOG_ERROR(...) \ + ((void)__android_log_write( \ + ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) #else -#define KP_LOG_ERROR(...) fmt::print("[{} {}] [debug] [{}:{}] {}\n", __DATE__, __TIME__, __FILE__, __LINE__, fmt::format(__VA_ARGS__)) +#define KP_LOG_ERROR(...) \ + fmt::print("[{} {}] [debug] [{}:{}] {}\n", \ + __DATE__, \ + __TIME__, \ + __FILE__, \ + __LINE__, \ + fmt::format(__VA_ARGS__)) #endif // VK_USE_PLATFORM_ANDROID_KHR #endif // SPDLOG_ACTIVE_LEVEL > 4 #endif // KOMPUTE_SPDLOG_ENABLED @@ -701,9 +729,9 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #include #include +#include #include #include -#include namespace kp { @@ -711,157 +739,161 @@ namespace kp { // Has been adobted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp const TBuiltInResource defaultResource = { -/* .MaxLights = */ 0, -/* .MaxClipPlanes = */ 0, -/* .MaxTextureUnits = */ 0, -/* .MaxTextureCoords = */ 0, -/* .MaxVertexAttribs = */ 64, -/* .MaxVertexUniformComponents = */ 4096, -/* .MaxVaryingFloats = */ 64, -/* .MaxVertexTextureImageUnits = */ 0, -/* .MaxCombinedTextureImageUnits = */ 0, -/* .MaxTextureImageUnits = */ 0, -/* .MaxFragmentUniformComponents = */ 0, -/* .MaxDrawBuffers = */ 0, -/* .MaxVertexUniformVectors = */ 128, -/* .MaxVaryingVectors = */ 8, -/* .MaxFragmentUniformVectors = */ 0, -/* .MaxVertexOutputVectors = */ 16, -/* .MaxFragmentInputVectors = */ 0, -/* .MinProgramTexelOffset = */ -8, -/* .MaxProgramTexelOffset = */ 7, -/* .MaxClipDistances = */ 8, -/* .MaxComputeWorkGroupCountX = */ 65535, -/* .MaxComputeWorkGroupCountY = */ 65535, -/* .MaxComputeWorkGroupCountZ = */ 65535, -/* .MaxComputeWorkGroupSizeX = */ 1024, -/* .MaxComputeWorkGroupSizeY = */ 1024, -/* .MaxComputeWorkGroupSizeZ = */ 64, -/* .MaxComputeUniformComponents = */ 1024, -/* .MaxComputeTextureImageUnits = */ 16, -/* .MaxComputeImageUniforms = */ 8, -/* .MaxComputeAtomicCounters = */ 8, -/* .MaxComputeAtomicCounterBuffers = */ 1, -/* .MaxVaryingComponents = */ 60, -/* .MaxVertexOutputComponents = */ 64, -/* .MaxGeometryInputComponents = */ 64, -/* .MaxGeometryOutputComponents = */ 128, -/* .MaxFragmentInputComponents = */ 0, -/* .MaxImageUnits = */ 0, -/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, -/* .MaxCombinedShaderOutputResources = */ 8, -/* .MaxImageSamples = */ 0, -/* .MaxVertexImageUniforms = */ 0, -/* .MaxTessControlImageUniforms = */ 0, -/* .MaxTessEvaluationImageUniforms = */ 0, -/* .MaxGeometryImageUniforms = */ 0, -/* .MaxFragmentImageUniforms = */ 0, -/* .MaxCombinedImageUniforms = */ 0, -/* .MaxGeometryTextureImageUnits = */ 0, -/* .MaxGeometryOutputVertices = */ 256, -/* .MaxGeometryTotalOutputComponents = */ 1024, -/* .MaxGeometryUniformComponents = */ 1024, -/* .MaxGeometryVaryingComponents = */ 64, -/* .MaxTessControlInputComponents = */ 128, -/* .MaxTessControlOutputComponents = */ 128, -/* .MaxTessControlTextureImageUnits = */ 0, -/* .MaxTessControlUniformComponents = */ 1024, -/* .MaxTessControlTotalOutputComponents = */ 4096, -/* .MaxTessEvaluationInputComponents = */ 128, -/* .MaxTessEvaluationOutputComponents = */ 128, -/* .MaxTessEvaluationTextureImageUnits = */ 16, -/* .MaxTessEvaluationUniformComponents = */ 1024, -/* .MaxTessPatchComponents = */ 120, -/* .MaxPatchVertices = */ 32, -/* .MaxTessGenLevel = */ 64, -/* .MaxViewports = */ 16, -/* .MaxVertexAtomicCounters = */ 0, -/* .MaxTessControlAtomicCounters = */ 0, -/* .MaxTessEvaluationAtomicCounters = */ 0, -/* .MaxGeometryAtomicCounters = */ 0, -/* .MaxFragmentAtomicCounters = */ 0, -/* .MaxCombinedAtomicCounters = */ 8, -/* .MaxAtomicCounterBindings = */ 1, -/* .MaxVertexAtomicCounterBuffers = */ 0, -/* .MaxTessControlAtomicCounterBuffers = */ 0, -/* .MaxTessEvaluationAtomicCounterBuffers = */ 0, -/* .MaxGeometryAtomicCounterBuffers = */ 0, -/* .MaxFragmentAtomicCounterBuffers = */ 0, -/* .MaxCombinedAtomicCounterBuffers = */ 1, -/* .MaxAtomicCounterBufferSize = */ 16384, -/* .MaxTransformFeedbackBuffers = */ 4, -/* .MaxTransformFeedbackInterleavedComponents = */ 64, -/* .MaxCullDistances = */ 8, -/* .MaxCombinedClipAndCullDistances = */ 8, -/* .MaxSamples = */ 4, -/* .maxMeshOutputVerticesNV = */ 256, -/* .maxMeshOutputPrimitivesNV = */ 512, -/* .maxMeshWorkGroupSizeX_NV = */ 32, -/* .maxMeshWorkGroupSizeY_NV = */ 1, -/* .maxMeshWorkGroupSizeZ_NV = */ 1, -/* .maxTaskWorkGroupSizeX_NV = */ 32, -/* .maxTaskWorkGroupSizeY_NV = */ 1, -/* .maxTaskWorkGroupSizeZ_NV = */ 1, -/* .maxMeshViewCountNV = */ 4, -/* .maxDualSourceDrawBuffersEXT = */ 1, + /* .MaxLights = */ 0, + /* .MaxClipPlanes = */ 0, + /* .MaxTextureUnits = */ 0, + /* .MaxTextureCoords = */ 0, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 0, + /* .MaxCombinedTextureImageUnits = */ 0, + /* .MaxTextureImageUnits = */ 0, + /* .MaxFragmentUniformComponents = */ 0, + /* .MaxDrawBuffers = */ 0, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 0, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 0, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 0, + /* .MaxImageUnits = */ 0, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 0, + /* .MaxCombinedImageUniforms = */ 0, + /* .MaxGeometryTextureImageUnits = */ 0, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 0, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 0, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 0, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ + { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; -/* .limits = */ { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, -}}; - /** Shader utily class with functions to compile and process glsl files. */ -class Shader { -public: +class Shader +{ + public: /** * Compile multiple sources with optional filenames. Currently this function * uses the glslang C++ interface which is not thread safe so this funciton * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline + * online shader processing multithreading use-case that can't use offline * compilation please open an issue. * * @param sources A list of raw glsl shaders in string format * @param files A list of file names respective to each of the sources * @param entryPoint The function name to use as entry point * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @param resourcesLimit A list that contains the resource limits for the + * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ static std::vector compile_sources( - const std::vector& sources, - const std::vector& files = {}, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const std::vector& sources, + const std::vector& files = {}, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); /** - * Compile a single glslang source from string value. Currently this function - * uses the glslang C++ interface which is not thread safe so this funciton - * should not be called from multiple threads concurrently. If you have a - * online shader processing multithreading use-case that can't use offline - * compilation please open an issue. + * Compile a single glslang source from string value. Currently this + * function uses the glslang C++ interface which is not thread safe so this + * funciton should not be called from multiple threads concurrently. If you + * have a online shader processing multithreading use-case that can't use + * offline compilation please open an issue. * * @param source An individual raw glsl shader in string format * @param entryPoint The function name to use as entry point * @param definitions List of pairs containing key value definitions - * @param resourcesLimit A list that contains the resource limits for the GLSL compiler + * @param resourcesLimit A list that contains the resource limits for the + * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ static std::vector compile_source( - const std::string& source, - const std::string& entryPoint = "main", - std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); - + const std::string& source, + const std::string& entryPoint = "main", + std::vector> definitions = {}, + const TBuiltInResource& resources = defaultResource); }; } @@ -919,7 +951,7 @@ class Tensor * otherwise there is no need to copy from host memory. */ void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); + TensorTypes tensorType = TensorTypes::eDevice); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -990,9 +1022,8 @@ class Tensor * @param createBarrier Whether to create a barrier that ensures the data is * copied before further operations. Default is true. */ - void recordCopyFromStagingToDevice( - const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, + bool createBarrier); /** * Records a copy from the internal device memory to the staging memory @@ -1003,9 +1034,8 @@ class Tensor * @param createBarrier Whether to create a barrier that ensures the data is * copied before further operations. Default is true. */ - void recordCopyFromDeviceToStaging( - const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, + bool createBarrier); /** * Records the buffer memory barrier into the command buffer which @@ -1017,12 +1047,11 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordBufferMemoryBarrier( - const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -1070,11 +1099,11 @@ class Tensor std::shared_ptr memory, vk::MemoryPropertyFlags memoryPropertyFlags); void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); // Private util functions vk::BufferUsageFlags getPrimaryBufferUsageFlags(); @@ -1094,8 +1123,7 @@ namespace kp { */ class Algorithm { -public: - + public: /** * Default constructor for Algorithm * @@ -1103,12 +1131,11 @@ public: * @param commandBuffer The vulkan command buffer to bind the pipeline and * shaders */ - Algorithm( - std::shared_ptr device, - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + Algorithm(std::shared_ptr device, + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -1116,14 +1143,13 @@ public: * * @param shaderFileData The bytes in spir-v format of the shader * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the function - * processing + * @specalizationInstalces The specialization parameters to pass to the + * function processing */ - void rebuild( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + void rebuild(const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -1143,7 +1169,8 @@ public: void bindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer, const Constants& pushConstants); + void bindPush(const vk::CommandBuffer& commandBuffer, + const Constants& pushConstants); bool isInit(); @@ -1155,7 +1182,7 @@ public: void destroy(); -private: + private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mDevice; std::vector> mTensors; @@ -1489,7 +1516,7 @@ namespace kp { /** * Container of operations that can be sent to GPU as batch */ -class Sequence: public std::enable_shared_from_this +class Sequence : public std::enable_shared_from_this { public: /** @@ -1526,8 +1553,9 @@ class Sequence: public std::enable_shared_from_this * which allows for extensible configurations on initialisation. */ template - std::shared_ptr - record(std::vector> tensors, TArgs&&... params) + std::shared_ptr record( + std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1536,14 +1564,13 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); } template - std::shared_ptr - record(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr record(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1552,8 +1579,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->record(op); } @@ -1576,8 +1603,8 @@ class Sequence: public std::enable_shared_from_this */ // TODO: Aim to have only a single function with tensors/algorithm template - std::shared_ptr - eval(std::vector> tensors, TArgs&&... params) + std::shared_ptr eval(std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1586,16 +1613,16 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - // TODO: Aim to be able to handle errors when returning without throw except + // TODO: Aim to be able to handle errors when returning without throw + // except return this->eval(op); } // Needded as otherise can't use initialiser list template - std::shared_ptr - eval(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr eval(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1604,8 +1631,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->eval(op); } @@ -1627,8 +1654,9 @@ class Sequence: public std::enable_shared_from_this * @return shared_ptr of the Sequence class itself */ template - std::shared_ptr - evalAsync(std::vector> tensors, TArgs&&... params) + std::shared_ptr evalAsync( + std::vector> tensors, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1637,15 +1665,14 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(tensors, std::forward(params)...) }; + std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->evalAsync(op); } // Needed as otherwise it's not possible to use initializer lists template - std::shared_ptr - evalAsync(std::shared_ptr algorithm, TArgs&&... params) + std::shared_ptr evalAsync(std::shared_ptr algorithm, + TArgs&&... params) { KP_LOG_DEBUG("Kompute Sequence record function started"); @@ -1654,8 +1681,8 @@ class Sequence: public std::enable_shared_from_this "OpBase derived classes"); KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); - std::shared_ptr op{ - new T(algorithm, std::forward(params)...) }; + std::shared_ptr op{ new T(algorithm, + std::forward(params)...) }; return this->evalAsync(op); } @@ -1670,7 +1697,8 @@ class Sequence: public std::enable_shared_from_this std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); /** - * Clear function clears all operations currently recorded and starts recording again. + * Clear function clears all operations currently recorded and starts + * recording again. */ void clear(); @@ -1821,10 +1849,10 @@ class Manager Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); std::shared_ptr algorithm( - const std::vector>& tensors = {}, - const std::vector& spirv = {}, - const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const std::vector>& tensors = {}, + const std::vector& spirv = {}, + const Workgroup& workgroup = {}, + const Constants& specializationConstants = {}); void destroy(); void clear(); @@ -1856,7 +1884,8 @@ class Manager // Create functions void createInstance(); - void createDevice(const std::vector& familyQueueIndices = {}, uint32_t hysicalDeviceIndex = 0); + void createDevice(const std::vector& familyQueueIndices = {}, + uint32_t hysicalDeviceIndex = 0); }; } // End namespace kp diff --git a/src/include/kompute/Core.hpp b/src/include/kompute/Core.hpp index b50bf081d..3510a2021 100644 --- a/src/include/kompute/Core.hpp +++ b/src/include/kompute/Core.hpp @@ -61,8 +61,8 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) #define KP_LOG_DEBUG(...) \ - ((void)__android_log_print( \ - ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) + ((void)__android_log_write( \ + ANDROID_LOG_DEBUG, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_DEBUG(...) kp_debug(fmt::format(__VA_ARGS__)) #else @@ -81,8 +81,8 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) #define KP_LOG_INFO(...) \ - ((void)__android_log_print( \ - ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) + ((void)__android_log_write( \ + ANDROID_LOG_INFO, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_INFO(...) kp_info(fmt::format(__VA_ARGS__)) #else @@ -101,8 +101,8 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) #define KP_LOG_WARN(...) \ - ((void)__android_log_print( \ - ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) + ((void)__android_log_write( \ + ANDROID_LOG_WARN, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_WARN(...) kp_warning(fmt::format(__VA_ARGS__)) #else @@ -121,8 +121,8 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; #else #if defined(VK_USE_PLATFORM_ANDROID_KHR) #define KP_LOG_ERROR(...) \ - ((void)__android_log_print( \ - ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__))) + ((void)__android_log_write( \ + ANDROID_LOG_ERROR, KOMPUTE_LOG_TAG, fmt::format(__VA_ARGS__).c_str())) #elif defined(KOMPUTE_BUILD_PYTHON) #define KP_LOG_ERROR(...) kp_error(fmt::format(__VA_ARGS__)) #else diff --git a/src/include/kompute/operations/OpBase.hpp b/src/include/kompute/operations/OpBase.hpp index ba1e892d5..34818fcf0 100644 --- a/src/include/kompute/operations/OpBase.hpp +++ b/src/include/kompute/operations/OpBase.hpp @@ -1,7 +1,6 @@ #pragma once #include "kompute/Core.hpp" - #include "kompute/Tensor.hpp" #include "kompute/Algorithm.hpp" From f163aaf5e8b0c24b63f25397aa69264e91292b4f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Mon, 1 Mar 2021 07:58:12 +0000 Subject: [PATCH 25/91] Updated advanced example docs --- docs/overview/advanced-examples.rst | 223 +++++++++------------------- 1 file changed, 69 insertions(+), 154 deletions(-) diff --git a/docs/overview/advanced-examples.rst b/docs/overview/advanced-examples.rst index bd9d5506a..f7e5432eb 100644 --- a/docs/overview/advanced-examples.rst +++ b/docs/overview/advanced-examples.rst @@ -28,107 +28,6 @@ End-to-end examples * `Game Development Kompute ML in Godot Engine `_ -Simple Shader Example -~~~~~~~~~~~~~~~~~~~~~ - -Pass compute shader data in glsl/hlsl text or compiled SPIR-V format (or as path to the file). Back to `examples list <#simple-examples>`_. - -.. code-block:: cpp - :linenos: - int main() { - - // You can allow Kompute to create the Vulkan components, or pass your existing ones - kp::Manager mgr; // Selects device 0 unless explicitly requested - - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensorA = std::make_shared(kp::Tensor({ 3., 4., 5. })); - auto tensorB = std::make_shared(kp::Tensor({ 0., 0., 0. })); - - // Create tensors data explicitly in GPU with an operation - mgr.rebuild({ tensorA, tensorB }); - - // Define your shader as a string (using string literals for simplicity) - // (You can also pass the raw compiled bytes, or even path to file) - std::string shader(R"( - #version 450 - - layout (local_size_x = 1) in; - - layout(set = 0, binding = 0) buffer a { float pa[]; }; - layout(set = 0, binding = 1) buffer b { float pb[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - pb[index] = pa[index]; - pa[index] = index; - } - )"); - - // Run Kompute operation on the parameters provided with dispatch layout - mgr.evalOpDefault( - { tensorA, tensorB }, - kp::Shader::compile_source(shader)); - - // Sync the GPU memory back to the local tensor - mgr.evalOpDefault({ tensorA, tensorB }); - - // Prints the output which is A: { 0, 1, 2 } B: { 3, 4, 5 } - std::cout << fmt::format("A: {}, B: {}", - tensorA.data(), tensorB.data()) << std::endl; - } - -Record batch commands -~~~~~~~~~~~~~~~~~~~~~ - -Record commands in a single submit by using a Sequence to send in batch to GPU. Back to `examples list <#simple-examples>`_ - -.. code-block:: cpp - :linenos: - - int main() { - - kp::Manager mgr; - - std::shared_ptr tensorLHS{ new kp::Tensor({ 1., 1., 1. }) }; - std::shared_ptr tensorRHS{ new kp::Tensor({ 2., 2., 2. }) }; - std::shared_ptr tensorOutput{ new kp::Tensor({ 0., 0., 0. }) }; - - // Create all the tensors in memory - mgr.evalOpDefault({tensorLHS, tensorRHS, tensorOutput}); - - // Create a new sequence - std::weak_ptr sqWeakPtr = mgr.sequence(); - - if (std::shared_ptr sq = sqWeakPtr.lock()) - { - // Begin recording commands - sq->begin(); - - // Record batch commands to send to GPU - sq->record({ tensorLHS, tensorRHS, tensorOutput }); - sq->record({tensorOutput, tensorLHS, tensorRHS}); - - // Stop recording - sq->end(); - - // Submit multiple batch operations to GPU - size_t ITERATIONS = 5; - for (size_t i = 0; i < ITERATIONS; i++) { - sq->eval(); - } - - // Sync GPU memory back to local tensor - sq->begin(); - sq->record({tensorOutput}); - sq->end(); - sq->eval(); - } - - // Print the output which iterates through OpMult 5 times - // in this case the output is {32, 32 , 32} - std::cout << fmt::format("Output: {}", tensorOutput.data()) << std::endl; - } - Asynchronous Operations ~~~~~~~~~~~~~~~~~~~~~~~ @@ -143,10 +42,7 @@ You can submit operations asynchronously with the async/await commands in the kp kp::Manager mgr; // Selects device 0 unless explicitly requested // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensor = std::make_shared(kp::Tensor(std::vector(10, 0.0))); - - // Create tensors data explicitly in GPU with an operation - mgr.rebuild(tensor) + auto tensor = mgr.tensor(10, 0.0); // Define your shader as a string (using string literals for simplicity) // (You can also pass the raw compiled bytes, or even path to file) @@ -176,25 +72,19 @@ You can submit operations asynchronously with the async/await commands in the kp std::vector spirv = kp::Shader::compile_source(shader); - // We can now await for the previous submitted command - // The first parameter can be the amount of time to wait - // The time provided is in nanoseconds - mgr.evalOpAwaitDefault(10000); + auto sq = mgr.sequence(); - // Run Async Kompute operation on the parameters provided - mgr.evalOpAsyncDefault( - { tensor }, - spirv); + sq.eval({tensor}); - // Here we can do other work + sq.evalAsync(mgr.algorithm({tensor}, spirv)); // When we're ready we can wait // The default wait time is UINT64_MAX - mgr.evalOpAwaitDefault() + sq.evalAwait(10000) // Sync the GPU memory back to the local tensor // We can still run synchronous jobs in our created sequence - mgr.evalOpDefault({ tensor }); + sq.eval({ tensor }); // Prints the output: B: { 100000000, ... } std::cout << fmt::format("B: {}", @@ -225,18 +115,12 @@ Back to `examples list <#simple-examples>`_. // We create a manager with device index, and queues by queue family index kp::Manager mgr(deviceIndex, familyIndices); - // We need to create explicit sequences with their respective queues - // The second parameter is the index in the familyIndex array which is relative - // to the vector we created the manager with. - mgr.sequence("queueOne", 0); - mgr.sequence("queueTwo", 1); - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensorA = std::make_shared(kp::Tensor(std::vector(10, 0.0))); - auto tensorB = std::make_shared(kp::Tensor(std::vector(10, 0.0))); + auto tensorA = mgr.tensor({ 10, 0.0 }); + auto tensorB = mgr.tensor({ 10, 0.0 }); - // We run the first step synchronously on the default sequence - mgr.rebuild({ tensorA, tensorB }); + // Copies the data into GPU memory + mgr.sequence().eval({tensorA tensorB}); // Define your shader as a string (using string literals for simplicity) // (You can also pass the raw compiled bytes, or even path to file) @@ -266,26 +150,28 @@ Back to `examples list <#simple-examples>`_. std::vector spirv = kp::Shader::compile_source(shader); + std::shared_ptr algo = mgr.algorithm({tensorA, tenssorB}, spirv); + + // We need to create explicit sequences with their respective queues + // The second parameter is the index in the familyIndex array which is relative + // to the vector we created the manager with. + sqOne = mgr.sequence(0); + sqTwo = mgr.sequence(1); + // Run the first parallel operation in the `queueOne` sequence - mgr.evalOpAsync( - { tensorA }, - "queueOne", - spirv); + sqOne->evalAsync(algo); // Run the second parallel operation in the `queueTwo` sequence - mgr.evalOpAsync( - { tensorB }, - "queueTwo", - spirv); + sqTwo->evalAsync(algo); // Here we can do other work // We can now wait for the two parallel tasks to finish - mgr.evalOpAwait("queueOne") - mgr.evalOpAwait("queueTwo") + sqOne.evalOpAwait() + sqTwo.evalOpAwait() // Sync the GPU memory back to the local tensor - mgr.evalOp({ tensorA, tensorB }); + mgr.sequence()->eval({ tensorA, tensorB }); // Prints the output: A: 100000000 B: 100000000 std::cout << fmt::format("A: {}, B: {}", @@ -302,17 +188,47 @@ We also provide tools that allow you to `convert shaders into C++ headers physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, "") + OpMyCustom(std::vector> tensors, + std::shared_ptr algorithm) + : OpAlgoBase(algorithm) { - // Perform your custom steps such as reading from a shader file - this->mShaderFilePath = "shaders/glsl/opmult.comp.spv"; + if (tensors.size() != 3) { + throw std::runtime_error("Kompute OpMult expected 3 tensors but got " + tensors.size()); + } + + std::vector spirv = kp::Shader::compile_source(R"( + #version 450 + + layout(set = 0, binding = 0) buffer tensorLhs { + float valuesLhs[ ]; + }; + + layout(set = 0, binding = 1) buffer tensorRhs { + float valuesRhs[ ]; + }; + + layout(set = 0, binding = 2) buffer tensorOutput { + float valuesOutput[ ]; + }; + + layout (constant_id = 0) const uint LEN_LHS = 0; + layout (constant_id = 1) const uint LEN_RHS = 0; + layout (constant_id = 2) const uint LEN_OUT = 0; + + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + )"); + + algorithm->rebuild(tensors, spirv); } } @@ -322,16 +238,15 @@ We also provide tools that allow you to `convert shaders into C++ headers (kp::Tensor({ 0., 1., 2. })); - auto tensorRhs = std::make_shared(kp::Tensor({ 2., 4., 6. })); - auto tensorOut = std::make_shared(kp::Tensor({ 0., 0., 0. })); + auto tensorLhs = mgr.tensor({ 0., 1., 2. }); + auto tensorRhs = mgr.tensor({ 2., 4., 6. }); + auto tensorOut = mgr.tensor({ 0., 0., 0. }); - // Create tensors data explicitly in GPU with an operation - mgr.rebuild({ tensorLhs, tensorRhs, tensorOut }); - - // Run Kompute operation on the parameters provided with dispatch layout - mgr.evalOpDefault>( - { tensorLhs, tensorRhs, tensorOut }); + mgr.sequence() + ->record({tensorLhs, tensorRhs, tensorOut}) + ->record({tensorLhs, tensorRhs, tensorOut}, mgr.algorithm()) + ->record({tensorLhs, tensorRhs, tensorOut}) + ->eval(); // Prints the output which is { 0, 4, 12 } std::cout << fmt::format("Output: {}", tensorOutput.data()) << std::endl; From 7f686b47daa9cba5d8ec37995364b44eaa329f0b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Mon, 1 Mar 2021 21:13:08 +0000 Subject: [PATCH 26/91] Updated examples to new interface --- README.md | 173 ++++++------ docs/overview/advanced-examples.rst | 398 +++++++++++++++++----------- docs/overview/async-parallel.rst | 255 +----------------- docs/overview/custom-operations.rst | 63 +---- docs/overview/memory-management.rst | 18 +- docs/overview/python-examples.rst | 110 ++++---- 6 files changed, 410 insertions(+), 607 deletions(-) diff --git a/README.md b/README.md index 657cfc4a1..eccccf4dc 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ Below you can find a GPU multiplication example using the C++ and Python Kompute The C++ interface provides low level access to the native components of Kompute and Vulkan, enabling for [advanced optimizations](https://kompute.cc/overview/async-parallel.html) as well as [extension of components](https://kompute.cc/overview/reference.html). ```c++ -int main() { + +void kompute(const std::string& shader) { // 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) kp::Manager mgr; @@ -62,6 +63,42 @@ int main() { std::vector> params = {tensorInA, tensorInB, tensorOutA, tensorOutB}; // 3. Create algorithm based on shader (supports buffers & push/spec constants) + kp::Workgroup workgroup({3, 1, 1}); + kp::Constants specConsts({ 2 }); + kp::Constants pushConstsA({ 2.0 }); + kp::Constants pushConstsB({ 3.0 }); + + auto algorithm = mgr.algorithm(params, + kp::Shader::compile_source(shader), + workgroup, + specConsts); + + // 4. Run operation synchronously using sequence + mgr.sequence() + ->record(params) + ->record(algorithm, pushConstsA) + ->record(algorithm, pushConstsB) + ->eval(); + + // 5. Sync results from the GPU asynchronously + sq = mgr.sequence() + sq->evalAsync(params); + + // ... Do other work asynchronously whilst GPU finishes + + sq->evalAwait(); + + // Prints the first output which is: { 4, 8, 12 } + for (const float& elem : tensorOutA->data()) std::cout << elem << " "; + // Prints the second output which is: { 10, 10, 10 } + for (const float& elem : tensorOutB->data()) std::cout << elem << " "; + +} // Manages / releases all CPU and GPU memory resources + +int main() { + + // Define a raw string shader (or use the Kompute tools to compile to SPIRV / C++ header + // files). This shader shows some of the main components including constants, buffers, etc std::string shader = (R"( #version 450 @@ -88,33 +125,8 @@ int main() { } )"); - kp::Workgroup workgroup({3, 1, 1}); - kp::Constants specConsts({ 2 }); - - auto algorithm = mgr.algorithm(params, kp::Shader::compile_source(shader), workgroup, specConsts); - - kp::Constants pushConstsA({ 2.0 }); - kp::Constants pushConstsB({ 3.0 }); - - // 4. Run operation synchronously using sequence - mgr.sequence() - ->record(params) - ->record(algorithm, pushConstsA) - ->record(algorithm, pushConstsB) - ->eval(); - - // 5. Sync results from the GPU asynchronously - sq = mgr.sequence() - sq->evalAsync(params); - - // ... Do other work asynchronously whilst GPU finishes - - sq->evalAwait(); - - // Prints the first output which is: { 4, 8, 12 } - for (const float& elem : tensorOutA->data()) std::cout << elem << " "; - // Prints the second output which is: { 10, 10, 10 } - for (const float& elem : tensorOutB->data()) std::cout << elem << " "; + // Run the function declared above with our raw string shader + kompute(shader); } ``` @@ -125,70 +137,77 @@ The [Python package](https://kompute.cc/overview/python-package.html) provides a ```python -# 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) -mgr = kp.Manager() +def kompute(shader): + # 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) + mgr = kp.Manager() -# 2. Create and initialise Kompute Tensors through manager -tensor_in_a = mgr.tensor([2, 2, 2]) -tensor_in_b = mgr.tensor([1, 2, 3]) -tensor_out_a = mgr.tensor([0, 0, 0]) -tensor_out_b = mgr.tensor([0, 0, 0]) + # 2. Create and initialise Kompute Tensors through manager + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out_a = mgr.tensor([0, 0, 0]) + tensor_out_b = mgr.tensor([0, 0, 0]) -params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] + params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] -# 3. Create algorithm based on shader (supports buffers & push/spec constants) -shader = """ - #version 450 + # 3. Create algorithm based on shader (supports buffers & push/spec constants) + workgroup = (3, 1, 1) + spec_consts = [2] + push_consts_a = [2] + push_consts_b = [3] - layout (local_size_x = 1) in; + algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) - // 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 { float out_a[]; }; - layout(set = 0, binding = 3) buffer buf_out_b { float out_b[]; }; + # 4. Run operation synchronously using sequence + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(algo, push_consts_a)) + .record(kp.OpAlgoDispatch(algo, push_consts_b)) + .eval()) - // Kompute supports push constants updated on dispatch - layout(push_constant) uniform PushConstants { - float val; - } push_const; + # 5. Sync results from the GPU asynchronously + sq = mgr.sequence() + sq.eval_async(kp.OpTensorSyncLocal(params)) - // Kompute also supports spec constants on initalization - layout(constant_id = 0) const float const_one = 0; + # ... Do other work asynchronously whilst GPU finishes - void main() { - uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; - } -""" + sq.eval_await() -workgroup = (3, 1, 1) -spec_consts = [2] -push_consts_a = [2] -push_consts_b = [3] + # Prints the first output which is: { 4, 8, 12 } + print(tensor_out_a) + # Prints the first output which is: { 10, 10, 10 } + print(tensor_out_b) -algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) +if __name__ == "__main__": -# 4. Run operation synchronously using sequence -(mgr.sequence() - .record(kp.OpTensorSyncDevice(params)) - .record(kp.OpAlgoDispatch(algo, push_consts_a)) - .record(kp.OpAlgoDispatch(algo, push_consts_b)) - .eval()) + # Define a raw string shader (or use the Kompute tools to compile to SPIRV / C++ header + # files). This shader shows some of the main components including constants, buffers, etc + shader = """ + #version 450 -# 5. Sync results from the GPU asynchronously -sq = mgr.sequence() -sq.eval_async(kp.OpTensorSyncLocal(params)) + layout (local_size_x = 1) in; -# ... Do other work asynchronously whilst GPU finishes + // 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 { float out_a[]; }; + layout(set = 0, binding = 3) buffer buf_out_b { float out_b[]; }; -sq.eval_await() + // Kompute supports push constants updated on dispatch + layout(push_constant) uniform PushConstants { + float val; + } push_const; -# Prints the first output which is: { 4, 8, 12 } -print(tensor_out_a) -# Prints the first output which is: { 10, 10, 10 } -print(tensor_out_b) + // 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] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; + } + """ + + kompute(shader) ``` diff --git a/docs/overview/advanced-examples.rst b/docs/overview/advanced-examples.rst index f7e5432eb..80df20e42 100644 --- a/docs/overview/advanced-examples.rst +++ b/docs/overview/advanced-examples.rst @@ -10,13 +10,9 @@ The power of Kompute comes in when the interface is used for complex computation Simple examples ^^^^^^^^^^^^^^^ - -* `Pass shader as raw string <#simple-shader-example>`_ -* `Record batch commands with a Kompute Sequence <#record-batch-commands>`_ +* `Create your custom Kompute Operations <#your-custom-kompute-operation>`_ * `Run Asynchronous Operations <#asynchronous-operations>`_ * `Run Parallel Operations Across Multiple GPU Queues <#parallel-operations>`_ -* `Create your custom Kompute Operations <#your-custom-kompute-operation>`_ -* `Implementing logistic regression from scratch <#logistic-regression-example>`_ End-to-end examples ^^^^^^^^^^^^^^^^^^^ @@ -28,156 +24,6 @@ End-to-end examples * `Game Development Kompute ML in Godot Engine `_ -Asynchronous Operations -~~~~~~~~~~~~~~~~~~~~~~~ - -You can submit operations asynchronously with the async/await commands in the kp::Manager and kp::Sequence, which provides granularity on waiting on the vk::Fence. Back to `examples list <#simple-examples>`_ - -.. code-block:: cpp - :linenos: - - int main() { - - // You can allow Kompute to create the Vulkan components, or pass your existing ones - kp::Manager mgr; // Selects device 0 unless explicitly requested - - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensor = mgr.tensor(10, 0.0); - - // Define your shader as a string (using string literals for simplicity) - // (You can also pass the raw compiled bytes, or even path to file) - 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; - - // Iterating to simulate longer process - for (int i = 0; i < 100000000; i++) - { - atomicAdd(sharedTotal[0], 1); - } - - pb[index] = sharedTotal[0]; - } - )"); - - std::vector spirv = kp::Shader::compile_source(shader); - - auto sq = mgr.sequence(); - - sq.eval({tensor}); - - sq.evalAsync(mgr.algorithm({tensor}, spirv)); - - // When we're ready we can wait - // The default wait time is UINT64_MAX - sq.evalAwait(10000) - - // Sync the GPU memory back to the local tensor - // We can still run synchronous jobs in our created sequence - sq.eval({ tensor }); - - // Prints the output: B: { 100000000, ... } - std::cout << fmt::format("B: {}", - tensor.data()) << std::endl; - } - -Parallel Operations -~~~~~~~~~~~~~~~~~~~ - -Besides being able to submit asynchronous operations, you can also leverage the underlying GPU compute queues to process operations in parallel. - -This will depend on your underlying graphics card, but for example in NVIDIA graphics cards the operations submitted across queues in one family are not parallelizable, but operations submitted across queueFamilies can be parallelizable. - -Below we show how you can parallelize operations in an `NVIDIA 1650 `_\ , which has a ``GRAPHICS+COMPUTE`` family on ``index 0``\ , and ``COMPUTE`` family on ``index 2``. - -Back to `examples list <#simple-examples>`_. - -.. code-block:: cpp - :linenos: - - int main() { - - // In this case we select device 0, and for queues, one queue from familyIndex 0 - // and one queue from familyIndex 2 - uint32_t deviceIndex(0); - std::vector familyIndices = {0, 2}; - - // We create a manager with device index, and queues by queue family index - kp::Manager mgr(deviceIndex, familyIndices); - - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensorA = mgr.tensor({ 10, 0.0 }); - auto tensorB = mgr.tensor({ 10, 0.0 }); - - // Copies the data into GPU memory - mgr.sequence().eval({tensorA tensorB}); - - // Define your shader as a string (using string literals for simplicity) - // (You can also pass the raw compiled bytes, or even path to file) - 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; - - // Iterating to simulate longer process - for (int i = 0; i < 100000000; i++) - { - atomicAdd(sharedTotal[0], 1); - } - - pb[index] = sharedTotal[0]; - } - )"); - - std::vector spirv = kp::Shader::compile_source(shader); - - std::shared_ptr algo = mgr.algorithm({tensorA, tenssorB}, spirv); - - // We need to create explicit sequences with their respective queues - // The second parameter is the index in the familyIndex array which is relative - // to the vector we created the manager with. - sqOne = mgr.sequence(0); - sqTwo = mgr.sequence(1); - - // Run the first parallel operation in the `queueOne` sequence - sqOne->evalAsync(algo); - - // Run the second parallel operation in the `queueTwo` sequence - sqTwo->evalAsync(algo); - - // Here we can do other work - - // We can now wait for the two parallel tasks to finish - sqOne.evalOpAwait() - sqTwo.evalOpAwait() - - // Sync the GPU memory back to the local tensor - mgr.sequence()->eval({ tensorA, tensorB }); - - // Prints the output: A: 100000000 B: 100000000 - std::cout << fmt::format("A: {}, B: {}", - tensorA.data()[0], tensorB.data()[0]) << std::endl; - } - Your Custom Kompute Operation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -252,4 +98,246 @@ We also provide tools that allow you to `convert shaders into C++ headers eval({tensor}); + + +While this is running we can actually do other things like in this case create the shader we'll be using. + +In this case we create a shader that should take a couple of milliseconds to run. + +.. code-block:: cpp + :linenos: + + // Define your shader as a string (using string literals for simplicity) + // (You can also pass the raw compiled bytes, or even path to file) + 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; + + // Iterating to simulate longer process + for (int i = 0; i < 100000000; i++) + { + atomicAdd(sharedTotal[0], 1); + } + + pb[index] = sharedTotal[0]; + } + )"); + + auto algo = mgr.algorithm({tensor}, kp::Shader::compile_source(shader)); + +Now we are able to run the await function on the default sequence. + +If we are using the manager, we need to make sure that we are awaiting the same named sequence that was triggered asynchronously. + +If the sequence is not running or has finished running, it would return immediately. + +The parameter provided is the maximum amount of time to wait in nanoseconds. When the timeout expires, the sequence would return (with false value), but it does not stop the processing in the GPU - the processing would continue as normal. + +.. code-block:: cpp + :linenos: + + auto sq = mgr.sequence() + + // Run Async Kompute operation on the parameters provided + sq->evalAsync(algo); + + // Here we can do other work + + // When we're ready we can wait + // The default wait time is UINT64_MAX + sq.evalAwait() + + +Finally, below you can see that we can also run syncrhonous commands without having to change anything. + +.. code-block:: cpp + :linenos: + + // Sync the GPU memory back to the local tensor + // We can still run synchronous jobs in our created sequence + sq.eval({ tensor }); + + // Prints the output: B: { 100000000, ... } + std::cout << fmt::format("B: {}", + tensor.data()) << std::endl; + + +Parallel Operation Submission +----------- + +In order to work with parallel execution of tasks, it is important that you understand some of the core GPU processing limitations, as these can be quite broad and hardware dependent, which means they will vary across NVIDIA / AMD / ETC video cards. + +Conceptual Overview +^^^^^^^^^^^^^^^^^^^^^ + +If you are familiar with Vulkan, you will have experience that the first few things you do is fetching the physical Queues from the device. The queues themselves tend to have three main particular features - they can be GRAPHICS, TRANSFER and COMPUTE (between a few others we'll skip for simplicity). + +Queues can have multiple properties - namely a queue can be of type GRAPHICS+TRANSFER+COMPUTE, etc. Now here comes the key point: the underlying hardware may (or may not) support parallelized processing at multiple levels. + +Let's take a tangible example. The [NVIDIA 1650](http://vulkan.gpuinfo.org/displayreport.php?id=9700#queuefamilies) for example has 16 `GRAPHICS+TRANSFER+COMPUTE` queues on `familyIndex 0`, then 2 `TRANSFER` queues in `familyIndex 1` and finally 8 `COMPUTE+TRANSFER` queues in `familyIndex 2`. + +With this in mind, the NVIDIA 1650 as of today does not support intra-family parallelization, which means that if you were to submit commands in multiple queues of the same family, these would still be exectured synchronously. + +However the NVIDIA 1650 does support inter-family parallelization, which means that if we were to submit commands across multiple queues from different families, these would execute in parallel. + +This means that we would be able to execute parallel workloads as long as we're running them across multiple queue families. This is one of the reasons why Vulkan Kompute enables users to explicitly select the underlying queues and queue families to run particular workloads on. + +It is important that you understand what are the capabilities and limitations of your hardware, as parallelization capabilities can vary, so you will want to make sure you account for potential discrepancies in processing structures, mainyl to avoid undesired/unexpected race conditions. + +Parallel Execution Example +^^^^^^^^^^^^^^^^^^^^^ + +In this example we will demonstrate how you can set up parallel processing across two compute families to achieve 2x speedups when running processing workloads. + +To start, you will see that we do have to create the manager with extra parameters. This includes the GPU device index we want to use, together with the array of the queues that we want to enable. + +In this case we are using only two queues, which as per the section above, these would be familyIndex 0 which is of type `GRAPHICS+COMPUTE+TRANSFER` and familyIndex 2 which is of type `COMPUTE+TRANSFER`. + +In this case based on the specifications of the NVIDIA 1650 we could define up to 16 graphics queues (familyIndex 0), 2 transfer queues (familyIndex 1), and 8 compute queues (familyIndex 2) in no particular order. This means that we could have something like `{ 0, 1, 1, 2, 2, 2, 0, ... }` as our initialization value. + +You will want to keep track of the indices you initialize your manager, as you will be referring back to this ordering when creating sequences with particular queues. + +.. code-block:: cpp + :linenos: + + // In this case we select device 0, and for queues, one queue from familyIndex 0 + // and one queue from familyIndex 2 + uint32_t deviceIndex(0); + std::vector familyIndices = {0, 2}; + + // We create a manager with device index, and queues by queue family index + kp::Manager mgr(deviceIndex, familyIndices); + + +We are now able to create sequences with a particular queue. + +By default the Kompute Manager is created with device 0, and with a single queue of the first compatible familyIndex. Similarly, by default sequences are created with the first available queue. + +In this case we are able to specify which queue we want to use. Below we initialize "queueOne" named sequence with the graphics family queue, and "queueTwo" with the compute family queue. + +It's worth mentioning you can have multiple sequences referencing the same queue. + +.. code-block:: cpp + :linenos: + + // We need to create explicit sequences with their respective queues + // The second parameter is the index in the familyIndex array which is relative + // to the vector we created the manager with. + sqOne = mgr.sequence(0); + sqTwo = mgr.sequence(1); + +We create the tensors without modifications. + +.. code-block:: cpp + :linenos: + + // Creates tensor an initializes GPU memory (below we show more granularity) + auto tensorA = mgr.tensor({ 10, 0.0 }); + auto tensorB = mgr.tensor({ 10, 0.0 }); + + // Copies the data into GPU memory + mgr.sequence().eval({tensorA tensorB}); + +Similar to the asyncrhonous usecase above, we can still run synchronous commands without modifications. + +.. code-block:: cpp + :linenos: + + // Define your shader as a string (using string literals for simplicity) + // (You can also pass the raw compiled bytes, or even path to file) + 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; + + // Iterating to simulate longer process + for (int i = 0; i < 100000000; i++) + { + atomicAdd(sharedTotal[0], 1); + } + + pb[index] = sharedTotal[0]; + } + )"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr algo = mgr.algorithm({tensorA, tenssorB}, spirv); + +Now we can actually trigger the parallel processing, running two OpAlgoBase Operations - each in a different sequence / queue. + +.. code-block:: cpp + :linenos: + + // Run the first parallel operation in the `queueOne` sequence + sqOne->evalAsync(algo); + + // Run the second parallel operation in the `queueTwo` sequence + sqTwo->evalAsync(algo); + + +Similar to the asynchronous example above, we are able to do other work whilst the tasks are executing. + +We are able to wait for the tasks to complete by triggering the `evalOpAwait` on the respective sequence. + +.. code-block:: cpp + :linenos: + + // Here we can do other work + + // We can now wait for the two parallel tasks to finish + sqOne.evalOpAwait() + sqTwo.evalOpAwait() + + // Sync the GPU memory back to the local tensor + mgr.sequence()->eval({ tensorA, tensorB }); + + // Prints the output: A: 100000000 B: 100000000 + std::cout << fmt::format("A: {}, B: {}", + tensorA.data()[0], tensorB.data()[0]) << std::endl; + diff --git a/docs/overview/async-parallel.rst b/docs/overview/async-parallel.rst index 0a31ef17f..6b1f68b62 100644 --- a/docs/overview/async-parallel.rst +++ b/docs/overview/async-parallel.rst @@ -40,257 +40,8 @@ One important thing to bare in mind when using asynchronous submissions, is that The reason why this is important is that the Await function not only waits for the fence, but also runs the `postEval` functions across all operations, which is required for several operations. -Async/Await Example -^^^^^^^^^^^^^^^^^^^^^ +Async and Parallel Examples +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A simple example of asynchronous submission can be found below. - -First we are able to create the manager as we normally would. - -.. code-block:: cpp - :linenos: - - // You can allow Kompute to create the Vulkan components, or pass your existing ones - kp::Manager mgr; // Selects device 0 unless explicitly requested - - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensor = std::make_shared(kp::Tensor(std::vector(10, 0.0))); - -We can now run our first asynchronous command, which in this case we can use the default sequence. - -Sequences can be executed in synchronously or asynchronously without having to change anything. - -.. code-block:: cpp - :linenos: - - // Create tensors data explicitly in GPU with an operation - mgr.rebuild({ tensor }); - - -While this is running we can actually do other things like in this case create the shader we'll be using. - -In this case we create a shader that should take a couple of milliseconds to run. - -.. code-block:: cpp - :linenos: - - // Define your shader as a string (using string literals for simplicity) - // (You can also pass the raw compiled bytes, or even path to file) - 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; - - // Iterating to simulate longer process - for (int i = 0; i < 100000000; i++) - { - atomicAdd(sharedTotal[0], 1); - } - - pb[index] = sharedTotal[0]; - } - )"); - -Now we are able to run the await function on the default sequence. - -If we are using the manager, we need to make sure that we are awaiting the same named sequence that was triggered asynchronously. - -If the sequence is not running or has finished running, it would return immediately. - -The parameter provided is the maximum amount of time to wait in nanoseconds. When the timeout expires, the sequence would return (with false value), but it does not stop the processing in the GPU - the processing would continue as normal. - -.. code-block:: cpp - :linenos: - - // We can now await for the previous submitted command - // The first parameter can be the amount of time to wait - // The time provided is in nanoseconds - mgr.evalOpAwaitDefault(10000); - - -Similar to above we can run other commands such as the `OpAlgoBase` asynchronously. - -.. code-block:: cpp - :linenos: - - // Run Async Kompute operation on the parameters provided - mgr.evalOpAsyncDefault>( - { tensor }, - kp::Shader::compile_source(shader)); - - // Here we can do other work - - // When we're ready we can wait - // The default wait time is UINT64_MAX - mgr.evalOpAwaitDefault() - - -Finally, below you can see that we can also run syncrhonous commands without having to change anything. - -.. code-block:: cpp - :linenos: - - // Sync the GPU memory back to the local tensor - // We can still run synchronous jobs in our created sequence - mgr.evalOpDefault({ tensor }); - - // Prints the output: B: { 100000000, ... } - std::cout << fmt::format("B: {}", - tensor.data()) << std::endl; - - -Parallel Operation Submission ------------ - -In order to work with parallel execution of tasks, it is important that you understand some of the core GPU processing limitations, as these can be quite broad and hardware dependent, which means they will vary across NVIDIA / AMD / ETC video cards. - -Conceptual Overview -^^^^^^^^^^^^^^^^^^^^^ - -If you are familiar with Vulkan, you will have experience that the first few things you do is fetching the physical Queues from the device. The queues themselves tend to have three main particular features - they can be GRAPHICS, TRANSFER and COMPUTE (between a few others we'll skip for simplicity). - -Queues can have multiple properties - namely a queue can be of type GRAPHICS+TRANSFER+COMPUTE, etc. Now here comes the key point: the underlying hardware may (or may not) support parallelized processing at multiple levels. - -Let's take a tangible example. The [NVIDIA 1650](http://vulkan.gpuinfo.org/displayreport.php?id=9700#queuefamilies) for example has 16 `GRAPHICS+TRANSFER+COMPUTE` queues on `familyIndex 0`, then 2 `TRANSFER` queues in `familyIndex 1` and finally 8 `COMPUTE+TRANSFER` queues in `familyIndex 2`. - -With this in mind, the NVIDIA 1650 as of today does not support intra-family parallelization, which means that if you were to submit commands in multiple queues of the same family, these would still be exectured synchronously. - -However the NVIDIA 1650 does support inter-family parallelization, which means that if we were to submit commands across multiple queues from different families, these would execute in parallel. - -This means that we would be able to execute parallel workloads as long as we're running them across multiple queue families. This is one of the reasons why Vulkan Kompute enables users to explicitly select the underlying queues and queue families to run particular workloads on. - -It is important that you understand what are the capabilities and limitations of your hardware, as parallelization capabilities can vary, so you will want to make sure you account for potential discrepancies in processing structures, mainyl to avoid undesired/unexpected race conditions. - -Parallel Execution Example -^^^^^^^^^^^^^^^^^^^^^ - -In this example we will demonstrate how you can set up parallel processing across two compute families to achieve 2x speedups when running processing workloads. - -To start, you will see that we do have to create the manager with extra parameters. This includes the GPU device index we want to use, together with the array of the queues that we want to enable. - -In this case we are using only two queues, which as per the section above, these would be familyIndex 0 which is of type `GRAPHICS+COMPUTE+TRANSFER` and familyIndex 2 which is of type `COMPUTE+TRANSFER`. - -In this case based on the specifications of the NVIDIA 1650 we could define up to 16 graphics queues (familyIndex 0), 2 transfer queues (familyIndex 1), and 8 compute queues (familyIndex 2) in no particular order. This means that we could have something like `{ 0, 1, 1, 2, 2, 2, 0, ... }` as our initialization value. - -You will want to keep track of the indices you initialize your manager, as you will be referring back to this ordering when creating sequences with particular queues. - -.. code-block:: cpp - :linenos: - - // In this case we select device 0, and for queues, one queue from familyIndex 0 - // and one queue from familyIndex 2 - uint32_t deviceIndex(0); - std::vector familyIndices = {0, 2}; - - // We create a manager with device index, and queues by queue family index - kp::Manager mgr(deviceIndex, familyIndices); - -We are now able to create sequences with a particular queue. - -By default the Kompute Manager is created with device 0, and with a single queue of the first compatible familyIndex. Similarly, by default sequences are created with the first available queue. - -In this case we are able to specify which queue we want to use. Below we initialize "queueOne" named sequence with the graphics family queue, and "queueTwo" with the compute family queue. - -It's worth mentioning you can have multiple sequences referencing the same queue. - -.. code-block:: cpp - :linenos: - - // We need to create explicit sequences with their respective queues - // The second parameter is the index in the familyIndex array which is relative - // to the vector we created the manager with. - mgr.sequence("queueOne", 0); - mgr.sequence("queueTwo", 1); - -We create the tensors without modifications. - -.. code-block:: cpp - :linenos: - - // Creates tensor an initializes GPU memory (below we show more granularity) - auto tensorA = std::make_shared(kp::Tensor(std::vector(10, 0.0))); - auto tensorB = std::make_shared(kp::Tensor(std::vector(10, 0.0))); - -Similar to the asyncrhonous usecase above, we can still run synchronous commands without modifications. - -.. code-block:: cpp - :linenos: - - // We run the first step synchronously on the default sequence - mgr.rebuild({ tensorA, tensorB }); - - // Define your shader as a string (using string literals for simplicity) - // (You can also pass the raw compiled bytes, or even path to file) - 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; - - // Iterating to simulate longer process - for (int i = 0; i < 100000000; i++) - { - atomicAdd(sharedTotal[0], 1); - } - - pb[index] = sharedTotal[0]; - } - )"); - -Now we can actually trigger the parallel processing, running two OpAlgoBase Operations - each in a different sequence / queue. - -.. code-block:: cpp - :linenos: - - std::vector spirv = kp::Shader::compile_source(shader); - - // Run the first parallel operation in the `queueOne` sequence - mgr.evalOpAsync>( - { tensorA }, - "queueOne", - spirv); - - // Run the second parallel operation in the `queueTwo` sequence - mgr.evalOpAsync>( - { tensorB }, - "queueTwo", - spirv); - - -Similar to the asynchronous example above, we are able to do other work whilst the tasks are executing. - -We are able to wait for the tasks to complete by triggering the `evalOpAwait` on the respective sequence. - -.. code-block:: cpp - :linenos: - - // Here we can do other work - - // We can now wait for the two parallel tasks to finish - mgr.evalOpAwait("queueOne") - mgr.evalOpAwait("queueTwo") - - // Sync the GPU memory back to the local tensor - mgr.evalOp({ tensorA, tensorB }); - - // Prints the output: A: 100000000 B: 100000000 - std::cout << fmt::format("A: {}, B: {}", - tensorA.data()[0], tensorB.data()[0]) << std::endl; +We have added a set of examples for asynchronous and parallel processing examples in the `Advanced Examples documentation page `_ diff --git a/docs/overview/custom-operations.rst b/docs/overview/custom-operations.rst index 4947196cd..21f1fb82c 100644 --- a/docs/overview/custom-operations.rst +++ b/docs/overview/custom-operations.rst @@ -39,74 +39,19 @@ Below you Simple Operation Extending OpAlgoBase ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Below we show a very simple example that enables you to create an operation with a pre-specified shader. In this case it is the multiplication shader. +You can find an example in the `Advanced Examples documentation section `_ that shows how to create your own custom function. -.. code-block:: cpp - :linenos: - - class OpMyCustom : public OpAlgoBase - { - public: - OpMyCustom(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors) - : OpAlgoBase(physicalDevice, device, commandBuffer, tensors, "") - { - // Perform your custom steps such as reading from a shader file - this->mShaderFilePath = "shaders/glsl/opmult.comp"; - } - } +You can also see an implementation in the codebase through the `OpMult` class: - int main() { - - kp::Manager mgr; // Automatically selects Device 0 - - // Create 3 tensors of default type float - auto tensorLhs = std::make_shared(kp::Tensor({ 0., 1., 2. })); - auto tensorRhs = std::make_shared(kp::Tensor({ 2., 4., 6. })); - auto tensorOut = std::make_shared(kp::Tensor({ 0., 0., 0. })); - - // Create tensors data explicitly in GPU with an operation - mgr.evalOpDefault({ tensorLhs, tensorRhs, tensorOut }); - - // Run Kompute operation on the parameters provided with dispatch layout - mgr.evalOpDefault( - { tensorLhs, tensorRhs, tensorOut }); - - // Prints the output which is { 0, 4, 12 } - std::cout << fmt::format("Output: {}", tensorOutput.data()) << std::endl; - } - - -More Complex Operation Extending OpAlgoBase -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Below we show a more complex operation that performs the following: - -* Expects three tensors for an operation, two inputs and one output -* Expects the tensors to be initialised -* Checks that the tensors are of the same size -* Expects output tensor to be of type TensorTypes::eDevice (and creates staging tensor) -* Has functionality to read shader from file or directly from spirv bytes -* Records relevant bufferMemoryBarriers -* Records dispatch command -* Records copy command from device tensor to staging output tensor -* In postEval it maps data from staging tensor to output tensor's data - - -For starters, the header file contains the functions that will be overriden: - - -.. literalinclude:: ../../src/include/kompute/operations/OpAlgoLhsRhsOut.hpp +.. literalinclude:: ../../src/include/kompute/operations/OpMult.hpp :language: cpp Then the implementation outlines all the implementations that perform the actions above: ~~~~~~~~~~~~~~~~~~~ -.. literalinclude:: ../../src/OpAlgoLhsRhsOut.cpp +.. literalinclude:: ../../src/OpMult.cpp :language: cpp diff --git a/docs/overview/memory-management.rst b/docs/overview/memory-management.rst index f47b989cd..5ecfd7c0f 100755 --- a/docs/overview/memory-management.rst +++ b/docs/overview/memory-management.rst @@ -4,18 +4,22 @@ Memory Management Principles The principle in Vulkan Kompute on memory management is summarised as follows: -* Explicit is better than implicit for specifying memory management -* Interfaces for memory management are constant until freed -* Memory management responsibilities are acyclic from static object references * Memory management by Kompute is optional and only in place if resource is created by Kompute +* Memory management ownership architecture are acyclic and with a single top manager +* Operations do not manage any GPU memory or resources +* Top level manager is main owner of GPU resources and removes all resources when destroyed +* Manager holds weak pointers to ensure that if object created outside is destroyed it's released +* Once a resource is destroyed it cannot be recreated +* Resources can only be rebuilt if they haven't been destroyed -Vulkan Kompute is responsible for managing both the CPU and GPU memory allocations and resources, and is important that they are able to explicitly define when these objects are released or destroyed. Similarly, it's important that the memory resources created by the application are released safely. +Vulkan Kompute is responsible for managing both the CPU and GPU memory allocations and resources that it creates, and is important that they are able to explicitly define when these objects are released or destroyed. Similarly, it's important that the memory resources created by the application are released safely. -Vulkan Kompute is built with the BYOV principle in mind (Bring your own Vulkan). This means that even though the top level resources are managing the memory to its owned resources, they themselves may not have full ownership of the GPU / Vulkan components themselves. +Vulkan Kompute is built with the BYOV principle in mind (Bring your own Vulkan). This means that even though the top level resources are managing the memory to its owned resources, they themselves may not have full ownership of the GPU / Vulkan components - this is in the case that you may want to use Kompute with an existing Vulkan enabled application, and may want to initialise Kompute components with existing Vulkan resources. -The memory ownership is hierarchically outlined in the component architecture - in this diagram, the arrows provide an intuition on the memory management ownership relationships (in this case you can ignore the arrow from the Algorithm, as this is the only one that as of today doesn't manage the memory of the Tensors). +The memory ownership is hierarchically outlined in the component architecture - in this diagram, the arrows provide an intuition on the memory management ownership relationships. It's worth mentioning that the memory relationship may be different to the way components interact with each other - for this, you can see the high level component overview. More specifically: +* The purple arrows denote GPU memory management -.. image:: ../images/kompute-architecture.jpg +.. image:: ../images/kompute-vulkan-architecture.jpg :width: 100% Optional Memory Management diff --git a/docs/overview/python-examples.rst b/docs/overview/python-examples.rst index 7c160dcfd..ac6417928 100644 --- a/docs/overview/python-examples.rst +++ b/docs/overview/python-examples.rst @@ -14,17 +14,19 @@ Then you can interact with it from your interpreter. Below is the same sample as .. code-block:: python :linenos: - from kp import Manager, Tensor + from kp import Manager, Tensor, OpTensorSyncDevice, OpTensorSyncLocal, OpAlgoDispatch from pyshader import python2shader, ivec3, f32, Array mgr = Manager() # Can be initialized with List[] or np.Array - tensor_in_a = Tensor([2, 2, 2]) - tensor_in_b = Tensor([1, 2, 3]) - tensor_out = Tensor([0, 0, 0]) + tensor_in_a = mgr.tensor([2, 2, 2]) + tensor_in_b = mgr.tensor([1, 2, 3]) + tensor_out = mgr.tensor([0, 0, 0]) - mgr.eval_tensor_create_def([tensor_in_a, tensor_in_b, tensor_out]) + sq = mgr.sequence() + + sq.eval(OpTensorSyncLocal([tensor_in_a, tensor_in_b, tensor_out])) # Define the function via PyShader or directly as glsl string or spirv bytes @python2shader @@ -35,15 +37,13 @@ Then you can interact with it from your interpreter. Below is the same sample as i = index.x data3[i] = data1[i] * data2[i] + algo = mgr.algorithm([tensor_in_a, tensor_in_b, tensor_out], compute_shader_multiply.to_spirv()) + # Run shader operation synchronously - mgr.eval_algo_data_def( - [tensor_in_a, tensor_in_b, tensor_out], compute_shader_multiply.to_spirv()) + sq.eval(OpAlgoDispatch(algo)) + sq.eval(OpAlgoSyncLocal([tensor_out])) - mgr.eval_await_def() - - mgr.eval_tensor_sync_local_def([tensor_out]) - - assert tensor_out.data() == [2.0, 4.0, 6.0] + assert tensor_out.data().tolist() == [2.0, 4.0, 6.0] Python Example (Extended) @@ -55,6 +55,7 @@ Similarly you can find the same extended example as above: :linenos: from kp import Manager, Tensor + import kp from pyshader import python2shader, ivec3, f32, Array mgr = Manager(0, [2]) @@ -77,20 +78,19 @@ Similarly you can find the same extended example as above: i = index.x data3[i] = data1[i] * data2[i] - # Run shader operation asynchronously and then await - mgr.eval_async_algo_data_def( - [tensor_in_a, tensor_in_b, tensor_out], compute_shader_multiply.to_spirv()) - mgr.eval_await_def() + algo = mgr.algorithm([tensor_in_a, tensor_in_b, tensor_out], compute_shader_multiply.to_spirv()) - seq.begin() - seq.record_tensor_sync_local([tensor_in_a]) - seq.record_tensor_sync_local([tensor_in_b]) - seq.record_tensor_sync_local([tensor_out]) - seq.end() + # Run shader operation asynchronously and then await + mgr.eval_async(kp.OpAlgoDispatch(algo))) + mgr.eval_await() + + seq.record(kp.OpTensorSyncLocal([tensor_in_a])) + seq.record(kp.OpTensorSyncLocal([tensor_in_b])) + seq.record(kp.OpTensorSyncLocal([tensor_out])) seq.eval() - assert tensor_out.data() == [2.0, 4.0, 6.0] + assert tensor_out.data().tolist() == [2.0, 4.0, 6.0] Kompute Operation Capabilities ^^^^^ @@ -101,33 +101,29 @@ Handling multiple capabilites of processing can be done by compute shaders being :linenos: from kp import Manager + import kp # We'll assume we have the shader data available from my_spv_shader_data import mult_shader, sum_shader mgr = Manager() - t1 = mgr.build_tensor([2,2,2]) - t2 = mgr.build_tensor([1,2,3]) - t3 = mgr.build_tensor([1,2,3]) + t1 = mgr.tensor([2,2,2]) + t2 = mgr.tensor([1,2,3]) + t3 = mgr.tensor([1,2,3]) + + mgr.sequence().eval(kp.OpTensorSyncLocal([t1, t3])) # Create multiple separate sequences - sq_mult = mgr.create_sequence("SQ_MULT") - sq_sum = mgr.create_sequence("SQ_SUM") - sq_sync = mgr.create_sequence("SQ_SYNC") + sq_mult = mgr.sequence() + sq_sum = mgr.sequence() + sq_sync = mgr.sequence() - # Initialize sq_mult - sq_mult.begin() - sq_mult.record_algo_data([t1, t2, t3], add_shader) - sq_mult.end() + sq_mult.record(kp.OpAlgoDispatch(mgr.algorithm([t1, t2, t3], add_shader)) - sq_sum.begin() - sq_sum.record_algo_data([t3, t2, t1], sum_shader) - sq_sum.end() + sq_sum.record(kp.OpAlgoDispatch(mgr.algorithm([t3, t2, t1], sum_shader)) - sq_sync.begin() - sq_sync.record_tensor_sync_local([t1, t3]) - sq_sync.end() + sq_sync.record(kp.OpTensorSyncLocal([t1, t3])) # Run multiple iterations for i in range(10): @@ -147,6 +143,7 @@ Similar to the logistic regression implementation in the C++ examples section, b :linenos: from kp import Manager, Tensor + import kp from pyshader import python2shader, ivec3, f32, Array @python2shader @@ -189,38 +186,37 @@ Similar to the logistic regression implementation in the C++ examples section, b l_out[i] = loss + mgr = Manager() + # First we create input and ouput tensors for shader - tensor_x_i = Tensor([0.0, 1.0, 1.0, 1.0, 1.0]) - tensor_x_j = Tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_x_i = mgr.tensor([0.0, 1.0, 1.0, 1.0, 1.0]) + tensor_x_j = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) - tensor_y = Tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_y = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) - tensor_w_in = Tensor([0.001, 0.001]) - tensor_w_out_i = Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_w_out_j = Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_w_in = mgr.tensor([0.001, 0.001]) + tensor_w_out_i = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_w_out_j = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_b_in = Tensor([0.0]) - tensor_b_out = Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_b_in = mgr.tensor([0.0]) + tensor_b_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_l_out = Tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_l_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_m = Tensor([ 5.0 ]) + tensor_m = mgr.tensor([ 5.0 ]) # We store them in an array for easier interaction params = [tensor_x_i, tensor_x_j, tensor_y, tensor_w_in, tensor_w_out_i, tensor_w_out_j, tensor_b_in, tensor_b_out, tensor_l_out, tensor_m] - mgr = Manager() - - mgr.eval_tensor_create_def(params) + sq.sequence().eval(kp.OpTensorSyncDevice(params)) # Record commands for efficient evaluation - sq = mgr.create_sequence() - sq.begin() - sq.record_tensor_sync_device([tensor_w_in, tensor_b_in]) - sq.record_algo_data(params, compute_shader.to_spirv()) - sq.record_tensor_sync_local([tensor_w_out_i, tensor_w_out_j, tensor_b_out, tensor_l_out]) - sq.end() + sq = mgr.sequence() + + sq.record(kp.OpTensorSyncDevice([tensor_w_in, tensor_b_in])) + sq.record(kp.OpAlgoDispatch(mgr.algorithm(params, compute_shader.to_spirv()))) + sq.record(kp.OpTensorSyncLocal([tensor_w_out_i, tensor_w_out_j, tensor_b_out, tensor_l_out])) ITERATIONS = 100 learning_rate = 0.1 From 6192dda520cf8ea22e09650fc8daca1c5ffce7e9 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Mon, 1 Mar 2021 22:08:05 +0000 Subject: [PATCH 27/91] Added rerecord functionality and tests --- single_include/kompute/Kompute.hpp | 61 ++-------------------- src/Sequence.cpp | 18 ++++++- src/include/kompute/Algorithm.hpp | 4 +- src/include/kompute/Sequence.hpp | 57 ++------------------ test/TestSequence.cpp | 83 ++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 113 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 52d574ad3..7b67e2024 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1146,8 +1146,8 @@ class Algorithm * @specalizationInstalces The specialization parameters to pass to the * function processing */ - void rebuild(const std::vector>& tensors = {}, - const std::vector& spirv = {}, + void rebuild(const std::vector>& tensors, + const std::vector& spirv, const Workgroup& workgroup = {}, const Constants& specializationConstants = {}); @@ -1554,34 +1554,17 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, - TArgs&&... params) + std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - return this->record(op); } template std::shared_ptr record(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->record(op); } @@ -1606,34 +1589,15 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - - // TODO: Aim to be able to handle errors when returning without throw - // except return this->eval(op); } - // Needded as otherise can't use initialiser list template std::shared_ptr eval(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->eval(op); } @@ -1658,32 +1622,15 @@ class Sequence : public std::enable_shared_from_this std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - return this->evalAsync(op); } - // Needed as otherwise it's not possible to use initializer lists template std::shared_ptr evalAsync(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->evalAsync(op); } @@ -1727,6 +1674,8 @@ class Sequence : public std::enable_shared_from_this bool isInit(); + void rerecord(); + /** * Returns true if the sequence is currently running - mostly used for async * workloads. diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 68ff082ce..fa715cefc 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -51,6 +51,11 @@ Sequence::end() { KP_LOG_DEBUG("Kompute Sequence calling END"); + if (this->isRunning()) { + throw std::runtime_error( + "Kompute Sequence begin called when sequence still running"); + } + if (!this->isRecording()) { KP_LOG_WARN("Kompute Sequence end called when not recording"); return; @@ -64,7 +69,7 @@ Sequence::end() void Sequence::clear() { - KP_LOG_DEBUG("Kompute Sequence calling clear"); + KP_LOG_DEBUG("Kompute Sequence calling clear"); this->end(); } @@ -171,6 +176,17 @@ Sequence::isInit() this->mComputeQueue; } +void +Sequence::rerecord() +{ + this->end(); + std::vector> ops = this->mOperations; + this->mOperations.clear(); + for (const std::shared_ptr& op : ops) { + this->record(op); + } +} + void Sequence::destroy() { diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index e5fd1287e..32e5d9bdf 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -35,8 +35,8 @@ class Algorithm * @specalizationInstalces The specialization parameters to pass to the * function processing */ - void rebuild(const std::vector>& tensors = {}, - const std::vector& spirv = {}, + void rebuild(const std::vector>& tensors, + const std::vector& spirv, const Workgroup& workgroup = {}, const Constants& specializationConstants = {}); diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index 29c6a0c3b..5741fb4e6 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -47,34 +47,17 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, - TArgs&&... params) + std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - return this->record(op); } template std::shared_ptr record(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->record(op); } @@ -99,34 +82,15 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - - // TODO: Aim to be able to handle errors when returning without throw - // except return this->eval(op); } - // Needded as otherise can't use initialiser list template std::shared_ptr eval(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->eval(op); } @@ -151,32 +115,15 @@ class Sequence : public std::enable_shared_from_this std::vector> tensors, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(tensors, std::forward(params)...) }; - return this->evalAsync(op); } - // Needed as otherwise it's not possible to use initializer lists template std::shared_ptr evalAsync(std::shared_ptr algorithm, TArgs&&... params) { - KP_LOG_DEBUG("Kompute Sequence record function started"); - - static_assert(std::is_base_of::value, - "Kompute Sequence record(...) template only valid with " - "OpBase derived classes"); - - KP_LOG_DEBUG("Kompute Sequence creating OpBase derived class instance"); std::shared_ptr op{ new T(algorithm, std::forward(params)...) }; - return this->evalAsync(op); } @@ -220,6 +167,8 @@ class Sequence : public std::enable_shared_from_this bool isInit(); + void rerecord(); + /** * Returns true if the sequence is currently running - mostly used for async * workloads. diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 4d0233694..482868a88 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -17,3 +17,86 @@ TEST(TestSequence, SequenceDestructorViaManager) EXPECT_FALSE(sq->isInit()); } + +TEST(TestSequence, SequenceDestructorOutsideManagerExplicit) +{ + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr; + + sq = mgr.sequence(); + + EXPECT_TRUE(sq->isInit()); + + sq->destroy(); + + EXPECT_FALSE(sq->isInit()); + } + + EXPECT_FALSE(sq->isInit()); +} + +TEST(TestSequence, SequenceDestructorOutsideManagerImplicit) +{ + kp::Manager mgr; + + std::weak_ptr sqWeak; + + { + std::shared_ptr sq = mgr.sequence(); + + sqWeak = sq; + + EXPECT_TRUE(sq->isInit()); + } + + EXPECT_FALSE(sqWeak.lock()); +} + +TEST(TestSequence, RerecordSequence) +{ + 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 = kp::Shader::compile_source(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}); + + sq->eval(); + + EXPECT_EQ(tensorOut->data(), std::vector({2, 4, 6})); + + algo->rebuild({tensorOut, tensorA, tensorB}, spirv); + + // Refresh and trigger a rerecord + sq->rerecord(); + sq->eval(); + + EXPECT_EQ(tensorB->data(), std::vector({2, 8, 18})); +} From 991c644bfd80fefb56279399d31ada6171ca09b2 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Mon, 1 Mar 2021 22:14:54 +0000 Subject: [PATCH 28/91] Added link to code coverage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eccccf4dc..8e136fe03 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ * [Mobile enabled](#mobile-enabled) with examples via Android NDK across several architectures * BYOV: [Bring-your-own-Vulkan design](#motivations) to play nice with existing Vulkan applications * Explicit relationships for GPU and host [memory ownership and memory management](https://kompute.cc/overview/memory-management.html) -* [Hands on examples](#simple-examples) showing the core features +* Well tested codebase with [90% unit test code coverage](https://kompute.cc/codecov/) * Longer tutorials for [machine learning 🤖](https://towardsdatascience.com/machine-learning-and-data-processing-in-the-gpu-with-vulkan-kompute-c9350e5e5d3a), [mobile development 📱](https://towardsdatascience.com/gpu-accelerated-machine-learning-in-your-mobile-applications-using-the-android-ndk-vulkan-kompute-1e9da37b7617) and [game development 🎮](https://towardsdatascience.com/supercharging-game-development-with-gpu-accelerated-ml-using-vulkan-kompute-the-godot-game-engine-4e75a84ea9f0). ![](https://raw.githubusercontent.com/ethicalml/vulkan-kompute/master/docs/images/komputer-logos.gif) From 812563b9c7e28e558334a6582f6c015e7adddb8c Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Tue, 2 Mar 2021 07:30:58 +0000 Subject: [PATCH 29/91] Added readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8e136fe03..95acd3de3 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ * [Mobile enabled](#mobile-enabled) with examples via Android NDK across several architectures * BYOV: [Bring-your-own-Vulkan design](#motivations) to play nice with existing Vulkan applications * Explicit relationships for GPU and host [memory ownership and memory management](https://kompute.cc/overview/memory-management.html) -* Well tested codebase with [90% unit test code coverage](https://kompute.cc/codecov/) -* Longer tutorials for [machine learning 🤖](https://towardsdatascience.com/machine-learning-and-data-processing-in-the-gpu-with-vulkan-kompute-c9350e5e5d3a), [mobile development 📱](https://towardsdatascience.com/gpu-accelerated-machine-learning-in-your-mobile-applications-using-the-android-ndk-vulkan-kompute-1e9da37b7617) and [game development 🎮](https://towardsdatascience.com/supercharging-game-development-with-gpu-accelerated-ml-using-vulkan-kompute-the-godot-game-engine-4e75a84ea9f0). +* Robust codebase with [90% unit test code coverage](https://kompute.cc/codecov/) +* Advanced use-cases on [machine learning 🤖](https://towardsdatascience.com/machine-learning-and-data-processing-in-the-gpu-with-vulkan-kompute-c9350e5e5d3a), [mobile development 📱](https://towardsdatascience.com/gpu-accelerated-machine-learning-in-your-mobile-applications-using-the-android-ndk-vulkan-kompute-1e9da37b7617) and [game development 🎮](https://towardsdatascience.com/supercharging-game-development-with-gpu-accelerated-ml-using-vulkan-kompute-the-godot-game-engine-4e75a84ea9f0). ![](https://raw.githubusercontent.com/ethicalml/vulkan-kompute/master/docs/images/komputer-logos.gif) From c211c22a78af3fc812fcc90fa1dd802530cae304 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 08:10:28 +0000 Subject: [PATCH 30/91] Updated constants to be set as part of descriptor layout --- single_include/kompute/Kompute.hpp | 15 ++++++---- src/Algorithm.cpp | 47 ++++++++++++++++++++++++------ src/Manager.cpp | 5 ++-- src/OpAlgoDispatch.cpp | 6 +++- src/include/kompute/Algorithm.hpp | 12 +++++--- src/include/kompute/Manager.hpp | 3 +- test/TestPushConstant.cpp | 42 ++++++++++++++++++++++++-- 7 files changed, 106 insertions(+), 24 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 7b67e2024..661824804 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1135,7 +1135,8 @@ class Algorithm const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -1149,7 +1150,8 @@ class Algorithm void rebuild(const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -1169,15 +1171,16 @@ class Algorithm void bindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer, - const Constants& pushConstants); + void bindPush(const vk::CommandBuffer& commandBuffer); bool isInit(); void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + void setPush(const Constants& pushConstants); const Workgroup& getWorkgroup(); const Constants& getSpecializationConstants(); + const Constants& getPush(); const std::vector>& getTensors(); void destroy(); @@ -1206,6 +1209,7 @@ class Algorithm // -------------- ALWAYS OWNED RESOURCES std::vector mSpirv; Constants mSpecializationConstants; + Constants mPushConstants; Workgroup mWorkgroup; bool mIsInit; @@ -1801,7 +1805,8 @@ class Manager const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); void destroy(); void clear(); diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index c58c5a228..50ec9ad28 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -8,7 +8,8 @@ Algorithm::Algorithm(std::shared_ptr device, const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants) + const Constants& specializationConstants, + const Constants& pushConstants) { KP_LOG_DEBUG("Kompute Algorithm Constructor with device"); @@ -19,7 +20,7 @@ Algorithm::Algorithm(std::shared_ptr device, "spirv size: {}", tensors.size(), spirv.size()); - this->rebuild(tensors, spirv, workgroup, specializationConstants); + this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); } else { KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or " "spirv so not rebuilding vulkan components"); @@ -37,13 +38,15 @@ void Algorithm::rebuild(const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants) + const Constants& specializationConstants, + const Constants& pushConstants) { KP_LOG_DEBUG("Kompute Algorithm rebuild started"); this->mTensors = tensors; this->mSpirv = spirv; this->mSpecializationConstants = specializationConstants; + this->mPushConstants = pushConstants; this->setWorkgroup(workgroup, this->mTensors.size() ? this->mTensors[0]->size() : 1); @@ -273,6 +276,16 @@ Algorithm::createPipeline() 1, // Set layout count this->mDescriptorSetLayout.get()); + vk::PushConstantRange pushConstantRange; + if (this->mPushConstants.size()) { + pushConstantRange.setStageFlags(vk::ShaderStageFlagBits::eCompute); + pushConstantRange.setOffset(0); + pushConstantRange.setSize(sizeof(float) * this->mPushConstants.size()); + + pipelineLayoutInfo.setPushConstantRangeCount(1); + pipelineLayoutInfo.setPPushConstantRanges(&pushConstantRange); + } + this->mPipelineLayout = std::make_shared(); this->mDevice->createPipelineLayout( &pipelineLayoutInfo, nullptr, this->mPipelineLayout.get()); @@ -364,18 +377,17 @@ Algorithm::bindCore(const vk::CommandBuffer& commandBuffer) } void -Algorithm::bindPush(const vk::CommandBuffer& commandBuffer, - const Constants& pushConstants) +Algorithm::bindPush(const vk::CommandBuffer& commandBuffer) { - if (pushConstants.size()) { + if (this->mPushConstants.size()) { KP_LOG_DEBUG("Kompute Algorithm binding push constants size: {}", - pushConstants.size()); + this->mPushConstants.size()); commandBuffer.pushConstants(*this->mPipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, - pushConstants.size() * sizeof(float), - pushConstants.data()); + this->mPushConstants.size() * sizeof(float), + this->mPushConstants.data()); } } @@ -412,6 +424,18 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) this->mWorkgroup[2]); } +void +Algorithm::setPush(const Constants& pushConstants) { + + if (pushConstants.size() != this->mPushConstants.size()) { + throw std::runtime_error(fmt::format("Kompute Algorithm push " + "constant provided is size {} but expected size {}", + pushConstants.size(), this->mPushConstants.size())); + } + + this->mPushConstants = pushConstants; +} + const Workgroup& Algorithm::getWorkgroup() { @@ -424,6 +448,11 @@ Algorithm::getSpecializationConstants() return this->mSpecializationConstants; } +const Constants& +Algorithm::getPush() { + return this->mPushConstants; +} + const std::vector>& Algorithm::getTensors() { diff --git a/src/Manager.cpp b/src/Manager.cpp index 38f67de0d..f32cda43e 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -361,13 +361,14 @@ std::shared_ptr Manager::algorithm(const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup, - const Constants& specializationConstants) + const Constants& specializationConstants, + const Constants& pushConstants) { KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); std::shared_ptr algorithm{ new kp::Algorithm( - this->mDevice, tensors, spirv, workgroup, specializationConstants) }; + this->mDevice, tensors, spirv, workgroup, specializationConstants, pushConstants) }; if (this->mManageResources) { this->mManagedAlgorithms.push_back(algorithm); diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 4a30751fb..3aef85e4f 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -34,8 +34,12 @@ OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) vk::PipelineStageFlagBits::eComputeShader); } + if (this->mPushConstants.size()) { + this->mAlgorithm->setPush(this->mPushConstants); + } + this->mAlgorithm->bindCore(commandBuffer); - this->mAlgorithm->bindPush(commandBuffer, this->mPushConstants); + this->mAlgorithm->bindPush(commandBuffer); this->mAlgorithm->recordDispatch(commandBuffer); } diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index 32e5d9bdf..cabc673b6 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -24,7 +24,8 @@ class Algorithm const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); /** * Initialiser for the shader data provided to the algorithm as well as @@ -38,7 +39,8 @@ class Algorithm void rebuild(const std::vector>& tensors, const std::vector& spirv, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); /** * Destructor for Algorithm which is responsible for freeing and desroying @@ -58,15 +60,16 @@ class Algorithm void bindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer, - const Constants& pushConstants); + void bindPush(const vk::CommandBuffer& commandBuffer); bool isInit(); void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + void setPush(const Constants& pushConstants); const Workgroup& getWorkgroup(); const Constants& getSpecializationConstants(); + const Constants& getPush(); const std::vector>& getTensors(); void destroy(); @@ -95,6 +98,7 @@ class Algorithm // -------------- ALWAYS OWNED RESOURCES std::vector mSpirv; Constants mSpecializationConstants; + Constants mPushConstants; Workgroup mWorkgroup; bool mIsInit; diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 61212abf2..0dbde246d 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -87,7 +87,8 @@ class Manager const std::vector>& tensors = {}, const std::vector& spirv = {}, const Workgroup& workgroup = {}, - const Constants& specializationConstants = {}); + const Constants& specializationConstants = {}, + const Constants& pushConstants = {}); void destroy(); void clear(); diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index ae8cf4a32..8c3357b33 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -4,7 +4,7 @@ #include "fmt/ranges.h" -TEST(TestPushConstants, TestTwoConstants) +TEST(TestPushConstants, TestConstants) { { std::string shader(R"( @@ -32,7 +32,7 @@ TEST(TestPushConstants, TestTwoConstants) std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = - mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 })); + mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); sq = mgr.sequence() ->record({ tensor }) @@ -47,3 +47,41 @@ TEST(TestPushConstants, TestTwoConstants) } } } + +TEST(TestPushConstants, TestConstantsWrongSize) +{ + { + std::string shader(R"( + #version 450 + layout(push_constant) uniform PushConstants { + float x; + float y; + float z; + } pcs; + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr; + + std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + + std::shared_ptr algo = + mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0 }); + + sq = mgr.sequence() + ->record({ tensor }); + + EXPECT_THROW(sq->record(algo, kp::Constants{ 0.1, 0.2, 0.3 }), std::runtime_error); + } + } +} From 647f2f1e62021b5049ca03ac0e3de3acd9f4d374 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 08:27:24 +0000 Subject: [PATCH 31/91] Updated python --- python/src/main.cpp | 7 +++--- python/test/test_kompute.py | 8 +++---- test/TestPushConstant.cpp | 45 ++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 8aac68c98..594d9f45b 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -151,15 +151,16 @@ PYBIND11_MODULE(kp, m) { const std::vector>& tensors, const py::bytes& spirv, const kp::Workgroup& workgroup, - const kp::Constants& spec_consts) { + const kp::Constants& spec_consts, + const kp::Constants& push_consts) { py::buffer_info info(py::buffer(spirv).request()); const char *data = reinterpret_cast(info.ptr); size_t length = static_cast(info.size); std::vector spirvVec((uint32_t*)data, (uint32_t*)(data + length)); - return self.algorithm(tensors, spirvVec, workgroup, spec_consts); + return self.algorithm(tensors, spirvVec, workgroup, spec_consts, push_consts); }, "Algorithm initialisation function", - py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants()); + py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), py::arg("push_consts") = kp::Constants()); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index 865f72d92..47887930a 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -72,11 +72,11 @@ def test_end_to_end(): push_consts_a = [2] push_consts_b = [3] - algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) + algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts, push_consts_a) (mgr.sequence() .record(kp.OpTensorSyncDevice(params)) - .record(kp.OpAlgoDispatch(algo, push_consts_a)) + .record(kp.OpAlgoDispatch(algo)) .record(kp.OpAlgoDispatch(algo, push_consts_b)) .eval()) @@ -206,11 +206,11 @@ def test_pushconsts(): tensor = mgr.tensor([0, 0, 0]) - algo = mgr.algorithm([tensor], spirv, (1, 1, 1)) + algo = mgr.algorithm([tensor], spirv, (1, 1, 1), [], [0.1, 0.2, 0.3]) (mgr.sequence() .record(kp.OpTensorSyncDevice([tensor])) - .record(kp.OpAlgoDispatch(algo, [0.1, 0.2, 0.3])) + .record(kp.OpAlgoDispatch(algo)) .record(kp.OpAlgoDispatch(algo, [0.3, 0.2, 0.1])) .record(kp.OpTensorSyncLocal([tensor])) .eval()) diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index 8c3357b33..6b37ab015 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -4,7 +4,7 @@ #include "fmt/ranges.h" -TEST(TestPushConstants, TestConstants) +TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) { { std::string shader(R"( @@ -48,6 +48,49 @@ TEST(TestPushConstants, TestConstants) } } +TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) +{ + { + std::string shader(R"( + #version 450 + layout(push_constant) uniform PushConstants { + float x; + float y; + float z; + } pcs; + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr; + + std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + + std::shared_ptr algo = + mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.1, 0.2, 0.3 }); + + sq = mgr.sequence() + ->record({ tensor }) + ->record(algo) + ->record(algo, + kp::Constants{ 0.3, 0.2, 0.1 }) + ->record({ tensor }) + ->eval(); + + EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); + } + } +} + TEST(TestPushConstants, TestConstantsWrongSize) { { From 0558a1b6fc4f56e2e91f94e6e410ce43a52c1291 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 08:40:24 +0000 Subject: [PATCH 32/91] Updated push const readme --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 95acd3de3..3d60aac55 100644 --- a/README.md +++ b/README.md @@ -71,13 +71,14 @@ void kompute(const std::string& shader) { auto algorithm = mgr.algorithm(params, kp::Shader::compile_source(shader), workgroup, - specConsts); + specConsts, + pushConstsA); // 4. Run operation synchronously using sequence mgr.sequence() ->record(params) - ->record(algorithm, pushConstsA) - ->record(algorithm, pushConstsB) + ->record(algorithm) // Binds default push consts + ->record(algorithm, pushConstsB) // Overrides push consts ->eval(); // 5. Sync results from the GPU asynchronously @@ -155,13 +156,15 @@ def kompute(shader): push_consts_a = [2] push_consts_b = [3] - algo = mgr.algorithm(params, kp.Shader.compile_source(shader), workgroup, spec_consts) + spirv = kp.Shader.compile_source(shader) + + algo = mgr.algorithm(params, spirv, workgroup, spec_consts, push_consts_a) # 4. Run operation synchronously using sequence (mgr.sequence() .record(kp.OpTensorSyncDevice(params)) - .record(kp.OpAlgoDispatch(algo, push_consts_a)) - .record(kp.OpAlgoDispatch(algo, push_consts_b)) + .record(kp.OpAlgoDispatch(algo)) # Binds default push consts provided + .record(kp.OpAlgoDispatch(algo, push_consts_b)) # Overrides push consts .eval()) # 5. Sync results from the GPU asynchronously From ee4ba75beaa5d1326d992d643e3f0707c3e5f73b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 19:30:02 +0000 Subject: [PATCH 33/91] Added fix for test end to end functionality --- test/TestMultipleAlgoExecutions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index e050e02ea..4b8eedb12 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -49,12 +49,12 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) kp::Constants pushConstsB({ 3.0 }); auto algorithm = mgr.algorithm( - params, kp::Shader::compile_source(shader), workgroup, specConsts); + params, kp::Shader::compile_source(shader), workgroup, specConsts, pushConstsA); // 3. Run operation with string shader synchronously mgr.sequence() ->record(params) - ->record(algorithm, pushConstsA) + ->record(algorithm) ->record(algorithm, pushConstsB) ->eval(); From 91872048d412a87f828ed89f224139f2722d37a0 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 21:42:06 +0000 Subject: [PATCH 34/91] Added kompute env layers --- src/Manager.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index f32cda43e..5131f2a16 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -1,9 +1,15 @@ #include #include +#include +#include #include "kompute/Manager.hpp" +#if DEBUG +#include "fmt/ranges.h" +#endif + namespace kp { #if DEBUG @@ -163,8 +169,20 @@ Manager::createInstance() // We'll identify the layers that are supported std::vector validLayerNames; std::vector desiredLayerNames = { - "VK_LAYER_LUNARG_assistant_layer", "VK_LAYER_LUNARG_standard_validation" + "VK_LAYER_LUNARG_assistant_layer", + "VK_LAYER_LUNARG_standard_validation", + "VK_LAYER_KHRONOS_validation", }; + std::string envLayerNamesVal = std::getenv("KOMPUTE_ENV_DEBUG_LAYERS"); + KP_LOG_DEBUG("Kompute Manager adding environment layers: {}", envLayerNamesVal); + std::istringstream iss(envLayerNamesVal); + std::istream_iterator beg(iss), end; + std::vector envLayerNames(beg, end); + for (const std::string& layerName : envLayerNames) { + desiredLayerNames.push_back(layerName.c_str()); + } + KP_LOG_DEBUG("Desired layers: {}", desiredLayerNames); + // Identify the valid layer names based on the desiredLayerNames { std::set uniqueLayerNames; @@ -174,6 +192,7 @@ Manager::createInstance() std::string layerName(layerProperties.layerName.data()); uniqueLayerNames.insert(layerName); } + KP_LOG_DEBUG("Available layers: {}", uniqueLayerNames); for (const char* desiredLayerName : desiredLayerNames) { if (uniqueLayerNames.count(desiredLayerName) != 0) { validLayerNames.push_back(desiredLayerName); @@ -182,10 +201,14 @@ Manager::createInstance() } if (validLayerNames.size() > 0) { + KP_LOG_DEBUG("Kompute Manager Initializing instance with valid layers: {}", validLayerNames); computeInstanceCreateInfo.enabledLayerCount = (uint32_t)validLayerNames.size(); computeInstanceCreateInfo.ppEnabledLayerNames = validLayerNames.data(); } + else { + KP_LOG_WARN("Kompute Manager no valid layer names found from desired layer names"); + } #endif #endif From ec92d7b340d868ccd6121f064ba31ece1ab3aa92 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 21:45:38 +0000 Subject: [PATCH 35/91] Added kompute env debug layer param --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 3d60aac55..313fcc138 100644 --- a/README.md +++ b/README.md @@ -432,6 +432,12 @@ We appreciate PRs and Issues. If you want to contribute try checking the "Good f * Uses doxygen and sphinx for documentation and autodocs * Uses vcpkg for finding the dependencies, it's the recommended set up to retrieve the libraries +If you want to run with debug layers you can add them with the `KOMPUTE_ENV_DEBUG_LAYERS` parameter as: + +``` +export KOMPUTE_ENV_DEBUG_LAYERS="VK_LAYER_LUNARG_api_dump" +``` + ##### Updating documentation To update the documentation you will need to: From ffdb41772dfe4acca470176307fc6829c1a5d682 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 21:47:58 +0000 Subject: [PATCH 36/91] Added kompute env debug layer param --- src/Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index 5131f2a16..6ea3b9ecd 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -178,7 +178,7 @@ Manager::createInstance() std::istringstream iss(envLayerNamesVal); std::istream_iterator beg(iss), end; std::vector envLayerNames(beg, end); - for (const std::string& layerName : envLayerNames) { + for (std::string layerName : envLayerNames) { desiredLayerNames.push_back(layerName.c_str()); } KP_LOG_DEBUG("Desired layers: {}", desiredLayerNames); From 9a40465d69464981e726a68d1ef166231697baad Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 4 Mar 2021 22:20:30 +0000 Subject: [PATCH 37/91] Added kompute env debug layer param --- src/Manager.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index 6ea3b9ecd..103e799d8 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -173,15 +173,18 @@ Manager::createInstance() "VK_LAYER_LUNARG_standard_validation", "VK_LAYER_KHRONOS_validation", }; - std::string envLayerNamesVal = std::getenv("KOMPUTE_ENV_DEBUG_LAYERS"); + std::vector envLayerNames; + const char* envLayerNamesVal = std::getenv("KOMPUTE_ENV_DEBUG_LAYERS"); KP_LOG_DEBUG("Kompute Manager adding environment layers: {}", envLayerNamesVal); - std::istringstream iss(envLayerNamesVal); - std::istream_iterator beg(iss), end; - std::vector envLayerNames(beg, end); - for (std::string layerName : envLayerNames) { - desiredLayerNames.push_back(layerName.c_str()); + if (envLayerNamesVal != NULL && *envLayerNamesVal != '\0') { + std::istringstream iss(envLayerNamesVal); + std::istream_iterator beg(iss), end; + envLayerNames = std::vector(beg, end); + for (const std::string& layerName : envLayerNames) { + desiredLayerNames.push_back(layerName.c_str()); + } + KP_LOG_DEBUG("Desired layers: {}", desiredLayerNames); } - KP_LOG_DEBUG("Desired layers: {}", desiredLayerNames); // Identify the valid layer names based on the desiredLayerNames { From 71129392c2ecb401f7c320636c10de2be59e8a5c Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 5 Mar 2021 08:19:56 +0000 Subject: [PATCH 38/91] Added functionality with atomicadd --- src/Manager.cpp | 41 ++++++++++++++++++++++++----- src/include/kompute/Manager.hpp | 6 +++-- test/TestMultipleAlgoExecutions.cpp | 6 +++-- test/TestPushConstant.cpp | 27 ++++++++++--------- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index 103e799d8..83676f9ec 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -6,9 +6,7 @@ #include "kompute/Manager.hpp" -#if DEBUG #include "fmt/ranges.h" -#endif namespace kp { @@ -35,12 +33,13 @@ Manager::Manager() {} Manager::Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices) + const std::vector& familyQueueIndices, + const std::vector& desiredExtensions) { this->mManageResources = true; this->createInstance(); - this->createDevice(familyQueueIndices, physicalDeviceIndex); + this->createDevice(familyQueueIndices, physicalDeviceIndex, desiredExtensions); } Manager::Manager(std::shared_ptr instance, @@ -152,7 +151,10 @@ Manager::createInstance() applicationInfo.applicationVersion = KOMPUTE_VK_API_VERSION; std::vector applicationExtensions; + +#if DEBUG applicationExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); +#endif vk::InstanceCreateInfo computeInstanceCreateInfo; computeInstanceCreateInfo.pApplicationInfo = &applicationInfo; @@ -266,7 +268,8 @@ Manager::clear() void Manager::createDevice(const std::vector& familyQueueIndices, - uint32_t physicalDeviceIndex) + uint32_t physicalDeviceIndex, + const std::vector& desiredExtensions) { KP_LOG_DEBUG("Kompute Manager creating Device"); @@ -294,7 +297,7 @@ Manager::createDevice(const std::vector& familyQueueIndices, KP_LOG_INFO("Using physical device index {} found {}", physicalDeviceIndex, - physicalDeviceProperties.deviceName); + physicalDeviceProperties.deviceName.data()); if (!familyQueueIndices.size()) { // Find compute queue @@ -344,9 +347,33 @@ Manager::createDevice(const std::vector& familyQueueIndices, deviceQueueCreateInfos.push_back(deviceQueueCreateInfo); } + KP_LOG_DEBUG("Kompute Manager desired extension layers {}", desiredExtensions); + + std::vector deviceExtensions = this->mPhysicalDevice->enumerateDeviceExtensionProperties(); + + std::set uniqueExtensionNames; + for (const vk::ExtensionProperties& ext : deviceExtensions) { + std::string extName(ext.extensionName.data()); + uniqueExtensionNames.insert(extName); + } + KP_LOG_DEBUG("Kompute Manager available extensions {}", uniqueExtensionNames); + std::vector validExtensions; + for (std::string ext : desiredExtensions) { + if (uniqueExtensionNames.count(ext) != 0) { + validExtensions.push_back(ext.c_str()); + } + } + if (desiredExtensions.size() != validExtensions.size()) { + KP_LOG_ERROR("Kompute Manager not all extensions were added: {}", validExtensions); + } + vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfos.size(), - deviceQueueCreateInfos.data()); + deviceQueueCreateInfos.data(), + {}, + {}, + validExtensions.size(), + validExtensions.data()); this->mDevice = std::make_shared(); physicalDevice.createDevice( diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 0dbde246d..1e6b0adb2 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -35,7 +35,8 @@ class Manager * @param totalQueues The total number of compute queues to create. */ Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices = {}); + const std::vector& familyQueueIndices = {}, + const std::vector& desiredExtensions = {}); /** * Manager constructor which allows your own vulkan application to integrate @@ -121,7 +122,8 @@ class Manager // Create functions void createInstance(); void createDevice(const std::vector& familyQueueIndices = {}, - uint32_t hysicalDeviceIndex = 0); + uint32_t hysicalDeviceIndex = 0, + const std::vector& desiredExtensions = {}); }; } // End namespace kp diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 4b8eedb12..7e772fc80 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -16,6 +16,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) std::string shader = (R"( #version 450 + #extension GL_EXT_shader_atomic_float: enable + layout (local_size_x = 1) in; // The input tensors bind index is relative to index in parameter passed @@ -34,8 +36,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) void main() { uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; + atomicAdd(out_a[index], in_a[index] * in_b[index]); + atomicAdd(out_b[index], const_one * push_const.val); } )"); diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index 6b37ab015..184a3f66b 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -9,6 +9,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) { std::string shader(R"( #version 450 + #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -17,9 +18,9 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - pa[0] += pcs.x; - pa[1] += pcs.y; - pa[2] += pcs.z; + atomicAdd(pa[0], pcs.x); + atomicAdd(pa[1], pcs.y); + atomicAdd(pa[2], pcs.z); })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -27,7 +28,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) std::shared_ptr sq = nullptr; { - kp::Manager mgr; + kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); @@ -53,6 +54,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) { std::string shader(R"( #version 450 + #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -61,9 +63,9 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - pa[0] += pcs.x; - pa[1] += pcs.y; - pa[2] += pcs.z; + atomicAdd(pa[0], pcs.x); + atomicAdd(pa[1], pcs.y); + atomicAdd(pa[2], pcs.z); })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -71,7 +73,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) std::shared_ptr sq = nullptr; { - kp::Manager mgr; + kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); @@ -96,6 +98,7 @@ TEST(TestPushConstants, TestConstantsWrongSize) { std::string shader(R"( #version 450 + #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -104,9 +107,9 @@ TEST(TestPushConstants, TestConstantsWrongSize) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - pa[0] += pcs.x; - pa[1] += pcs.y; - pa[2] += pcs.z; + atomicAdd(pa[0], pcs.x); + atomicAdd(pa[1], pcs.y); + atomicAdd(pa[2], pcs.z); })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -114,7 +117,7 @@ TEST(TestPushConstants, TestConstantsWrongSize) std::shared_ptr sq = nullptr; { - kp::Manager mgr; + kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); From 33a0ef933bf944bd10167266e8eccce97cc68fe7 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 5 Mar 2021 08:33:53 +0000 Subject: [PATCH 39/91] Updated tests to run synchronous --- single_include/kompute/Kompute.hpp | 6 ++-- test/TestMultipleAlgoExecutions.cpp | 7 ++-- test/TestPushConstant.cpp | 56 ++++++++++++++--------------- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 661824804..346764888 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1753,7 +1753,8 @@ class Manager * @param totalQueues The total number of compute queues to create. */ Manager(uint32_t physicalDeviceIndex, - const std::vector& familyQueueIndices = {}); + const std::vector& familyQueueIndices = {}, + const std::vector& desiredExtensions = {}); /** * Manager constructor which allows your own vulkan application to integrate @@ -1839,7 +1840,8 @@ class Manager // Create functions void createInstance(); void createDevice(const std::vector& familyQueueIndices = {}, - uint32_t hysicalDeviceIndex = 0); + uint32_t hysicalDeviceIndex = 0, + const std::vector& desiredExtensions = {}); }; } // End namespace kp diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 7e772fc80..b94591308 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -16,8 +16,6 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) std::string shader = (R"( #version 450 - #extension GL_EXT_shader_atomic_float: enable - layout (local_size_x = 1) in; // The input tensors bind index is relative to index in parameter passed @@ -36,8 +34,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) void main() { uint index = gl_GlobalInvocationID.x; - atomicAdd(out_a[index], in_a[index] * in_b[index]); - atomicAdd(out_b[index], const_one * push_const.val); + out_a[index] += in_a[index] * in_b[index]; + out_b[index] += const_one * push_const.val; } )"); @@ -57,6 +55,7 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) mgr.sequence() ->record(params) ->record(algorithm) + ->eval() ->record(algorithm, pushConstsB) ->eval(); diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index 184a3f66b..f51f8cc42 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -9,7 +9,6 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) { std::string shader(R"( #version 450 - #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -18,9 +17,9 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - atomicAdd(pa[0], pcs.x); - atomicAdd(pa[1], pcs.y); - atomicAdd(pa[2], pcs.z); + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -28,21 +27,20 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) std::shared_ptr sq = nullptr; { - kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); + kp::Manager mgr; std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); - sq = mgr.sequence() - ->record({ tensor }) - ->record(algo, - kp::Constants{ 0.1, 0.2, 0.3 }) - ->record(algo, - kp::Constants{ 0.3, 0.2, 0.1 }) - ->record({ tensor }) - ->eval(); + sq = mgr.sequence()->eval({ tensor }); + + // We need to run this in sequence to avoid race condition + // We can't use atomicAdd as swiftshader doesn't support it for float + sq->eval(algo, kp::Constants{ 0.1, 0.2, 0.3 }); + sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); + sq->eval({ tensor }); EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); } @@ -54,7 +52,6 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) { std::string shader(R"( #version 450 - #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -63,9 +60,9 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - atomicAdd(pa[0], pcs.x); - atomicAdd(pa[1], pcs.y); - atomicAdd(pa[2], pcs.z); + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -73,20 +70,20 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) std::shared_ptr sq = nullptr; { - kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); + kp::Manager mgr; std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.1, 0.2, 0.3 }); - sq = mgr.sequence() - ->record({ tensor }) - ->record(algo) - ->record(algo, - kp::Constants{ 0.3, 0.2, 0.1 }) - ->record({ tensor }) - ->eval(); + sq = mgr.sequence()->eval({ tensor }); + + // We need to run this in sequence to avoid race condition + // We can't use atomicAdd as swiftshader doesn't support it for float + sq->eval(algo); + sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); + sq->eval({ tensor }); EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); } @@ -98,7 +95,6 @@ TEST(TestPushConstants, TestConstantsWrongSize) { std::string shader(R"( #version 450 - #extension GL_EXT_shader_atomic_float: enable layout(push_constant) uniform PushConstants { float x; float y; @@ -107,9 +103,9 @@ TEST(TestPushConstants, TestConstantsWrongSize) layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; void main() { - atomicAdd(pa[0], pcs.x); - atomicAdd(pa[1], pcs.y); - atomicAdd(pa[2], pcs.z); + pa[0] += pcs.x; + pa[1] += pcs.y; + pa[2] += pcs.z; })"); std::vector spirv = kp::Shader::compile_source(shader); @@ -117,7 +113,7 @@ TEST(TestPushConstants, TestConstantsWrongSize) std::shared_ptr sq = nullptr; { - kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float", "SPV_EXT_shader_atomic_float_add" }); + kp::Manager mgr; std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); From d26ef2738dd4b567af4c0f999e5d63b6357d2f12 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 5 Mar 2021 08:35:21 +0000 Subject: [PATCH 40/91] Added the raedme exampel --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 313fcc138..301572a09 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,9 @@ void kompute(const std::string& shader) { mgr.sequence() ->record(params) ->record(algorithm) // Binds default push consts + ->eval() // Evaluates the two recorded operations ->record(algorithm, pushConstsB) // Overrides push consts - ->eval(); + ->eval(); // Evaluates only last recorded operation // 5. Sync results from the GPU asynchronously sq = mgr.sequence() @@ -164,8 +165,9 @@ def kompute(shader): (mgr.sequence() .record(kp.OpTensorSyncDevice(params)) .record(kp.OpAlgoDispatch(algo)) # Binds default push consts provided + .eval() # evaluates the two recorded ops .record(kp.OpAlgoDispatch(algo, push_consts_b)) # Overrides push consts - .eval()) + .eval()) # evaluates only the last recorded op # 5. Sync results from the GPU asynchronously sq = mgr.sequence() From 52acd0eb179950b7dfa62ba9e34ef1563bf5f158 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 07:59:14 +0000 Subject: [PATCH 41/91] ADded example to add extensions --- README.md | 4 +- docs/overview/advanced-examples.rst | 57 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 301572a09..41596cb00 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The C++ interface provides low level access to the native components of Kompute void kompute(const std::string& shader) { - // 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) + // 1. Create Kompute Manager with default settings (device 0, first queue and no extensions) kp::Manager mgr; // 2. Create and initialise Kompute Tensors through manager @@ -140,7 +140,7 @@ The [Python package](https://kompute.cc/overview/python-package.html) provides a ```python def kompute(shader): - # 1. Create Kompute Manager with default settings (device 0 and first compute compatible queue) + # 1. Create Kompute Manager with default settings (device 0, first queue and no extensions) mgr = kp.Manager() # 2. Create and initialise Kompute Tensors through manager diff --git a/docs/overview/advanced-examples.rst b/docs/overview/advanced-examples.rst index 80df20e42..90066e8cc 100644 --- a/docs/overview/advanced-examples.rst +++ b/docs/overview/advanced-examples.rst @@ -23,6 +23,63 @@ End-to-end examples * `Android NDK Mobile Kompute ML Application `_ * `Game Development Kompute ML in Godot Engine `_ +Add Vulkan Extensions +^^^^^^^^^^^^^^^^^^^^ + +Kompute provides a simple way to add Vulkan extensions through kp::Manager initialisation. When debug is enabled you will be able to see logs that show what are the desired extensions requested and the ones that are added based on the available extensions on the current driver. + +The example below shows how you can enable the "VK_EXT_shader_atomic_float" extension so we can use the adomicAdd for floats in the shaders. + +.. code-block:: cpp + :linenos: + + int main() { + std::string shader(R"( + #version 450 + + #extension GL_EXT_shader_atomic_float: enable + + layout(push_constant) uniform PushConstants { + float x; + float y; + float z; + } pcs; + + layout (local_size_x = 1) in; + + layout(set = 0, binding = 0) buffer a { float pa[]; }; + + void main() { + atomicAdd(pa[0], pcs.x); + atomicAdd(pa[1], pcs.y); + atomicAdd(pa[2], pcs.z); + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + std::shared_ptr sq = nullptr; + + { + kp::Manager mgr(0, {}, { "VK_EXT_shader_atomic_float" }); + + std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + + std::shared_ptr algo = + mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); + + sq = mgr.sequence() + ->record({ tensor }) + ->record(algo, + kp::Constants{ 0.1, 0.2, 0.3 }) + ->record(algo, + kp::Constants{ 0.3, 0.2, 0.1 }) + ->record({ tensor }) + ->eval(); + + EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); + } + } + Your Custom Kompute Operation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 76bd16748597a146b8afcd5e79521602d8ff201f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 08:05:09 +0000 Subject: [PATCH 42/91] Updated pthon to support desired extensions --- python/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 594d9f45b..c851a2e43 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -135,8 +135,8 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "Manager") .def(py::init()) .def(py::init()) - .def(py::init&>()) - .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0) + .def(py::init&,const std::vector&>()) + .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0, py::arg("desired_extensions") = std::vector()) .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { From 6f5a8f8968c980e76202be62acc4f252639006f0 Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Sat, 6 Mar 2021 11:45:29 +0100 Subject: [PATCH 43/91] support for timestamps --- python/src/main.cpp | 3 +- single_include/kompute/Kompute.hpp | 16 +++++++- src/Manager.cpp | 5 ++- src/Sequence.cpp | 63 +++++++++++++++++++++++++++++- src/include/kompute/Manager.hpp | 4 +- src/include/kompute/Sequence.hpp | 13 +++++- 6 files changed, 96 insertions(+), 8 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 8aac68c98..9f660618e 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -129,6 +129,7 @@ PYBIND11_MODULE(kp, m) { .def("is_recording", &kp::Sequence::isRecording) .def("is_running", &kp::Sequence::isRunning) .def("is_init", &kp::Sequence::isInit) + .def("get_timestamps", &kp::Sequence::getTimestamps) .def("clear", &kp::Sequence::clear) .def("destroy", &kp::Sequence::destroy); @@ -136,7 +137,7 @@ PYBIND11_MODULE(kp, m) { .def(py::init()) .def(py::init()) .def(py::init&>()) - .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0) + .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0, py::arg("nrOfTimestamps") = 0) .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 7b67e2024..663d1d6d1 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1527,11 +1527,13 @@ class Sequence : public std::enable_shared_from_this * @param device Vulkan logical device * @param computeQueue Vulkan compute queue * @param queueIndex Vulkan compute queue index in device + * @param nrOfTimestamps Maximum number of timestamps to allocate */ Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, - uint32_t queueIndex); + uint32_t queueIndex, + uint32_t nrOfTimestamps = 0); /** * Destructor for sequence which is responsible for cleaning all subsequent * owned operations. @@ -1649,6 +1651,12 @@ class Sequence : public std::enable_shared_from_this */ void clear(); + /** + * Return the timestamps that were latched at the beginning and + * after each operation during the last eval() call. + */ + std::vector getTimestamps(); + /** * Begins recording commands for commands to be submitted into the command * buffer. @@ -1706,6 +1714,7 @@ class Sequence : public std::enable_shared_from_this // -------------- ALWAYS OWNED RESOURCES vk::Fence mFence; std::vector> mOperations; + std::shared_ptr timestampQueryPool = nullptr; // State bool mRecording = false; @@ -1714,6 +1723,7 @@ class Sequence : public std::enable_shared_from_this // Create functions void createCommandPool(); void createCommandBuffer(); + void createTimestampQueryPool(uint32_t); }; } // End namespace kp @@ -1778,9 +1788,11 @@ class Manager * @param sequenceName The name for the named sequence to be retrieved or * created * @param queueIndex The queue to use from the available queues + * @param nrOfTimestamps The maximum number of timestamps to allocate. + * If zero (default), disables latching of timestamps. * @return Shared pointer to the manager owned sequence resource */ - std::shared_ptr sequence(uint32_t queueIndex = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t nrOfTimestamps = 0); /** * Function that simplifies the common workflow of tensor creation and diff --git a/src/Manager.cpp b/src/Manager.cpp index 38f67de0d..a364eb07e 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -377,7 +377,7 @@ Manager::algorithm(const std::vector>& tensors, } std::shared_ptr -Manager::sequence(uint32_t queueIndex) +Manager::sequence(uint32_t queueIndex, uint32_t nrOfTimestamps) { KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", queueIndex); @@ -385,7 +385,8 @@ Manager::sequence(uint32_t queueIndex) this->mPhysicalDevice, this->mDevice, this->mComputeQueues[queueIndex], - this->mComputeQueueFamilyIndices[queueIndex]) }; + this->mComputeQueueFamilyIndices[queueIndex], + nrOfTimestamps) }; if (this->mManageResources) { this->mManagedSequences.push_back(sq); diff --git a/src/Sequence.cpp b/src/Sequence.cpp index fa715cefc..21cbf5af2 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -6,7 +6,8 @@ namespace kp { Sequence::Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, - uint32_t queueIndex) + uint32_t queueIndex, + uint32_t nrOfTimestamps) { KP_LOG_DEBUG("Kompute Sequence Constructor with existing device & queue"); @@ -17,6 +18,8 @@ Sequence::Sequence(std::shared_ptr physicalDevice, this->createCommandPool(); this->createCommandBuffer(); + if(nrOfTimestamps>0) + this->createTimestampQueryPool(nrOfTimestamps+1); //+1 for the first one } Sequence::~Sequence() @@ -44,6 +47,13 @@ Sequence::begin() KP_LOG_INFO("Kompute Sequence command now started recording"); this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); this->mRecording = true; + + //latch the first timestamp before any commands are submitted + if(this->timestampQueryPool) + this->mCommandBuffer->writeTimestamp( + vk::PipelineStageFlagBits::eAllCommands, + *this->timestampQueryPool, 0 + ); } void @@ -261,6 +271,12 @@ Sequence::record(std::shared_ptr op) this->mOperations.push_back(op); + if(this->timestampQueryPool) + this->mCommandBuffer->writeTimestamp( + vk::PipelineStageFlagBits::eAllCommands, + *this->timestampQueryPool, this->mOperations.size() + ); + return shared_from_this(); } @@ -308,4 +324,49 @@ Sequence::createCommandBuffer() KP_LOG_DEBUG("Kompute Sequence Command Buffer Created"); } +void +Sequence::createTimestampQueryPool(uint32_t query_size) +{ + KP_LOG_DEBUG("Kompute Sequence creating query pool"); + if (!this->mDevice) { + throw std::runtime_error("Kompute Sequence device is null"); + } + if (!this->mPhysicalDevice) { + throw std::runtime_error("Kompute Sequence physical device is null"); + } + + vk::PhysicalDeviceProperties physicalDeviceProperties = + this->mPhysicalDevice->getProperties(); + + if(physicalDeviceProperties.limits.timestampComputeAndGraphics){ + vk::QueryPoolCreateInfo queryPoolInfo; + queryPoolInfo.setQueryCount(query_size); + queryPoolInfo.setQueryType(vk::QueryType::eTimestamp); + this->timestampQueryPool = std::make_shared(this->mDevice->createQueryPool(queryPoolInfo)); + + KP_LOG_DEBUG("Query pool for timestamps created"); + } + else{ + KP_LOG_DEBUG("Device does not support timestamps"); + } +} + +std::vector +Sequence::getTimestamps(){ + if(!this->timestampQueryPool) + throw std::runtime_error("Timestamp latching not enabled"); + + const auto n = this->mOperations.size()+1; + std::vector timestamps(n, 0); + //XXX: the C++ method this->mDevice->getQueryPoolResults does not compile for me + const VkResult result = + vkGetQueryPoolResults(*this->mDevice, *this->timestampQueryPool, + 0, n, timestamps.size()*sizeof(std::uint64_t), timestamps.data(), + sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + if(result!=VK_SUCCESS) + throw std::runtime_error("vkGetQueryPoolResults failed"); + + return timestamps; +} + } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 61212abf2..214da7839 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -64,9 +64,11 @@ class Manager * @param sequenceName The name for the named sequence to be retrieved or * created * @param queueIndex The queue to use from the available queues + * @param nrOfTimestamps The maximum number of timestamps to allocate. + * If zero (default), disables latching of timestamps. * @return Shared pointer to the manager owned sequence resource */ - std::shared_ptr sequence(uint32_t queueIndex = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t nrOfTimestamps = 0); /** * Function that simplifies the common workflow of tensor creation and diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index 5741fb4e6..c25f8a6eb 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -3,6 +3,7 @@ #include "kompute/Core.hpp" #include "kompute/operations/OpBase.hpp" +#include "kompute/operations/OpAlgoDispatch.hpp" namespace kp { @@ -20,11 +21,13 @@ class Sequence : public std::enable_shared_from_this * @param device Vulkan logical device * @param computeQueue Vulkan compute queue * @param queueIndex Vulkan compute queue index in device + * @param nrOfTimestamps Maximum number of timestamps to allocate */ Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, - uint32_t queueIndex); + uint32_t queueIndex, + uint32_t nrOfTimestamps = 0); /** * Destructor for sequence which is responsible for cleaning all subsequent * owned operations. @@ -142,6 +145,12 @@ class Sequence : public std::enable_shared_from_this */ void clear(); + /** + * Return the timestamps that were latched at the beginning and + * after each operation during the last eval() call. + */ + std::vector getTimestamps(); + /** * Begins recording commands for commands to be submitted into the command * buffer. @@ -199,6 +208,7 @@ class Sequence : public std::enable_shared_from_this // -------------- ALWAYS OWNED RESOURCES vk::Fence mFence; std::vector> mOperations; + std::shared_ptr timestampQueryPool = nullptr; // State bool mRecording = false; @@ -207,6 +217,7 @@ class Sequence : public std::enable_shared_from_this // Create functions void createCommandPool(); void createCommandBuffer(); + void createTimestampQueryPool(uint32_t); }; } // End namespace kp From 196c896bd68d5e0356460e5e02fd54def8533164 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 11:19:56 +0000 Subject: [PATCH 44/91] Fixed #175 --- src/Shader.cpp | 109 +++++++++++++++++++++++++++++ src/include/kompute/Shader.hpp | 122 +++------------------------------ test/TestShaderResources.cpp | 6 +- 3 files changed, 120 insertions(+), 117 deletions(-) diff --git a/src/Shader.cpp b/src/Shader.cpp index 428b5a667..968e53234 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -105,5 +105,114 @@ Shader::compile_source( resource); } +const TBuiltInResource Shader::defaultResource = { + /* .MaxLights = */ 0, + /* .MaxClipPlanes = */ 0, + /* .MaxTextureUnits = */ 0, + /* .MaxTextureCoords = */ 0, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 0, + /* .MaxCombinedTextureImageUnits = */ 0, + /* .MaxTextureImageUnits = */ 0, + /* .MaxFragmentUniformComponents = */ 0, + /* .MaxDrawBuffers = */ 0, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 0, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 0, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 0, + /* .MaxImageUnits = */ 0, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 0, + /* .MaxCombinedImageUniforms = */ 0, + /* .MaxGeometryTextureImageUnits = */ 0, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 0, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 0, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 0, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ + { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; + } #endif // DKOMPUTE_DISABLE_SHADER_UTILS diff --git a/src/include/kompute/Shader.hpp b/src/include/kompute/Shader.hpp index 2d0e43741..9fd1709be 100644 --- a/src/include/kompute/Shader.hpp +++ b/src/include/kompute/Shader.hpp @@ -12,124 +12,18 @@ namespace kp { -// The default resource limit for the GLSL compiler, can be overwritten -// Has been adobted by: -// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp -const TBuiltInResource defaultResource = { - /* .MaxLights = */ 0, - /* .MaxClipPlanes = */ 0, - /* .MaxTextureUnits = */ 0, - /* .MaxTextureCoords = */ 0, - /* .MaxVertexAttribs = */ 64, - /* .MaxVertexUniformComponents = */ 4096, - /* .MaxVaryingFloats = */ 64, - /* .MaxVertexTextureImageUnits = */ 0, - /* .MaxCombinedTextureImageUnits = */ 0, - /* .MaxTextureImageUnits = */ 0, - /* .MaxFragmentUniformComponents = */ 0, - /* .MaxDrawBuffers = */ 0, - /* .MaxVertexUniformVectors = */ 128, - /* .MaxVaryingVectors = */ 8, - /* .MaxFragmentUniformVectors = */ 0, - /* .MaxVertexOutputVectors = */ 16, - /* .MaxFragmentInputVectors = */ 0, - /* .MinProgramTexelOffset = */ -8, - /* .MaxProgramTexelOffset = */ 7, - /* .MaxClipDistances = */ 8, - /* .MaxComputeWorkGroupCountX = */ 65535, - /* .MaxComputeWorkGroupCountY = */ 65535, - /* .MaxComputeWorkGroupCountZ = */ 65535, - /* .MaxComputeWorkGroupSizeX = */ 1024, - /* .MaxComputeWorkGroupSizeY = */ 1024, - /* .MaxComputeWorkGroupSizeZ = */ 64, - /* .MaxComputeUniformComponents = */ 1024, - /* .MaxComputeTextureImageUnits = */ 16, - /* .MaxComputeImageUniforms = */ 8, - /* .MaxComputeAtomicCounters = */ 8, - /* .MaxComputeAtomicCounterBuffers = */ 1, - /* .MaxVaryingComponents = */ 60, - /* .MaxVertexOutputComponents = */ 64, - /* .MaxGeometryInputComponents = */ 64, - /* .MaxGeometryOutputComponents = */ 128, - /* .MaxFragmentInputComponents = */ 0, - /* .MaxImageUnits = */ 0, - /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, - /* .MaxCombinedShaderOutputResources = */ 8, - /* .MaxImageSamples = */ 0, - /* .MaxVertexImageUniforms = */ 0, - /* .MaxTessControlImageUniforms = */ 0, - /* .MaxTessEvaluationImageUniforms = */ 0, - /* .MaxGeometryImageUniforms = */ 0, - /* .MaxFragmentImageUniforms = */ 0, - /* .MaxCombinedImageUniforms = */ 0, - /* .MaxGeometryTextureImageUnits = */ 0, - /* .MaxGeometryOutputVertices = */ 256, - /* .MaxGeometryTotalOutputComponents = */ 1024, - /* .MaxGeometryUniformComponents = */ 1024, - /* .MaxGeometryVaryingComponents = */ 64, - /* .MaxTessControlInputComponents = */ 128, - /* .MaxTessControlOutputComponents = */ 128, - /* .MaxTessControlTextureImageUnits = */ 0, - /* .MaxTessControlUniformComponents = */ 1024, - /* .MaxTessControlTotalOutputComponents = */ 4096, - /* .MaxTessEvaluationInputComponents = */ 128, - /* .MaxTessEvaluationOutputComponents = */ 128, - /* .MaxTessEvaluationTextureImageUnits = */ 16, - /* .MaxTessEvaluationUniformComponents = */ 1024, - /* .MaxTessPatchComponents = */ 120, - /* .MaxPatchVertices = */ 32, - /* .MaxTessGenLevel = */ 64, - /* .MaxViewports = */ 16, - /* .MaxVertexAtomicCounters = */ 0, - /* .MaxTessControlAtomicCounters = */ 0, - /* .MaxTessEvaluationAtomicCounters = */ 0, - /* .MaxGeometryAtomicCounters = */ 0, - /* .MaxFragmentAtomicCounters = */ 0, - /* .MaxCombinedAtomicCounters = */ 8, - /* .MaxAtomicCounterBindings = */ 1, - /* .MaxVertexAtomicCounterBuffers = */ 0, - /* .MaxTessControlAtomicCounterBuffers = */ 0, - /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, - /* .MaxGeometryAtomicCounterBuffers = */ 0, - /* .MaxFragmentAtomicCounterBuffers = */ 0, - /* .MaxCombinedAtomicCounterBuffers = */ 1, - /* .MaxAtomicCounterBufferSize = */ 16384, - /* .MaxTransformFeedbackBuffers = */ 4, - /* .MaxTransformFeedbackInterleavedComponents = */ 64, - /* .MaxCullDistances = */ 8, - /* .MaxCombinedClipAndCullDistances = */ 8, - /* .MaxSamples = */ 4, - /* .maxMeshOutputVerticesNV = */ 256, - /* .maxMeshOutputPrimitivesNV = */ 512, - /* .maxMeshWorkGroupSizeX_NV = */ 32, - /* .maxMeshWorkGroupSizeY_NV = */ 1, - /* .maxMeshWorkGroupSizeZ_NV = */ 1, - /* .maxTaskWorkGroupSizeX_NV = */ 32, - /* .maxTaskWorkGroupSizeY_NV = */ 1, - /* .maxTaskWorkGroupSizeZ_NV = */ 1, - /* .maxMeshViewCountNV = */ 4, - /* .maxDualSourceDrawBuffersEXT = */ 1, - - /* .limits = */ - { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, - } -}; - /** Shader utily class with functions to compile and process glsl files. */ class Shader { public: + + // The default resource limit for the GLSL compiler, can be overwritten + // Has been adopted by: + // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp + const static TBuiltInResource defaultResource; + /** * Compile multiple sources with optional filenames. Currently this function * uses the glslang C++ interface which is not thread safe so this funciton @@ -150,7 +44,7 @@ class Shader const std::vector& files = {}, const std::string& entryPoint = "main", std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const TBuiltInResource& resources = Shader::defaultResource); /** * Compile a single glslang source from string value. Currently this @@ -170,7 +64,7 @@ class Shader const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const TBuiltInResource& resources = Shader::defaultResource); }; } diff --git a/test/TestShaderResources.cpp b/test/TestShaderResources.cpp index f3436f228..b0013ef80 100644 --- a/test/TestShaderResources.cpp +++ b/test/TestShaderResources.cpp @@ -32,7 +32,7 @@ void compileShaderWithGivenResources(const std::string shaderString, const TBuil TEST(TestShaderResources, TestNoMaxLight) { - TBuiltInResource noMaxLightResources = kp::defaultResource; + TBuiltInResource noMaxLightResources = kp::Shader::defaultResource; noMaxLightResources.maxLights=0; EXPECT_NO_THROW(compileShaderWithGivenResources(shaderString, noMaxLightResources)); @@ -41,7 +41,7 @@ TEST(TestShaderResources, TestNoMaxLight) TEST(TestShaderResources, TestSmallComputeWorkGroupSizeX) { - TBuiltInResource smallComputeWorkGroupSizeXResources = kp::defaultResource; + TBuiltInResource smallComputeWorkGroupSizeXResources = kp::Shader::defaultResource; smallComputeWorkGroupSizeXResources.maxComputeWorkGroupSizeX=0; ASSERT_THROW(compileShaderWithGivenResources(shaderString, smallComputeWorkGroupSizeXResources), std::runtime_error); @@ -50,7 +50,7 @@ TEST(TestShaderResources, TestSmallComputeWorkGroupSizeX) TEST(TestShaderResources, TestNoWhileLoopLimit) { - TBuiltInResource noWhileLoopLimitResources = kp::defaultResource; + TBuiltInResource noWhileLoopLimitResources = kp::Shader::defaultResource; noWhileLoopLimitResources.limits.whileLoops=0; ASSERT_THROW(compileShaderWithGivenResources(shaderString, noWhileLoopLimitResources), std::runtime_error); From b753660c297390b4bdbc99b5079003fd0e4b0b8a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 11:20:09 +0000 Subject: [PATCH 45/91] Updated documentation for all classes --- src/Algorithm.cpp | 4 +- src/OpAlgoDispatch.cpp | 4 +- src/include/kompute/Algorithm.hpp | 96 +++++++++++++++---- src/include/kompute/Manager.hpp | 47 +++++---- src/include/kompute/Parameter.hpp | 47 --------- src/include/kompute/Sequence.hpp | 81 ++++++++++++++-- src/include/kompute/Tensor.hpp | 20 ++-- .../kompute/operations/OpAlgoDispatch.hpp | 17 +++- src/include/kompute/operations/OpBase.hpp | 6 ++ src/include/kompute/operations/OpMult.hpp | 6 +- .../kompute/operations/OpTensorCopy.hpp | 23 +++-- .../kompute/operations/OpTensorSyncDevice.hpp | 22 +++-- .../kompute/operations/OpTensorSyncLocal.hpp | 26 +++-- 13 files changed, 276 insertions(+), 123 deletions(-) delete mode 100644 src/include/kompute/Parameter.hpp diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index 50ec9ad28..d5263628b 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -359,7 +359,7 @@ Algorithm::createPipeline() } void -Algorithm::bindCore(const vk::CommandBuffer& commandBuffer) +Algorithm::recordBindCore(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute Algorithm binding pipeline"); @@ -377,7 +377,7 @@ Algorithm::bindCore(const vk::CommandBuffer& commandBuffer) } void -Algorithm::bindPush(const vk::CommandBuffer& commandBuffer) +Algorithm::recordBindPush(const vk::CommandBuffer& commandBuffer) { if (this->mPushConstants.size()) { KP_LOG_DEBUG("Kompute Algorithm binding push constants size: {}", diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 3aef85e4f..44908adb3 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -38,8 +38,8 @@ OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) this->mAlgorithm->setPush(this->mPushConstants); } - this->mAlgorithm->bindCore(commandBuffer); - this->mAlgorithm->bindPush(commandBuffer); + this->mAlgorithm->recordBindCore(commandBuffer); + this->mAlgorithm->recordBindPush(commandBuffer); this->mAlgorithm->recordDispatch(commandBuffer); } diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index cabc673b6..fae9cfd4b 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -14,11 +14,19 @@ class Algorithm { public: /** - * Default constructor for Algorithm + * Main constructor for algorithm with configuration parameters to create + * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders + * @param tensors (optional) The tensors to use to create the descriptor resources + * @param spirv (optional) The spirv code to use to create the algorithm + * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to + * kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to initialize + * the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when initializing the + * pipeline, which set the size of the push constants - these can be modified but + * all new values must have the same vector size as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -28,13 +36,18 @@ class Algorithm const Constants& pushConstants = {}); /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. + * Rebuild function to reconstruct algorithm with configuration parameters to create + * the underlying resources. * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the - * function processing + * @param tensors The tensors to use to create the descriptor resources + * @param spirv The spirv code to use to create the algorithm + * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to + * kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to initialize + * the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when initializing the + * pipeline, which set the size of the push constants - these can be modified but + * all new values must have the same vector size as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -52,24 +65,77 @@ class Algorithm * Records the dispatch function with the provided template parameters or * alternatively using the size of the tensor by default. * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value + * @param commandBuffer Command buffer to record the algorithm resources to */ void recordDispatch(const vk::CommandBuffer& commandBuffer); - void bindCore(const vk::CommandBuffer& commandBuffer); + /** + * Records command that binds the "core" algorithm components which consist of + * binding the pipeline and binding the descriptorsets. + * + * @param commandBuffer Command buffer to record the algorithm resources to + */ + void recordBindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer); + /** + * Records command that binds the push constants to the command buffer provided + * - it is required that the pushConstants provided are of the same size as the + * ones provided during initialization. + * + * @param commandBuffer Command buffer to record the algorithm resources to + */ + void recordBindPush(const vk::CommandBuffer& commandBuffer); + /** + * function that checks all the gpu resource components to verify if these have + * been created and returns true if all are valid. + * + * @returns returns true if the algorithm is currently initialized. + */ bool isInit(); + /** + * Sets the work group to use in the recordDispatch + * + * @param workgroup The kp::Workgroup value to use to update the algorithm. It + * must have a value greater than 1 on the x value (index 1) otherwise it will + * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + /** + * Sets the push constants to the new value provided to use in the next bindPush() + * + * @param The kp::Constant to use to set the push constants to use in the next + * bindPush(...) calls. The constants provided must be of the same size as the + * ones created during initialization. + */ void setPush(const Constants& pushConstants); + /** + * Gets the current workgroup from the algorithm. + * + * @param The kp::Constant to use to set the push constants to use in the next + * bindPush(...) calls. The constants provided must be of the same size as the + * ones created during initialization. + */ const Workgroup& getWorkgroup(); + /** + * Gets the specialization constants of the current algorithm. + * + * @returns The kp::Constants currently set for specialization constants + */ const Constants& getSpecializationConstants(); + /** + * Gets the specialization constants of the current algorithm. + * + * @returns The kp::Constants currently set for push constants + */ const Constants& getPush(); + /** + * Gets the current tensors that are used in the algorithm. + * + * @returns The list of tensors used in the algorithm. + */ const std::vector>& getTensors(); void destroy(); @@ -101,8 +167,6 @@ class Algorithm Constants mPushConstants; Workgroup mWorkgroup; - bool mIsInit; - // Create util functions void createShaderModule(); void createPipeline(); diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 1e6b0adb2..957e45d2e 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -24,15 +24,13 @@ class Manager Manager(); /** - * Similar to base constructor but allows the user to provide the device - * they would like to create the resources on. + * Similar to base constructor but allows for further configuration to use when + * creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use - * @param manageResources (Optional) Whether to manage the memory of the - * resources created and destroy when the manager is destroyed. * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param totalQueues The total number of compute queues to create. + * @param desiredExtensions The desired extensions to load from physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -58,32 +56,40 @@ class Manager ~Manager(); /** - * Get or create a managed Sequence that will be contained by this manager. - * If the named sequence does not currently exist, it would be created and - * initialised. + * Create a managed sequence that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. * - * @param sequenceName The name for the named sequence to be retrieved or - * created * @param queueIndex The queue to use from the available queues - * @return Shared pointer to the manager owned sequence resource + * @returns Shared pointer with initialised sequence */ std::shared_ptr sequence(uint32_t queueIndex = 0); /** - * Function that simplifies the common workflow of tensor creation and - * initialization. It will take the constructor parameters for a Tensor - * and will will us it to create a new Tensor and then create it. The - * tensor memory will then be managed and owned by the manager. + * Create a managed tensor that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. * * @param data The data to initialize the tensor with * @param tensorType The type of tensor to initialize - * @param syncDataToGPU Whether to sync the data to GPU memory - * @returns Initialized Tensor with memory Syncd to GPU device + * @returns Shared pointer with initialised tensor */ std::shared_ptr tensor( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + /** + * Create a managed algorithm that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. + * + * @param tensors (optional) The tensors to initialise the algorithm with + * @param spirv (optional) The SPIRV bytes for the algorithm to dispatch + * @param workgroup (optional) kp::Workgroup for algorithm to use, and + * defaults to (tensor[0].size(), 1, 1) + * @param specializationConstants (optional) kp::Constant to use for + * specialization constants, and defaults to an empty constant + * @param pushConstants (optional) kp::Constant to use for push constants, + * and defaults to an empty constant + * @returns Shared pointer with initialised algorithm + */ std::shared_ptr algorithm( const std::vector>& tensors = {}, const std::vector& spirv = {}, @@ -91,7 +97,14 @@ class Manager const Constants& specializationConstants = {}, const Constants& pushConstants = {}); + /** + * Destroy the GPU resources and all managed resources by manager. + **/ void destroy(); + /** + * Run a pseudo-garbage collection to release all the managed resources + * that have been already freed due to these reaching to zero ref count. + **/ void clear(); private: diff --git a/src/include/kompute/Parameter.hpp b/src/include/kompute/Parameter.hpp deleted file mode 100644 index a37eb31f8..000000000 --- a/src/include/kompute/Parameter.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "kompute/Core.hpp" - -#include "kompute/Tensor.hpp" - -namespace kp { - -class Algorithm -{ - public: - Algorithm(); - - Algorithm(std::shared_ptr device); - - void init(std::string shaderFilePath, - std::vector> tensorParams); - - ~Algorithm(); - - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mDevice; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mDescriptorSetLayout; - bool mFreeDescriptorSetLayout = false; - std::shared_ptr mDescriptorPool; - bool mFreeDescriptorPool = false; - std::shared_ptr mDescriptorSet; - bool mFreeDescriptorSet = false; - std::shared_ptr mShaderModule; - bool mFreeShaderModule = false; - std::shared_ptr mPipelineLayout; - bool mFreePipelineLayout = false; - std::shared_ptr mPipelineCache; - bool mFreePipelineCache = false; - std::shared_ptr mPipeline; - bool mFreePipeline = false; - - // Create util functions - void createParameters(); - void createShaderModule(std::string shaderFilePath); - void createPipeline(); -}; - -} // End namespace kp diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index 5741fb4e6..10aa80148 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -32,6 +32,14 @@ class Sequence : public std::enable_shared_from_this ~Sequence(); /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param op Object derived from kp::BaseOp that will be recoreded by the sequence + * which will be used when the operation is evaluated. + * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -44,6 +52,7 @@ class Sequence : public std::enable_shared_from_this * @param tensors Vector of tensors to use for the operation * @param TArgs Template parameters that are used to initialise operation * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself */ template std::shared_ptr record( @@ -52,6 +61,18 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); } + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr record(std::shared_ptr algorithm, TArgs&&... params) @@ -63,21 +84,29 @@ class Sequence : public std::enable_shared_from_this /** * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. + * operations into the gpu as a submit job synchronously (with a barrier). * * @return shared_ptr of the Sequence class itself */ std::shared_ptr eval(); + /** + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a barrier). + * + * @return shared_ptr of the Sequence class itself + */ std::shared_ptr eval(std::shared_ptr op); /** * Eval sends all the recorded and stored operations in the vector of * operations into the gpu as a submit job with a barrier. * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. * @return shared_ptr of the Sequence class itself */ - // TODO: Aim to have only a single function with tensors/algorithm template std::shared_ptr eval(std::vector> tensors, TArgs&&... params) @@ -85,6 +114,16 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->eval(op); } + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr eval(std::shared_ptr algorithm, TArgs&&... params) @@ -96,18 +135,27 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() must + * ALWAYS be called after to ensure the sequence is terminated correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); + /** + * Clears currnet operations to record provided one in the vector of + * operations into the gpu as a submit job without a barrier. EvalAwait() must + * ALWAYS be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ std::shared_ptr evalAsync(std::shared_ptr op); - /** * Eval sends all the recorded and stored operations in the vector of * operations into the gpu as a submit job with a barrier. * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. * @return shared_ptr of the Sequence class itself */ template @@ -118,6 +166,16 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->evalAsync(op); } + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr evalAsync(std::shared_ptr algorithm, TArgs&&... params) @@ -132,7 +190,7 @@ class Sequence : public std::enable_shared_from_this * finishes, it runs the postEval of all operations. * * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. + * @return shared_ptr of the Sequence class itself */ std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); @@ -165,8 +223,19 @@ class Sequence : public std::enable_shared_from_this */ bool isRecording(); + /** + * Returns true if the sequence has been initialised, and it's based on the + * GPU resources being refrenced. + * + * @return Boolean stating if is initialized + */ bool isInit(); + /** + * Clears command buffer and triggers re-record of all the current operations + * saved, which is useful if the underlying kp::Tensors or kp::Algorithms + * are modified and need to be re-recorded. + */ void rerecord(); /** diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 7b24f3de7..195af44f4 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -29,12 +29,14 @@ class Tensor }; /** - * Default constructor with data provided which would be used to create the + * Constructor with data provided which would be used to create the * respective vulkan buffer and memory. * + * @param physicalDevice The physical device to use to fetch properties + * @param device The device to use to create the buffer and memory from * @param data Non-zero-sized vector of data that will be used by the * tensor - * @param tensorType Type for the tensor which is of type TensorTypes + * @param tensorTypes Type for the tensor which is of type TensorTypes */ Tensor(std::shared_ptr physicalDevice, std::shared_ptr device, @@ -48,10 +50,11 @@ class Tensor ~Tensor(); /** - * Initialiser which calls the initialisation for all the respective tensors - * as well as creates the respective staging tensors. The staging tensors - * would only be created for the tensors of type TensorType::eDevice as - * otherwise there is no need to copy from host memory. + * Function to trigger reinitialisation of the tensor buffer and memory with + * new data as well as new potential device type. + * + * @param data Vector of data to use to initialise vector from + * @param tensorType The type to use for the tensor */ void rebuild(const std::vector& data, TensorTypes tensorType = TensorTypes::eDevice); @@ -61,6 +64,11 @@ class Tensor */ void destroy(); + /** + * Check whether tensor is initialized based on the created gpu resources. + * + * @returns Boolean stating whether tensor is initialized + */ bool isInit(); /** diff --git a/src/include/kompute/operations/OpAlgoDispatch.hpp b/src/include/kompute/operations/OpAlgoDispatch.hpp index 6975f2793..018fbced5 100644 --- a/src/include/kompute/operations/OpAlgoDispatch.hpp +++ b/src/include/kompute/operations/OpAlgoDispatch.hpp @@ -17,6 +17,13 @@ class OpAlgoDispatch : public OpBase { public: + /** + * Constructor that stores the algorithm to use as well as the relevant + * push constants to override when recording. + * + * @param algorithm The algorithm object to use for dispatch + * @param pushConstants The push constants to use for override + */ OpAlgoDispatch(const std::shared_ptr& algorithm, const kp::Constants& pushConstants = {}); @@ -33,18 +40,22 @@ class OpAlgoDispatch : public OpBase * shader processing to the gpu. This function also records the GPU memory * copy of the output data for the staging buffer so it can be read by the * host. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. + * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; diff --git a/src/include/kompute/operations/OpBase.hpp b/src/include/kompute/operations/OpBase.hpp index 34818fcf0..f4efb2e9b 100644 --- a/src/include/kompute/operations/OpBase.hpp +++ b/src/include/kompute/operations/OpBase.hpp @@ -32,6 +32,8 @@ class OpBase * The record function is intended to only send a record command or run * commands that are expected to record operations that are to be submitted * as a batch into the GPU. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void record(const vk::CommandBuffer& commandBuffer) = 0; @@ -42,6 +44,8 @@ class OpBase * there are situations where eval can be called multiple times, so the * resources that are created should be idempotent in case it's called multiple * times in a row. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) = 0; @@ -52,6 +56,8 @@ class OpBase * there are situations where eval can be called multiple times, so the * resources that are destroyed should not require a re-init unless explicitly * provided by the user. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) = 0; }; diff --git a/src/include/kompute/operations/OpMult.hpp b/src/include/kompute/operations/OpMult.hpp index 992b0e8a0..5c6dec9f0 100644 --- a/src/include/kompute/operations/OpMult.hpp +++ b/src/include/kompute/operations/OpMult.hpp @@ -26,11 +26,9 @@ class OpMult : public OpAlgoDispatch * requirements for the operations to be able to create and manage their * sub-components. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing + * @param algorithm An algorithm that will be overridden with the OpMult + * shader data and the tensors provided which are expected to be 3 */ OpMult(std::vector> tensors, std::shared_ptr algorithm) : OpAlgoDispatch(algorithm) diff --git a/src/include/kompute/operations/OpTensorCopy.hpp b/src/include/kompute/operations/OpTensorCopy.hpp index 3d202031f..892528996 100644 --- a/src/include/kompute/operations/OpTensorCopy.hpp +++ b/src/include/kompute/operations/OpTensorCopy.hpp @@ -9,38 +9,47 @@ namespace kp { /** - Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type + * Operation that copies the data from the first tensor to the rest of the tensors + * provided, using a record command for all the vectors. This operation does not + * own/manage the memory of the tensors passed to it. The operation must only + * receive tensors of type */ class OpTensorCopy : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorCopy(const std::vector>& tensors); /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + * Default destructor. This class does not manage memory so it won't be + * expecting the parent to perform a release. */ ~OpTensorCopy() override; /** - * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. + * Records the copy commands from the first tensor into all the other + * tensors provided. Also optionally records a barrier. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Copies the local vectors for all the tensors to sync the data with the gpu. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; diff --git a/src/include/kompute/operations/OpTensorSyncDevice.hpp b/src/include/kompute/operations/OpTensorSyncDevice.hpp index cbb8ec40e..216ac74c9 100644 --- a/src/include/kompute/operations/OpTensorSyncDevice.hpp +++ b/src/include/kompute/operations/OpTensorSyncDevice.hpp @@ -8,17 +8,20 @@ namespace kp { /** - Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. + * Operation that syncs tensor's device by mapping local data into the device memory. + * For TensorTypes::eDevice it will use a record operation for the memory to be syncd + * into GPU memory which means that the operation will be done in sync with GPU commands. + * For TensorTypes::eHost it will only map the data into host memory which will + * happen during preEval before the recorded commands are dispatched. */ class OpTensorSyncDevice : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. The tensos provided cannot + * be of type TensorTypes::eStorage. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorSyncDevice(const std::vector>& tensors); @@ -29,17 +32,24 @@ class OpTensorSyncDevice : public OpBase ~OpTensorSyncDevice() override; /** - * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. + * For device tensors, it records the copy command for the tensor to copy the + * data from its staging to device memory. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; diff --git a/src/include/kompute/operations/OpTensorSyncLocal.hpp b/src/include/kompute/operations/OpTensorSyncLocal.hpp index 276f38137..fc52acc35 100644 --- a/src/include/kompute/operations/OpTensorSyncLocal.hpp +++ b/src/include/kompute/operations/OpTensorSyncLocal.hpp @@ -9,38 +9,50 @@ namespace kp { /** - Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. + * Operation that syncs tensor's local memory by mapping device data into the + * local CPU memory. For TensorTypes::eDevice it will use a record operation + * for the memory to be syncd into GPU memory which means that the operation + * will be done in sync with GPU commands. For TensorTypes::eHost it will + * only map the data into host memory which will happen during preEval before + * the recorded commands are dispatched. */ class OpTensorSyncLocal : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. The tensors provided + * cannot be of type TensorTypes::eStorage. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorSyncLocal(const std::vector>& tensors); /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + * Default destructor. This class does not manage memory so it won't be expecting + * the parent to perform a release. */ ~OpTensorSyncLocal() override; /** - * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. + * For device tensors, it records the copy command for the tensor to copy the + * data from its device to staging memory. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * For host tensors it performs the map command from the host memory into local memory. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; From 0a856c3f82114a051022b1a85c9cdd04b3dba8dd Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 11:20:27 +0000 Subject: [PATCH 46/91] Regenerated python docstrings --- Makefile | 3 + python/src/docstrings.hpp | 650 +++++++++++++---------------- single_include/kompute/Kompute.hpp | 346 ++++++++------- 3 files changed, 481 insertions(+), 518 deletions(-) diff --git a/Makefile b/Makefile index 74a6822b0..872209015 100644 --- a/Makefile +++ b/Makefile @@ -163,6 +163,9 @@ generate_python_docstrings: python -m pybind11_mkdoc \ -o python/src/docstrings.hpp \ single_include/kompute/Kompute.hpp \ + -Iexternal/fmt/include/ \ + -Iexternal/spdlog/include/ \ + -Iexternal/glslang/ \ -I/usr/include/c++/7.5.0/ install_python_reqs: diff --git a/python/src/docstrings.hpp b/python/src/docstrings.hpp index 2000421c3..bf98e6581 100644 --- a/python/src/docstrings.hpp +++ b/python/src/docstrings.hpp @@ -28,17 +28,20 @@ R"doc(Abstraction for compute shaders that are run on top of tensors grouped via ParameterGroups (which group descriptorsets))doc"; static const char *__doc_kp_Algorithm_Algorithm = -R"doc(Base constructor for Algorithm. Should not be used unless explicit -intended.)doc"; - -static const char *__doc_kp_Algorithm_Algorithm_2 = -R"doc(Default constructor for Algorithm +R"doc(Main constructor for algorithm with configuration parameters to create +the underlying resources. @param device The Vulkan device to use for creating resources @param -commandBuffer The vulkan command buffer to bind the pipeline and -shaders)doc"; - -static const char *__doc_kp_Algorithm_createDescriptorPool = R"doc()doc"; +tensors (optional) The tensors to use to create the descriptor +resources @param spirv (optional) The spirv code to use to create the +algorithm @param workgroup (optional) The kp::Workgroup to use for the +dispatch which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if +not set. @param specializationConstants (optional) The kp::Constants +to use to initialize the specialization constants which cannot be +changed once set. @param pushConstants (optional) The kp::Constants to +use when initializing the pipeline, which set the size of the push +constants - these can be modified but all new values must have the +same vector size as this initial value.)doc"; static const char *__doc_kp_Algorithm_createParameters = R"doc()doc"; @@ -46,15 +49,35 @@ static const char *__doc_kp_Algorithm_createPipeline = R"doc()doc"; static const char *__doc_kp_Algorithm_createShaderModule = R"doc()doc"; -static const char *__doc_kp_Algorithm_init = -R"doc(Initialiser for the shader data provided to the algorithm as well as -tensor parameters that will be used in shader. +static const char *__doc_kp_Algorithm_destroy = R"doc()doc"; -@param shaderFileData The bytes in spir-v format of the shader -@tensorParams The Tensors to be used in the Algorithm / shader for -processing)doc"; +static const char *__doc_kp_Algorithm_getPush = +R"doc(Gets the specialization constants of the current algorithm. -static const char *__doc_kp_Algorithm_mCommandBuffer = R"doc()doc"; +@returns The kp::Constants currently set for push constants)doc"; + +static const char *__doc_kp_Algorithm_getSpecializationConstants = +R"doc(Gets the specialization constants of the current algorithm. + +@returns The kp::Constants currently set for specialization constants)doc"; + +static const char *__doc_kp_Algorithm_getTensors = +R"doc(Gets the current tensors that are used in the algorithm. + +@returns The list of tensors used in the algorithm.)doc"; + +static const char *__doc_kp_Algorithm_getWorkgroup = +R"doc(Gets the current workgroup from the algorithm. + +@param The kp::Constant to use to set the push constants to use in the +next bindPush(...) calls. The constants provided must be of the same +size as the ones created during initialization.)doc"; + +static const char *__doc_kp_Algorithm_isInit = +R"doc(function that checks all the gpu resource components to verify if +these have been created and returns true if all are valid. + +@returns returns true if the algorithm is currently initialized.)doc"; static const char *__doc_kp_Algorithm_mDescriptorPool = R"doc()doc"; @@ -84,14 +107,70 @@ static const char *__doc_kp_Algorithm_mPipelineCache = R"doc()doc"; static const char *__doc_kp_Algorithm_mPipelineLayout = R"doc()doc"; +static const char *__doc_kp_Algorithm_mPushConstants = R"doc()doc"; + static const char *__doc_kp_Algorithm_mShaderModule = R"doc()doc"; +static const char *__doc_kp_Algorithm_mSpecializationConstants = R"doc()doc"; + +static const char *__doc_kp_Algorithm_mSpirv = R"doc()doc"; + +static const char *__doc_kp_Algorithm_mTensors = R"doc()doc"; + +static const char *__doc_kp_Algorithm_mWorkgroup = R"doc()doc"; + +static const char *__doc_kp_Algorithm_rebuild = +R"doc(Rebuild function to reconstruct algorithm with configuration +parameters to create the underlying resources. + +@param tensors The tensors to use to create the descriptor resources +@param spirv The spirv code to use to create the algorithm @param +workgroup (optional) The kp::Workgroup to use for the dispatch which +defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. @param +specializationConstants (optional) The kp::Constants to use to +initialize the specialization constants which cannot be changed once +set. @param pushConstants (optional) The kp::Constants to use when +initializing the pipeline, which set the size of the push constants - +these can be modified but all new values must have the same vector +size as this initial value.)doc"; + +static const char *__doc_kp_Algorithm_recordBindCore = +R"doc(Records command that binds the "core" algorithm components which +consist of binding the pipeline and binding the descriptorsets. + +@param commandBuffer Command buffer to record the algorithm resources +to)doc"; + +static const char *__doc_kp_Algorithm_recordBindPush = +R"doc(Records command that binds the push constants to the command buffer +provided - it is required that the pushConstants provided are of the +same size as the ones provided during initialization. + +@param commandBuffer Command buffer to record the algorithm resources +to)doc"; + static const char *__doc_kp_Algorithm_recordDispatch = R"doc(Records the dispatch function with the provided template parameters or alternatively using the size of the tensor by default. -@param x Layout X dispatch value @param y Layout Y dispatch value -@param z Layout Z dispatch value)doc"; +@param commandBuffer Command buffer to record the algorithm resources +to)doc"; + +static const char *__doc_kp_Algorithm_setPush = +R"doc(Sets the push constants to the new value provided to use in the next +bindPush() + +@param The kp::Constant to use to set the push constants to use in the +next bindPush(...) calls. The constants provided must be of the same +size as the ones created during initialization.)doc"; + +static const char *__doc_kp_Algorithm_setWorkgroup = +R"doc(Sets the work group to use in the recordDispatch + +@param workgroup The kp::Workgroup value to use to update the +algorithm. It must have a value greater than 1 on the x value (index +1) otherwise it will be initialized on the size of the first tensor +(ie. this->mTensor[0]->size()))doc"; static const char *__doc_kp_Manager = R"doc(Base orchestrator which creates and manages device and child @@ -102,13 +181,13 @@ R"doc(Base constructor and default used which creates the base resources including choosing the device 0 by default.)doc"; static const char *__doc_kp_Manager_Manager_2 = -R"doc(Similar to base constructor but allows the user to provide the device -they would like to create the resources on. +R"doc(Similar to base constructor but allows for further configuration to +use when creating the Vulkan resources. @param physicalDeviceIndex The index of the physical device to use @param familyQueueIndices (Optional) List of queue indices to add for -explicit allocation @param totalQueues The total number of compute -queues to create.)doc"; +explicit allocation @param desiredExtensions The desired extensions to +load from physicalDevice)doc"; static const char *__doc_kp_Manager_Manager_3 = R"doc(Manager constructor which allows your own vulkan application to @@ -119,99 +198,33 @@ integrate with the vulkan kompute use. @param device Vulkan logical device to use for all base resources @param physicalDeviceIndex Index for vulkan physical device used)doc"; +static const char *__doc_kp_Manager_algorithm = +R"doc(Create a managed algorithm that will be destroyed by this manager if +it hasn't been destroyed by its reference count going to zero. + +@param tensors (optional) The tensors to initialise the algorithm with +@param spirv (optional) The SPIRV bytes for the algorithm to dispatch +@param workgroup (optional) kp::Workgroup for algorithm to use, and +defaults to (tensor[0].size(), 1, 1) @param specializationConstants +(optional) kp::Constant to use for specialization constants, and +defaults to an empty constant @param pushConstants (optional) +kp::Constant to use for push constants, and defaults to an empty +constant @returns Shared pointer with initialised algorithm)doc"; + +static const char *__doc_kp_Manager_clear = +R"doc(Run a pseudo-garbage collection to release all the managed resources +that have been already freed due to these reaching to zero ref count.)doc"; + static const char *__doc_kp_Manager_createDevice = R"doc()doc"; static const char *__doc_kp_Manager_createInstance = R"doc()doc"; -static const char *__doc_kp_Manager_destroy = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for single -tensor. - -@param tensors Single tensor to rebuild)doc"; - -static const char *__doc_kp_Manager_destroy_2 = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for vector of -tensors. - -@param tensors Single tensor to rebuild)doc"; - -static const char *__doc_kp_Manager_destroy_3 = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for vector of -sequences. Destroying by sequence name is more efficent and hence -recommended instead of by object. - -@param sequences Vector for shared ptrs with sequences to destroy)doc"; - -static const char *__doc_kp_Manager_destroy_4 = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for single -sequence. Destroying by sequence name is more efficent and hence -recommended instead of by object. - -@param sequences Single sequence to rebuild)doc"; - -static const char *__doc_kp_Manager_destroy_5 = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for sequence by -name. - -@param sequenceName Single name of named sequence to destroy)doc"; - -static const char *__doc_kp_Manager_destroy_6 = -R"doc(Destroy owned Vulkan GPU resources and free GPU memory for sequences -using vector of named sequence names. - -@param sequenceName Vector of sequence names to destroy)doc"; - -static const char *__doc_kp_Manager_evalOp = -R"doc(Function that evaluates operation against named sequence. - -@param tensors The tensors to be used in the operation recorded @param -sequenceName The name of the sequence to be retrieved or created -@param TArgs Template parameters that will be used to initialise -Operation to allow for extensible configurations on initialisation)doc"; - -static const char *__doc_kp_Manager_evalOpAsync = -R"doc(Function that evaluates operation against named sequence -asynchronously. - -@param tensors The tensors to be used in the operation recorded @param -sequenceName The name of the sequence to be retrieved or created -@param params Template parameters that will be used to initialise -Operation to allow for extensible configurations on initialisation)doc"; - -static const char *__doc_kp_Manager_evalOpAsyncDefault = -R"doc(Operation that evaluates operation against default sequence -asynchronously. - -@param tensors The tensors to be used in the operation recorded @param -params Template parameters that will be used to initialise Operation -to allow for extensible configurations on initialisation)doc"; - -static const char *__doc_kp_Manager_evalOpAwait = -R"doc(Operation that awaits for named sequence to finish. - -@param sequenceName The name of the sequence to wait for termination -@param waitFor The amount of time to wait before timing out)doc"; - -static const char *__doc_kp_Manager_evalOpAwaitDefault = -R"doc(Operation that awaits for default sequence to finish. - -@param tensors The tensors to be used in the operation recorded @param -params Template parameters that will be used to initialise Operation -to allow for extensible configurations on initialisation)doc"; - -static const char *__doc_kp_Manager_evalOpDefault = -R"doc(Function that evaluates operation against a newly created sequence. - -@param tensors The tensors to be used in the operation recorded @param -TArgs Template parameters that will be used to initialise Operation to -allow for extensible configurations on initialisation)doc"; +static const char *__doc_kp_Manager_destroy = R"doc(Destroy the GPU resources and all managed resources by manager.)doc"; static const char *__doc_kp_Manager_mComputeQueueFamilyIndices = R"doc()doc"; static const char *__doc_kp_Manager_mComputeQueues = R"doc()doc"; -static const char *__doc_kp_Manager_mCurrentSequenceIndex = R"doc()doc"; - static const char *__doc_kp_Manager_mDevice = R"doc()doc"; static const char *__doc_kp_Manager_mFreeDevice = R"doc()doc"; @@ -220,190 +233,51 @@ static const char *__doc_kp_Manager_mFreeInstance = R"doc()doc"; static const char *__doc_kp_Manager_mInstance = R"doc()doc"; +static const char *__doc_kp_Manager_mManageResources = R"doc()doc"; + +static const char *__doc_kp_Manager_mManagedAlgorithms = R"doc()doc"; + static const char *__doc_kp_Manager_mManagedSequences = R"doc()doc"; static const char *__doc_kp_Manager_mManagedTensors = R"doc()doc"; static const char *__doc_kp_Manager_mPhysicalDevice = R"doc()doc"; -static const char *__doc_kp_Manager_mPhysicalDeviceIndex = R"doc()doc"; - -static const char *__doc_kp_Manager_rebuild = -R"doc(Function that simplifies the common workflow of tensor initialisation. -It will take the constructor parameters for a Tensor and will will us -it to create a new Tensor. The tensor memory will then be managed and -owned by the manager. - -@param tensors Array of tensors to rebuild @param syncDataToGPU -Whether to sync the data to GPU memory)doc"; - -static const char *__doc_kp_Manager_rebuild_2 = -R"doc(Function that simplifies the common workflow of tensor initialisation. -It will take the constructor parameters for a Tensor and will will us -it to create a new Tensor. The tensor memory will then be managed and -owned by the manager. - -@param tensors Single tensor to rebuild @param syncDataToGPU Whether -to sync the data to GPU memory)doc"; - static const char *__doc_kp_Manager_sequence = -R"doc(Get or create a managed Sequence that will be contained by this -manager. If the named sequence does not currently exist, it would be -created and initialised. +R"doc(Create a managed sequence that will be destroyed by this manager if it +hasn't been destroyed by its reference count going to zero. -@param sequenceName The name for the named sequence to be retrieved or -created @param queueIndex The queue to use from the available queues -@return Shared pointer to the manager owned sequence resource)doc"; +@param queueIndex The queue to use from the available queues @returns +Shared pointer with initialised sequence)doc"; static const char *__doc_kp_Manager_tensor = -R"doc(Function that simplifies the common workflow of tensor creation and -initialization. It will take the constructor parameters for a Tensor -and will will us it to create a new Tensor and then create it. The -tensor memory will then be managed and owned by the manager. +R"doc(Create a managed tensor that will be destroyed by this manager if it +hasn't been destroyed by its reference count going to zero. @param data The data to initialize the tensor with @param tensorType -The type of tensor to initialize @param syncDataToGPU Whether to sync -the data to GPU memory @returns Initialized Tensor with memory Syncd -to GPU device)doc"; +The type of tensor to initialize @returns Shared pointer with +initialised tensor)doc"; -static const char *__doc_kp_OpAlgoCreate = +static const char *__doc_kp_OpAlgoDispatch = R"doc(Operation that provides a general abstraction that simplifies the use of algorithm and parameter components which can be used with shaders. By default it enables the user to provide a dynamic number of tensors which are then passed as inputs.)doc"; -static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup = R"doc()doc"; +static const char *__doc_kp_OpAlgoDispatch_OpAlgoDispatch = R"doc()doc"; -static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_x = R"doc()doc"; +static const char *__doc_kp_OpAlgoDispatch_mAlgorithm = R"doc()doc"; -static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_y = R"doc()doc"; +static const char *__doc_kp_OpAlgoDispatch_mPushConstants = R"doc()doc"; -static const char *__doc_kp_OpAlgoCreate_KomputeWorkgroup_z = R"doc()doc"; - -static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; - -static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_2 = -R"doc(Default constructor with parameters that provides the bare minimum -requirements for the operations to be able to create and manage their -sub-components. - -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into -@param tensors Tensors that are to be used in this operation @param -shaderFilePath Optional parameter to specify the shader to load -(either in spirv or raw format) @param komputeWorkgroup Optional -parameter to specify the layout for processing)doc"; - -static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_3 = -R"doc(Constructor that enables a file to be passed to the operation with the -contents of the shader. This can be either in raw format or in -compiled SPIR-V binary format. - -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into -@param tensors Tensors that are to be used in this operation @param -shaderFilePath Parameter to specify the shader to load (either in -spirv or raw format) @param komputeWorkgroup Optional parameter to -specify the layout for processing)doc"; - -static const char *__doc_kp_OpAlgoCreate_OpAlgoCreate_4 = -R"doc(Constructor that enables raw shader data to be passed to the main -operation which can be either in raw shader glsl code or in compiled -SPIR-V binary. - -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into -@param tensors Tensors that are to be used in this operation @param -shaderDataRaw Optional parameter to specify the shader data either in -binary or raw form @param komputeWorkgroup Optional parameter to -specify the layout for processing)doc"; - -static const char *__doc_kp_OpAlgoCreate_fetchSpirvBinaryData = R"doc()doc"; - -static const char *__doc_kp_OpAlgoCreate_init = -R"doc(The init function is responsible for the initialisation of the -algorithm component based on the parameters specified, and allows for -extensibility on the options provided. Further dependent classes can -perform more specific checks such as ensuring tensors provided are -initialised, etc.)doc"; - -static const char *__doc_kp_OpAlgoCreate_mAlgorithm = R"doc()doc"; - -static const char *__doc_kp_OpAlgoCreate_mFreeAlgorithm = R"doc()doc"; - -static const char *__doc_kp_OpAlgoCreate_mKomputeWorkgroup = R"doc()doc"; - -static const char *__doc_kp_OpAlgoCreate_mShaderDataRaw = -R"doc(< Optional member variable which can be provided to contain either the -raw shader content or the spirv binary content)doc"; - -static const char *__doc_kp_OpAlgoCreate_mShaderFilePath = -R"doc(< Optional member variable which can be provided for the OpAlgoCreate to -find the data automatically and load for processing)doc"; - -static const char *__doc_kp_OpAlgoCreate_postEval = +static const char *__doc_kp_OpAlgoDispatch_postEval = R"doc(Executes after the recorded commands are submitted, and performs a copy of the GPU Device memory into the staging buffer so the output data can be retrieved.)doc"; -static const char *__doc_kp_OpAlgoCreate_preEval = R"doc(Does not perform any preEval commands.)doc"; +static const char *__doc_kp_OpAlgoDispatch_preEval = R"doc(Does not perform any preEval commands.)doc"; -static const char *__doc_kp_OpAlgoCreate_record = -R"doc(This records the commands that are to be sent to the GPU. This -includes the barriers that ensure the memory has been copied before -going in and out of the shader, as well as the dispatch operation that -sends the shader processing to the gpu. This function also records the -GPU memory copy of the output data for the staging buffer so it can be -read by the host.)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut = -R"doc(Operation base class to simplify the creation of operations that -require right hand and left hand side datapoints together with a -single output. The expected data passed is two input tensors and one -output tensor.)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_OpAlgoLhsRhsOut = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_OpAlgoLhsRhsOut_2 = -R"doc(Default constructor with parameters that provides the bare minimum -requirements for the operations to be able to create and manage their -sub-components. - -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into -@param tensors Tensors that are to be used in this operation @param -freeTensors Whether operation manages the memory of the Tensors @param -komputeWorkgroup Optional parameter to specify the layout for -processing)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_init = -R"doc(The init function is responsible for ensuring that all of the tensors -provided are aligned with requirements such as LHS, RHS and Output -tensors, and creates the algorithm component which processes the -computation.)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_mTensorLHS = -R"doc(< Reference to the parameter used in the left hand side equation of -the shader)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_mTensorOutput = -R"doc(< Reference to the parameter used in the output of the shader and will -be copied with a staging vector)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_mTensorRHS = -R"doc(< Reference to the parameter used in the right hand side equation of -the shader)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_postEval = -R"doc(Executes after the recorded commands are submitted, and performs a -copy of the GPU Device memory into the staging buffer so the output -data can be retrieved.)doc"; - -static const char *__doc_kp_OpAlgoLhsRhsOut_record = +static const char *__doc_kp_OpAlgoDispatch_record = R"doc(This records the commands that are to be sent to the GPU. This includes the barriers that ensure the memory has been copied before going in and out of the shader, as well as the dispatch operation that @@ -419,36 +293,6 @@ Operations can perform actions on tensors, and optionally can also own an Algorithm with respective parameters. kp::Operations with kp::Algorithms would inherit from kp::OpBaseAlgo.)doc"; -static const char *__doc_kp_OpBase_OpBase = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; - -static const char *__doc_kp_OpBase_OpBase_2 = -R"doc(Default constructor with parameters that provides the bare minimum -requirements for the operations to be able to create and manage their -sub-components. - -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into -@param tensors Tensors that are to be used in this operation)doc"; - -static const char *__doc_kp_OpBase_init = -R"doc(The init function is responsible for setting up all the resources and -should be called after the Operation has been created.)doc"; - -static const char *__doc_kp_OpBase_mCommandBuffer = R"doc(< Vulkan Command Buffer)doc"; - -static const char *__doc_kp_OpBase_mDevice = R"doc(< Vulkan Logical Device)doc"; - -static const char *__doc_kp_OpBase_mFreeTensors = -R"doc(< Explicit boolean that specifies whether the < tensors are freed (if -they are managed))doc"; - -static const char *__doc_kp_OpBase_mPhysicalDevice = R"doc(< Vulkan Physical Device)doc"; - -static const char *__doc_kp_OpBase_mTensors = -R"doc(< Tensors referenced by operation that can be managed < optionally by -operation)doc"; - static const char *__doc_kp_OpBase_postEval = R"doc(Post eval is called after the Sequence has called eval and submitted the commands to the GPU for processing, and can be used to perform any @@ -474,9 +318,7 @@ static const char *__doc_kp_OpMult = R"doc(Operation that performs multiplication on two tensors and outpus on third tensor.)doc"; -static const char *__doc_kp_OpMult_OpMult = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; - -static const char *__doc_kp_OpMult_OpMult_2 = +static const char *__doc_kp_OpMult_OpMult = R"doc(Default constructor with parameters that provides the bare minimum requirements for the operations to be able to create and manage their sub-components. @@ -494,9 +336,7 @@ the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type)doc"; -static const char *__doc_kp_OpTensorCopy_OpTensorCopy = R"doc()doc"; - -static const char *__doc_kp_OpTensorCopy_OpTensorCopy_2 = +static const char *__doc_kp_OpTensorCopy_OpTensorCopy = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. @@ -505,10 +345,7 @@ queues @param device Vulkan logical device for passing to Algorithm @param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; -static const char *__doc_kp_OpTensorCopy_init = -R"doc(Performs basic checks such as ensuring there are at least two tensors -provided, that they are initialised and that they are not of type -TensorTypes::eStorage.)doc"; +static const char *__doc_kp_OpTensorCopy_mTensors = R"doc()doc"; static const char *__doc_kp_OpTensorCopy_postEval = R"doc(Copies the local vectors for all the tensors to sync the data with the @@ -530,9 +367,7 @@ will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging.)doc"; -static const char *__doc_kp_OpTensorSyncDevice_OpTensorSyncDevice = R"doc()doc"; - -static const char *__doc_kp_OpTensorSyncDevice_OpTensorSyncDevice_2 = +static const char *__doc_kp_OpTensorSyncDevice_OpTensorSyncDevice = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. @@ -542,9 +377,7 @@ queues @param device Vulkan logical device for passing to Algorithm @param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; -static const char *__doc_kp_OpTensorSyncDevice_init = -R"doc(Performs basic checks such as ensuring that there is at least one -tensor provided with min memory of 1 element.)doc"; +static const char *__doc_kp_OpTensorSyncDevice_mTensors = R"doc()doc"; static const char *__doc_kp_OpTensorSyncDevice_postEval = R"doc(Does not perform any postEval commands.)doc"; @@ -564,9 +397,7 @@ will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging.)doc"; -static const char *__doc_kp_OpTensorSyncLocal_OpTensorSyncLocal = R"doc()doc"; - -static const char *__doc_kp_OpTensorSyncLocal_OpTensorSyncLocal_2 = +static const char *__doc_kp_OpTensorSyncLocal_OpTensorSyncLocal = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. @@ -576,9 +407,7 @@ queues @param device Vulkan logical device for passing to Algorithm @param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; -static const char *__doc_kp_OpTensorSyncLocal_init = -R"doc(Performs basic checks such as ensuring that there is at least one -tensor provided with min memory of 1 element.)doc"; +static const char *__doc_kp_OpTensorSyncLocal_mTensors = R"doc()doc"; static const char *__doc_kp_OpTensorSyncLocal_postEval = R"doc(For host tensors it performs the map command from the host memory into @@ -593,10 +422,6 @@ the data from its device to staging memory.)doc"; static const char *__doc_kp_Sequence = R"doc(Container of operations that can be sent to GPU as batch)doc"; static const char *__doc_kp_Sequence_Sequence = -R"doc(Base constructor for Sequence. Should not be used unless explicit -intended.)doc"; - -static const char *__doc_kp_Sequence_Sequence_2 = R"doc(Main constructor for sequence which requires core vulkan components to generate all dependent resources. @@ -610,10 +435,18 @@ command buffer. @return Boolean stating whether execution was successful.)doc"; +static const char *__doc_kp_Sequence_clear = +R"doc(Clear function clears all operations currently recorded and starts +recording again.)doc"; + static const char *__doc_kp_Sequence_createCommandBuffer = R"doc()doc"; static const char *__doc_kp_Sequence_createCommandPool = R"doc()doc"; +static const char *__doc_kp_Sequence_destroy = +R"doc(Destroys and frees the GPU resources which include the buffer and +memory and sets the sequence as init=False.)doc"; + static const char *__doc_kp_Sequence_end = R"doc(Ends the recording and stops recording commands when the record command is sent. @@ -622,36 +455,84 @@ command is sent. static const char *__doc_kp_Sequence_eval = R"doc(Eval sends all the recorded and stored operations in the vector of +operations into the gpu as a submit job synchronously (with a +barrier). + +@return shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_eval_2 = +R"doc(Resets all the recorded and stored operations, records the operation +provided and submits into the gpu as a submit job synchronously (with +a barrier). + +@return shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_eval_3 = +R"doc(Eval sends all the recorded and stored operations in the vector of operations into the gpu as a submit job with a barrier. -@return Boolean stating whether execution was successful.)doc"; +@param tensors Vector of tensors to use for the operation @param TArgs +Template parameters that are used to initialise operation which allows +for extensible configurations on initialisation. @return +shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_eval_4 = +R"doc(Eval sends all the recorded and stored operations in the vector of +operations into the gpu as a submit job with a barrier. + +@param algorithm Algorithm to use for the record often used for OpAlgo +operations @param TArgs Template parameters that are used to +initialise operation which allows for extensible configurations on +initialisation. @return shared_ptr of the Sequence class +itself)doc"; static const char *__doc_kp_Sequence_evalAsync = R"doc(Eval Async sends all the recorded and stored operations in the vector -of operations into the gpu as a submit job with a barrier. EvalAwait() -must be called after to ensure the sequence is terminated correctly. +of operations into the gpu as a submit job without a barrier. +EvalAwait() must ALWAYS be called after to ensure the sequence is +terminated correctly. @return Boolean stating whether execution was successful.)doc"; +static const char *__doc_kp_Sequence_evalAsync_2 = +R"doc(Clears currnet operations to record provided one in the vector of +operations into the gpu as a submit job without a barrier. EvalAwait() +must ALWAYS be called after to ensure the sequence is terminated +correctly. + +@return Boolean stating whether execution was successful.)doc"; + +static const char *__doc_kp_Sequence_evalAsync_3 = +R"doc(Eval sends all the recorded and stored operations in the vector of +operations into the gpu as a submit job with a barrier. + +@param tensors Vector of tensors to use for the operation @param TArgs +Template parameters that are used to initialise operation which allows +for extensible configurations on initialisation. @return +shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_evalAsync_4 = +R"doc(Eval sends all the recorded and stored operations in the vector of +operations into the gpu as a submit job with a barrier. + +@param algorithm Algorithm to use for the record often used for OpAlgo +operations @param TArgs Template parameters that are used to +initialise operation which allows for extensible configurations on +initialisation. @return shared_ptr of the Sequence class +itself)doc"; + static const char *__doc_kp_Sequence_evalAwait = R"doc(Eval Await waits for the fence to finish processing and then once it finishes, it runs the postEval of all operations. @param waitFor Number of milliseconds to wait before timing out. -@return Boolean stating whether execution was successful.)doc"; - -static const char *__doc_kp_Sequence_freeMemoryDestroyGPUResources = -R"doc(Destroys and frees the GPU resources which include the buffer and -memory and sets the sequence as init=False.)doc"; - -static const char *__doc_kp_Sequence_init = -R"doc(Initialises sequence including the creation of the command pool and -the command buffer.)doc"; +@return shared_ptr of the Sequence class itself)doc"; static const char *__doc_kp_Sequence_isInit = -R"doc(Returns true if the sequence has been successfully initialised. +R"doc(Returns true if the sequence has been initialised, and it's based on +the GPU resources being refrenced. -@return Boolean stating if sequence has been initialised.)doc"; +@return Boolean stating if is initialized)doc"; static const char *__doc_kp_Sequence_isRecording = R"doc(Returns true if the sequence is currently in recording activated. @@ -678,8 +559,6 @@ static const char *__doc_kp_Sequence_mFreeCommandBuffer = R"doc()doc"; static const char *__doc_kp_Sequence_mFreeCommandPool = R"doc()doc"; -static const char *__doc_kp_Sequence_mIsInit = R"doc()doc"; - static const char *__doc_kp_Sequence_mIsRunning = R"doc()doc"; static const char *__doc_kp_Sequence_mOperations = R"doc()doc"; @@ -696,9 +575,66 @@ This template requires classes to be derived from the OpBase class. This function also requires the Sequence to be recording, otherwise it will not be able to add the operation. +@param op Object derived from kp::BaseOp that will be recoreded by the +sequence which will be used when the operation is evaluated. @return +shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_record_2 = +R"doc(Record function for operation to be added to the GPU queue in batch. +This template requires classes to be derived from the OpBase class. +This function also requires the Sequence to be recording, otherwise it +will not be able to add the operation. + @param tensors Vector of tensors to use for the operation @param TArgs Template parameters that are used to initialise operation which allows -for extensible configurations on initialisation.)doc"; +for extensible configurations on initialisation. @return +shared_ptr of the Sequence class itself)doc"; + +static const char *__doc_kp_Sequence_record_3 = +R"doc(Record function for operation to be added to the GPU queue in batch. +This template requires classes to be derived from the OpBase class. +This function also requires the Sequence to be recording, otherwise it +will not be able to add the operation. + +@param algorithm Algorithm to use for the record often used for OpAlgo +operations @param TArgs Template parameters that are used to +initialise operation which allows for extensible configurations on +initialisation. @return shared_ptr of the Sequence class +itself)doc"; + +static const char *__doc_kp_Sequence_rerecord = +R"doc(Clears command buffer and triggers re-record of all the current +operations saved, which is useful if the underlying kp::Tensors or +kp::Algorithms are modified and need to be re-recorded.)doc"; + +static const char *__doc_kp_Shader = R"doc(Shader utily class with functions to compile and process glsl files.)doc"; + +static const char *__doc_kp_Shader_compile_source = +R"doc(Compile a single glslang source from string value. Currently this +function uses the glslang C++ interface which is not thread safe so +this funciton should not be called from multiple threads concurrently. +If you have a online shader processing multithreading use-case that +can't use offline compilation please open an issue. + +@param source An individual raw glsl shader in string format @param +entryPoint The function name to use as entry point @param definitions +List of pairs containing key value definitions @param resourcesLimit A +list that contains the resource limits for the GLSL compiler @return +The compiled SPIR-V binary in unsigned int32 format)doc"; + +static const char *__doc_kp_Shader_compile_sources = +R"doc(Compile multiple sources with optional filenames. Currently this +function uses the glslang C++ interface which is not thread safe so +this funciton should not be called from multiple threads concurrently. +If you have a online shader processing multithreading use-case that +can't use offline compilation please open an issue. + +@param sources A list of raw glsl shaders in string format @param +files A list of file names respective to each of the sources @param +entryPoint The function name to use as entry point @param definitions +List of pairs containing key value definitions @param resourcesLimit A +list that contains the resource limits for the GLSL compiler @return +The compiled SPIR-V binary in unsigned int32 format)doc"; static const char *__doc_kp_Tensor = R"doc(Structured data used in GPU operations. @@ -708,9 +644,7 @@ across GPUs. Each tensor would have a respective Vulkan memory and buffer, which would be used to store their respective data. The tensors can be used for GPU data storage or transfer.)doc"; -static const char *__doc_kp_Tensor_Tensor = R"doc(Base constructor, should not be used unless explicitly intended.)doc"; - -static const char *__doc_kp_Tensor_Tensor_2 = +static const char *__doc_kp_Tensor_Tensor = R"doc(Default constructor with data provided which would be used to create the respective vulkan buffer and memory. @@ -741,8 +675,6 @@ without exposing it. @return Descriptor buffer info with own buffer)doc"; -static const char *__doc_kp_Tensor_copyBuffer = R"doc()doc"; - static const char *__doc_kp_Tensor_createBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_data = @@ -753,7 +685,7 @@ memory. @return Reference to vector of elements representing the data in the tensor.)doc"; -static const char *__doc_kp_Tensor_freeMemoryDestroyGPUResources = +static const char *__doc_kp_Tensor_destroy = R"doc(Destroys and frees the GPU resources which include the buffer and memory.)doc"; @@ -765,17 +697,7 @@ static const char *__doc_kp_Tensor_getStagingBufferUsageFlags = R"doc()doc"; static const char *__doc_kp_Tensor_getStagingMemoryPropertyFlags = R"doc()doc"; -static const char *__doc_kp_Tensor_init = -R"doc(Initialiser which calls the initialisation for all the respective -tensors as well as creates the respective staging tensors. The staging -tensors would only be created for the tensors of type -TensorType::eDevice as otherwise there is no need to copy from host -memory.)doc"; - -static const char *__doc_kp_Tensor_isInit = -R"doc(Returns true if the tensor initialisation function has been carried -out successful, which would mean that the buffer and memory will have -been provisioned.)doc"; +static const char *__doc_kp_Tensor_isInit = R"doc()doc"; static const char *__doc_kp_Tensor_mData = R"doc()doc"; @@ -789,16 +711,12 @@ static const char *__doc_kp_Tensor_mFreeStagingBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_mFreeStagingMemory = R"doc()doc"; -static const char *__doc_kp_Tensor_mIsInit = R"doc()doc"; - static const char *__doc_kp_Tensor_mPhysicalDevice = R"doc()doc"; static const char *__doc_kp_Tensor_mPrimaryBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_mPrimaryMemory = R"doc()doc"; -static const char *__doc_kp_Tensor_mShape = R"doc()doc"; - static const char *__doc_kp_Tensor_mStagingBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_mStagingMemory = R"doc()doc"; @@ -823,6 +741,13 @@ vector's. @param i The index where the element will be returned from. @return Returns the element in the position requested.)doc"; +static const char *__doc_kp_Tensor_rebuild = +R"doc(Initialiser which calls the initialisation for all the respective +tensors as well as creates the respective staging tensors. The staging +tensors would only be created for the tensors of type +TensorType::eDevice as otherwise there is no need to copy from host +memory.)doc"; + static const char *__doc_kp_Tensor_recordBufferMemoryBarrier = R"doc(Records the buffer memory barrier into the command buffer which ensures that relevant data transfers are carried out correctly. @@ -833,6 +758,8 @@ dstAccessMask Access flags for destination access mask @param scrStageMask Pipeline stage flags for source stage mask @param dstStageMask Pipeline stage flags for destination stage mask)doc"; +static const char *__doc_kp_Tensor_recordCopyBuffer = R"doc()doc"; + static const char *__doc_kp_Tensor_recordCopyFrom = R"doc(Records a copy from the memory of the tensor provided to the current thensor. This is intended to pass memory into a processing, to perform @@ -865,13 +792,6 @@ static const char *__doc_kp_Tensor_setData = R"doc(Sets / resets the vector data of the tensor. This function does not perform any copies into GPU memory and is only performed on the host.)doc"; -static const char *__doc_kp_Tensor_shape = -R"doc(Returns the shape of the tensor, which includes the number of -dimensions and the size per dimension. - -@return Array containing the sizes for each dimension. Zero means -respective dimension is not active.)doc"; - static const char *__doc_kp_Tensor_size = R"doc(Returns the size/magnitude of the Tensor, which will be the total number of elements across all dimensions diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 346764888..607928f0c 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -735,124 +735,18 @@ extern py::object kp_debug, kp_info, kp_warning, kp_error; namespace kp { -// The default resource limit for the GLSL compiler, can be overwritten -// Has been adobted by: -// https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp -const TBuiltInResource defaultResource = { - /* .MaxLights = */ 0, - /* .MaxClipPlanes = */ 0, - /* .MaxTextureUnits = */ 0, - /* .MaxTextureCoords = */ 0, - /* .MaxVertexAttribs = */ 64, - /* .MaxVertexUniformComponents = */ 4096, - /* .MaxVaryingFloats = */ 64, - /* .MaxVertexTextureImageUnits = */ 0, - /* .MaxCombinedTextureImageUnits = */ 0, - /* .MaxTextureImageUnits = */ 0, - /* .MaxFragmentUniformComponents = */ 0, - /* .MaxDrawBuffers = */ 0, - /* .MaxVertexUniformVectors = */ 128, - /* .MaxVaryingVectors = */ 8, - /* .MaxFragmentUniformVectors = */ 0, - /* .MaxVertexOutputVectors = */ 16, - /* .MaxFragmentInputVectors = */ 0, - /* .MinProgramTexelOffset = */ -8, - /* .MaxProgramTexelOffset = */ 7, - /* .MaxClipDistances = */ 8, - /* .MaxComputeWorkGroupCountX = */ 65535, - /* .MaxComputeWorkGroupCountY = */ 65535, - /* .MaxComputeWorkGroupCountZ = */ 65535, - /* .MaxComputeWorkGroupSizeX = */ 1024, - /* .MaxComputeWorkGroupSizeY = */ 1024, - /* .MaxComputeWorkGroupSizeZ = */ 64, - /* .MaxComputeUniformComponents = */ 1024, - /* .MaxComputeTextureImageUnits = */ 16, - /* .MaxComputeImageUniforms = */ 8, - /* .MaxComputeAtomicCounters = */ 8, - /* .MaxComputeAtomicCounterBuffers = */ 1, - /* .MaxVaryingComponents = */ 60, - /* .MaxVertexOutputComponents = */ 64, - /* .MaxGeometryInputComponents = */ 64, - /* .MaxGeometryOutputComponents = */ 128, - /* .MaxFragmentInputComponents = */ 0, - /* .MaxImageUnits = */ 0, - /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 0, - /* .MaxCombinedShaderOutputResources = */ 8, - /* .MaxImageSamples = */ 0, - /* .MaxVertexImageUniforms = */ 0, - /* .MaxTessControlImageUniforms = */ 0, - /* .MaxTessEvaluationImageUniforms = */ 0, - /* .MaxGeometryImageUniforms = */ 0, - /* .MaxFragmentImageUniforms = */ 0, - /* .MaxCombinedImageUniforms = */ 0, - /* .MaxGeometryTextureImageUnits = */ 0, - /* .MaxGeometryOutputVertices = */ 256, - /* .MaxGeometryTotalOutputComponents = */ 1024, - /* .MaxGeometryUniformComponents = */ 1024, - /* .MaxGeometryVaryingComponents = */ 64, - /* .MaxTessControlInputComponents = */ 128, - /* .MaxTessControlOutputComponents = */ 128, - /* .MaxTessControlTextureImageUnits = */ 0, - /* .MaxTessControlUniformComponents = */ 1024, - /* .MaxTessControlTotalOutputComponents = */ 4096, - /* .MaxTessEvaluationInputComponents = */ 128, - /* .MaxTessEvaluationOutputComponents = */ 128, - /* .MaxTessEvaluationTextureImageUnits = */ 16, - /* .MaxTessEvaluationUniformComponents = */ 1024, - /* .MaxTessPatchComponents = */ 120, - /* .MaxPatchVertices = */ 32, - /* .MaxTessGenLevel = */ 64, - /* .MaxViewports = */ 16, - /* .MaxVertexAtomicCounters = */ 0, - /* .MaxTessControlAtomicCounters = */ 0, - /* .MaxTessEvaluationAtomicCounters = */ 0, - /* .MaxGeometryAtomicCounters = */ 0, - /* .MaxFragmentAtomicCounters = */ 0, - /* .MaxCombinedAtomicCounters = */ 8, - /* .MaxAtomicCounterBindings = */ 1, - /* .MaxVertexAtomicCounterBuffers = */ 0, - /* .MaxTessControlAtomicCounterBuffers = */ 0, - /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, - /* .MaxGeometryAtomicCounterBuffers = */ 0, - /* .MaxFragmentAtomicCounterBuffers = */ 0, - /* .MaxCombinedAtomicCounterBuffers = */ 1, - /* .MaxAtomicCounterBufferSize = */ 16384, - /* .MaxTransformFeedbackBuffers = */ 4, - /* .MaxTransformFeedbackInterleavedComponents = */ 64, - /* .MaxCullDistances = */ 8, - /* .MaxCombinedClipAndCullDistances = */ 8, - /* .MaxSamples = */ 4, - /* .maxMeshOutputVerticesNV = */ 256, - /* .maxMeshOutputPrimitivesNV = */ 512, - /* .maxMeshWorkGroupSizeX_NV = */ 32, - /* .maxMeshWorkGroupSizeY_NV = */ 1, - /* .maxMeshWorkGroupSizeZ_NV = */ 1, - /* .maxTaskWorkGroupSizeX_NV = */ 32, - /* .maxTaskWorkGroupSizeY_NV = */ 1, - /* .maxTaskWorkGroupSizeZ_NV = */ 1, - /* .maxMeshViewCountNV = */ 4, - /* .maxDualSourceDrawBuffersEXT = */ 1, - - /* .limits = */ - { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, - } -}; - /** Shader utily class with functions to compile and process glsl files. */ class Shader { public: + + // The default resource limit for the GLSL compiler, can be overwritten + // Has been adopted by: + // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp + const static TBuiltInResource defaultResource; + /** * Compile multiple sources with optional filenames. Currently this function * uses the glslang C++ interface which is not thread safe so this funciton @@ -873,7 +767,7 @@ class Shader const std::vector& files = {}, const std::string& entryPoint = "main", std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const TBuiltInResource& resources = Shader::defaultResource); /** * Compile a single glslang source from string value. Currently this @@ -893,7 +787,7 @@ class Shader const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, - const TBuiltInResource& resources = defaultResource); + const TBuiltInResource& resources = Shader::defaultResource); }; } @@ -1125,11 +1019,19 @@ class Algorithm { public: /** - * Default constructor for Algorithm + * Main constructor for algorithm with configuration parameters to create + * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param commandBuffer The vulkan command buffer to bind the pipeline and - * shaders + * @param tensors (optional) The tensors to use to create the descriptor resources + * @param spirv (optional) The spirv code to use to create the algorithm + * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to + * kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to initialize + * the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when initializing the + * pipeline, which set the size of the push constants - these can be modified but + * all new values must have the same vector size as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -1139,13 +1041,18 @@ class Algorithm const Constants& pushConstants = {}); /** - * Initialiser for the shader data provided to the algorithm as well as - * tensor parameters that will be used in shader. + * Rebuild function to reconstruct algorithm with configuration parameters to create + * the underlying resources. * - * @param shaderFileData The bytes in spir-v format of the shader - * @tensorParams The Tensors to be used in the Algorithm / shader for - * @specalizationInstalces The specialization parameters to pass to the - * function processing + * @param tensors The tensors to use to create the descriptor resources + * @param spirv The spirv code to use to create the algorithm + * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to + * kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to initialize + * the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when initializing the + * pipeline, which set the size of the push constants - these can be modified but + * all new values must have the same vector size as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -1163,24 +1070,77 @@ class Algorithm * Records the dispatch function with the provided template parameters or * alternatively using the size of the tensor by default. * - * @param x Layout X dispatch value - * @param y Layout Y dispatch value - * @param z Layout Z dispatch value + * @param commandBuffer Command buffer to record the algorithm resources to */ void recordDispatch(const vk::CommandBuffer& commandBuffer); - void bindCore(const vk::CommandBuffer& commandBuffer); + /** + * Records command that binds the "core" algorithm components which consist of + * binding the pipeline and binding the descriptorsets. + * + * @param commandBuffer Command buffer to record the algorithm resources to + */ + void recordBindCore(const vk::CommandBuffer& commandBuffer); - void bindPush(const vk::CommandBuffer& commandBuffer); + /** + * Records command that binds the push constants to the command buffer provided + * - it is required that the pushConstants provided are of the same size as the + * ones provided during initialization. + * + * @param commandBuffer Command buffer to record the algorithm resources to + */ + void recordBindPush(const vk::CommandBuffer& commandBuffer); + /** + * function that checks all the gpu resource components to verify if these have + * been created and returns true if all are valid. + * + * @returns returns true if the algorithm is currently initialized. + */ bool isInit(); + /** + * Sets the work group to use in the recordDispatch + * + * @param workgroup The kp::Workgroup value to use to update the algorithm. It + * must have a value greater than 1 on the x value (index 1) otherwise it will + * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); + /** + * Sets the push constants to the new value provided to use in the next bindPush() + * + * @param The kp::Constant to use to set the push constants to use in the next + * bindPush(...) calls. The constants provided must be of the same size as the + * ones created during initialization. + */ void setPush(const Constants& pushConstants); + /** + * Gets the current workgroup from the algorithm. + * + * @param The kp::Constant to use to set the push constants to use in the next + * bindPush(...) calls. The constants provided must be of the same size as the + * ones created during initialization. + */ const Workgroup& getWorkgroup(); + /** + * Gets the specialization constants of the current algorithm. + * + * @returns The kp::Constants currently set for specialization constants + */ const Constants& getSpecializationConstants(); + /** + * Gets the specialization constants of the current algorithm. + * + * @returns The kp::Constants currently set for push constants + */ const Constants& getPush(); + /** + * Gets the current tensors that are used in the algorithm. + * + * @returns The list of tensors used in the algorithm. + */ const std::vector>& getTensors(); void destroy(); @@ -1212,8 +1172,6 @@ class Algorithm Constants mPushConstants; Workgroup mWorkgroup; - bool mIsInit; - // Create util functions void createShaderModule(); void createPipeline(); @@ -1543,6 +1501,14 @@ class Sequence : public std::enable_shared_from_this ~Sequence(); /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param op Object derived from kp::BaseOp that will be recoreded by the sequence + * which will be used when the operation is evaluated. + * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -1555,6 +1521,7 @@ class Sequence : public std::enable_shared_from_this * @param tensors Vector of tensors to use for the operation * @param TArgs Template parameters that are used to initialise operation * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself */ template std::shared_ptr record( @@ -1563,6 +1530,18 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); } + /** + * Record function for operation to be added to the GPU queue in batch. This + * template requires classes to be derived from the OpBase class. This + * function also requires the Sequence to be recording, otherwise it will + * not be able to add the operation. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr record(std::shared_ptr algorithm, TArgs&&... params) @@ -1574,21 +1553,29 @@ class Sequence : public std::enable_shared_from_this /** * Eval sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. + * operations into the gpu as a submit job synchronously (with a barrier). * * @return shared_ptr of the Sequence class itself */ std::shared_ptr eval(); + /** + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a barrier). + * + * @return shared_ptr of the Sequence class itself + */ std::shared_ptr eval(std::shared_ptr op); /** * Eval sends all the recorded and stored operations in the vector of * operations into the gpu as a submit job with a barrier. * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. * @return shared_ptr of the Sequence class itself */ - // TODO: Aim to have only a single function with tensors/algorithm template std::shared_ptr eval(std::vector> tensors, TArgs&&... params) @@ -1596,6 +1583,16 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->eval(op); } + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr eval(std::shared_ptr algorithm, TArgs&&... params) @@ -1607,18 +1604,27 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job with a barrier. EvalAwait() must - * be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() must + * ALWAYS be called after to ensure the sequence is terminated correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); + /** + * Clears currnet operations to record provided one in the vector of + * operations into the gpu as a submit job without a barrier. EvalAwait() must + * ALWAYS be called after to ensure the sequence is terminated correctly. + * + * @return Boolean stating whether execution was successful. + */ std::shared_ptr evalAsync(std::shared_ptr op); - /** * Eval sends all the recorded and stored operations in the vector of * operations into the gpu as a submit job with a barrier. * + * @param tensors Vector of tensors to use for the operation + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. * @return shared_ptr of the Sequence class itself */ template @@ -1629,6 +1635,16 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->evalAsync(op); } + /** + * Eval sends all the recorded and stored operations in the vector of + * operations into the gpu as a submit job with a barrier. + * + * @param algorithm Algorithm to use for the record often used for OpAlgo + * operations + * @param TArgs Template parameters that are used to initialise operation + * which allows for extensible configurations on initialisation. + * @return shared_ptr of the Sequence class itself + */ template std::shared_ptr evalAsync(std::shared_ptr algorithm, TArgs&&... params) @@ -1643,7 +1659,7 @@ class Sequence : public std::enable_shared_from_this * finishes, it runs the postEval of all operations. * * @param waitFor Number of milliseconds to wait before timing out. - * @return Boolean stating whether execution was successful. + * @return shared_ptr of the Sequence class itself */ std::shared_ptr evalAwait(uint64_t waitFor = UINT64_MAX); @@ -1676,8 +1692,19 @@ class Sequence : public std::enable_shared_from_this */ bool isRecording(); + /** + * Returns true if the sequence has been initialised, and it's based on the + * GPU resources being refrenced. + * + * @return Boolean stating if is initialized + */ bool isInit(); + /** + * Clears command buffer and triggers re-record of all the current operations + * saved, which is useful if the underlying kp::Tensors or kp::Algorithms + * are modified and need to be re-recorded. + */ void rerecord(); /** @@ -1742,15 +1769,13 @@ class Manager Manager(); /** - * Similar to base constructor but allows the user to provide the device - * they would like to create the resources on. + * Similar to base constructor but allows for further configuration to use when + * creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use - * @param manageResources (Optional) Whether to manage the memory of the - * resources created and destroy when the manager is destroyed. * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param totalQueues The total number of compute queues to create. + * @param desiredExtensions The desired extensions to load from physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -1776,32 +1801,40 @@ class Manager ~Manager(); /** - * Get or create a managed Sequence that will be contained by this manager. - * If the named sequence does not currently exist, it would be created and - * initialised. + * Create a managed sequence that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. * - * @param sequenceName The name for the named sequence to be retrieved or - * created * @param queueIndex The queue to use from the available queues - * @return Shared pointer to the manager owned sequence resource + * @returns Shared pointer with initialised sequence */ std::shared_ptr sequence(uint32_t queueIndex = 0); /** - * Function that simplifies the common workflow of tensor creation and - * initialization. It will take the constructor parameters for a Tensor - * and will will us it to create a new Tensor and then create it. The - * tensor memory will then be managed and owned by the manager. + * Create a managed tensor that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. * * @param data The data to initialize the tensor with * @param tensorType The type of tensor to initialize - * @param syncDataToGPU Whether to sync the data to GPU memory - * @returns Initialized Tensor with memory Syncd to GPU device + * @returns Shared pointer with initialised tensor */ std::shared_ptr tensor( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + /** + * Create a managed algorithm that will be destroyed by this manager + * if it hasn't been destroyed by its reference count going to zero. + * + * @param tensors (optional) The tensors to initialise the algorithm with + * @param spirv (optional) The SPIRV bytes for the algorithm to dispatch + * @param workgroup (optional) kp::Workgroup for algorithm to use, and + * defaults to (tensor[0].size(), 1, 1) + * @param specializationConstants (optional) kp::Constant to use for + * specialization constants, and defaults to an empty constant + * @param pushConstants (optional) kp::Constant to use for push constants, + * and defaults to an empty constant + * @returns Shared pointer with initialised algorithm + */ std::shared_ptr algorithm( const std::vector>& tensors = {}, const std::vector& spirv = {}, @@ -1809,7 +1842,14 @@ class Manager const Constants& specializationConstants = {}, const Constants& pushConstants = {}); + /** + * Destroy the GPU resources and all managed resources by manager. + **/ void destroy(); + /** + * Run a pseudo-garbage collection to release all the managed resources + * that have been already freed due to these reaching to zero ref count. + **/ void clear(); private: From 45688c7ab9a581fbd6e1394a0f562a20699da1a7 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 11:22:57 +0000 Subject: [PATCH 47/91] Updated python main.cpp --- python/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index c851a2e43..a20120d4f 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -136,7 +136,7 @@ PYBIND11_MODULE(kp, m) { .def(py::init()) .def(py::init()) .def(py::init&,const std::vector&>()) - .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0, py::arg("desired_extensions") = std::vector()) + .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0) .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { From 0a285ef9afe021b0921822ceff42d0576cece7af Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 11:25:25 +0000 Subject: [PATCH 48/91] Updated string --- python/src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index a20120d4f..f13347aa8 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -135,7 +135,10 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "Manager") .def(py::init()) .def(py::init()) - .def(py::init&,const std::vector&>()) + .def(py::init&,const std::vector&>(), + py::arg("device") = 0, + py::arg("family_queue_indices") = std::vector(), + py::arg("desired_extensions") = std::vector()) .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0) .def("tensor", [np](kp::Manager& self, const py::array_t data, From 6da6bca339811c53499e637fe7b1aac76b1145d5 Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Sun, 7 Mar 2021 11:35:20 +0100 Subject: [PATCH 49/91] requested changes --- python/src/main.cpp | 2 +- single_include/kompute/Kompute.hpp | 126 ++++++++++++++++++++--------- src/Manager.cpp | 4 +- src/Sequence.cpp | 47 ++++++----- src/include/kompute/Sequence.hpp | 6 +- 5 files changed, 123 insertions(+), 62 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index a6a03562b..7165d41e7 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -140,7 +140,7 @@ PYBIND11_MODULE(kp, m) { py::arg("device") = 0, py::arg("family_queue_indices") = std::vector(), py::arg("desired_extensions") = std::vector()) - .def("sequence", &kp::Manager::sequence, py::arg("queueIndex") = 0, py::arg("nrOfTimestamps") = 0) + .def("sequence", &kp::Manager::sequence, py::arg("queue_index") = 0, py::arg("total_timestamps") = 0) .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 0ae6cce5a..38213bb6e 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -820,12 +820,14 @@ class Tensor }; /** - * Default constructor with data provided which would be used to create the + * Constructor with data provided which would be used to create the * respective vulkan buffer and memory. * + * @param physicalDevice The physical device to use to fetch properties + * @param device The device to use to create the buffer and memory from * @param data Non-zero-sized vector of data that will be used by the * tensor - * @param tensorType Type for the tensor which is of type TensorTypes + * @param tensorTypes Type for the tensor which is of type TensorTypes */ Tensor(std::shared_ptr physicalDevice, std::shared_ptr device, @@ -839,10 +841,11 @@ class Tensor ~Tensor(); /** - * Initialiser which calls the initialisation for all the respective tensors - * as well as creates the respective staging tensors. The staging tensors - * would only be created for the tensors of type TensorType::eDevice as - * otherwise there is no need to copy from host memory. + * Function to trigger reinitialisation of the tensor buffer and memory with + * new data as well as new potential device type. + * + * @param data Vector of data to use to initialise vector from + * @param tensorType The type to use for the tensor */ void rebuild(const std::vector& data, TensorTypes tensorType = TensorTypes::eDevice); @@ -852,6 +855,11 @@ class Tensor */ void destroy(); + /** + * Check whether tensor is initialized based on the created gpu resources. + * + * @returns Boolean stating whether tensor is initialized + */ bool isInit(); /** @@ -1210,6 +1218,8 @@ class OpBase * The record function is intended to only send a record command or run * commands that are expected to record operations that are to be submitted * as a batch into the GPU. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void record(const vk::CommandBuffer& commandBuffer) = 0; @@ -1220,6 +1230,8 @@ class OpBase * there are situations where eval can be called multiple times, so the * resources that are created should be idempotent in case it's called multiple * times in a row. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) = 0; @@ -1230,6 +1242,8 @@ class OpBase * there are situations where eval can be called multiple times, so the * resources that are destroyed should not require a re-init unless explicitly * provided by the user. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) = 0; }; @@ -1239,38 +1253,47 @@ class OpBase namespace kp { /** - Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type + * Operation that copies the data from the first tensor to the rest of the tensors + * provided, using a record command for all the vectors. This operation does not + * own/manage the memory of the tensors passed to it. The operation must only + * receive tensors of type */ class OpTensorCopy : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorCopy(const std::vector>& tensors); /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + * Default destructor. This class does not manage memory so it won't be + * expecting the parent to perform a release. */ ~OpTensorCopy() override; /** - * Records the copy commands from the first tensor into all the other tensors provided. Also optionally records a barrier. + * Records the copy commands from the first tensor into all the other + * tensors provided. Also optionally records a barrier. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Copies the local vectors for all the tensors to sync the data with the gpu. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; @@ -1284,17 +1307,20 @@ class OpTensorCopy : public OpBase namespace kp { /** - Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. + * Operation that syncs tensor's device by mapping local data into the device memory. + * For TensorTypes::eDevice it will use a record operation for the memory to be syncd + * into GPU memory which means that the operation will be done in sync with GPU commands. + * For TensorTypes::eHost it will only map the data into host memory which will + * happen during preEval before the recorded commands are dispatched. */ class OpTensorSyncDevice : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. The tensos provided cannot + * be of type TensorTypes::eStorage. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorSyncDevice(const std::vector>& tensors); @@ -1305,17 +1331,24 @@ class OpTensorSyncDevice : public OpBase ~OpTensorSyncDevice() override; /** - * For device tensors, it records the copy command for the tensor to copy the data from its staging to device memory. + * For device tensors, it records the copy command for the tensor to copy the + * data from its staging to device memory. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; @@ -1329,38 +1362,50 @@ class OpTensorSyncDevice : public OpBase namespace kp { /** - Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For TensorTypes::eStaging it will only map the data into host memory which will happen during preEval before the recorded commands are dispatched. This operation won't have any effect on TensorTypes::eStaging. + * Operation that syncs tensor's local memory by mapping device data into the + * local CPU memory. For TensorTypes::eDevice it will use a record operation + * for the memory to be syncd into GPU memory which means that the operation + * will be done in sync with GPU commands. For TensorTypes::eHost it will + * only map the data into host memory which will happen during preEval before + * the recorded commands are dispatched. */ class OpTensorSyncLocal : public OpBase { public: /** - * Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. + * Default constructor with parameters that provides the core vulkan resources + * and the tensors that will be used in the operation. The tensors provided + * cannot be of type TensorTypes::eStorage. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that will be used to create in operation. */ OpTensorSyncLocal(const std::vector>& tensors); /** - * Default destructor. This class does not manage memory so it won't be expecting the parent to perform a release. + * Default destructor. This class does not manage memory so it won't be expecting + * the parent to perform a release. */ ~OpTensorSyncLocal() override; /** - * For device tensors, it records the copy command for the tensor to copy the data from its device to staging memory. + * For device tensors, it records the copy command for the tensor to copy the + * data from its device to staging memory. + * + * @param commandBuffer The command buffer to record the command into. */ void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** * For host tensors it performs the map command from the host memory into local memory. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; @@ -1383,6 +1428,13 @@ class OpAlgoDispatch : public OpBase { public: + /** + * Constructor that stores the algorithm to use as well as the relevant + * push constants to override when recording. + * + * @param algorithm The algorithm object to use for dispatch + * @param pushConstants The push constants to use for override + */ OpAlgoDispatch(const std::shared_ptr& algorithm, const kp::Constants& pushConstants = {}); @@ -1399,18 +1451,22 @@ class OpAlgoDispatch : public OpBase * shader processing to the gpu. This function also records the GPU memory * copy of the output data for the staging buffer so it can be read by the * host. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void record(const vk::CommandBuffer& commandBuffer) override; /** * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void preEval(const vk::CommandBuffer& commandBuffer) override; /** - * Executes after the recorded commands are submitted, and performs a copy - * of the GPU Device memory into the staging buffer so the output data can - * be retrieved. + * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. */ virtual void postEval(const vk::CommandBuffer& commandBuffer) override; @@ -1439,11 +1495,9 @@ class OpMult : public OpAlgoDispatch * requirements for the operations to be able to create and manage their * sub-components. * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into * @param tensors Tensors that are to be used in this operation - * @param komputeWorkgroup Optional parameter to specify the layout for processing + * @param algorithm An algorithm that will be overridden with the OpMult + * shader data and the tensors provided which are expected to be 3 */ OpMult(std::vector> tensors, std::shared_ptr algorithm) : OpAlgoDispatch(algorithm) @@ -1489,13 +1543,13 @@ class Sequence : public std::enable_shared_from_this * @param device Vulkan logical device * @param computeQueue Vulkan compute queue * @param queueIndex Vulkan compute queue index in device - * @param nrOfTimestamps Maximum number of timestamps to allocate + * @param totalTimestamps Maximum number of timestamps to allocate */ Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, uint32_t queueIndex, - uint32_t nrOfTimestamps = 0); + uint32_t totalTimestamps = 0); /** * Destructor for sequence which is responsible for cleaning all subsequent * owned operations. @@ -1754,7 +1808,7 @@ class Sequence : public std::enable_shared_from_this // Create functions void createCommandPool(); void createCommandBuffer(); - void createTimestampQueryPool(uint32_t); + void createTimestampQueryPool(uint32_t totalTimestamps); }; } // End namespace kp diff --git a/src/Manager.cpp b/src/Manager.cpp index 563e102bf..d6743739c 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -431,7 +431,7 @@ Manager::algorithm(const std::vector>& tensors, } std::shared_ptr -Manager::sequence(uint32_t queueIndex, uint32_t nrOfTimestamps) +Manager::sequence(uint32_t queueIndex, uint32_t total_timestamps) { KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", queueIndex); @@ -440,7 +440,7 @@ Manager::sequence(uint32_t queueIndex, uint32_t nrOfTimestamps) this->mDevice, this->mComputeQueues[queueIndex], this->mComputeQueueFamilyIndices[queueIndex], - nrOfTimestamps) }; + total_timestamps) }; if (this->mManageResources) { this->mManagedSequences.push_back(sq); diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 21cbf5af2..6e379eb92 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -7,7 +7,7 @@ Sequence::Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, uint32_t queueIndex, - uint32_t nrOfTimestamps) + uint32_t totalTimestamps) { KP_LOG_DEBUG("Kompute Sequence Constructor with existing device & queue"); @@ -18,8 +18,8 @@ Sequence::Sequence(std::shared_ptr physicalDevice, this->createCommandPool(); this->createCommandBuffer(); - if(nrOfTimestamps>0) - this->createTimestampQueryPool(nrOfTimestamps+1); //+1 for the first one + if(totalTimestamps>0) + this->createTimestampQueryPool(totalTimestamps+1); //+1 for the first one } Sequence::~Sequence() @@ -246,6 +246,16 @@ Sequence::destroy() this->mOperations.clear(); } + if(this->timestampQueryPool){ + KP_LOG_INFO("Destroying QueryPool"); + this->mDevice->destroy( + *this->timestampQueryPool, + (vk::Optional)nullptr); + + this->timestampQueryPool = nullptr; + KP_LOG_DEBUG("Kompute Sequence Destroyed QueryPool"); + } + if (this->mDevice) { this->mDevice = nullptr; } @@ -325,11 +335,11 @@ Sequence::createCommandBuffer() } void -Sequence::createTimestampQueryPool(uint32_t query_size) +Sequence::createTimestampQueryPool(uint32_t totalTimestamps) { KP_LOG_DEBUG("Kompute Sequence creating query pool"); - if (!this->mDevice) { - throw std::runtime_error("Kompute Sequence device is null"); + if (!this->isInit()) { + throw std::runtime_error("createTimestampQueryPool() called on uninitialized Sequence"); } if (!this->mPhysicalDevice) { throw std::runtime_error("Kompute Sequence physical device is null"); @@ -339,32 +349,29 @@ Sequence::createTimestampQueryPool(uint32_t query_size) this->mPhysicalDevice->getProperties(); if(physicalDeviceProperties.limits.timestampComputeAndGraphics){ - vk::QueryPoolCreateInfo queryPoolInfo; - queryPoolInfo.setQueryCount(query_size); - queryPoolInfo.setQueryType(vk::QueryType::eTimestamp); - this->timestampQueryPool = std::make_shared(this->mDevice->createQueryPool(queryPoolInfo)); + vk::QueryPoolCreateInfo queryPoolInfo; + queryPoolInfo.setQueryCount(totalTimestamps); + queryPoolInfo.setQueryType(vk::QueryType::eTimestamp); + this->timestampQueryPool = std::make_shared(this->mDevice->createQueryPool(queryPoolInfo)); - KP_LOG_DEBUG("Query pool for timestamps created"); + KP_LOG_DEBUG("Query pool for timestamps created"); } else{ - KP_LOG_DEBUG("Device does not support timestamps"); + throw std::runtime_error("Device does not support timestamps"); } } std::vector -Sequence::getTimestamps(){ +Sequence::getTimestamps() +{ if(!this->timestampQueryPool) throw std::runtime_error("Timestamp latching not enabled"); const auto n = this->mOperations.size()+1; std::vector timestamps(n, 0); - //XXX: the C++ method this->mDevice->getQueryPoolResults does not compile for me - const VkResult result = - vkGetQueryPoolResults(*this->mDevice, *this->timestampQueryPool, - 0, n, timestamps.size()*sizeof(std::uint64_t), timestamps.data(), - sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); - if(result!=VK_SUCCESS) - throw std::runtime_error("vkGetQueryPoolResults failed"); + this->mDevice->getQueryPoolResults(*this->timestampQueryPool, + 0, n, timestamps.size()*sizeof(std::uint64_t), timestamps.data(), + sizeof(uint64_t), vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait); return timestamps; } diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index 502720f72..d29f6aaf0 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -21,13 +21,13 @@ class Sequence : public std::enable_shared_from_this * @param device Vulkan logical device * @param computeQueue Vulkan compute queue * @param queueIndex Vulkan compute queue index in device - * @param nrOfTimestamps Maximum number of timestamps to allocate + * @param totalTimestamps Maximum number of timestamps to allocate */ Sequence(std::shared_ptr physicalDevice, std::shared_ptr device, std::shared_ptr computeQueue, uint32_t queueIndex, - uint32_t nrOfTimestamps = 0); + uint32_t totalTimestamps = 0); /** * Destructor for sequence which is responsible for cleaning all subsequent * owned operations. @@ -286,7 +286,7 @@ class Sequence : public std::enable_shared_from_this // Create functions void createCommandPool(); void createCommandBuffer(); - void createTimestampQueryPool(uint32_t); + void createTimestampQueryPool(uint32_t totalTimestamps); }; } // End namespace kp From 259d3f1d13604cf639f7ea034b1fa2b69e93623f Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Sun, 7 Mar 2021 14:16:50 +0100 Subject: [PATCH 50/91] test case --- Makefile | 2 +- src/Manager.cpp | 4 ++-- src/include/kompute/Manager.hpp | 2 +- test/TestSequence.cpp | 30 ++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 872209015..9fdcbdcbe 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ VCPKG_WIN_PATH ?= "C:\\Users\\axsau\\Programming\\lib\\vcpkg\\scripts\\buildsyst VCPKG_UNIX_PATH ?= "/c/Users/axsau/Programming/lib/vcpkg/scripts/buildsystems/vcpkg.cmake" # Regext to pass to catch2 to filter tests -FILTER_TESTS ?= "-TestAsyncOperations.TestManagerParallelExecution" +FILTER_TESTS ?= "-TestAsyncOperations.TestManagerParallelExecution:TestSequence.SequenceTimestamps" ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... CMAKE_BIN ?= "C:\Program Files\CMake\bin\cmake.exe" diff --git a/src/Manager.cpp b/src/Manager.cpp index d6743739c..e3bdbb2d9 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -431,7 +431,7 @@ Manager::algorithm(const std::vector>& tensors, } std::shared_ptr -Manager::sequence(uint32_t queueIndex, uint32_t total_timestamps) +Manager::sequence(uint32_t queueIndex, uint32_t totalTimestamps) { KP_LOG_DEBUG("Kompute Manager sequence() with queueIndex: {}", queueIndex); @@ -440,7 +440,7 @@ Manager::sequence(uint32_t queueIndex, uint32_t total_timestamps) this->mDevice, this->mComputeQueues[queueIndex], this->mComputeQueueFamilyIndices[queueIndex], - total_timestamps) }; + totalTimestamps) }; if (this->mManageResources) { this->mManagedSequences.push_back(sq); diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index db99c6d1e..d9c6ddf3e 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -64,7 +64,7 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t nrOfTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 482868a88..b8afd1ad6 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -100,3 +100,33 @@ TEST(TestSequence, RerecordSequence) EXPECT_EQ(tensorB->data(), std::vector({2, 8, 18})); } + + +TEST(TestSequence, SequenceTimestamps) +{ + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + + std::string shader(R"( + #version 450 + layout (local_size_x = 1) in; + layout(set = 0, binding = 0) buffer a { float pa[]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + pa[index] = pa[index] + 1; + })"); + + std::vector spirv = kp::Shader::compile_source(shader); + + auto seq = mgr.sequence(0, 100); //100 timestamps + seq->record({ tensorA }) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record({ tensorA }) + ->eval(); + const std::vector timestamps = seq->getTimestamps(); + + EXPECT_EQ(timestamps.size(), 6); //1 timestamp at start + 1 after each operation +} From fa5dc43b443f41c6438766036fc8b58aa3bfadcc Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 12:02:08 +0000 Subject: [PATCH 51/91] Updated compile_shader to compileShader --- examples/array_multiplication/src/Main.cpp | 2 +- .../kompute_summator/KomputeSummatorNode.cpp | 2 +- .../gdnative_shared/src/KomputeSummator.cpp | 2 +- single_include/kompute/Kompute.hpp | 4 ++-- src/Shader.cpp | 6 +++--- src/include/kompute/Shader.hpp | 4 ++-- test/TestAsyncOperations.cpp | 4 ++-- test/TestDestroy.cpp | 6 +++--- test/TestMultipleAlgoExecutions.cpp | 12 ++++++------ test/TestOpShadersFromStringAndFile.cpp | 2 +- test/TestPushConstant.cpp | 6 +++--- test/TestSequence.cpp | 2 +- test/TestShaderResources.cpp | 2 +- test/TestSpecializationConstant.cpp | 2 +- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index fd823bca8..acb76898c 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -39,7 +39,7 @@ int main() std::vector> params = { tensorInA, tensorInB, tensorOut }; - std::shared_ptr algo = mgr.algorithm(params, kp::Shader::compile_source(shader)); + std::shared_ptr algo = mgr.algorithm(params, kp::Shader::compileSource(shader)); mgr.sequence() ->record(params) diff --git a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp index f50c56d5c..e901ef816 100644 --- a/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp +++ b/examples/godot_examples/custom_module/kompute_summator/KomputeSummatorNode.cpp @@ -54,7 +54,7 @@ void KomputeSummatorNode::_init() { std::shared_ptr algo = mgr.algorithm( { this->mPrimaryTensor, this->mSecondaryTensor }, - kp::Shader::compile_source(shader)); + kp::Shader::compileSource(shader)); // First we ensure secondary tensor loads to GPU diff --git a/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp b/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp index ece095c8e..99aabb338 100644 --- a/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp +++ b/examples/godot_examples/gdnative_shared/src/KomputeSummator.cpp @@ -58,7 +58,7 @@ void KomputeSummator::_init() { // Then we run the operation with both tensors this->mSequence->record( { this->mPrimaryTensor, this->mSecondaryTensor }, - kp::Shader::compile_source(shader)); + kp::Shader::compileSource(shader)); // We map the result back to local this->mSequence->record( diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 38213bb6e..593390dbe 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -762,7 +762,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_sources( + static std::vector compileSources( const std::vector& sources, const std::vector& files = {}, const std::string& entryPoint = "main", @@ -783,7 +783,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_source( + static std::vector compileSource( const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, diff --git a/src/Shader.cpp b/src/Shader.cpp index 968e53234..bedac0165 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -5,7 +5,7 @@ namespace kp { std::vector -Shader::compile_sources( +Shader::compileSources( const std::vector& sources, const std::vector& files, const std::string& entryPoint, @@ -92,13 +92,13 @@ Shader::compile_sources( } std::vector -Shader::compile_source( +Shader::compileSource( const std::string& source, const std::string& entryPoint, std::vector> definitions, const TBuiltInResource& resource) { - return compile_sources({ source }, + return compileSources({ source }, std::vector({}), entryPoint, definitions, diff --git a/src/include/kompute/Shader.hpp b/src/include/kompute/Shader.hpp index 9fd1709be..9ecab24cd 100644 --- a/src/include/kompute/Shader.hpp +++ b/src/include/kompute/Shader.hpp @@ -39,7 +39,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_sources( + static std::vector compileSources( const std::vector& sources, const std::vector& files = {}, const std::string& entryPoint = "main", @@ -60,7 +60,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_source( + static std::vector compileSource( const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index b1919ce52..2f8c7d819 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -37,7 +37,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) } )"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::vector data(size, 0.0); std::vector resultSync(size, 100000000); @@ -145,7 +145,7 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) } )"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::vector data(size, 0.0); std::vector resultAsync(size, 100000000); diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index fee3854c4..0b948d64f 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -16,7 +16,7 @@ TEST(TestDestroy, TestDestroyTensorSingle) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); { std::shared_ptr sq = nullptr; @@ -57,7 +57,7 @@ TEST(TestDestroy, TestDestroyTensorVector) pa[index] = pa[index] + 1; pb[index] = pb[index] + 2; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); { std::shared_ptr sq = nullptr; @@ -101,7 +101,7 @@ TEST(TestDestroy, TestDestroySequenceSingle) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); { std::shared_ptr sq = nullptr; diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index b94591308..63dd5f7fe 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -49,7 +49,7 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) kp::Constants pushConstsB({ 3.0 }); auto algorithm = mgr.algorithm( - params, kp::Shader::compile_source(shader), workgroup, specConsts, pushConstsA); + params, kp::Shader::compileSource(shader), workgroup, specConsts, pushConstsA); // 3. Run operation with string shader synchronously mgr.sequence() @@ -84,7 +84,7 @@ TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); { mgr.sequence() @@ -114,7 +114,7 @@ TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr algorithm = mgr.algorithm({ tensorA }, spirv); @@ -150,7 +150,7 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr algorithm = mgr.algorithm({ tensorA }, spirv); @@ -185,7 +185,7 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr algorithm = mgr.algorithm({ tensorA }, spirv); @@ -221,7 +221,7 @@ TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr algorithm = mgr.algorithm({ tensorA }, spirv); diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index 3e6856a21..e766c8efb 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -27,7 +27,7 @@ TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) } )"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::vector> params = { tensorA, tensorB }; diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index f51f8cc42..b37fe4d72 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -22,7 +22,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) pa[2] += pcs.z; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr sq = nullptr; @@ -65,7 +65,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) pa[2] += pcs.z; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr sq = nullptr; @@ -108,7 +108,7 @@ TEST(TestPushConstants, TestConstantsWrongSize) pa[2] += pcs.z; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr sq = nullptr; diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index b8afd1ad6..7d70a477b 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -66,7 +66,7 @@ TEST(TestSequence, RerecordSequence) sq->eval({ tensorA, tensorB, tensorOut }); - std::vector spirv = kp::Shader::compile_source(R"( + std::vector spirv = kp::Shader::compileSource(R"( #version 450 layout (local_size_x = 1) in; diff --git a/test/TestShaderResources.cpp b/test/TestShaderResources.cpp index b0013ef80..536f4ca0c 100644 --- a/test/TestShaderResources.cpp +++ b/test/TestShaderResources.cpp @@ -25,7 +25,7 @@ static const std::string shaderString = (R"( )"); void compileShaderWithGivenResources(const std::string shaderString, const TBuiltInResource resources) { - kp::Shader::compile_source(shaderString, std::string("main"), std::vector>({}), resources); + kp::Shader::compileSource(shaderString, std::string("main"), std::vector>({}), resources); } diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index e66f9d52e..2c6e284d2 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -18,7 +18,7 @@ TEST(TestSpecializationConstants, TestTwoConstants) pb[index] = cTwo; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr sq = nullptr; From f569bae998a8805f23f8ca7d53ec2c4c9b142dce Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 12:02:26 +0000 Subject: [PATCH 52/91] Updated python docstrings --- python/src/docstrings.hpp | 145 ++++++++++++++++++++++++-------------- python/src/main.cpp | 137 +++++++++++++++++++++-------------- 2 files changed, 178 insertions(+), 104 deletions(-) diff --git a/python/src/docstrings.hpp b/python/src/docstrings.hpp index bf98e6581..a5bda0a4d 100644 --- a/python/src/docstrings.hpp +++ b/python/src/docstrings.hpp @@ -247,8 +247,10 @@ static const char *__doc_kp_Manager_sequence = R"doc(Create a managed sequence that will be destroyed by this manager if it hasn't been destroyed by its reference count going to zero. -@param queueIndex The queue to use from the available queues @returns -Shared pointer with initialised sequence)doc"; +@param queueIndex The queue to use from the available queues @param +nrOfTimestamps The maximum number of timestamps to allocate. If zero +(default), disables latching of timestamps. @returns Shared pointer +with initialised sequence)doc"; static const char *__doc_kp_Manager_tensor = R"doc(Create a managed tensor that will be destroyed by this manager if it @@ -264,18 +266,26 @@ of algorithm and parameter components which can be used with shaders. By default it enables the user to provide a dynamic number of tensors which are then passed as inputs.)doc"; -static const char *__doc_kp_OpAlgoDispatch_OpAlgoDispatch = R"doc()doc"; +static const char *__doc_kp_OpAlgoDispatch_OpAlgoDispatch = +R"doc(Constructor that stores the algorithm to use as well as the relevant +push constants to override when recording. + +@param algorithm The algorithm object to use for dispatch @param +pushConstants The push constants to use for override)doc"; static const char *__doc_kp_OpAlgoDispatch_mAlgorithm = R"doc()doc"; static const char *__doc_kp_OpAlgoDispatch_mPushConstants = R"doc()doc"; static const char *__doc_kp_OpAlgoDispatch_postEval = -R"doc(Executes after the recorded commands are submitted, and performs a -copy of the GPU Device memory into the staging buffer so the output -data can be retrieved.)doc"; +R"doc(Does not perform any postEval commands. -static const char *__doc_kp_OpAlgoDispatch_preEval = R"doc(Does not perform any preEval commands.)doc"; +@param commandBuffer The command buffer to record the command into.)doc"; + +static const char *__doc_kp_OpAlgoDispatch_preEval = +R"doc(Does not perform any preEval commands. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpAlgoDispatch_record = R"doc(This records the commands that are to be sent to the GPU. This @@ -283,7 +293,9 @@ includes the barriers that ensure the memory has been copied before going in and out of the shader, as well as the dispatch operation that sends the shader processing to the gpu. This function also records the GPU memory copy of the output data for the staging buffer so it can be -read by the host.)doc"; +read by the host. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpBase = R"doc(Base Operation which provides the high level interface that Kompute @@ -299,7 +311,9 @@ the commands to the GPU for processing, and can be used to perform any tear-down steps required as the computation iteration finishes. It's worth noting that there are situations where eval can be called multiple times, so the resources that are destroyed should not require -a re-init unless explicitly provided by the user.)doc"; +a re-init unless explicitly provided by the user. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpBase_preEval = R"doc(Pre eval is called before the Sequence has called eval and submitted @@ -307,12 +321,16 @@ the commands to the GPU for processing, and can be used to perform any per-eval setup steps required as the computation iteration begins. It's worth noting that there are situations where eval can be called multiple times, so the resources that are created should be idempotent -in case it's called multiple times in a row.)doc"; +in case it's called multiple times in a row. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpBase_record = R"doc(The record function is intended to only send a record command or run commands that are expected to record operations that are to be -submitted as a batch into the GPU.)doc"; +submitted as a batch into the GPU. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpMult = R"doc(Operation that performs multiplication on two tensors and outpus on @@ -323,12 +341,9 @@ R"doc(Default constructor with parameters that provides the bare minimum requirements for the operations to be able to create and manage their sub-components. -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that are to be used in this operation @param -komputeWorkgroup Optional parameter to specify the layout for -processing)doc"; +algorithm An algorithm that will be overridden with the OpMult shader +data and the tensors provided which are expected to be 3)doc"; static const char *__doc_kp_OpTensorCopy = R"doc(Operation that copies the data from the first tensor to the rest of @@ -340,84 +355,95 @@ static const char *__doc_kp_OpTensorCopy_OpTensorCopy = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; static const char *__doc_kp_OpTensorCopy_mTensors = R"doc()doc"; static const char *__doc_kp_OpTensorCopy_postEval = R"doc(Copies the local vectors for all the tensors to sync the data with the -gpu.)doc"; +gpu. -static const char *__doc_kp_OpTensorCopy_preEval = R"doc(Does not perform any preEval commands.)doc"; +@param commandBuffer The command buffer to record the command into.)doc"; + +static const char *__doc_kp_OpTensorCopy_preEval = +R"doc(Does not perform any preEval commands. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpTensorCopy_record = R"doc(Records the copy commands from the first tensor into all the other -tensors provided. Also optionally records a barrier.)doc"; +tensors provided. Also optionally records a barrier. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpTensorSyncDevice = R"doc(Operation that syncs tensor's device by mapping local data into the device memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For -TensorTypes::eStaging it will only map the data into host memory which +TensorTypes::eHost it will only map the data into host memory which will happen during preEval before the recorded commands are -dispatched. This operation won't have any effect on -TensorTypes::eStaging.)doc"; +dispatched.)doc"; static const char *__doc_kp_OpTensorSyncDevice_OpTensorSyncDevice = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensos provided cannot be of type TensorTypes::eStorage. -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; static const char *__doc_kp_OpTensorSyncDevice_mTensors = R"doc()doc"; -static const char *__doc_kp_OpTensorSyncDevice_postEval = R"doc(Does not perform any postEval commands.)doc"; +static const char *__doc_kp_OpTensorSyncDevice_postEval = +R"doc(Does not perform any postEval commands. -static const char *__doc_kp_OpTensorSyncDevice_preEval = R"doc(Does not perform any preEval commands.)doc"; +@param commandBuffer The command buffer to record the command into.)doc"; + +static const char *__doc_kp_OpTensorSyncDevice_preEval = +R"doc(Does not perform any preEval commands. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpTensorSyncDevice_record = R"doc(For device tensors, it records the copy command for the tensor to copy -the data from its staging to device memory.)doc"; +the data from its staging to device memory. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpTensorSyncLocal = R"doc(Operation that syncs tensor's local memory by mapping device data into the local CPU memory. For TensorTypes::eDevice it will use a record operation for the memory to be syncd into GPU memory which means that the operation will be done in sync with GPU commands. For -TensorTypes::eStaging it will only map the data into host memory which +TensorTypes::eHost it will only map the data into host memory which will happen during preEval before the recorded commands are -dispatched. This operation won't have any effect on -TensorTypes::eStaging.)doc"; +dispatched.)doc"; static const char *__doc_kp_OpTensorSyncLocal_OpTensorSyncLocal = R"doc(Default constructor with parameters that provides the core vulkan resources and the tensors that will be used in the operation. The tensors provided cannot be of type TensorTypes::eStorage. -@param physicalDevice Vulkan physical device used to find device -queues @param device Vulkan logical device for passing to Algorithm -@param commandBuffer Vulkan Command Buffer to record commands into @param tensors Tensors that will be used to create in operation.)doc"; static const char *__doc_kp_OpTensorSyncLocal_mTensors = R"doc()doc"; static const char *__doc_kp_OpTensorSyncLocal_postEval = R"doc(For host tensors it performs the map command from the host memory into -local memory.)doc"; +local memory. -static const char *__doc_kp_OpTensorSyncLocal_preEval = R"doc(Does not perform any preEval commands.)doc"; +@param commandBuffer The command buffer to record the command into.)doc"; + +static const char *__doc_kp_OpTensorSyncLocal_preEval = +R"doc(Does not perform any preEval commands. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_OpTensorSyncLocal_record = R"doc(For device tensors, it records the copy command for the tensor to copy -the data from its device to staging memory.)doc"; +the data from its device to staging memory. + +@param commandBuffer The command buffer to record the command into.)doc"; static const char *__doc_kp_Sequence = R"doc(Container of operations that can be sent to GPU as batch)doc"; @@ -427,7 +453,8 @@ generate all dependent resources. @param physicalDevice Vulkan physical device @param device Vulkan logical device @param computeQueue Vulkan compute queue @param -queueIndex Vulkan compute queue index in device)doc"; +queueIndex Vulkan compute queue index in device @param totalTimestamps +Maximum number of timestamps to allocate)doc"; static const char *__doc_kp_Sequence_begin = R"doc(Begins recording commands for commands to be submitted into the @@ -443,6 +470,8 @@ static const char *__doc_kp_Sequence_createCommandBuffer = R"doc()doc"; static const char *__doc_kp_Sequence_createCommandPool = R"doc()doc"; +static const char *__doc_kp_Sequence_createTimestampQueryPool = R"doc()doc"; + static const char *__doc_kp_Sequence_destroy = R"doc(Destroys and frees the GPU resources which include the buffer and memory and sets the sequence as init=False.)doc"; @@ -528,6 +557,10 @@ finishes, it runs the postEval of all operations. @param waitFor Number of milliseconds to wait before timing out. @return shared_ptr of the Sequence class itself)doc"; +static const char *__doc_kp_Sequence_getTimestamps = +R"doc(Return the timestamps that were latched at the beginning and after +each operation during the last eval() call.)doc"; + static const char *__doc_kp_Sequence_isInit = R"doc(Returns true if the sequence has been initialised, and it's based on the GPU resources being refrenced. @@ -607,9 +640,11 @@ R"doc(Clears command buffer and triggers re-record of all the current operations saved, which is useful if the underlying kp::Tensors or kp::Algorithms are modified and need to be re-recorded.)doc"; +static const char *__doc_kp_Sequence_timestampQueryPool = R"doc()doc"; + static const char *__doc_kp_Shader = R"doc(Shader utily class with functions to compile and process glsl files.)doc"; -static const char *__doc_kp_Shader_compile_source = +static const char *__doc_kp_Shader_compileSource = R"doc(Compile a single glslang source from string value. Currently this function uses the glslang C++ interface which is not thread safe so this funciton should not be called from multiple threads concurrently. @@ -622,7 +657,7 @@ List of pairs containing key value definitions @param resourcesLimit A list that contains the resource limits for the GLSL compiler @return The compiled SPIR-V binary in unsigned int32 format)doc"; -static const char *__doc_kp_Shader_compile_sources = +static const char *__doc_kp_Shader_compileSources = R"doc(Compile multiple sources with optional filenames. Currently this function uses the glslang C++ interface which is not thread safe so this funciton should not be called from multiple threads concurrently. @@ -645,11 +680,13 @@ buffer, which would be used to store their respective data. The tensors can be used for GPU data storage or transfer.)doc"; static const char *__doc_kp_Tensor_Tensor = -R"doc(Default constructor with data provided which would be used to create -the respective vulkan buffer and memory. +R"doc(Constructor with data provided which would be used to create the +respective vulkan buffer and memory. +@param physicalDevice The physical device to use to fetch properties +@param device The device to use to create the buffer and memory from @param data Non-zero-sized vector of data that will be used by the -tensor @param tensorType Type for the tensor which is of type +tensor @param tensorTypes Type for the tensor which is of type TensorTypes)doc"; static const char *__doc_kp_Tensor_TensorTypes = @@ -697,7 +734,11 @@ static const char *__doc_kp_Tensor_getStagingBufferUsageFlags = R"doc()doc"; static const char *__doc_kp_Tensor_getStagingMemoryPropertyFlags = R"doc()doc"; -static const char *__doc_kp_Tensor_isInit = R"doc()doc"; +static const char *__doc_kp_Tensor_isInit = +R"doc(Check whether tensor is initialized based on the created gpu +resources. + +@returns Boolean stating whether tensor is initialized)doc"; static const char *__doc_kp_Tensor_mData = R"doc()doc"; @@ -742,11 +783,11 @@ vector's. Returns the element in the position requested.)doc"; static const char *__doc_kp_Tensor_rebuild = -R"doc(Initialiser which calls the initialisation for all the respective -tensors as well as creates the respective staging tensors. The staging -tensors would only be created for the tensors of type -TensorType::eDevice as otherwise there is no need to copy from host -memory.)doc"; +R"doc(Function to trigger reinitialisation of the tensor buffer and memory +with new data as well as new potential device type. + +@param data Vector of data to use to initialise vector from @param +tensorType The type to use for the tensor)doc"; static const char *__doc_kp_Tensor_recordBufferMemoryBarrier = R"doc(Records the buffer memory barrier into the command buffer which diff --git a/python/src/main.cpp b/python/src/main.cpp index 7165d41e7..d4b0f2084 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -26,9 +26,9 @@ PYBIND11_MODULE(kp, m) { py::module_ np = py::module_::import("numpy"); py::enum_(m, "TensorTypes") - .value("device", kp::Tensor::TensorTypes::eDevice, "Tensor holding data in GPU memory.") - .value("host", kp::Tensor::TensorTypes::eHost, "Tensor used for CPU visible GPU data.") - .value("storage", kp::Tensor::TensorTypes::eStorage, "Tensor with host visible gpu memory.") + .value("device", kp::Tensor::TensorTypes::eDevice, DOC(kp, Tensor, TensorTypes, eDevice)) + .value("host", kp::Tensor::TensorTypes::eHost, DOC(kp, Tensor, TensorTypes, eHost)) + .value("storage", kp::Tensor::TensorTypes::eStorage, DOC(kp, Tensor, TensorTypes, eStorage)) .export_values(); #if !defined(KOMPUTE_DISABLE_SHADER_UTILS) || !KOMPUTE_DISABLE_SHADER_UTILS @@ -37,51 +37,63 @@ PYBIND11_MODULE(kp, m) { const std::string& source, const std::string& entryPoint, const std::vector>& definitions) { - std::vector spirv = kp::Shader::compile_source(source, entryPoint, definitions); + std::vector spirv = kp::Shader::compileSource(source, entryPoint, definitions); return py::bytes((const char*)spirv.data(), spirv.size() * sizeof(uint32_t)); }, - "Compiles string source provided and returns the value in bytes", - py::arg("source"), py::arg("entryPoint") = "main", py::arg("definitions") = std::vector>() ) + DOC(kp, Shader, compileSource), + py::arg("source"), + py::arg("entryPoint") = "main", + py::arg("definitions") = std::vector>() ) .def_static("compile_sources", []( const std::vector& source, const std::vector& files, const std::string& entryPoint, const std::vector>& definitions) { - std::vector spirv = kp::Shader::compile_sources(source, files, entryPoint, definitions); + std::vector spirv = kp::Shader::compileSources(source, files, entryPoint, definitions); return py::bytes((const char*)spirv.data(), spirv.size() * sizeof(uint32_t)); }, - "Compiles sources provided with file names and returns the value in bytes", - py::arg("sources"), py::arg("files") = std::vector(), py::arg("entryPoint") = "main", py::arg("definitions") = std::vector>() ); + DOC(kp, Shader, compileSources), + py::arg("sources"), + py::arg("files") = std::vector(), + py::arg("entryPoint") = "main", + py::arg("definitions") = std::vector>() ); #endif // KOMPUTE_DISABLE_SHADER_UTILS - py::class_>(m, "OpBase"); + py::class_>(m, "OpBase", DOC(kp, OpBase)); - py::class_>(m, "OpTensorSyncDevice", py::base()) - .def(py::init>&>()); + py::class_>( + m, "OpTensorSyncDevice", py::base(), DOC(kp, OpTensorSyncDevice)) + .def(py::init>&>(), DOC(kp, OpTensorSyncDevice, OpTensorSyncDevice)); - py::class_>(m, "OpTensorSyncLocal", py::base()) - .def(py::init>&>()); + py::class_>( + m, "OpTensorSyncLocal", py::base(), DOC(kp, OpTensorSyncLocal)) + .def(py::init>&>(), DOC(kp, OpTensorSyncLocal, OpTensorSyncLocal)); - py::class_>(m, "OpTensorCopy", py::base()) - .def(py::init>&>()); + py::class_>( + m, "OpTensorCopy", py::base(), DOC(kp, OpTensorCopy)) + .def(py::init>&>(), DOC(kp, OpTensorCopy, OpTensorCopy)); - py::class_>(m, "OpAlgoDispatch", py::base()) + py::class_>( + m, "OpAlgoDispatch", py::base(), DOC(kp, OpAlgoDispatch)) .def(py::init&,const kp::Constants&>(), + DOC(kp, OpAlgoDispatch, OpAlgoDispatch), py::arg("algorithm"), py::arg("push_consts") = kp::Constants()); - py::class_>(m, "OpMult", py::base()) - .def(py::init>&,const std::shared_ptr&>()); + py::class_>( + m, "OpMult", py::base(), DOC(kp, OpMult)) + .def(py::init>&,const std::shared_ptr&>(), + DOC(kp, OpMult, OpMult)); - py::class_>(m, "Algorithm") - .def("get_tensors", &kp::Algorithm::getTensors) - .def("destroy", &kp::Algorithm::destroy) - .def("get_spec_consts", &kp::Algorithm::getSpecializationConstants) - .def("is_init", &kp::Algorithm::isInit); + py::class_>(m, "Algorithm", DOC(kp, Algorithm, Algorithm)) + .def("get_tensors", &kp::Algorithm::getTensors, DOC(kp, Algorithm, getTensors)) + .def("destroy", &kp::Algorithm::destroy, DOC(kp, Algorithm, destroy)) + .def("get_spec_consts", &kp::Algorithm::getSpecializationConstants, DOC(kp, Algorithm, getSpecializationConstants)) + .def("is_init", &kp::Algorithm::isInit, DOC(kp, Algorithm, isInit)); py::class_>(m, "Tensor", DOC(kp, Tensor)) .def("data", [](kp::Tensor& self) { return py::array(self.data().size(), self.data().data()); - }, "Returns stored data as a new numpy array.") + }, DOC(kp, Tensor, data)) .def("__getitem__", [](kp::Tensor &self, size_t index) -> float { return self.data()[index]; }, "When only an index is necessary") .def("__setitem__", [](kp::Tensor &self, size_t index, float value) { @@ -91,7 +103,7 @@ PYBIND11_MODULE(kp, m) { const py::buffer_info info = flatdata.request(); const float* ptr = (float*) info.ptr; self.setData(std::vector(ptr, ptr+flatdata.size())); - }, "Overrides the data in the local Tensor memory.") + }, DOC(kp, Tensor, setData)) .def("__iter__", [](kp::Tensor &self) { return py::make_iterator(self.data().begin(), self.data().end()); }, py::keep_alive<0, 1>(), // Required to keep alive iterator while exists @@ -112,35 +124,52 @@ PYBIND11_MODULE(kp, m) { } return reversed; }) - .def("size", &kp::Tensor::size, "Retrieves the size of the Tensor data as per the local Tensor memory.") - .def("__len__", &kp::Tensor::size, "Retrieves the size of the Tensor data as per the local Tensor memory.") - .def("tensor_type", &kp::Tensor::tensorType, "Retreves the memory type of the tensor.") - .def("is_init", &kp::Tensor::isInit, "Checks whether the tensor GPU memory has been initialised.") - .def("destroy", &kp::Tensor::destroy, "Destroy tensor GPU resources."); + .def("size", &kp::Tensor::size, DOC(kp, Tensor, size)) + .def("__len__", &kp::Tensor::size, DOC(kp, Tensor, size)) + .def("tensor_type", &kp::Tensor::tensorType, DOC(kp, Tensor, tensorType)) + .def("is_init", &kp::Tensor::isInit, DOC(kp, Tensor, isInit)) + .def("destroy", &kp::Tensor::destroy, DOC(kp, Tensor, destroy)); - py::class_>(m, "Sequence") - .def("record", [](kp::Sequence& self, std::shared_ptr op) { return self.record(op); }) - .def("eval", [](kp::Sequence& self) { return self.eval(); }) - .def("eval", [](kp::Sequence& self, std::shared_ptr op) { return self.eval(op); }) - .def("eval_async", [](kp::Sequence& self) { return self.eval(); }) - .def("eval_async", [](kp::Sequence& self, std::shared_ptr op) { return self.evalAsync(op); }) - .def("eval_await", [](kp::Sequence& self) { return self.evalAwait(); }) - .def("eval_await", [](kp::Sequence& self, uint32_t wait) { return self.evalAwait(wait); }) - .def("is_recording", &kp::Sequence::isRecording) - .def("is_running", &kp::Sequence::isRunning) - .def("is_init", &kp::Sequence::isInit) - .def("get_timestamps", &kp::Sequence::getTimestamps) - .def("clear", &kp::Sequence::clear) - .def("destroy", &kp::Sequence::destroy); + py::class_>(m, "Sequence", DOC(kp, Sequence)) + .def("record", [](kp::Sequence& self, std::shared_ptr op) { return self.record(op); }, + DOC(kp, Sequence, record)) + .def("eval", [](kp::Sequence& self) { return self.eval(); }, + DOC(kp, Sequence, eval)) + .def("eval", [](kp::Sequence& self, std::shared_ptr op) { return self.eval(op); }, + DOC(kp, Sequence, eval)) + .def("eval_async", [](kp::Sequence& self) { return self.eval(); }, + DOC(kp, Sequence, evalAsync)) + .def("eval_async", [](kp::Sequence& self, std::shared_ptr op) { return self.evalAsync(op); }, + DOC(kp, Sequence, evalAsync)) + .def("eval_await", [](kp::Sequence& self) { return self.evalAwait(); }, + DOC(kp, Sequence, evalAwait)) + .def("eval_await", [](kp::Sequence& self, uint32_t wait) { return self.evalAwait(wait); }, + DOC(kp, Sequence, evalAwait)) + .def("is_recording", &kp::Sequence::isRecording, + DOC(kp, Sequence, isRecording)) + .def("is_running", &kp::Sequence::isRunning, + DOC(kp, Sequence, isRunning)) + .def("is_init", &kp::Sequence::isInit, + DOC(kp, Sequence, isInit)) + .def("clear", &kp::Sequence::clear, + DOC(kp, Sequence, clear)) + .def("rerecord", &kp::Sequence::rerecord, + DOC(kp, Sequence, rerecord)) + .def("get_timestamps", &kp::Sequence::getTimestamps, + DOC(kp, Sequence, getTimestamps)) + .def("destroy", &kp::Sequence::destroy, + DOC(kp, Sequence, destroy)); - py::class_>(m, "Manager") - .def(py::init()) - .def(py::init()) + py::class_>(m, "Manager", DOC(kp, Manager)) + .def(py::init(), DOC(kp, Manager, Manager)) + .def(py::init(), DOC(kp, Manager, Manager_2)) .def(py::init&,const std::vector&>(), + DOC(kp, Manager, Manager_2), py::arg("device") = 0, py::arg("family_queue_indices") = std::vector(), py::arg("desired_extensions") = std::vector()) - .def("sequence", &kp::Manager::sequence, py::arg("queue_index") = 0, py::arg("total_timestamps") = 0) + .def("sequence", &kp::Manager::sequence, DOC(kp, Manager, sequence), + py::arg("queue_index") = 0, py::arg("total_timestamps") = 0) .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { @@ -149,7 +178,7 @@ PYBIND11_MODULE(kp, m) { const float* ptr = (float*) info.ptr; return self.tensor(std::vector(ptr, ptr+flatdata.size()), tensor_type); }, - "Tensor initialisation function with data and tensor type", + DOC(kp, Manager, tensor), py::arg("data"), py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice) .def("algorithm", [](kp::Manager& self, const std::vector>& tensors, @@ -163,8 +192,12 @@ PYBIND11_MODULE(kp, m) { std::vector spirvVec((uint32_t*)data, (uint32_t*)(data + length)); return self.algorithm(tensors, spirvVec, workgroup, spec_consts, push_consts); }, - "Algorithm initialisation function", - py::arg("tensors"), py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), py::arg("push_consts") = kp::Constants()); + DOC(kp, Manager, algorithm), + py::arg("tensors"), + py::arg("spirv"), + py::arg("workgroup") = kp::Workgroup(), + py::arg("spec_consts") = kp::Constants(), + py::arg("push_consts") = kp::Constants()); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; From b81896a78062fb53b56ecadadb66936e434879bd Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 16:15:03 +0000 Subject: [PATCH 53/91] Innitial iteration of multiple type tensor --- src/Tensor.cpp | 101 +++---------- src/include/kompute/Tensor.hpp | 268 +++++++++++++++++++++++++++------ 2 files changed, 243 insertions(+), 126 deletions(-) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index f584c07bd..dc254fe83 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -5,17 +5,20 @@ namespace kp { Tensor::Tensor(std::shared_ptr physicalDevice, std::shared_ptr device, - const std::vector& data, + void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const TensorDataTypes& dataType, const TensorTypes& tensorType) { KP_LOG_DEBUG("Kompute Tensor constructor data length: {}, and type: {}", - data.size(), + elementTotalCount, tensorType); this->mPhysicalDevice = physicalDevice; this->mDevice = device; - this->rebuild(data, tensorType); + this->rebuild(data, elementTotalCount, elementMemorySize, dataType, tensorType); } Tensor::~Tensor() @@ -29,11 +32,17 @@ Tensor::~Tensor() } void -Tensor::rebuild(const std::vector& data, TensorTypes tensorType) +Tensor::rebuild(void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const TensorDataTypes& dataType, + TensorTypes tensorType) { - KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", data.size()); + KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", elementTotalCount); - this->mData = data; + this->mSize = elementTotalCount; + this->mElementMemorySize = elementMemorySize; + this->mDataType = dataType; this->mTensorType = tensorType; if (this->mPrimaryBuffer || this->mPrimaryMemory) { @@ -43,30 +52,7 @@ Tensor::rebuild(const std::vector& data, TensorTypes tensorType) } this->allocateMemoryCreateGPUResources(); -} - -std::vector& -Tensor::data() -{ - return this->mData; -} - -float& -Tensor::operator[](int index) -{ - return this->mData[index]; -} - -uint64_t -Tensor::memorySize() -{ - return this->size() * sizeof(float); -} - -uint32_t -Tensor::size() -{ - return static_cast(this->mData.size()); + this->rawMapDataIntoHostMemory(data); } Tensor::TensorTypes @@ -81,15 +67,6 @@ Tensor::isInit() return this->mDevice && this->mPrimaryBuffer && this->mPrimaryMemory; } -void -Tensor::setData(const std::vector& data) -{ - if (data.size() != this->mData.size()) { - throw std::runtime_error( - "Kompute Tensor Cannot set data of different sizes"); - } - this->mData = data; -} void Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, @@ -204,55 +181,13 @@ Tensor::constructDescriptorBufferInfo() void Tensor::mapDataFromHostMemory() { - KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - void* mapped = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->invalidateMappedMemoryRanges(mappedMemoryRange); - memcpy(this->mData.data(), mapped, bufferSize); - this->mDevice->unmapMemory(*hostVisibleMemory); + KP_LOG_DEBUG("Kompute Tensor mapDataFromHostMemory - SKIPPING"); } void Tensor::mapDataIntoHostMemory() { - - KP_LOG_DEBUG("Kompute Tensor local mapping tensor data to host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - - void* mapped = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - memcpy(mapped, this->mData.data(), bufferSize); - vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->flushMappedMemoryRanges(1, &mappedRange); - this->mDevice->unmapMemory(*hostVisibleMemory); + KP_LOG_DEBUG("Kompute Tensor mapDataIntoHostMemory - SKIPPING"); } vk::BufferUsageFlags diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 195af44f4..f2583708d 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -27,6 +27,14 @@ class Tensor eHost = 1, ///< Type is host memory, source and destination eStorage = 2, ///< Type is Device memory (only) }; + enum class TensorDataTypes + { + eBool = 0, + eInt = 1, + eUnsignedInt = 2, + eFloat = 3, + eDouble = 4, + }; /** * Constructor with data provided which would be used to create the @@ -40,7 +48,10 @@ class Tensor */ Tensor(std::shared_ptr physicalDevice, std::shared_ptr device, - const std::vector& data, + void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const TensorDataTypes& dataType = TensorDataTypes::eFloat, const TensorTypes& tensorType = TensorTypes::eDevice); /** @@ -49,6 +60,48 @@ class Tensor */ ~Tensor(); + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + // TODO: move to cpp + virtual uint32_t size() { + return this->mElementMemorySize; + } + + // TODO: move to cpp + virtual uint32_t memorySize() { + return this->mSize * this->mElementMemorySize; + } + + /** + * Retrieve the underlying data type of the Tensor + * + * @return Data type of tensor of type kp::Tensor::TensorDataTypes + */ + virtual TensorDataTypes dataType() { + return this->mDataType; + } + + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + virtual void mapDataFromHostMemory(); + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + virtual void mapDataIntoHostMemory(); + + // TODO: Decide whether this is one we prefer to have also overriden in the underlying tensorView + // TODO: move to cpp + void getRawData(void* data) { + this->rawMapDataFromHostMemory(data); + } + /** * Function to trigger reinitialisation of the tensor buffer and memory with * new data as well as new potential device type. @@ -56,7 +109,10 @@ class Tensor * @param data Vector of data to use to initialise vector from * @param tensorType The type to use for the tensor */ - void rebuild(const std::vector& data, + void rebuild(void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const TensorDataTypes& dataType = TensorDataTypes::eFloat, TensorTypes tensorType = TensorTypes::eDevice); /** @@ -71,32 +127,6 @@ class Tensor */ bool isInit(); - /** - * Returns the vector of data currently contained by the Tensor. It is - * important to ensure that there is no out-of-sync data with the GPU - * memory. - * - * @return Reference to vector of elements representing the data in the - * tensor. - */ - std::vector& data(); - /** - * Overrides the subscript operator to expose the underlying data's - * subscript operator which in this case would be its underlying - * vector's. - * - * @param i The index where the element will be returned from. - * @return Returns the element in the position requested. - */ - float& operator[](int index); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - uint32_t size(); - /** * Retrieve the tensor type of the Tensor * @@ -108,7 +138,15 @@ class Tensor * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. */ - void setData(const std::vector& data); + void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { + if (elementTotalCount * elementMemorySize != this->memorySize()) { + throw std::runtime_error( + "Kompute Tensor Cannot set data of different sizes"); + } + this->mSize = elementTotalCount; + this->mElementMemorySize = elementMemorySize; + this->rawMapDataIntoHostMemory(data); + } /** * Records a copy from the memory of the tensor provided to the current @@ -172,16 +210,6 @@ class Tensor * @return Descriptor buffer info with own buffer */ vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory(); private: // -------------- NEVER OWNED RESOURCES @@ -199,9 +227,10 @@ class Tensor bool mFreeStagingMemory = false; // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - - TensorTypes mTensorType = TensorTypes::eDevice; + TensorTypes mTensorType; + TensorDataTypes mDataType; + uint32_t mSize; + uint32_t mElementMemorySize; void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, @@ -221,7 +250,160 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - uint64_t memorySize(); + + void rawMapDataFromHostMemory(void* data) { + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + void* mapped = this->mDevice->mapMemory( + *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->invalidateMappedMemoryRanges(mappedMemoryRange); + memcpy(data, mapped, bufferSize); + this->mDevice->unmapMemory(*hostVisibleMemory); + } + + void rawMapDataIntoHostMemory(void* data) { + KP_LOG_DEBUG("Kompute Tensor mapping data into host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + + void* mapped = this->mDevice->mapMemory( + *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + memcpy(mapped, data, bufferSize); + vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->flushMappedMemoryRanges(1, &mappedRange); + this->mDevice->unmapMemory(*hostVisibleMemory); + } }; +// TODO: Limit T to be only float, bool, double, etc +template +class TensorView: public Tensor +{ + public: + TensorView(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice); + + ~TensorView(); + + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice) { + + this->mData = data; + Tensor::rebuild(data.data(), data.size(), sizeof(T), this->dataType(), tensorType); + } + + std::vector& data() { + return this->mData; + } + + T& operator[](int index) { + return this->mData[index]; + } + + void setData(const std::vector& data) { + + if (data.size() != this->mData.size()) { + throw std::runtime_error( + "Kompute TensorView Cannot set data of different sizes"); + } + + this->mData = data; + + this->setRawData(this->mData.data(), this->mData.size(), sizeof(T), this->dataType()); + } + + TensorDataTypes dataType() override; + + uint32_t size() override { + return this->mData->size(); + } + + uint32_t memorySize() override { + return this->mData->size() * sizeof(T); + } + + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + void mapDataFromHostMemory() override { + KP_LOG_DEBUG("Kompute TensorView mapDataFromHostMemory copying data"); + + this->rawMapDataFromHostMemory(this->mData.data()); + } + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + void mapDataIntoHostMemory() override { + KP_LOG_DEBUG("Kompute TensorView mapDataIntoHostMemory copying data"); + + this->rawMapDataIntoHostMemory(this->mData.data()); + } + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector mData; + +}; + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eBool; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eInt; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eUnsignedInt; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eFloat; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eDouble; +} + } // End namespace kp From ad18c2e54698e1496347727ce61d46a8e9562e7b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 17:25:35 +0000 Subject: [PATCH 54/91] Initial implementation of tensor working compiling --- examples/array_multiplication/CMakeLists.txt | 6 +- examples/array_multiplication/README.md | 7 +- examples/array_multiplication/src/Main.cpp | 11 +- examples/logistic_regression/CMakeLists.txt | 8 +- examples/logistic_regression/README.md | 7 +- examples/logistic_regression/src/Main.cpp | 18 +- single_include/kompute/Kompute.hpp | 294 +++++++++++++++---- src/Manager.cpp | 15 - src/OpTensorCopy.cpp | 17 +- src/Tensor.cpp | 42 ++- src/include/kompute/Manager.hpp | 19 +- src/include/kompute/Tensor.hpp | 183 ++++++------ 12 files changed, 417 insertions(+), 210 deletions(-) diff --git a/examples/array_multiplication/CMakeLists.txt b/examples/array_multiplication/CMakeLists.txt index 0b648382e..bfc4c1c79 100644 --- a/examples/array_multiplication/CMakeLists.txt +++ b/examples/array_multiplication/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17.0) +cmake_minimum_required(VERSION 3.4.1) project(kompute_array_mult VERSION 0.1.0) set(CMAKE_CXX_STANDARD 14) @@ -23,10 +23,6 @@ endif() find_package(Vulkan REQUIRED) -if(KOMPUTE_OPT_ENABLE_SPDLOG) - find_package(spdlog REQUIRED) -endif() - add_executable(kompute_array_mult src/Main.cpp) diff --git a/examples/array_multiplication/README.md b/examples/array_multiplication/README.md index 931c7d639..d4082c713 100644 --- a/examples/array_multiplication/README.md +++ b/examples/array_multiplication/README.md @@ -15,8 +15,11 @@ This project has the option to either import the Kompute dependency relative to To build you just need to run the cmake command in this folder as follows: ``` -cmake \ - -Bbuild +cmake -Bbuild/ \ + -DCMAKE_BUILD_TYPE=Debug \ + -DKOMPUTE_OPT_INSTALL=0 \ + -DKOMPUTE_OPT_REPO_SUBMODULE_BUILD=1 \ + -DKOMPUTE_OPT_ENABLE_SPDLOG=1 ``` You can pass the following optional parameters based on your desired configuration: diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index acb76898c..dacc67f89 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -7,16 +7,11 @@ int main() { -#if KOMPUTE_ENABLE_SPDLOG - spdlog::set_level( - static_cast(SPDLOG_ACTIVE_LEVEL)); -#endif - kp::Manager mgr; - auto tensorInA = mgr.tensor({ 2.0, 4.0, 6.0 }); - auto tensorInB = mgr.tensor({ 0.0, 1.0, 2.0 }); - auto tensorOut = mgr.tensor({ 0.0, 0.0, 0.0 }); + auto tensorInA = mgr.tensor({ 2.0, 4.0, 6.0 }); + auto tensorInB = mgr.tensor({ 0.0, 1.0, 2.0 }); + auto tensorOut = mgr.tensor({ 0.0, 0.0, 0.0 }); std::string shader(R"( // The version to use diff --git a/examples/logistic_regression/CMakeLists.txt b/examples/logistic_regression/CMakeLists.txt index f918bbf21..8c8e0eb8f 100644 --- a/examples/logistic_regression/CMakeLists.txt +++ b/examples/logistic_regression/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17.0) +cmake_minimum_required(VERSION 3.4.1) project(kompute_linear_reg VERSION 0.1.0) set(CMAKE_CXX_STANDARD 14) @@ -23,10 +23,6 @@ endif() find_package(Vulkan REQUIRED) -if(KOMPUTE_OPT_ENABLE_SPDLOG) - find_package(spdlog REQUIRED) -endif() - add_executable(kompute_linear_reg src/Main.cpp) @@ -39,7 +35,7 @@ include_directories( ../../single_include/) if(KOMPUTE_OPT_ENABLE_SPDLOG) - target_link_libraries(kompute_array_mult + target_link_libraries(kompute_linear_reg spdlog::spdlog) endif() diff --git a/examples/logistic_regression/README.md b/examples/logistic_regression/README.md index 0de7ee30a..342bbfca1 100644 --- a/examples/logistic_regression/README.md +++ b/examples/logistic_regression/README.md @@ -15,8 +15,11 @@ This project has the option to either import the Kompute dependency relative to To build you just need to run the cmake command in this folder as follows: ``` -cmake \ - -Bbuild +cmake -Bbuild/ \ + -DCMAKE_BUILD_TYPE=Debug \ + -DKOMPUTE_OPT_INSTALL=0 \ + -DKOMPUTE_OPT_REPO_SUBMODULE_BUILD=1 \ + -DKOMPUTE_OPT_ENABLE_SPDLOG=1 ``` You can pass the following optional parameters based on your desired configuration: diff --git a/examples/logistic_regression/src/Main.cpp b/examples/logistic_regression/src/Main.cpp index c435575e2..3b6ec11e1 100755 --- a/examples/logistic_regression/src/Main.cpp +++ b/examples/logistic_regression/src/Main.cpp @@ -17,19 +17,19 @@ int main() kp::Manager mgr; - std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); - std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); + auto xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + auto xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); + auto y = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); - std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto wIn = mgr.tensor({ 0.001, 0.001 }); + auto wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr bIn = mgr.tensor({ 0 }); - std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto bIn = mgr.tensor({ 0 }); + auto bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 593390dbe..41e9434f8 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -762,7 +762,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compileSources( + static std::vector compile_sources( const std::vector& sources, const std::vector& files = {}, const std::string& entryPoint = "main", @@ -783,7 +783,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compileSource( + static std::vector compile_source( const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, @@ -818,6 +818,14 @@ class Tensor eHost = 1, ///< Type is host memory, source and destination eStorage = 2, ///< Type is Device memory (only) }; + enum class TensorDataTypes + { + eBool = 0, + eInt = 1, + eUnsignedInt = 2, + eFloat = 3, + eDouble = 4, + }; /** * Constructor with data provided which would be used to create the @@ -831,14 +839,78 @@ class Tensor */ Tensor(std::shared_ptr physicalDevice, std::shared_ptr device, - const std::vector& data, + void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const TensorDataTypes& dataType, const TensorTypes& tensorType = TensorTypes::eDevice); /** * Destructor which is in charge of freeing vulkan resources unless they * have been provided externally. */ - ~Tensor(); + virtual ~Tensor(); + + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + // TODO: move to cpp + virtual uint32_t size() { + return this->mSize; + } + + // TODO: move to cpp + virtual uint32_t dataTypeMemorySize() { + return this->mDataTypeMemorySize; + } + + // TODO: move to cpp + virtual uint32_t memorySize() { + return this->mSize * this->mDataTypeMemorySize; + } + + /** + * Retrieve the underlying data type of the Tensor + * + * @return Data type of tensor of type kp::Tensor::TensorDataTypes + */ + virtual TensorDataTypes dataType() { + return this->mDataType; + } + + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + virtual void mapDataFromHostMemory(); + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + virtual void mapDataIntoHostMemory(); + + // TODO: Decide whether this is one we prefer to have also overriden in the underlying tensorView + // TODO: move to cpp + virtual void getRawData(void* data) { + this->rawMapDataFromHostMemory(data); + } + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + virtual void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { + if (elementTotalCount * elementMemorySize != this->memorySize()) { + throw std::runtime_error( + "Kompute Tensor Cannot set data of different sizes"); + } + this->mSize = elementTotalCount; + this->mDataTypeMemorySize = elementMemorySize; + this->rawMapDataIntoHostMemory(data); + } /** * Function to trigger reinitialisation of the tensor buffer and memory with @@ -847,8 +919,9 @@ class Tensor * @param data Vector of data to use to initialise vector from * @param tensorType The type to use for the tensor */ - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice); + void rebuild(void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -862,32 +935,6 @@ class Tensor */ bool isInit(); - /** - * Returns the vector of data currently contained by the Tensor. It is - * important to ensure that there is no out-of-sync data with the GPU - * memory. - * - * @return Reference to vector of elements representing the data in the - * tensor. - */ - std::vector& data(); - /** - * Overrides the subscript operator to expose the underlying data's - * subscript operator which in this case would be its underlying - * vector's. - * - * @param i The index where the element will be returned from. - * @return Returns the element in the position requested. - */ - float& operator[](int index); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - uint32_t size(); - /** * Retrieve the tensor type of the Tensor * @@ -895,12 +942,6 @@ class Tensor */ TensorTypes tensorType(); - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setData(const std::vector& data); - /** * Records a copy from the memory of the tensor provided to the current * thensor. This is intended to pass memory into a processing, to perform @@ -963,17 +1004,57 @@ class Tensor * @return Descriptor buffer info with own buffer */ vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory(); + protected: + void rawMapDataFromHostMemory(void* data) { + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + void* mapped = this->mDevice->mapMemory( + *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->invalidateMappedMemoryRanges(mappedMemoryRange); + memcpy(data, mapped, bufferSize); + this->mDevice->unmapMemory(*hostVisibleMemory); + } + + void rawMapDataIntoHostMemory(void* data) { + KP_LOG_DEBUG("Kompute Tensor mapping data into host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + + void* mapped = this->mDevice->mapMemory( + *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + memcpy(mapped, data, bufferSize); + vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->flushMappedMemoryRanges(1, &mappedRange); + this->mDevice->unmapMemory(*hostVisibleMemory); + } private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; @@ -990,9 +1071,10 @@ class Tensor bool mFreeStagingMemory = false; // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - - TensorTypes mTensorType = TensorTypes::eDevice; + TensorTypes mTensorType; + TensorDataTypes mDataType; + uint32_t mSize; + uint32_t mDataTypeMemorySize; void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, @@ -1012,9 +1094,98 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - uint64_t memorySize(); + }; +// TODO: Limit T to be only float, bool, double, etc +template +class TensorView: public Tensor +{ + public: + TensorView(std::shared_ptr physicalDevice, + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) + { + + } + + ~TensorView() { + + } + + void rebuild(const std::vector& data, + TensorTypes tensorType = TensorTypes::eDevice) { + + this->mData = data; + Tensor::rebuild(data.data(), data.size(), sizeof(T)); + } + + std::vector& data() { + return this->mData; + } + + T& operator[](int index) { + return this->mData[index]; + } + + void setData(const std::vector& data) { + + if (data.size() != this->mData.size()) { + throw std::runtime_error( + "Kompute TensorView Cannot set data of different sizes"); + } + + this->mData = data; + + Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); + } + + void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override + { + assert(elementMemorySize == sizeof(T)); + + this->mData = { (T*)data, ((T*)data) + elementTotalCount }; + Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); + } + + TensorDataTypes dataType() override; + + uint32_t size() override { + return this->mData.size(); + } + + uint32_t memorySize() override { + return this->mData.size() * sizeof(T); + } + + /** + * Maps data from the Host Visible GPU memory into the data vector. It + * requires the Tensor to be of staging type for it to work. + */ + void mapDataFromHostMemory() override { + KP_LOG_DEBUG("Kompute TensorView mapDataFromHostMemory copying data"); + + this->rawMapDataFromHostMemory(this->mData.data()); + } + /** + * Maps data from the data vector into the Host Visible GPU memory. It + * requires the tensor to be of staging type for it to work. + */ + void mapDataIntoHostMemory() override { + KP_LOG_DEBUG("Kompute TensorView mapDataIntoHostMemory copying data"); + + this->rawMapDataIntoHostMemory(this->mData.data()); + } + + private: + // -------------- ALWAYS OWNED RESOURCES + std::vector mData; + +}; + + } // End namespace kp namespace kp { @@ -1883,9 +2054,22 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - std::shared_ptr tensor( - const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + template + std::shared_ptr> tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); + + std::shared_ptr> tensor{ new kp::TensorView( + this->mPhysicalDevice, this->mDevice, data, tensorType) }; + + if (this->mManageResources) { + this->mManagedTensors.push_back(tensor); + } + + return tensor; + } /** * Create a managed algorithm that will be destroyed by this manager diff --git a/src/Manager.cpp b/src/Manager.cpp index e3bdbb2d9..5d6bf4cd4 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -395,21 +395,6 @@ Manager::createDevice(const std::vector& familyQueueIndices, KP_LOG_DEBUG("Kompute Manager compute queue obtained"); } -std::shared_ptr -Manager::tensor(const std::vector& data, Tensor::TensorTypes tensorType) -{ - KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - - std::shared_ptr tensor{ new kp::Tensor( - this->mPhysicalDevice, this->mDevice, data, tensorType) }; - - if (this->mManageResources) { - this->mManagedTensors.push_back(tensor); - } - - return tensor; -} - std::shared_ptr Manager::algorithm(const std::vector>& tensors, const std::vector& spirv, diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 6950a4cd2..16e3017e9 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -13,6 +13,14 @@ OpTensorCopy::OpTensorCopy(const std::vector>& tensors) throw std::runtime_error( "Kompute OpTensorCopy called with less than 2 tensor"); } + + kp::Tensor::TensorDataTypes dataType = this->mTensors[0]->dataType(); + for (const std::shared_ptr& tensor : tensors) { + if (tensor->dataType() != dataType) { + throw std::runtime_error(fmt::format("Attempting to copy tensors of different types from {} to {}", + dataType, tensor->dataType())); + } + } } OpTensorCopy::~OpTensorCopy() @@ -43,9 +51,16 @@ OpTensorCopy::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy postEval called"); + // TODO: Simplify with a copyRawData + uint32_t size = this->mTensors[0]->size(); + uint32_t dataTypeMemSize = this->mTensors[0]->dataTypeMemorySize(); + uint32_t memSize = size * dataTypeMemSize; + void* data = operator new(memSize); + this->mTensors[0]->getRawData(data); + // Copy the data from the first tensor into all the tensors for (size_t i = 1; i < this->mTensors.size(); i++) { - this->mTensors[i]->setData(this->mTensors[0]->data()); + this->mTensors[i]->setRawData(data, size, dataTypeMemSize); } } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index dc254fe83..4f188d5af 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -17,8 +17,10 @@ Tensor::Tensor(std::shared_ptr physicalDevice, this->mPhysicalDevice = physicalDevice; this->mDevice = device; + this->mDataType = dataType; + this->mTensorType = tensorType; - this->rebuild(data, elementTotalCount, elementMemorySize, dataType, tensorType); + this->rebuild(data, elementTotalCount, elementMemorySize); } Tensor::~Tensor() @@ -34,16 +36,12 @@ Tensor::~Tensor() void Tensor::rebuild(void* data, uint32_t elementTotalCount, - uint32_t elementMemorySize, - const TensorDataTypes& dataType, - TensorTypes tensorType) + uint32_t elementMemorySize) { KP_LOG_DEBUG("Kompute Tensor rebuilding with size {}", elementTotalCount); this->mSize = elementTotalCount; - this->mElementMemorySize = elementMemorySize; - this->mDataType = dataType; - this->mTensorType = tensorType; + this->mDataTypeMemorySize = elementMemorySize; if (this->mPrimaryBuffer || this->mPrimaryMemory) { KP_LOG_DEBUG( @@ -439,4 +437,34 @@ Tensor::destroy() KP_LOG_DEBUG("Kompute Tensor successful destroy()"); } +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eBool; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eInt; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eUnsignedInt; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eFloat; +} + +template<> +Tensor::TensorDataTypes +TensorView::dataType() { + return Tensor::TensorDataTypes::eDouble; +} + } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index d9c6ddf3e..d27bccacc 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -74,9 +74,22 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - std::shared_ptr tensor( - const std::vector& data, - Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice); + template + std::shared_ptr> tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); + + std::shared_ptr> tensor{ new kp::TensorView( + this->mPhysicalDevice, this->mDevice, data, tensorType) }; + + if (this->mManageResources) { + this->mManagedTensors.push_back(tensor); + } + + return tensor; + } /** * Create a managed algorithm that will be destroyed by this manager diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index f2583708d..03e52d43d 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -51,14 +51,14 @@ class Tensor void* data, uint32_t elementTotalCount, uint32_t elementMemorySize, - const TensorDataTypes& dataType = TensorDataTypes::eFloat, + const TensorDataTypes& dataType, const TensorTypes& tensorType = TensorTypes::eDevice); /** * Destructor which is in charge of freeing vulkan resources unless they * have been provided externally. */ - ~Tensor(); + virtual ~Tensor(); /** * Returns the size/magnitude of the Tensor, which will be the total number @@ -68,12 +68,17 @@ class Tensor */ // TODO: move to cpp virtual uint32_t size() { - return this->mElementMemorySize; + return this->mSize; + } + + // TODO: move to cpp + virtual uint32_t dataTypeMemorySize() { + return this->mDataTypeMemorySize; } // TODO: move to cpp virtual uint32_t memorySize() { - return this->mSize * this->mElementMemorySize; + return this->mSize * this->mDataTypeMemorySize; } /** @@ -98,10 +103,24 @@ class Tensor // TODO: Decide whether this is one we prefer to have also overriden in the underlying tensorView // TODO: move to cpp - void getRawData(void* data) { + virtual void getRawData(void* data) { this->rawMapDataFromHostMemory(data); } + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + virtual void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { + if (elementTotalCount * elementMemorySize != this->memorySize()) { + throw std::runtime_error( + "Kompute Tensor Cannot set data of different sizes"); + } + this->mSize = elementTotalCount; + this->mDataTypeMemorySize = elementMemorySize; + this->rawMapDataIntoHostMemory(data); + } + /** * Function to trigger reinitialisation of the tensor buffer and memory with * new data as well as new potential device type. @@ -111,9 +130,7 @@ class Tensor */ void rebuild(void* data, uint32_t elementTotalCount, - uint32_t elementMemorySize, - const TensorDataTypes& dataType = TensorDataTypes::eFloat, - TensorTypes tensorType = TensorTypes::eDevice); + uint32_t elementMemorySize); /** * Destroys and frees the GPU resources which include the buffer and memory. @@ -134,19 +151,6 @@ class Tensor */ TensorTypes tensorType(); - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { - if (elementTotalCount * elementMemorySize != this->memorySize()) { - throw std::runtime_error( - "Kompute Tensor Cannot set data of different sizes"); - } - this->mSize = elementTotalCount; - this->mElementMemorySize = elementMemorySize; - this->rawMapDataIntoHostMemory(data); - } /** * Records a copy from the memory of the tensor provided to the current @@ -211,46 +215,7 @@ class Tensor */ vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - private: - // -------------- NEVER OWNED RESOURCES - std::shared_ptr mPhysicalDevice; - std::shared_ptr mDevice; - - // -------------- OPTIONALLY OWNED RESOURCES - std::shared_ptr mPrimaryBuffer; - bool mFreePrimaryBuffer = false; - std::shared_ptr mStagingBuffer; - bool mFreeStagingBuffer = false; - std::shared_ptr mPrimaryMemory; - bool mFreePrimaryMemory = false; - std::shared_ptr mStagingMemory; - bool mFreeStagingMemory = false; - - // -------------- ALWAYS OWNED RESOURCES - TensorTypes mTensorType; - TensorDataTypes mDataType; - uint32_t mSize; - uint32_t mElementMemorySize; - - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer - void createBuffer(std::shared_ptr buffer, - vk::BufferUsageFlags bufferUsageFlags); - void allocateBindMemory(std::shared_ptr buffer, - std::shared_ptr memory, - vk::MemoryPropertyFlags memoryPropertyFlags); - void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, - std::shared_ptr bufferFrom, - std::shared_ptr bufferTo, - vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); - - // Private util functions - vk::BufferUsageFlags getPrimaryBufferUsageFlags(); - vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); - vk::BufferUsageFlags getStagingBufferUsageFlags(); - vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - + protected: void rawMapDataFromHostMemory(void* data) { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -300,6 +265,46 @@ class Tensor this->mDevice->flushMappedMemoryRanges(1, &mappedRange); this->mDevice->unmapMemory(*hostVisibleMemory); } + private: + // -------------- NEVER OWNED RESOURCES + std::shared_ptr mPhysicalDevice; + std::shared_ptr mDevice; + + // -------------- OPTIONALLY OWNED RESOURCES + std::shared_ptr mPrimaryBuffer; + bool mFreePrimaryBuffer = false; + std::shared_ptr mStagingBuffer; + bool mFreeStagingBuffer = false; + std::shared_ptr mPrimaryMemory; + bool mFreePrimaryMemory = false; + std::shared_ptr mStagingMemory; + bool mFreeStagingMemory = false; + + // -------------- ALWAYS OWNED RESOURCES + TensorTypes mTensorType; + TensorDataTypes mDataType; + uint32_t mSize; + uint32_t mDataTypeMemorySize; + + void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer + void createBuffer(std::shared_ptr buffer, + vk::BufferUsageFlags bufferUsageFlags); + void allocateBindMemory(std::shared_ptr buffer, + std::shared_ptr memory, + vk::MemoryPropertyFlags memoryPropertyFlags); + void recordCopyBuffer(const vk::CommandBuffer& commandBuffer, + std::shared_ptr bufferFrom, + std::shared_ptr bufferTo, + vk::DeviceSize bufferSize, + vk::BufferCopy copyRegion, + bool createBarrier); + + // Private util functions + vk::BufferUsageFlags getPrimaryBufferUsageFlags(); + vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); + vk::BufferUsageFlags getStagingBufferUsageFlags(); + vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + }; // TODO: Limit T to be only float, bool, double, etc @@ -310,15 +315,21 @@ class TensorView: public Tensor TensorView(std::shared_ptr physicalDevice, std::shared_ptr device, const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice); + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) + { - ~TensorView(); + } + + ~TensorView() { + + } void rebuild(const std::vector& data, TensorTypes tensorType = TensorTypes::eDevice) { this->mData = data; - Tensor::rebuild(data.data(), data.size(), sizeof(T), this->dataType(), tensorType); + Tensor::rebuild(data.data(), data.size(), sizeof(T)); } std::vector& data() { @@ -338,17 +349,25 @@ class TensorView: public Tensor this->mData = data; - this->setRawData(this->mData.data(), this->mData.size(), sizeof(T), this->dataType()); + Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); + } + + void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override + { + assert(elementMemorySize == sizeof(T)); + + this->mData = { (T*)data, ((T*)data) + elementTotalCount }; + Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); } TensorDataTypes dataType() override; uint32_t size() override { - return this->mData->size(); + return this->mData.size(); } uint32_t memorySize() override { - return this->mData->size() * sizeof(T); + return this->mData.size() * sizeof(T); } /** @@ -376,34 +395,4 @@ class TensorView: public Tensor }; -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eBool; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eInt; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eUnsignedInt; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eFloat; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eDouble; -} - } // End namespace kp From 956883e0cdee22541b284892ff3f53efcf562cbf Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 17:44:17 +0000 Subject: [PATCH 55/91] Working iteration of kompute tensor with multiplee types --- examples/array_multiplication/src/Main.cpp | 8 +++++++- single_include/kompute/Kompute.hpp | 15 +++++++++++---- src/Tensor.cpp | 1 + src/include/kompute/Tensor.hpp | 14 +++++++++++--- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index dacc67f89..812a5039f 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -7,6 +7,11 @@ int main() { +#if KOMPUTE_ENABLE_SPDLOG + spdlog::set_level( + static_cast(SPDLOG_ACTIVE_LEVEL)); +#endif + kp::Manager mgr; auto tensorInA = mgr.tensor({ 2.0, 4.0, 6.0 }); @@ -39,7 +44,8 @@ int main() mgr.sequence() ->record(params) ->record(algo) - ->record(params); + ->record(params) + ->eval(); // prints "Output { 0 4 12 }" std::cout<< "Output: { "; diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 41e9434f8..989f58c20 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1108,16 +1108,17 @@ class TensorView: public Tensor const TensorTypes& tensorType = TensorTypes::eDevice) : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) { - + KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); + this->mData = data; } ~TensorView() { - + KP_LOG_DEBUG("Kompute TensorView destructor"); } void rebuild(const std::vector& data, TensorTypes tensorType = TensorTypes::eDevice) { - + KP_LOG_DEBUG("Kompute TensorView creating with data size {}", data.size()); this->mData = data; Tensor::rebuild(data.data(), data.size(), sizeof(T)); } @@ -1131,6 +1132,7 @@ class TensorView: public Tensor } void setData(const std::vector& data) { + KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); if (data.size() != this->mData.size()) { throw std::runtime_error( @@ -1144,6 +1146,8 @@ class TensorView: public Tensor void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override { + KP_LOG_DEBUG("Kompute TensorView setRawData with data size {}", elementTotalCount); + assert(elementMemorySize == sizeof(T)); this->mData = { (T*)data, ((T*)data) + elementTotalCount }; @@ -1153,10 +1157,14 @@ class TensorView: public Tensor TensorDataTypes dataType() override; uint32_t size() override { + KP_LOG_DEBUG("Kompute TensorView retrieving size: {}", this->mData.size()); + return this->mData.size(); } uint32_t memorySize() override { + KP_LOG_DEBUG("Kompute TensorView retrieving memory size: {}", this->mData.size() * sizeof(T)); + return this->mData.size() * sizeof(T); } @@ -1185,7 +1193,6 @@ class TensorView: public Tensor }; - } // End namespace kp namespace kp { diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 4f188d5af..d3225987e 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -170,6 +170,7 @@ Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, vk::DescriptorBufferInfo Tensor::constructDescriptorBufferInfo() { + KP_LOG_WARN("Kompute Tensor construct descriptor buffer info size {}", this->memorySize()); vk::DeviceSize bufferSize = this->memorySize(); return vk::DescriptorBufferInfo(*this->mPrimaryBuffer, 0, // offset diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 03e52d43d..6af4682d6 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -318,16 +318,17 @@ class TensorView: public Tensor const TensorTypes& tensorType = TensorTypes::eDevice) : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) { - + KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); + this->mData = data; } ~TensorView() { - + KP_LOG_DEBUG("Kompute TensorView destructor"); } void rebuild(const std::vector& data, TensorTypes tensorType = TensorTypes::eDevice) { - + KP_LOG_DEBUG("Kompute TensorView creating with data size {}", data.size()); this->mData = data; Tensor::rebuild(data.data(), data.size(), sizeof(T)); } @@ -341,6 +342,7 @@ class TensorView: public Tensor } void setData(const std::vector& data) { + KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); if (data.size() != this->mData.size()) { throw std::runtime_error( @@ -354,6 +356,8 @@ class TensorView: public Tensor void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override { + KP_LOG_DEBUG("Kompute TensorView setRawData with data size {}", elementTotalCount); + assert(elementMemorySize == sizeof(T)); this->mData = { (T*)data, ((T*)data) + elementTotalCount }; @@ -363,10 +367,14 @@ class TensorView: public Tensor TensorDataTypes dataType() override; uint32_t size() override { + KP_LOG_DEBUG("Kompute TensorView retrieving size: {}", this->mData.size()); + return this->mData.size(); } uint32_t memorySize() override { + KP_LOG_DEBUG("Kompute TensorView retrieving memory size: {}", this->mData.size() * sizeof(T)); + return this->mData.size() * sizeof(T); } From cf7d46cd23a0e76cbc181eb58dbb059a73f22ee2 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 6 Mar 2021 19:42:41 +0000 Subject: [PATCH 56/91] Initial simpification of interface implementation --- single_include/kompute/Kompute.hpp | 215 ++++++++-------------------- src/OpTensorCopy.cpp | 11 +- src/OpTensorSyncDevice.cpp | 6 - src/OpTensorSyncLocal.cpp | 5 - src/Tensor.cpp | 49 +------ src/include/kompute/Tensor.hpp | 216 ++++++++--------------------- 6 files changed, 135 insertions(+), 367 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 989f58c20..496e6f198 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -851,67 +851,6 @@ class Tensor */ virtual ~Tensor(); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - // TODO: move to cpp - virtual uint32_t size() { - return this->mSize; - } - - // TODO: move to cpp - virtual uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } - - // TODO: move to cpp - virtual uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } - - /** - * Retrieve the underlying data type of the Tensor - * - * @return Data type of tensor of type kp::Tensor::TensorDataTypes - */ - virtual TensorDataTypes dataType() { - return this->mDataType; - } - - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - virtual void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - virtual void mapDataIntoHostMemory(); - - // TODO: Decide whether this is one we prefer to have also overriden in the underlying tensorView - // TODO: move to cpp - virtual void getRawData(void* data) { - this->rawMapDataFromHostMemory(data); - } - - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - virtual void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { - if (elementTotalCount * elementMemorySize != this->memorySize()) { - throw std::runtime_error( - "Kompute Tensor Cannot set data of different sizes"); - } - this->mSize = elementTotalCount; - this->mDataTypeMemorySize = elementMemorySize; - this->rawMapDataIntoHostMemory(data); - } - /** * Function to trigger reinitialisation of the tensor buffer and memory with * new data as well as new potential device type. @@ -1005,8 +944,53 @@ class Tensor */ vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - protected: - void rawMapDataFromHostMemory(void* data) { + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + // TODO: move to cpp + uint32_t size() { + return this->mSize; + } + + // TODO: move to cpp + uint32_t dataTypeMemorySize() { + return this->mDataTypeMemorySize; + } + + // TODO: move to cpp + uint32_t memorySize() { + return this->mSize * this->mDataTypeMemorySize; + } + + /** + * Retrieve the underlying data type of the Tensor + * + * @return Data type of tensor of type kp::Tensor::TensorDataTypes + */ + TensorDataTypes dataType() { + return this->mDataType; + } + + // TODO: move to cpp + const void* getRawData() { + return this->mRawData; + } + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + void setRawData(const void* data) + { + // Copy data + memcpy(this->mRawData, data, this->memorySize()); + } + + private: + void rawMapData() { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1023,39 +1007,12 @@ class Tensor } vk::DeviceSize bufferSize = this->memorySize(); - void* mapped = this->mDevice->mapMemory( + // Given we request coherent host memory we don't need to invalidate / flush + this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->invalidateMappedMemoryRanges(mappedMemoryRange); - memcpy(data, mapped, bufferSize); - this->mDevice->unmapMemory(*hostVisibleMemory); } - void rawMapDataIntoHostMemory(void* data) { - KP_LOG_DEBUG("Kompute Tensor mapping data into host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - - void* mapped = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - memcpy(mapped, data, bufferSize); - vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->flushMappedMemoryRanges(1, &mappedRange); - this->mDevice->unmapMemory(*hostVisibleMemory); - } - private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; @@ -1075,6 +1032,7 @@ class Tensor TensorDataTypes mDataType; uint32_t mSize; uint32_t mDataTypeMemorySize; + void* mRawData; void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, @@ -1106,91 +1064,40 @@ class TensorView: public Tensor std::shared_ptr device, const std::vector& data, const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType()) { KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); - this->mData = data; } ~TensorView() { KP_LOG_DEBUG("Kompute TensorView destructor"); } - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice) { - KP_LOG_DEBUG("Kompute TensorView creating with data size {}", data.size()); - this->mData = data; - Tensor::rebuild(data.data(), data.size(), sizeof(T)); - } - - std::vector& data() { - return this->mData; + std::vector data() { + return { (T*)this->getRawData(), ((T*)this->getRawData()) + this->size() }; } T& operator[](int index) { - return this->mData[index]; + return ((T*)this->mRawData)[index]; } void setData(const std::vector& data) { + KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); - if (data.size() != this->mData.size()) { + if (data.size() != this->mSize) { throw std::runtime_error( "Kompute TensorView Cannot set data of different sizes"); } - this->mData = data; - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); } - void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override - { - KP_LOG_DEBUG("Kompute TensorView setRawData with data size {}", elementTotalCount); - - assert(elementMemorySize == sizeof(T)); - - this->mData = { (T*)data, ((T*)data) + elementTotalCount }; - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); - } - - TensorDataTypes dataType() override; - - uint32_t size() override { - KP_LOG_DEBUG("Kompute TensorView retrieving size: {}", this->mData.size()); - - return this->mData.size(); - } - - uint32_t memorySize() override { - KP_LOG_DEBUG("Kompute TensorView retrieving memory size: {}", this->mData.size() * sizeof(T)); - - return this->mData.size() * sizeof(T); - } - - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory() override { - KP_LOG_DEBUG("Kompute TensorView mapDataFromHostMemory copying data"); - - this->rawMapDataFromHostMemory(this->mData.data()); - } - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory() override { - KP_LOG_DEBUG("Kompute TensorView mapDataIntoHostMemory copying data"); - - this->rawMapDataIntoHostMemory(this->mData.data()); - } - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - }; } // End namespace kp diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 16e3017e9..ce53455a3 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -15,11 +15,17 @@ OpTensorCopy::OpTensorCopy(const std::vector>& tensors) } kp::Tensor::TensorDataTypes dataType = this->mTensors[0]->dataType(); + uint32_t size = this->mTensors[0]->size(); for (const std::shared_ptr& tensor : tensors) { if (tensor->dataType() != dataType) { throw std::runtime_error(fmt::format("Attempting to copy tensors of different types from {} to {}", dataType, tensor->dataType())); } + if (tensor->size() != size) { + throw std::runtime_error(fmt::format("Attempting to copy tensors of different sizes from {} to {}", + size, tensor->size())); + + } } } @@ -55,12 +61,11 @@ OpTensorCopy::postEval(const vk::CommandBuffer& commandBuffer) uint32_t size = this->mTensors[0]->size(); uint32_t dataTypeMemSize = this->mTensors[0]->dataTypeMemorySize(); uint32_t memSize = size * dataTypeMemSize; - void* data = operator new(memSize); - this->mTensors[0]->getRawData(data); + const void* data = this->mTensors[0]->getRawData(); // Copy the data from the first tensor into all the tensors for (size_t i = 1; i < this->mTensors.size(); i++) { - this->mTensors[i]->setRawData(data, size, dataTypeMemSize); + this->mTensors[i]->setRawData(data); } } diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index 85cefde77..4dbfaec83 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -41,12 +41,6 @@ OpTensorSyncDevice::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice preEval called"); - // Performing sync of data as eval can be called multiple times with same op - for (size_t i = 0; i < this->mTensors.size(); i++) { - if (this->mTensors[i]->tensorType() != Tensor::TensorTypes::eStorage) { - this->mTensors[i]->mapDataIntoHostMemory(); - } - } } void diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 092490d15..f7e15ffd5 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -48,11 +48,6 @@ OpTensorSyncLocal::postEval(const vk::CommandBuffer& commandBuffer) KP_LOG_DEBUG("Kompute OpTensorSyncLocal postEval called"); KP_LOG_DEBUG("Kompute OpTensorSyncLocal mapping data into tensor local"); - for (size_t i = 0; i < this->mTensors.size(); i++) { - if (this->mTensors[i]->tensorType() != Tensor::TensorTypes::eStorage) { - this->mTensors[i]->mapDataFromHostMemory(); - } - } } } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index d3225987e..4d7dcd2db 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -50,7 +50,9 @@ Tensor::rebuild(void* data, } this->allocateMemoryCreateGPUResources(); - this->rawMapDataIntoHostMemory(data); + this->rawMapData(); + + memcpy(this->mRawData, data, this->memorySize()); } Tensor::TensorTypes @@ -177,18 +179,6 @@ Tensor::constructDescriptorBufferInfo() bufferSize); } -void -Tensor::mapDataFromHostMemory() -{ - KP_LOG_DEBUG("Kompute Tensor mapDataFromHostMemory - SKIPPING"); -} - -void -Tensor::mapDataIntoHostMemory() -{ - KP_LOG_DEBUG("Kompute Tensor mapDataIntoHostMemory - SKIPPING"); -} - vk::BufferUsageFlags Tensor::getPrimaryBufferUsageFlags() { @@ -219,7 +209,8 @@ Tensor::getPrimaryMemoryPropertyFlags() return vk::MemoryPropertyFlagBits::eDeviceLocal; break; case TensorTypes::eHost: - return vk::MemoryPropertyFlagBits::eHostVisible; + return vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent; break; case TensorTypes::eStorage: return vk::MemoryPropertyFlagBits::eDeviceLocal; @@ -438,34 +429,4 @@ Tensor::destroy() KP_LOG_DEBUG("Kompute Tensor successful destroy()"); } -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eBool; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eInt; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eUnsignedInt; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eFloat; -} - -template<> -Tensor::TensorDataTypes -TensorView::dataType() { - return Tensor::TensorDataTypes::eDouble; -} - } diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 6af4682d6..f041d57e3 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -60,67 +60,6 @@ class Tensor */ virtual ~Tensor(); - /** - * Returns the size/magnitude of the Tensor, which will be the total number - * of elements across all dimensions - * - * @return Unsigned integer representing the total number of elements - */ - // TODO: move to cpp - virtual uint32_t size() { - return this->mSize; - } - - // TODO: move to cpp - virtual uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } - - // TODO: move to cpp - virtual uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } - - /** - * Retrieve the underlying data type of the Tensor - * - * @return Data type of tensor of type kp::Tensor::TensorDataTypes - */ - virtual TensorDataTypes dataType() { - return this->mDataType; - } - - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - virtual void mapDataFromHostMemory(); - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - virtual void mapDataIntoHostMemory(); - - // TODO: Decide whether this is one we prefer to have also overriden in the underlying tensorView - // TODO: move to cpp - virtual void getRawData(void* data) { - this->rawMapDataFromHostMemory(data); - } - - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - virtual void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) { - if (elementTotalCount * elementMemorySize != this->memorySize()) { - throw std::runtime_error( - "Kompute Tensor Cannot set data of different sizes"); - } - this->mSize = elementTotalCount; - this->mDataTypeMemorySize = elementMemorySize; - this->rawMapDataIntoHostMemory(data); - } - /** * Function to trigger reinitialisation of the tensor buffer and memory with * new data as well as new potential device type. @@ -151,7 +90,6 @@ class Tensor */ TensorTypes tensorType(); - /** * Records a copy from the memory of the tensor provided to the current * thensor. This is intended to pass memory into a processing, to perform @@ -215,8 +153,53 @@ class Tensor */ vk::DescriptorBufferInfo constructDescriptorBufferInfo(); - protected: - void rawMapDataFromHostMemory(void* data) { + /** + * Returns the size/magnitude of the Tensor, which will be the total number + * of elements across all dimensions + * + * @return Unsigned integer representing the total number of elements + */ + // TODO: move to cpp + uint32_t size() { + return this->mSize; + } + + // TODO: move to cpp + uint32_t dataTypeMemorySize() { + return this->mDataTypeMemorySize; + } + + // TODO: move to cpp + uint32_t memorySize() { + return this->mSize * this->mDataTypeMemorySize; + } + + /** + * Retrieve the underlying data type of the Tensor + * + * @return Data type of tensor of type kp::Tensor::TensorDataTypes + */ + TensorDataTypes dataType() { + return this->mDataType; + } + + // TODO: move to cpp + const void* getRawData() { + return this->mRawData; + } + + /** + * Sets / resets the vector data of the tensor. This function does not + * perform any copies into GPU memory and is only performed on the host. + */ + void setRawData(const void* data) + { + // Copy data + memcpy(this->mRawData, data, this->memorySize()); + } + + private: + void rawMapData() { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -233,39 +216,12 @@ class Tensor } vk::DeviceSize bufferSize = this->memorySize(); - void* mapped = this->mDevice->mapMemory( + // Given we request coherent host memory we don't need to invalidate / flush + this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->invalidateMappedMemoryRanges(mappedMemoryRange); - memcpy(data, mapped, bufferSize); - this->mDevice->unmapMemory(*hostVisibleMemory); } - void rawMapDataIntoHostMemory(void* data) { - KP_LOG_DEBUG("Kompute Tensor mapping data into host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - - void* mapped = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - memcpy(mapped, data, bufferSize); - vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->flushMappedMemoryRanges(1, &mappedRange); - this->mDevice->unmapMemory(*hostVisibleMemory); - } - private: // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; @@ -285,6 +241,7 @@ class Tensor TensorDataTypes mDataType; uint32_t mSize; uint32_t mDataTypeMemorySize; + void* mRawData; void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, @@ -316,91 +273,40 @@ class TensorView: public Tensor std::shared_ptr device, const std::vector& data, const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, device, (void*)data.data(), data.size(), sizeof(T), this->dataType()) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType()) { KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); - this->mData = data; } ~TensorView() { KP_LOG_DEBUG("Kompute TensorView destructor"); } - void rebuild(const std::vector& data, - TensorTypes tensorType = TensorTypes::eDevice) { - KP_LOG_DEBUG("Kompute TensorView creating with data size {}", data.size()); - this->mData = data; - Tensor::rebuild(data.data(), data.size(), sizeof(T)); - } - - std::vector& data() { - return this->mData; + std::vector data() { + return { (T*)this->getRawData(), ((T*)this->getRawData()) + this->size() }; } T& operator[](int index) { - return this->mData[index]; + return ((T*)this->mRawData)[index]; } void setData(const std::vector& data) { + KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); - if (data.size() != this->mData.size()) { + if (data.size() != this->mSize) { throw std::runtime_error( "Kompute TensorView Cannot set data of different sizes"); } - this->mData = data; - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); } - void setRawData(void* data, uint32_t elementTotalCount, uint32_t elementMemorySize) override - { - KP_LOG_DEBUG("Kompute TensorView setRawData with data size {}", elementTotalCount); - - assert(elementMemorySize == sizeof(T)); - - this->mData = { (T*)data, ((T*)data) + elementTotalCount }; - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); - } - - TensorDataTypes dataType() override; - - uint32_t size() override { - KP_LOG_DEBUG("Kompute TensorView retrieving size: {}", this->mData.size()); - - return this->mData.size(); - } - - uint32_t memorySize() override { - KP_LOG_DEBUG("Kompute TensorView retrieving memory size: {}", this->mData.size() * sizeof(T)); - - return this->mData.size() * sizeof(T); - } - - /** - * Maps data from the Host Visible GPU memory into the data vector. It - * requires the Tensor to be of staging type for it to work. - */ - void mapDataFromHostMemory() override { - KP_LOG_DEBUG("Kompute TensorView mapDataFromHostMemory copying data"); - - this->rawMapDataFromHostMemory(this->mData.data()); - } - /** - * Maps data from the data vector into the Host Visible GPU memory. It - * requires the tensor to be of staging type for it to work. - */ - void mapDataIntoHostMemory() override { - KP_LOG_DEBUG("Kompute TensorView mapDataIntoHostMemory copying data"); - - this->rawMapDataIntoHostMemory(this->mData.data()); - } - - private: - // -------------- ALWAYS OWNED RESOURCES - std::vector mData; - }; } // End namespace kp From f02b9d6915c9e05b71b426c5f609178c5946aa04 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 08:00:19 +0000 Subject: [PATCH 57/91] Working implementation with tests --- examples/array_multiplication/src/Main.cpp | 8 +-- examples/logistic_regression/src/Main.cpp | 21 ++++--- single_include/kompute/Kompute.hpp | 72 +++++++++++++++------- src/OpTensorCopy.cpp | 2 +- src/Tensor.cpp | 30 +++++++++ src/include/kompute/Manager.hpp | 11 +++- src/include/kompute/Tensor.hpp | 61 +++++++++++------- test/TestAsyncOperations.cpp | 12 ++-- test/TestDestroy.cpp | 18 +++--- test/TestLogisticRegression.cpp | 38 ++++++------ test/TestManager.cpp | 24 ++++---- test/TestMultipleAlgoExecutions.cpp | 24 ++++---- test/TestOpShadersFromStringAndFile.cpp | 24 ++++---- test/TestOpTensorCopy.cpp | 56 ++++++++--------- test/TestOpTensorCreate.cpp | 14 ++--- test/TestOpTensorSync.cpp | 16 ++--- test/TestPushConstant.cpp | 10 +-- test/TestSequence.cpp | 10 +-- test/TestSpecializationConstant.cpp | 8 +-- test/TestTensor.cpp | 4 +- test/TestWorkgroup.cpp | 50 +++++++-------- 21 files changed, 297 insertions(+), 216 deletions(-) diff --git a/examples/array_multiplication/src/Main.cpp b/examples/array_multiplication/src/Main.cpp index 812a5039f..95e0781ad 100755 --- a/examples/array_multiplication/src/Main.cpp +++ b/examples/array_multiplication/src/Main.cpp @@ -14,9 +14,9 @@ int main() kp::Manager mgr; - auto tensorInA = mgr.tensor({ 2.0, 4.0, 6.0 }); - auto tensorInB = mgr.tensor({ 0.0, 1.0, 2.0 }); - auto tensorOut = mgr.tensor({ 0.0, 0.0, 0.0 }); + auto tensorInA = mgr.tensor({ 2.0, 4.0, 6.0 }); + auto tensorInB = mgr.tensor({ 0.0, 1.0, 2.0 }); + auto tensorOut = mgr.tensor({ 0.0, 0.0, 0.0 }); std::string shader(R"( // The version to use @@ -49,7 +49,7 @@ int main() // prints "Output { 0 4 12 }" std::cout<< "Output: { "; - for (const float& elem : tensorOut->data()) { + for (const float& elem : tensorOut->vector()) { std::cout << elem << " "; } std::cout << "}" << std::endl; diff --git a/examples/logistic_regression/src/Main.cpp b/examples/logistic_regression/src/Main.cpp index 3b6ec11e1..c7cc827ba 100755 --- a/examples/logistic_regression/src/Main.cpp +++ b/examples/logistic_regression/src/Main.cpp @@ -17,19 +17,19 @@ int main() kp::Manager mgr; - auto xI = mgr.tensor({ 0, 1, 1, 1, 1 }); - auto xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); + auto xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + auto xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - auto y = mgr.tensor({ 0, 0, 0, 1, 1 }); + auto y = mgr.tensor({ 0, 0, 0, 1, 1 }); - auto wIn = mgr.tensor({ 0.001, 0.001 }); - auto wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - auto wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto wIn = mgr.tensor({ 0.001, 0.001 }); + auto wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - auto bIn = mgr.tensor({ 0 }); - auto bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto bIn = mgr.tensor({ 0 }); + auto bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); - auto lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + auto lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, @@ -40,7 +40,8 @@ int main() (uint32_t*)(kp::shader_data::shaders_glsl_logisticregression_comp_spv + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len)); - std::shared_ptr algo = mgr.algorithm(params, spirv); + std::shared_ptr algo = mgr.algorithm( + params, spirv, kp::Workgroup({ 5 }), kp::Constants({ 5.0 })); mgr.sequence()->eval(params); diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 496e6f198..df9549aab 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -974,11 +974,21 @@ class Tensor return this->mDataType; } - // TODO: move to cpp - const void* getRawData() { + void* rawData() { return this->mRawData; } + // TODO: move to cpp + template + T* data() { + return this->mRawData; + } + + template + std::vector vector() { + return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; + } + /** * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. @@ -989,6 +999,14 @@ class Tensor memcpy(this->mRawData, data, this->memorySize()); } + protected: + // -------------- ALWAYS OWNED RESOURCES + TensorTypes mTensorType; + TensorDataTypes mDataType; + uint32_t mSize; + uint32_t mDataTypeMemorySize; + void* mRawData; + private: void rawMapData() { @@ -1027,13 +1045,6 @@ class Tensor std::shared_ptr mStagingMemory; bool mFreeStagingMemory = false; - // -------------- ALWAYS OWNED RESOURCES - TensorTypes mTensorType; - TensorDataTypes mDataType; - uint32_t mSize; - uint32_t mDataTypeMemorySize; - void* mRawData; - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, vk::BufferUsageFlags bufferUsageFlags); @@ -1057,10 +1068,11 @@ class Tensor // TODO: Limit T to be only float, bool, double, etc template -class TensorView: public Tensor +class TensorT: public Tensor { + public: - TensorView(std::shared_ptr physicalDevice, + TensorT(std::shared_ptr physicalDevice, std::shared_ptr device, const std::vector& data, const TensorTypes& tensorType = TensorTypes::eDevice) @@ -1069,35 +1081,42 @@ class TensorView: public Tensor (void*)data.data(), data.size(), sizeof(T), - this->dataType()) + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); } - ~TensorView() { - KP_LOG_DEBUG("Kompute TensorView destructor"); + ~TensorT() { + KP_LOG_DEBUG("Kompute TensorT destructor"); } - std::vector data() { - return { (T*)this->getRawData(), ((T*)this->getRawData()) + this->size() }; + T* data() { + return (T*)this->mRawData; + } + + std::vector vector() { + return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } T& operator[](int index) { - return ((T*)this->mRawData)[index]; + return *(((T*)this->mRawData) + index); } void setData(const std::vector& data) { - KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); if (data.size() != this->mSize) { throw std::runtime_error( - "Kompute TensorView Cannot set data of different sizes"); + "Kompute TensorT Cannot set data of different sizes"); } - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); + Tensor::setRawData(data.data()); } + TensorDataTypes dataType(); + }; } // End namespace kp @@ -1969,13 +1988,13 @@ class Manager * @returns Shared pointer with initialised tensor */ template - std::shared_ptr> tensor( + std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - std::shared_ptr> tensor{ new kp::TensorView( + std::shared_ptr> tensor{ new kp::TensorT( this->mPhysicalDevice, this->mDevice, data, tensorType) }; if (this->mManageResources) { @@ -1985,6 +2004,13 @@ class Manager return tensor; } + std::shared_ptr> tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + return this->tensorT(data, tensorType); + } + /** * Create a managed algorithm that will be destroyed by this manager * if it hasn't been destroyed by its reference count going to zero. diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index ce53455a3..c93830902 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -61,7 +61,7 @@ OpTensorCopy::postEval(const vk::CommandBuffer& commandBuffer) uint32_t size = this->mTensors[0]->size(); uint32_t dataTypeMemSize = this->mTensors[0]->dataTypeMemorySize(); uint32_t memSize = size * dataTypeMemSize; - const void* data = this->mTensors[0]->getRawData(); + void* data = this->mTensors[0]->rawData(); // Copy the data from the first tensor into all the tensors for (size_t i = 1; i < this->mTensors.size(); i++) { diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 4d7dcd2db..335e48959 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -429,4 +429,34 @@ Tensor::destroy() KP_LOG_DEBUG("Kompute Tensor successful destroy()"); } +template<> +Tensor::TensorDataTypes +TensorT::dataType() { + return Tensor::TensorDataTypes::eBool; +} + +template<> +Tensor::TensorDataTypes +TensorT::dataType() { + return Tensor::TensorDataTypes::eInt; +} + +template<> +Tensor::TensorDataTypes +TensorT::dataType() { + return Tensor::TensorDataTypes::eUnsignedInt; +} + +template<> +Tensor::TensorDataTypes +TensorT::dataType() { + return Tensor::TensorDataTypes::eFloat; +} + +template<> +Tensor::TensorDataTypes +TensorT::dataType() { + return Tensor::TensorDataTypes::eDouble; +} + } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index d27bccacc..c39f5d6b5 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -75,13 +75,13 @@ class Manager * @returns Shared pointer with initialised tensor */ template - std::shared_ptr> tensor( + std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { KP_LOG_DEBUG("Kompute Manager tensor creation triggered"); - std::shared_ptr> tensor{ new kp::TensorView( + std::shared_ptr> tensor{ new kp::TensorT( this->mPhysicalDevice, this->mDevice, data, tensorType) }; if (this->mManageResources) { @@ -91,6 +91,13 @@ class Manager return tensor; } + std::shared_ptr> tensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + return this->tensorT(data, tensorType); + } + /** * Create a managed algorithm that will be destroyed by this manager * if it hasn't been destroyed by its reference count going to zero. diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index f041d57e3..898a2df08 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -183,11 +183,21 @@ class Tensor return this->mDataType; } - // TODO: move to cpp - const void* getRawData() { + void* rawData() { return this->mRawData; } + // TODO: move to cpp + template + T* data() { + return this->mRawData; + } + + template + std::vector vector() { + return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; + } + /** * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. @@ -198,6 +208,14 @@ class Tensor memcpy(this->mRawData, data, this->memorySize()); } + protected: + // -------------- ALWAYS OWNED RESOURCES + TensorTypes mTensorType; + TensorDataTypes mDataType; + uint32_t mSize; + uint32_t mDataTypeMemorySize; + void* mRawData; + private: void rawMapData() { @@ -236,13 +254,6 @@ class Tensor std::shared_ptr mStagingMemory; bool mFreeStagingMemory = false; - // -------------- ALWAYS OWNED RESOURCES - TensorTypes mTensorType; - TensorDataTypes mDataType; - uint32_t mSize; - uint32_t mDataTypeMemorySize; - void* mRawData; - void allocateMemoryCreateGPUResources(); // Creates the vulkan buffer void createBuffer(std::shared_ptr buffer, vk::BufferUsageFlags bufferUsageFlags); @@ -266,10 +277,11 @@ class Tensor // TODO: Limit T to be only float, bool, double, etc template -class TensorView: public Tensor +class TensorT: public Tensor { + public: - TensorView(std::shared_ptr physicalDevice, + TensorT(std::shared_ptr physicalDevice, std::shared_ptr device, const std::vector& data, const TensorTypes& tensorType = TensorTypes::eDevice) @@ -278,35 +290,42 @@ class TensorView: public Tensor (void*)data.data(), data.size(), sizeof(T), - this->dataType()) + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorView constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); } - ~TensorView() { - KP_LOG_DEBUG("Kompute TensorView destructor"); + ~TensorT() { + KP_LOG_DEBUG("Kompute TensorT destructor"); } - std::vector data() { - return { (T*)this->getRawData(), ((T*)this->getRawData()) + this->size() }; + T* data() { + return (T*)this->mRawData; + } + + std::vector vector() { + return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } T& operator[](int index) { - return ((T*)this->mRawData)[index]; + return *(((T*)this->mRawData) + index); } void setData(const std::vector& data) { - KP_LOG_DEBUG("Kompute TensorView setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); if (data.size() != this->mSize) { throw std::runtime_error( - "Kompute TensorView Cannot set data of different sizes"); + "Kompute TensorT Cannot set data of different sizes"); } - Tensor::setRawData(this->mData.data(), this->mData.size(), sizeof(T)); + Tensor::setRawData(data.data()); } + TensorDataTypes dataType(); + }; } // End namespace kp diff --git a/test/TestAsyncOperations.cpp b/test/TestAsyncOperations.cpp index 2f8c7d819..7feaaa30e 100644 --- a/test/TestAsyncOperations.cpp +++ b/test/TestAsyncOperations.cpp @@ -73,7 +73,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) sq->eval(inputsSyncB); for (uint32_t i = 0; i < numParallel; i++) { - EXPECT_EQ(inputsSyncB[i]->data(), resultSync); + EXPECT_EQ(inputsSyncB[i]->vector(), resultSync); } kp::Manager mgrAsync(0, { 0, 2 }); @@ -111,7 +111,7 @@ TEST(TestAsyncOperations, TestManagerParallelExecution) sq->eval({ inputsAsyncB }); for (uint32_t i = 0; i < numParallel; i++) { - EXPECT_EQ(inputsAsyncB[i]->data(), resultAsync); + EXPECT_EQ((inputsAsyncB[i]->vector()), resultAsync); } // The speedup should be at least 40% @@ -152,8 +152,8 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor(data); - std::shared_ptr tensorB = mgr.tensor(data); + 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(); @@ -172,6 +172,6 @@ TEST(TestAsyncOperations, TestManagerAsyncExecution) sq1->evalAsync({ tensorA, tensorB }); sq1->evalAwait(); - EXPECT_EQ(tensorA->data(), resultAsync); - EXPECT_EQ(tensorB->data(), resultAsync); + EXPECT_EQ(tensorA->vector(), resultAsync); + EXPECT_EQ(tensorB->vector(), resultAsync); } diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 0b948d64f..0ccfdb0f8 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -5,9 +5,9 @@ TEST(TestDestroy, TestDestroyTensorSingle) { - std::shared_ptr tensorA = nullptr; + std::shared_ptr> tensorA = nullptr; - std::string shader(R"( + std::string shader(R"( #version 450 layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; @@ -39,13 +39,13 @@ TEST(TestDestroy, TestDestroyTensorSingle) } EXPECT_FALSE(tensorA->isInit()); } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); } TEST(TestDestroy, TestDestroyTensorVector) { - std::shared_ptr tensorA = nullptr; - std::shared_ptr tensorB = nullptr; + std::shared_ptr> tensorA = nullptr; + std::shared_ptr> tensorB = nullptr; std::string shader(R"( #version 450 @@ -84,13 +84,13 @@ TEST(TestDestroy, TestDestroyTensorVector) EXPECT_FALSE(tensorB->isInit()); } } - EXPECT_EQ(tensorA->data(), std::vector({ 2, 2, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 2, 2, 2 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 3, 3, 3 })); } TEST(TestDestroy, TestDestroySequenceSingle) { - std::shared_ptr tensorA = nullptr; + std::shared_ptr> tensorA = nullptr; std::string shader(R"( #version 450 @@ -123,5 +123,5 @@ TEST(TestDestroy, TestDestroySequenceSingle) EXPECT_FALSE(sq->isInit()); } } - EXPECT_EQ(tensorA->data(), std::vector({ 1, 1, 1 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); } diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index 980273246..a4402637f 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -14,19 +14,19 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) { kp::Manager mgr; - std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); - std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); + std::shared_ptr> xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + std::shared_ptr> xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); + std::shared_ptr> y = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr wIn = mgr.tensor({ 0.001, 0.001 }); - std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wIn = mgr.tensor({ 0.001, 0.001 }); + std::shared_ptr> wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr bIn = mgr.tensor({ 0 }); - std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> bIn = mgr.tensor({ 0 }); + std::shared_ptr> bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, @@ -88,21 +88,21 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) { kp::Manager mgr; - std::shared_ptr xI = mgr.tensor({ 0, 1, 1, 1, 1 }); - std::shared_ptr xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); + std::shared_ptr> xI = mgr.tensor({ 0, 1, 1, 1, 1 }); + std::shared_ptr> xJ = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr y = mgr.tensor({ 0, 0, 0, 1, 1 }); + std::shared_ptr> y = mgr.tensor({ 0, 0, 0, 1, 1 }); - std::shared_ptr wIn = + std::shared_ptr> wIn = mgr.tensor({ 0.001, 0.001 }, kp::Tensor::TensorTypes::eHost); - std::shared_ptr wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr bIn = + std::shared_ptr> bIn = mgr.tensor({ 0 }, kp::Tensor::TensorTypes::eHost); - std::shared_ptr bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, @@ -136,8 +136,6 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) wIn->data()[1] -= learningRate * wOutJ->data()[j]; bIn->data()[0] -= learningRate * bOut->data()[j]; } - wIn->mapDataIntoHostMemory(); - bIn->mapDataIntoHostMemory(); } // Based on the inputs the outputs should be at least: diff --git a/test/TestManager.cpp b/test/TestManager.cpp index ce055ff8c..f759208aa 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -7,9 +7,9 @@ TEST(TestManager, EndToEndOpMultEvalFlow) { kp::Manager mgr; - 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::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, @@ -20,16 +20,16 @@ TEST(TestManager, EndToEndOpMultEvalFlow) ->eval(params, mgr.algorithm()) ->eval(params); - EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); + EXPECT_EQ(tensorOutput->vector(), std::vector({ 0, 4, 12 })); } TEST(TestManager, EndToEndOpMultSeqFlow) { kp::Manager mgr; - 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::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, @@ -41,16 +41,16 @@ TEST(TestManager, EndToEndOpMultSeqFlow) ->record(params) ->eval(); - EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); + EXPECT_EQ(tensorOutput->vector(), std::vector({ 0, 4, 12 })); } TEST(TestManager, TestMultipleSequences) { kp::Manager mgr; - 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::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, @@ -60,5 +60,5 @@ TEST(TestManager, TestMultipleSequences) mgr.sequence()->eval(params, mgr.algorithm()); mgr.sequence()->eval(params); - EXPECT_EQ(tensorOutput->data(), std::vector({ 0, 4, 12 })); + EXPECT_EQ(tensorOutput->vector(), std::vector({ 0, 4, 12 })); } diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index 63dd5f7fe..b934f7e83 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -64,8 +64,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) sq->evalAwait(); - EXPECT_EQ(tensorOutA->data(), std::vector({ 4, 8, 12 })); - EXPECT_EQ(tensorOutB->data(), std::vector({ 10, 10, 10 })); + EXPECT_EQ(tensorOutA->vector(), std::vector({ 4, 8, 12 })); + EXPECT_EQ(tensorOutB->vector(), std::vector({ 10, 10, 10 })); } TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) @@ -73,7 +73,7 @@ TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); std::string shader(R"( #version 450 @@ -96,14 +96,14 @@ TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) ->eval(); } - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); std::string shader(R"( #version 450 @@ -131,7 +131,7 @@ TEST(TestMultipleAlgoExecutions, MultipleCmdBufRecords) mgr.sequence()->record({ tensorA })->eval(); - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } TEST(TestMultipleAlgoExecutions, MultipleSequences) @@ -139,7 +139,7 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); std::string shader(R"( #version 450 @@ -167,14 +167,14 @@ TEST(TestMultipleAlgoExecutions, MultipleSequences) sq->record({ tensorA })->eval(); - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); std::string shader(R"( #version 450 @@ -198,12 +198,12 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) sq->record({ tensorA })->eval(); - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) { - std::shared_ptr tensorA = nullptr; + std::shared_ptr> tensorA = nullptr; { std::shared_ptr sq = nullptr; @@ -236,5 +236,5 @@ TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) } } - EXPECT_EQ(tensorA->data(), std::vector({ 3, 3, 3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index e766c8efb..a1f8eda99 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -9,8 +9,8 @@ TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 3, 4, 5 }); - std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 3, 4, 5 }); + std::shared_ptr> tensorB = mgr.tensor({ 0, 0, 0 }); std::string shader(R"( #version 450 @@ -36,16 +36,16 @@ TEST(TestOpAlgoCreate, ShaderRawDataFromConstructor) ->eval(mgr.algorithm(params, spirv)) ->eval(params); - EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 0, 1, 2 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 3, 4, 5 })); } TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 3, 4, 5 }); - std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 3, 4, 5 }); + std::shared_ptr> tensorB = mgr.tensor({ 0, 0, 0 }); std::vector spirv = std::vector( (uint32_t*) @@ -62,8 +62,8 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) ->eval(mgr.algorithm(params, spirv)) ->eval(params); - EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); - EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 0, 1, 2 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 3, 4, 5 })); } // TODO: Add support to read from file for shader @@ -71,8 +71,8 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) //{ // kp::Manager mgr; // -// std::shared_ptr tensorA{ new kp::Tensor({ 3, 4, 5 }) }; -// std::shared_ptr tensorB{ new kp::Tensor({ 0, 0, 0 }) }; +// std::shared_ptr> tensorA{ new kp::Tensor({ 3, 4, 5 }) }; +// std::shared_ptr> tensorB{ new kp::Tensor({ 0, 0, 0 }) }; // mgr.rebuild({ tensorA, tensorB }); // // mgr.evalOpDefault( @@ -81,6 +81,6 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) // // mgr.evalOpDefault({ tensorA, tensorB }); // -// EXPECT_EQ(tensorA->data(), std::vector({ 0, 1, 2 })); -// EXPECT_EQ(tensorB->data(), std::vector({ 3, 4, 5 })); +// EXPECT_EQ(tensorA->vector(), std::vector({ 0, 1, 2 })); +// EXPECT_EQ(tensorB->vector(), std::vector({ 3, 4, 5 })); //} diff --git a/test/TestOpTensorCopy.cpp b/test/TestOpTensorCopy.cpp index 85e0b545b..6978eeeea 100644 --- a/test/TestOpTensorCopy.cpp +++ b/test/TestOpTensorCopy.cpp @@ -11,8 +11,8 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) std::vector testVecA{ 1, 2, 3 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = mgr.tensor(testVecA); - std::shared_ptr tensorB = mgr.tensor(testVecB); + std::shared_ptr> tensorA = mgr.tensor(testVecA); + std::shared_ptr> tensorB = mgr.tensor(testVecB); EXPECT_TRUE(tensorA->isInit()); EXPECT_TRUE(tensorB->isInit()); @@ -22,8 +22,8 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensor) ->eval({ tensorA, tensorB }) ->eval({ tensorA, tensorB }); - // Making sure the GPU holds the same data - EXPECT_EQ(tensorA->data(), tensorB->data()); + // Making sure the GPU holds the same vector + EXPECT_EQ(tensorA->vector(), tensorB->vector()); } TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) @@ -35,9 +35,9 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) std::vector testVecB{ 0, 0, 0 }; std::vector testVecC{ 0, 0, 0 }; - std::shared_ptr tensorA = mgr.tensor(testVecA); - std::shared_ptr tensorB = mgr.tensor(testVecB); - std::shared_ptr tensorC = mgr.tensor(testVecC); + std::shared_ptr> tensorA = mgr.tensor(testVecA); + std::shared_ptr> tensorB = mgr.tensor(testVecB); + std::shared_ptr> tensorC = mgr.tensor(testVecC); EXPECT_TRUE(tensorA->isInit()); EXPECT_TRUE(tensorB->isInit()); @@ -47,14 +47,14 @@ TEST(TestOpTensorCopy, CopyDeviceToDeviceTensorMulti) ->eval({ tensorA, tensorB, tensorC }) ->eval({ tensorA, tensorB, tensorC }); - EXPECT_EQ(tensorA->data(), tensorB->data()); - EXPECT_EQ(tensorA->data(), tensorC->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); + EXPECT_EQ(tensorA->vector(), tensorC->vector()); - // Making sure the GPU holds the same data + // Making sure the GPU holds the same vector mgr.sequence()->eval({ tensorB, tensorC }); - EXPECT_EQ(tensorA->data(), tensorB->data()); - EXPECT_EQ(tensorA->data(), tensorC->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); + EXPECT_EQ(tensorA->vector(), tensorC->vector()); } TEST(TestOpTensorCopy, CopyDeviceToHostTensor) @@ -65,8 +65,8 @@ TEST(TestOpTensorCopy, CopyDeviceToHostTensor) std::vector testVecA{ 3, 4, 5 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = mgr.tensor(testVecA); - std::shared_ptr tensorB = + std::shared_ptr> tensorA = mgr.tensor(testVecA); + std::shared_ptr> tensorB = mgr.tensor(testVecB, kp::Tensor::TensorTypes::eHost); // Only calling sync on device type tensor @@ -77,11 +77,11 @@ TEST(TestOpTensorCopy, CopyDeviceToHostTensor) mgr.sequence()->eval({ tensorA, tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); - // Making sure the GPU holds the same data + // Making sure the GPU holds the same vector mgr.sequence()->eval({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); } TEST(TestOpTensorCopy, CopyHostToDeviceTensor) @@ -92,9 +92,9 @@ TEST(TestOpTensorCopy, CopyHostToDeviceTensor) std::vector testVecA{ 4, 5, 6 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = + std::shared_ptr> tensorA = mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); - std::shared_ptr tensorB = mgr.tensor(testVecB); + std::shared_ptr> tensorB = mgr.tensor(testVecB); // Only calling sync on device type tensor mgr.sequence()->eval({ tensorA, tensorB }); @@ -104,11 +104,11 @@ TEST(TestOpTensorCopy, CopyHostToDeviceTensor) mgr.sequence()->eval({ tensorA, tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); - // Making sure the GPU holds the same data + // Making sure the GPU holds the same vector mgr.sequence()->eval({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); } TEST(TestOpTensorCopy, CopyHostToHostTensor) @@ -119,9 +119,9 @@ TEST(TestOpTensorCopy, CopyHostToHostTensor) std::vector testVecA{ 5, 6, 7 }; std::vector testVecB{ 0, 0, 0 }; - std::shared_ptr tensorA = + std::shared_ptr> tensorA = mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); - std::shared_ptr tensorB = + std::shared_ptr> tensorB = mgr.tensor(testVecB, kp::Tensor::TensorTypes::eHost); EXPECT_TRUE(tensorA->isInit()); @@ -131,11 +131,11 @@ TEST(TestOpTensorCopy, CopyHostToHostTensor) ->eval({ tensorA }) ->eval({ tensorA, tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); - // Making sure the GPU holds the same data + // Making sure the GPU holds the same vector mgr.sequence()->eval({ tensorB }); - EXPECT_EQ(tensorA->data(), tensorB->data()); + EXPECT_EQ(tensorA->vector(), tensorB->vector()); } TEST(TestOpTensorCopy, SingleTensorShouldFail) @@ -145,7 +145,7 @@ TEST(TestOpTensorCopy, SingleTensorShouldFail) std::vector testVecA{ 6, 7, 8 }; - std::shared_ptr tensorA = + std::shared_ptr> tensorA = mgr.tensor(testVecA, kp::Tensor::TensorTypes::eHost); EXPECT_TRUE(tensorA->isInit()); diff --git a/test/TestOpTensorCreate.cpp b/test/TestOpTensorCreate.cpp index 14153427e..7ba1be615 100644 --- a/test/TestOpTensorCreate.cpp +++ b/test/TestOpTensorCreate.cpp @@ -6,7 +6,7 @@ TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) { std::vector testVecA{ 9, 8, 7 }; - std::shared_ptr tensorA = nullptr; + std::shared_ptr> tensorA = nullptr; { kp::Manager mgr; @@ -15,7 +15,7 @@ TEST(TestOpTensorCreate, CreateSingleTensorSingleOp) EXPECT_TRUE(tensorA->isInit()); - EXPECT_EQ(tensorA->data(), testVecA); + EXPECT_EQ(tensorA->vector(), testVecA); } EXPECT_FALSE(tensorA->isInit()); @@ -29,11 +29,11 @@ TEST(TestOpTensorCreate, NoErrorIfTensorFreedBefore) kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor(testVecA); - std::shared_ptr tensorB = mgr.tensor(testVecB); + std::shared_ptr> tensorA = mgr.tensor(testVecA); + std::shared_ptr> tensorB = mgr.tensor(testVecB); - EXPECT_EQ(tensorA->data(), testVecA); - EXPECT_EQ(tensorB->data(), testVecB); + EXPECT_EQ(tensorA->vector(), testVecA); + EXPECT_EQ(tensorB->vector(), testVecB); tensorA->destroy(); tensorB->destroy(); @@ -49,7 +49,7 @@ TEST(TestOpTensorCreate, ExceptionOnZeroSizeTensor) kp::Manager mgr; try { - std::shared_ptr tensorA = mgr.tensor(testVecA); + std::shared_ptr> tensorA = mgr.tensor(testVecA); } catch (const std::runtime_error& err) { // check exception ASSERT_TRUE(std::string(err.what()).find("zero-sized") != diff --git a/test/TestOpTensorSync.cpp b/test/TestOpTensorSync.cpp index 55e02ad13..02271c618 100644 --- a/test/TestOpTensorSync.cpp +++ b/test/TestOpTensorSync.cpp @@ -11,7 +11,7 @@ TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) std::vector testVecPreA{ 0, 0, 0 }; std::vector testVecPostA{ 9, 8, 7 }; - std::shared_ptr tensorA = mgr.tensor(testVecPreA); + std::shared_ptr> tensorA = mgr.tensor(testVecPreA); EXPECT_TRUE(tensorA->isInit()); @@ -21,7 +21,7 @@ TEST(TestOpTensorSync, SyncToDeviceMemorySingleTensor) mgr.sequence()->eval({ tensorA }); - EXPECT_EQ(tensorA->data(), testVecPostA); + EXPECT_EQ(tensorA->vector(), testVecPostA); } TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) @@ -31,9 +31,9 @@ TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) std::vector testVec{ 9, 8, 7 }; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); - std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); - std::shared_ptr tensorC = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorC = mgr.tensor({ 0, 0, 0 }); EXPECT_TRUE(tensorA->isInit()); EXPECT_TRUE(tensorB->isInit()); @@ -47,7 +47,7 @@ TEST(TestOpTensorSync, SyncToDeviceMemoryMultiTensor) mgr.sequence()->eval({ tensorA, tensorB, tensorC }); - EXPECT_EQ(tensorA->data(), testVec); - EXPECT_EQ(tensorB->data(), testVec); - EXPECT_EQ(tensorC->data(), testVec); + EXPECT_EQ(tensorA->vector(), testVec); + EXPECT_EQ(tensorB->vector(), testVec); + EXPECT_EQ(tensorC->vector(), testVec); } diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index b37fe4d72..9599596ed 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -29,7 +29,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) { kp::Manager mgr; - std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); @@ -42,7 +42,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); sq->eval({ tensor }); - EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); + EXPECT_EQ(tensor->vector(), kp::Constants({ 0.4, 0.4, 0.4 })); } } } @@ -72,7 +72,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) { kp::Manager mgr; - std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.1, 0.2, 0.3 }); @@ -85,7 +85,7 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); sq->eval({ tensor }); - EXPECT_EQ(tensor->data(), kp::Constants({ 0.4, 0.4, 0.4 })); + EXPECT_EQ(tensor->vector(), kp::Constants({ 0.4, 0.4, 0.4 })); } } } @@ -115,7 +115,7 @@ TEST(TestPushConstants, TestConstantsWrongSize) { kp::Manager mgr; - std::shared_ptr tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); std::shared_ptr algo = mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0 }); diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 7d70a477b..090a6317b 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -60,9 +60,9 @@ TEST(TestSequence, RerecordSequence) 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}); + 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 }); @@ -90,7 +90,7 @@ TEST(TestSequence, RerecordSequence) sq->eval(); - EXPECT_EQ(tensorOut->data(), std::vector({2, 4, 6})); + EXPECT_EQ(tensorOut->vector(), std::vector({2, 4, 6})); algo->rebuild({tensorOut, tensorA, tensorB}, spirv); @@ -98,7 +98,7 @@ TEST(TestSequence, RerecordSequence) sq->rerecord(); sq->eval(); - EXPECT_EQ(tensorB->data(), std::vector({2, 8, 18})); + EXPECT_EQ(tensorB->vector(), std::vector({2, 8, 18})); } diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index 2c6e284d2..fe40fb5ea 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -25,8 +25,8 @@ TEST(TestSpecializationConstants, TestTwoConstants) { kp::Manager mgr; - std::shared_ptr tensorA = mgr.tensor({ 0, 0, 0 }); - std::shared_ptr tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorB = mgr.tensor({ 0, 0, 0 }); std::vector> params = { tensorA, tensorB }; @@ -42,8 +42,8 @@ TEST(TestSpecializationConstants, TestTwoConstants) ->record(params) ->eval(); - EXPECT_EQ(tensorA->data(), std::vector({ 5, 5, 5 })); - EXPECT_EQ(tensorB->data(), std::vector({ 0.3, 0.3, 0.3 })); + EXPECT_EQ(tensorA->vector(), std::vector({ 5, 5, 5 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 0.3, 0.3, 0.3 })); } } } diff --git a/test/TestTensor.cpp b/test/TestTensor.cpp index d33367722..c267024db 100644 --- a/test/TestTensor.cpp +++ b/test/TestTensor.cpp @@ -7,7 +7,7 @@ TEST(TestTensor, ConstructorData) { kp::Manager mgr; std::vector vec{ 0, 1, 2 }; - std::shared_ptr tensor = mgr.tensor(vec); + std::shared_ptr> tensor = mgr.tensor(vec); EXPECT_EQ(tensor->size(), vec.size()); - EXPECT_EQ(tensor->data(), vec); + EXPECT_EQ(tensor->vector(), vec); } diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 3eb9147a1..8836840a6 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -7,8 +7,8 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) { - std::shared_ptr tensorA = nullptr; - std::shared_ptr tensorB = nullptr; + std::shared_ptr> tensorA = nullptr; + std::shared_ptr> tensorB = nullptr; { std::shared_ptr sq = nullptr; @@ -39,29 +39,29 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) sq->record(algorithm); sq->record(params); sq->eval(); + + std::vector expectedA = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15 + }; + + std::vector expectedB = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, + 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 + }; + + EXPECT_EQ(tensorA->vector(), expectedA); + EXPECT_EQ(tensorB->vector(), expectedB); } } - - std::vector expectedA = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, - 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, - 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15 - }; - - std::vector expectedB = { - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, - 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, - 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, - 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, - 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 - }; - - EXPECT_EQ(tensorA->data(), expectedA); - EXPECT_EQ(tensorB->data(), expectedB); } From 1cc369cb191db337d30d588e6f8ebeabb813e0ec Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 08:02:30 +0000 Subject: [PATCH 58/91] Mark pointer invalid after destroy tensor --- src/Tensor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 335e48959..aaf6ba388 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -360,6 +360,10 @@ Tensor::destroy() { KP_LOG_DEBUG("Kompute Tensor started destroy()"); + this->mRawData = nullptr; + this->mSize = 0; + this->mDataTypeMemorySize = 0; + if (!this->mDevice) { KP_LOG_WARN( "Kompute Tensor destructor reached with null Device pointer"); From bb64b2b37c44038c01a27bd1e985cfc4c92d00fe Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 08:10:42 +0000 Subject: [PATCH 59/91] Updated destroy and amended tests to ensure they test tensor in scope --- src/Tensor.cpp | 11 +++++++++-- src/include/kompute/Tensor.hpp | 26 +++++++++++++++++++++++++- test/TestDestroy.cpp | 11 +++++++---- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index aaf6ba388..8b96be163 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -50,7 +50,7 @@ Tensor::rebuild(void* data, } this->allocateMemoryCreateGPUResources(); - this->rawMapData(); + this->mapRawData(); memcpy(this->mRawData, data, this->memorySize()); } @@ -64,7 +64,10 @@ Tensor::tensorType() bool Tensor::isInit() { - return this->mDevice && this->mPrimaryBuffer && this->mPrimaryMemory; + return this->mDevice + && this->mPrimaryBuffer + && this->mPrimaryMemory + && this->mRawData; } @@ -360,6 +363,7 @@ Tensor::destroy() { KP_LOG_DEBUG("Kompute Tensor started destroy()"); + // Setting raw data to null regardless whether device is available to invalidate Tensor this->mRawData = nullptr; this->mSize = 0; this->mDataTypeMemorySize = 0; @@ -370,6 +374,9 @@ Tensor::destroy() return; } + // Unmap the current memory data + this->unmapRawData(); + if (this->mFreePrimaryBuffer) { if (!this->mPrimaryBuffer) { KP_LOG_WARN("Kompose Tensor expected to destroy primary buffer " diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 898a2df08..efc3cda18 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -217,7 +217,7 @@ class Tensor void* mRawData; private: - void rawMapData() { + void mapRawData() { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -234,12 +234,36 @@ class Tensor } vk::DeviceSize bufferSize = this->memorySize(); + // Given we request coherent host memory we don't need to invalidate / flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); } + void unmapRawData() { + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->flushMappedMemoryRanges(1, &mappedRange); + this->mDevice->unmapMemory(*hostVisibleMemory); + } + // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 0ccfdb0f8..72eeaf72b 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -34,12 +34,13 @@ TEST(TestDestroy, TestDestroyTensorSingle) ->eval() ->eval(algo->getTensors()); + EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); + tensorA->destroy(); EXPECT_FALSE(tensorA->isInit()); } EXPECT_FALSE(tensorA->isInit()); } - EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); } TEST(TestDestroy, TestDestroyTensorVector) @@ -82,10 +83,11 @@ TEST(TestDestroy, TestDestroyTensorVector) EXPECT_FALSE(tensorA->isInit()); EXPECT_FALSE(tensorB->isInit()); + + EXPECT_EQ(tensorA->vector(), std::vector({ 2, 2, 2 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 3, 3, 3 })); } } - EXPECT_EQ(tensorA->vector(), std::vector({ 2, 2, 2 })); - EXPECT_EQ(tensorB->vector(), std::vector({ 3, 3, 3 })); } TEST(TestDestroy, TestDestroySequenceSingle) @@ -121,7 +123,8 @@ TEST(TestDestroy, TestDestroySequenceSingle) sq->destroy(); EXPECT_FALSE(sq->isInit()); + + EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); } } - EXPECT_EQ(tensorA->vector(), std::vector({ 1, 1, 1 })); } From a2ee928f4c3503127bf773ad8348f37e6db191cd Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 10:39:30 +0000 Subject: [PATCH 60/91] Updated tests and rebased --- python/src/main.cpp | 105 +++++++++++++++-------- python/test/test_array_multiplication.py | 6 +- python/test/test_logistic_regression.py | 27 +++--- setup.py | 2 +- single_include/kompute/Kompute.hpp | 49 ++++++++++- src/include/kompute/Manager.hpp | 17 ++++ src/include/kompute/Tensor.hpp | 2 +- test/TestDestroy.cpp | 6 +- test/TestMultipleAlgoExecutions.cpp | 37 -------- 9 files changed, 151 insertions(+), 100 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index d4b0f2084..eab8e5ef4 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -92,59 +92,46 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "Tensor", DOC(kp, Tensor)) .def("data", [](kp::Tensor& self) { - return py::array(self.data().size(), self.data().data()); - }, DOC(kp, Tensor, data)) - .def("__getitem__", [](kp::Tensor &self, size_t index) -> float { return self.data()[index]; }, - "When only an index is necessary") - .def("__setitem__", [](kp::Tensor &self, size_t index, float value) { - self.data()[index] = value; }) - .def("set_data", [np](kp::Tensor &self, const py::array_t data){ - const py::array_t flatdata = np.attr("ravel")(data); - const py::buffer_info info = flatdata.request(); - const float* ptr = (float*) info.ptr; - self.setData(std::vector(ptr, ptr+flatdata.size())); - }, DOC(kp, Tensor, setData)) - .def("__iter__", [](kp::Tensor &self) { - return py::make_iterator(self.data().begin(), self.data().end()); - }, py::keep_alive<0, 1>(), // Required to keep alive iterator while exists - "Iterator to enable looping within data structure as required.") - .def("__contains__", [](kp::Tensor &self, float v) { - for (size_t i = 0; i < self.data().size(); ++i) { - if (v == self.data()[i]) { - return true; - } - } - return false; - }) - .def("__reversed__", [](kp::Tensor &self) { - size_t size = self.data().size(); - std::vector reversed(size); - for (size_t i = 0; i < size; i++) { - reversed[size - i - 1] = self.data()[i]; + // Non-owning container exposing the underlying pointer + py::str dummyDataOwner; // Explicitly request data to not be owned by np + switch (self.dataType()) { + case kp::Tensor::TensorDataTypes::eFloat: + return py::array(self.size(), self.data(), dummyDataOwner); + case kp::Tensor::TensorDataTypes::eUnsignedInt: + return py::array(self.size(), self.data(), dummyDataOwner); + case kp::Tensor::TensorDataTypes::eInt: + return py::array(self.size(), self.data(), dummyDataOwner); + case kp::Tensor::TensorDataTypes::eDouble: + return py::array(self.size(), self.data(), dummyDataOwner); + case kp::Tensor::TensorDataTypes::eBool: + return py::array(self.size(), self.data(), dummyDataOwner); + default: + throw std::runtime_error("Kompute Python data type not supported"); } - return reversed; - }) + }, DOC(kp, Tensor, data)) .def("size", &kp::Tensor::size, DOC(kp, Tensor, size)) .def("__len__", &kp::Tensor::size, DOC(kp, Tensor, size)) .def("tensor_type", &kp::Tensor::tensorType, DOC(kp, Tensor, tensorType)) + .def("data_type", &kp::Tensor::dataType, DOC(kp, Tensor, dataType)) .def("is_init", &kp::Tensor::isInit, DOC(kp, Tensor, isInit)) .def("destroy", &kp::Tensor::destroy, DOC(kp, Tensor, destroy)); - py::class_>(m, "Sequence", DOC(kp, Sequence)) + py::class_>(m, "Sequence") .def("record", [](kp::Sequence& self, std::shared_ptr op) { return self.record(op); }, DOC(kp, Sequence, record)) .def("eval", [](kp::Sequence& self) { return self.eval(); }, DOC(kp, Sequence, eval)) .def("eval", [](kp::Sequence& self, std::shared_ptr op) { return self.eval(op); }, - DOC(kp, Sequence, eval)) + DOC(kp, Sequence, eval_2)) .def("eval_async", [](kp::Sequence& self) { return self.eval(); }, - DOC(kp, Sequence, evalAsync)) + DOC(kp, Sequence, evalAwait)) .def("eval_async", [](kp::Sequence& self, std::shared_ptr op) { return self.evalAsync(op); }, DOC(kp, Sequence, evalAsync)) .def("eval_await", [](kp::Sequence& self) { return self.evalAwait(); }, DOC(kp, Sequence, evalAwait)) .def("eval_await", [](kp::Sequence& self, uint32_t wait) { return self.evalAwait(wait); }, DOC(kp, Sequence, evalAwait)) +<<<<<<< HEAD .def("is_recording", &kp::Sequence::isRecording, DOC(kp, Sequence, isRecording)) .def("is_running", &kp::Sequence::isRunning, @@ -163,6 +150,17 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "Manager", DOC(kp, Manager)) .def(py::init(), DOC(kp, Manager, Manager)) .def(py::init(), DOC(kp, Manager, Manager_2)) +======= + .def("is_recording", &kp::Sequence::isRecording, DOC(kp, Sequence, isRecording)) + .def("is_running", &kp::Sequence::isRunning, DOC(kp, Sequence, isRunning)) + .def("is_init", &kp::Sequence::isInit, DOC(kp, Sequence, isInit)) + .def("clear", &kp::Sequence::clear, DOC(kp, Sequence, clear)) + .def("destroy", &kp::Sequence::destroy, DOC(kp, Sequence, destroy)); + + py::class_>(m, "Manager") + .def(py::init()) + .def(py::init()) +>>>>>>> cc1a6cc (Updated tests and rebased) .def(py::init&,const std::vector&>(), DOC(kp, Manager, Manager_2), py::arg("device") = 0, @@ -173,13 +171,44 @@ PYBIND11_MODULE(kp, m) { .def("tensor", [np](kp::Manager& self, const py::array_t data, kp::Tensor::TensorTypes tensor_type) { - const py::array_t flatdata = np.attr("ravel")(data); - const py::buffer_info info = flatdata.request(); - const float* ptr = (float*) info.ptr; - return self.tensor(std::vector(ptr, ptr+flatdata.size()), tensor_type); + const py::buffer_info info = data.request(); + return self.tensor( + info.ptr, + data.size(), + sizeof(float), + kp::Tensor::TensorDataTypes::eFloat, + tensor_type); }, DOC(kp, Manager, tensor), py::arg("data"), py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice) + .def("tensor_t", [np](kp::Manager& self, + const py::array data, + kp::Tensor::TensorTypes tensor_type) { + // TODO: confirm if ravel is required as numpy data is always flat + //const py::array_t flatdata = np.attr("ravel")(data); + //const py::buffer_info info = flatdata.request(); + const py::buffer_info info = data.request(); + if (data.dtype() == py::dtype::of()) { + return self.tensor( + info.ptr, data.size(), sizeof(float), kp::Tensor::TensorDataTypes::eFloat, tensor_type); + } else if (data.dtype() == py::dtype::of()) { + return self.tensor( + info.ptr, data.size(), sizeof(uint32_t), kp::Tensor::TensorDataTypes::eUnsignedInt, tensor_type); + } else if (data.dtype() == py::dtype::of()) { + return self.tensor( + info.ptr, data.size(), sizeof(int32_t), kp::Tensor::TensorDataTypes::eInt, tensor_type); + } else if (data.dtype() == py::dtype::of()) { + return self.tensor( + info.ptr, data.size(), sizeof(double), kp::Tensor::TensorDataTypes::eDouble, tensor_type); + } else if (data.dtype() == py::dtype::of()) { + return self.tensor( + info.ptr, data.size(), sizeof(bool), kp::Tensor::TensorDataTypes::eBool, tensor_type); + } else { + throw std::runtime_error("Kompute Python no valid dtype supported"); + } + }, + DOC(kp, Manager, tensorT), + py::arg("data"), py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice) .def("algorithm", [](kp::Manager& self, const std::vector>& tensors, const py::bytes& spirv, diff --git a/python/test/test_array_multiplication.py b/python/test/test_array_multiplication.py index 0dab581c6..e8de68328 100644 --- a/python/test/test_array_multiplication.py +++ b/python/test/test_array_multiplication.py @@ -9,9 +9,9 @@ def test_array_multiplication(): mgr = kp.Manager() # 2. Create Kompute Tensors to hold data - tensor_in_a = mgr.tensor([2, 2, 2]) - tensor_in_b = mgr.tensor([1, 2, 3]) - tensor_out = mgr.tensor([0, 0, 0]) + tensor_in_a = mgr.tensor(np.array([2, 2, 2])) + tensor_in_b = mgr.tensor(np.array([1, 2, 3])) + tensor_out = mgr.tensor(np.array([0, 0, 0])) params = [tensor_in_a, tensor_in_b, tensor_out] diff --git a/python/test/test_logistic_regression.py b/python/test/test_logistic_regression.py index 4bd0c28fa..862758413 100644 --- a/python/test/test_logistic_regression.py +++ b/python/test/test_logistic_regression.py @@ -1,4 +1,5 @@ import pyshader as ps +import numpy as np import kp def test_logistic_regression(): @@ -46,21 +47,21 @@ def test_logistic_regression(): mgr = kp.Manager(0) # First we create input and ouput tensors for shader - tensor_x_i = mgr.tensor([0.0, 1.0, 1.0, 1.0, 1.0]) - tensor_x_j = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_x_i = mgr.tensor(np.array([0.0, 1.0, 1.0, 1.0, 1.0])) + tensor_x_j = mgr.tensor(np.array([0.0, 0.0, 0.0, 1.0, 1.0])) - tensor_y = mgr.tensor([0.0, 0.0, 0.0, 1.0, 1.0]) + tensor_y = mgr.tensor(np.array([0.0, 0.0, 0.0, 1.0, 1.0])) - tensor_w_in = mgr.tensor([0.001, 0.001]) - tensor_w_out_i = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) - tensor_w_out_j = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_w_in = mgr.tensor(np.array([0.001, 0.001])) + tensor_w_out_i = mgr.tensor(np.array([0.0, 0.0, 0.0, 0.0, 0.0])) + tensor_w_out_j = mgr.tensor(np.array([0.0, 0.0, 0.0, 0.0, 0.0])) - tensor_b_in = mgr.tensor([0.0]) - tensor_b_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_b_in = mgr.tensor(np.array([0.0])) + tensor_b_out = mgr.tensor(np.array([0.0, 0.0, 0.0, 0.0, 0.0])) - tensor_l_out = mgr.tensor([0.0, 0.0, 0.0, 0.0, 0.0]) + tensor_l_out = mgr.tensor(np.array([0.0, 0.0, 0.0, 0.0, 0.0])) - tensor_m = mgr.tensor([ tensor_y.size() ]) + tensor_m = mgr.tensor(np.array([ tensor_y.size() ])) # We store them in an array for easier interaction params = [tensor_x_i, tensor_x_j, tensor_y, tensor_w_in, tensor_w_out_i, @@ -91,9 +92,9 @@ def test_logistic_regression(): # Calculate the parameters based on the respective derivatives calculated for j_iter in range(tensor_b_out.size()): - tensor_w_in[0] -= learning_rate * tensor_w_out_i.data()[j_iter] - tensor_w_in[1] -= learning_rate * tensor_w_out_j.data()[j_iter] - tensor_b_in[0] -= learning_rate * tensor_b_out.data()[j_iter] + tensor_w_in.data()[0] -= learning_rate * tensor_w_out_i.data()[j_iter] + tensor_w_in.data()[1] -= learning_rate * tensor_w_out_j.data()[j_iter] + tensor_b_in.data()[0] -= learning_rate * tensor_b_out.data()[j_iter] assert tensor_w_in.data()[0] < 0.01 assert tensor_w_in.data()[0] > 0.0 diff --git a/setup.py b/setup.py index ee3521064..733c4c185 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ class CMakeBuild(build_ext): else: cmake_args += ['-DKOMPUTE_EXTRA_CXX_FLAGS="-fPIC"'] cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] - build_args += ['--', '-j2'] + build_args += ['--', '-j'] env = os.environ.copy() env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index df9549aab..572f0e4da 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -762,7 +762,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_sources( + static std::vector compileSources( const std::vector& sources, const std::vector& files = {}, const std::string& entryPoint = "main", @@ -783,7 +783,7 @@ class Shader * GLSL compiler * @return The compiled SPIR-V binary in unsigned int32 format */ - static std::vector compile_source( + static std::vector compileSource( const std::string& source, const std::string& entryPoint = "main", std::vector> definitions = {}, @@ -981,7 +981,7 @@ class Tensor // TODO: move to cpp template T* data() { - return this->mRawData; + return (T*)this->mRawData; } template @@ -1008,7 +1008,7 @@ class Tensor void* mRawData; private: - void rawMapData() { + void mapRawData() { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1025,12 +1025,36 @@ class Tensor } vk::DeviceSize bufferSize = this->memorySize(); + // Given we request coherent host memory we don't need to invalidate / flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); } + void unmapRawData() { + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->flushMappedMemoryRanges(1, &mappedRange); + this->mDevice->unmapMemory(*hostVisibleMemory); + } + // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; @@ -2011,6 +2035,23 @@ class Manager return this->tensorT(data, tensorType); } + std::shared_ptr tensor( + void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const Tensor::TensorDataTypes& dataType, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + std::shared_ptr tensor{ new kp::Tensor( + this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + + if (this->mManageResources) { + this->mManagedTensors.push_back(tensor); + } + + return tensor; + } + /** * Create a managed algorithm that will be destroyed by this manager * if it hasn't been destroyed by its reference count going to zero. diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index c39f5d6b5..6eb2042eb 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -98,6 +98,23 @@ class Manager return this->tensorT(data, tensorType); } + std::shared_ptr tensor( + void* data, + uint32_t elementTotalCount, + uint32_t elementMemorySize, + const Tensor::TensorDataTypes& dataType, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + std::shared_ptr tensor{ new kp::Tensor( + this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + + if (this->mManageResources) { + this->mManagedTensors.push_back(tensor); + } + + return tensor; + } + /** * Create a managed algorithm that will be destroyed by this manager * if it hasn't been destroyed by its reference count going to zero. diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index efc3cda18..0194e208f 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -190,7 +190,7 @@ class Tensor // TODO: move to cpp template T* data() { - return this->mRawData; + return (T*)this->mRawData; } template diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index 72eeaf72b..defd40998 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -78,14 +78,14 @@ TEST(TestDestroy, TestDestroyTensorVector) ->record(algo->getTensors()) ->eval(); + EXPECT_EQ(tensorA->vector(), std::vector({ 2, 2, 2 })); + EXPECT_EQ(tensorB->vector(), std::vector({ 3, 3, 3 })); + tensorA->destroy(); tensorB->destroy(); EXPECT_FALSE(tensorA->isInit()); EXPECT_FALSE(tensorB->isInit()); - - EXPECT_EQ(tensorA->vector(), std::vector({ 2, 2, 2 })); - EXPECT_EQ(tensorB->vector(), std::vector({ 3, 3, 3 })); } } } diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index b934f7e83..effc75227 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -201,40 +201,3 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } -TEST(TestMultipleAlgoExecutions, SequenceAlgoDestroyOutsideManagerScope) -{ - std::shared_ptr> tensorA = nullptr; - - { - std::shared_ptr sq = nullptr; - { - kp::Manager mgr; - - tensorA = mgr.tensor({ 0, 0, 0 }); - - std::string shader(R"( - #version 450 - layout (local_size_x = 1) in; - layout(set = 0, binding = 0) buffer a { float pa[]; }; - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pa[index] + 1; - })"); - - std::vector spirv = kp::Shader::compileSource(shader); - - std::shared_ptr algorithm = - mgr.algorithm({ tensorA }, spirv); - - sq = mgr.sequence(); - - sq->record({ tensorA })->eval(); - - sq->record(algorithm)->eval()->eval()->eval(); - - sq->record({ tensorA })->eval(); - } - } - - EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); -} From 6a7f410675a8b7416f48dcfc458267a3c03e73e7 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 11:12:01 +0000 Subject: [PATCH 61/91] Updated to use flatdata on the python --- python/src/main.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index eab8e5ef4..a82cd160d 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -169,12 +169,13 @@ PYBIND11_MODULE(kp, m) { .def("sequence", &kp::Manager::sequence, DOC(kp, Manager, sequence), py::arg("queue_index") = 0, py::arg("total_timestamps") = 0) .def("tensor", [np](kp::Manager& self, - const py::array_t data, + const py::array_t& data, kp::Tensor::TensorTypes tensor_type) { - const py::buffer_info info = data.request(); + const py::array_t& flatdata = np.attr("ravel")(data); + const py::buffer_info info = flatdata.request(); return self.tensor( info.ptr, - data.size(), + flatdata.size(), sizeof(float), kp::Tensor::TensorDataTypes::eFloat, tensor_type); @@ -182,27 +183,26 @@ PYBIND11_MODULE(kp, m) { DOC(kp, Manager, tensor), py::arg("data"), py::arg("tensor_type") = kp::Tensor::TensorTypes::eDevice) .def("tensor_t", [np](kp::Manager& self, - const py::array data, + const py::array& data, kp::Tensor::TensorTypes tensor_type) { - // TODO: confirm if ravel is required as numpy data is always flat - //const py::array_t flatdata = np.attr("ravel")(data); - //const py::buffer_info info = flatdata.request(); - const py::buffer_info info = data.request(); - if (data.dtype() == py::dtype::of()) { + // TODO: Suppport strides in numpy format + const py::array_t& flatdata = np.attr("ravel")(data); + const py::buffer_info info = flatdata.request(); + if (flatdata.dtype() == py::dtype::of()) { return self.tensor( - info.ptr, data.size(), sizeof(float), kp::Tensor::TensorDataTypes::eFloat, tensor_type); - } else if (data.dtype() == py::dtype::of()) { + info.ptr, flatdata.size(), sizeof(float), kp::Tensor::TensorDataTypes::eFloat, tensor_type); + } else if (flatdata.dtype() == py::dtype::of()) { return self.tensor( - info.ptr, data.size(), sizeof(uint32_t), kp::Tensor::TensorDataTypes::eUnsignedInt, tensor_type); - } else if (data.dtype() == py::dtype::of()) { + info.ptr, flatdata.size(), sizeof(uint32_t), kp::Tensor::TensorDataTypes::eUnsignedInt, tensor_type); + } else if (flatdata.dtype() == py::dtype::of()) { return self.tensor( - info.ptr, data.size(), sizeof(int32_t), kp::Tensor::TensorDataTypes::eInt, tensor_type); - } else if (data.dtype() == py::dtype::of()) { + info.ptr, flatdata.size(), sizeof(int32_t), kp::Tensor::TensorDataTypes::eInt, tensor_type); + } else if (flatdata.dtype() == py::dtype::of()) { return self.tensor( - info.ptr, data.size(), sizeof(double), kp::Tensor::TensorDataTypes::eDouble, tensor_type); - } else if (data.dtype() == py::dtype::of()) { + info.ptr, flatdata.size(), sizeof(double), kp::Tensor::TensorDataTypes::eDouble, tensor_type); + } else if (flatdata.dtype() == py::dtype::of()) { return self.tensor( - info.ptr, data.size(), sizeof(bool), kp::Tensor::TensorDataTypes::eBool, tensor_type); + info.ptr, flatdata.size(), sizeof(bool), kp::Tensor::TensorDataTypes::eBool, tensor_type); } else { throw std::runtime_error("Kompute Python no valid dtype supported"); } From 8abb2313d0d08010226a83100c3fdda5bcb2a89f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 12:16:25 +0000 Subject: [PATCH 62/91] Updated python and cpp end to end test and readme to show support for different types on tensor --- README.md | 30 +++++++++++++++++------------ python/src/main.cpp | 5 ++++- python/test/test_kompute.py | 13 +++++++------ test/TestMultipleAlgoExecutions.cpp | 18 +++++++++-------- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 41596cb00..7a7375a6a 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,13 @@ void kompute(const std::string& shader) { kp::Manager mgr; // 2. Create and initialise Kompute Tensors through manager + + // Default tensor constructor simplifies creation of float values auto tensorInA = mgr.tensor({ 2., 2., 2. }); auto tensorInB = mgr.tensor({ 1., 2., 3. }); - auto tensorOutA = mgr.tensor({ 0., 0., 0. }); - auto tensorOutB = mgr.tensor({ 0., 0., 0. }); + // Explicit type constructor supports uint32, int32, double, float and bool + auto tensorOutA = mgr.tensorT({ 0, 0, 0 }); + auto tensorOutB = mgr.tensorT({ 0, 0, 0 }); std::vector> params = {tensorInA, tensorInB, tensorOutA, tensorOutB}; @@ -109,8 +112,8 @@ int main() { // 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 { float out_a[]; }; - layout(set = 0, binding = 3) buffer buf_out_b { float out_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 { @@ -122,8 +125,8 @@ int main() { void main() { uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; + out_a[index] += uint( in_a[index] * in_b[index] ); + out_b[index] += uint( const_one * push_const.val ); } )"); @@ -144,10 +147,13 @@ def kompute(shader): mgr = kp.Manager() # 2. Create and initialise Kompute Tensors through manager + + # Default tensor constructor simplifies creation of float values tensor_in_a = mgr.tensor([2, 2, 2]) tensor_in_b = mgr.tensor([1, 2, 3]) - tensor_out_a = mgr.tensor([0, 0, 0]) - tensor_out_b = mgr.tensor([0, 0, 0]) + # Explicit type constructor supports uint32, int32, double, float and bool + tensor_out_a = mgr.tensor_t(np.array([0, 0, 0], dtype=np.uint32)) + tensor_out_b = mgr.tensor_t(np.array([0, 0, 0], dtype=np.uint32)) params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] @@ -194,8 +200,8 @@ if __name__ == "__main__": // 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 { float out_a[]; }; - layout(set = 0, binding = 3) buffer buf_out_b { float out_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 { @@ -207,8 +213,8 @@ if __name__ == "__main__": void main() { uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; + out_a[index] += uint( in_a[index] * in_b[index] ); + out_b[index] += uint( const_one * push_const.val ); } """ diff --git a/python/src/main.cpp b/python/src/main.cpp index a82cd160d..495d0ed0c 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -173,6 +173,7 @@ PYBIND11_MODULE(kp, m) { kp::Tensor::TensorTypes tensor_type) { const py::array_t& flatdata = np.attr("ravel")(data); const py::buffer_info info = flatdata.request(); + KP_LOG_DEBUG("Kompute Python Manager tensor() creating tensor float with data size {}", flatdata.size()); return self.tensor( info.ptr, flatdata.size(), @@ -186,8 +187,10 @@ PYBIND11_MODULE(kp, m) { const py::array& data, kp::Tensor::TensorTypes tensor_type) { // TODO: Suppport strides in numpy format - const py::array_t& flatdata = np.attr("ravel")(data); + const py::array& flatdata = np.attr("ravel")(data); const py::buffer_info info = flatdata.request(); + KP_LOG_DEBUG("Kompute Python Manager creating tensor_T with data size {} dtype {}", + flatdata.size(), std::string(py::str(flatdata.dtype()))); if (flatdata.dtype() == py::dtype::of()) { return self.tensor( info.ptr, flatdata.size(), sizeof(float), kp::Tensor::TensorDataTypes::eFloat, tensor_type); diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index 47887930a..736768053 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -36,8 +36,9 @@ def test_end_to_end(): tensor_in_a = mgr.tensor([2, 2, 2]) tensor_in_b = mgr.tensor([1, 2, 3]) - tensor_out_a = mgr.tensor([0, 0, 0]) - tensor_out_b = mgr.tensor([0, 0, 0]) + # Explicit type constructor supports int, in32, double, float and int + tensor_out_a = mgr.tensor_t(np.array([0, 0, 0], dtype=np.uint32)) + tensor_out_b = mgr.tensor_t(np.array([0, 0, 0], dtype=np.uint32)) params = [tensor_in_a, tensor_in_b, tensor_out_a, tensor_out_b] @@ -49,8 +50,8 @@ def test_end_to_end(): // 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 { float out_a[]; }; - layout(set = 0, binding = 3) buffer buf_out_b { float out_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 { @@ -62,8 +63,8 @@ def test_end_to_end(): void main() { uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; + out_a[index] += uint( in_a[index] * in_b[index] ); + out_b[index] += uint( const_one * push_const.val ); } """ diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index effc75227..f9e066f47 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -8,10 +8,12 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) 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. }); - auto tensorOutA = mgr.tensor({ 0., 0., 0. }); - auto tensorOutB = mgr.tensor({ 0., 0., 0. }); + // 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 @@ -21,8 +23,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) // 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 { float out_a[]; }; - layout(set = 0, binding = 3) buffer buf_out_b { float out_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 { @@ -34,8 +36,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) void main() { uint index = gl_GlobalInvocationID.x; - out_a[index] += in_a[index] * in_b[index]; - out_b[index] += const_one * push_const.val; + out_a[index] += uint( in_a[index] * in_b[index] ); + out_b[index] += uint( const_one * push_const.val ); } )"); @@ -64,8 +66,8 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) sq->evalAwait(); - EXPECT_EQ(tensorOutA->vector(), std::vector({ 4, 8, 12 })); - EXPECT_EQ(tensorOutB->vector(), std::vector({ 10, 10, 10 })); + EXPECT_EQ(tensorOutA->vector(), std::vector({ 4, 8, 12 })); + EXPECT_EQ(tensorOutB->vector(), std::vector({ 10, 10, 10 })); } TEST(TestMultipleAlgoExecutions, SingleSequenceRecord) From df0dfd351f41f93884baa166f922c4b77d10a42b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 13:37:54 +0000 Subject: [PATCH 63/91] Added types tests --- python/test/test_kompute.py | 21 ---- python/test/test_tensor_types.py | 206 +++++++++++++++++++++++++++++++ src/Tensor.cpp | 2 +- 3 files changed, 207 insertions(+), 22 deletions(-) create mode 100644 python/test/test_tensor_types.py diff --git a/python/test/test_kompute.py b/python/test/test_kompute.py index 736768053..e1bcee940 100644 --- a/python/test/test_kompute.py +++ b/python/test/test_kompute.py @@ -9,27 +9,6 @@ DIRNAME = os.path.dirname(os.path.abspath(__file__)) kp_log = logging.getLogger("kp") -# TODO: Add example with file -#def test_opalgobase_file(): -# """ -# Test basic OpMult operation -# """ -# -# tensor_in_a = kp.Tensor([2, 2, 2]) -# tensor_in_b = kp.Tensor([1, 2, 3]) -# tensor_out = kp.Tensor([0, 0, 0]) -# -# mgr = kp.Manager() -# mgr.rebuild([tensor_in_a, tensor_in_b, tensor_out]) -# -# shader_path = os.path.join(DIRNAME, "../../shaders/glsl/opmult.comp.spv") -# -# mgr.eval_algo_file_def([tensor_in_a, tensor_in_b, tensor_out], shader_path) -# -# mgr.eval_tensor_sync_local_def([tensor_out]) -# -# assert tensor_out.data() == [2.0, 4.0, 6.0] - def test_end_to_end(): mgr = kp.Manager() diff --git a/python/test/test_tensor_types.py b/python/test/test_tensor_types.py new file mode 100644 index 000000000..b1d90fe03 --- /dev/null +++ b/python/test/test_tensor_types.py @@ -0,0 +1,206 @@ +import pyshader as ps +import os +import pytest +import kp +import numpy as np + + +def test_type_float(): + + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs {float valuesLhs[];}; + layout(set = 0, binding = 1) buffer tensorRhs {float valuesRhs[];}; + layout(set = 0, binding = 2) buffer tensorOutput { float valuesOutput[];}; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ + + spirv = kp.Shader.compile_source(shader) + + arr_in_a = np.array([123., 153., 231.], dtype=np.float32) + arr_in_b = np.array([9482, 1208, 1238], dtype=np.float32) + arr_out = np.array([0, 0, 0], dtype=np.float32) + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor(arr_in_a) + tensor_in_b = mgr.tensor(arr_in_b) + tensor_out = mgr.tensor(arr_out) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, spirv))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) + + assert np.all(tensor_out.data() == arr_in_a * arr_in_b) + + +def test_type_float_double_incorrect(): + + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs {float valuesLhs[];}; + layout(set = 0, binding = 1) buffer tensorRhs {float valuesRhs[];}; + layout(set = 0, binding = 2) buffer tensorOutput { float valuesOutput[];}; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ + + spirv = kp.Shader.compile_source(shader) + + arr_in_a = np.array([123., 153., 231.], dtype=np.float32) + arr_in_b = np.array([9482, 1208, 1238], dtype=np.uint32) + arr_out = np.array([0, 0, 0], dtype=np.float32) + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor_t(arr_in_a) + tensor_in_b = mgr.tensor_t(arr_in_b) + tensor_out = mgr.tensor_t(arr_out) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, spirv))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) + + assert np.all(tensor_out.data() != arr_in_a * arr_in_b) + +@pytest.mark.skipif("swiftshader" in os.environ.get("VK_ICD_FILENAMES"), + reason="Swiftshader doesn't support double") +def test_type_double(): + + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs { double valuesLhs[]; }; + layout(set = 0, binding = 1) buffer tensorRhs { double valuesRhs[]; }; + layout(set = 0, binding = 2) buffer tensorOutput { double valuesOutput[]; }; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ + + spirv = kp.Shader.compile_source(shader) + + arr_in_a = np.array([123., 153., 231.], dtype=np.float64) + arr_in_b = np.array([9482, 1208, 1238], dtype=np.float64) + arr_out = np.array([0, 0, 0], dtype=np.float64) + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor_t(arr_in_a) + tensor_in_b = mgr.tensor_t(arr_in_b) + tensor_out = mgr.tensor_t(arr_out) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, spirv))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) + + print(f"Dtype value {tensor_out.data().dtype}") + + assert np.all(tensor_out.data() == arr_in_a * arr_in_b) + +def test_type_int(): + + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs { int valuesLhs[]; }; + layout(set = 0, binding = 1) buffer tensorRhs { int valuesRhs[]; }; + layout(set = 0, binding = 2) buffer tensorOutput { int valuesOutput[]; }; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ + + spirv = kp.Shader.compile_source(shader) + + arr_in_a = np.array([123, 153, 231], dtype=np.int32) + arr_in_b = np.array([9482, 1208, 1238], dtype=np.int32) + arr_out = np.array([0, 0, 0], dtype=np.int32) + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor_t(arr_in_a) + tensor_in_b = mgr.tensor_t(arr_in_b) + tensor_out = mgr.tensor_t(arr_out) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, spirv))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) + + print(f"Dtype value {tensor_out.data().dtype}") + + assert np.all(tensor_out.data() == arr_in_a * arr_in_b) + +def test_type_unsigned_int(): + + shader = """ + #version 450 + layout(set = 0, binding = 0) buffer tensorLhs { uint valuesLhs[]; }; + layout(set = 0, binding = 1) buffer tensorRhs { uint valuesRhs[]; }; + layout(set = 0, binding = 2) buffer tensorOutput { uint valuesOutput[]; }; + layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + void main() + { + uint index = gl_GlobalInvocationID.x; + valuesOutput[index] = valuesLhs[index] * valuesRhs[index]; + } + """ + + spirv = kp.Shader.compile_source(shader) + + arr_in_a = np.array([123, 153, 231], dtype=np.uint32) + arr_in_b = np.array([9482, 1208, 1238], dtype=np.uint32) + arr_out = np.array([0, 0, 0], dtype=np.uint32) + + mgr = kp.Manager() + + tensor_in_a = mgr.tensor_t(arr_in_a) + tensor_in_b = mgr.tensor_t(arr_in_b) + tensor_out = mgr.tensor_t(arr_out) + + params = [tensor_in_a, tensor_in_b, tensor_out] + + (mgr.sequence() + .record(kp.OpTensorSyncDevice(params)) + .record(kp.OpAlgoDispatch(mgr.algorithm(params, spirv))) + .record(kp.OpTensorSyncLocal([tensor_out])) + .eval()) + + print(f"Dtype value {tensor_out.data().dtype}") + + assert np.all(tensor_out.data() == arr_in_a * arr_in_b) + diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 8b96be163..947714693 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -175,7 +175,7 @@ Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, vk::DescriptorBufferInfo Tensor::constructDescriptorBufferInfo() { - KP_LOG_WARN("Kompute Tensor construct descriptor buffer info size {}", this->memorySize()); + KP_LOG_DEBUG("Kompute Tensor construct descriptor buffer info size {}", this->memorySize()); vk::DeviceSize bufferSize = this->memorySize(); return vk::DescriptorBufferInfo(*this->mPrimaryBuffer, 0, // offset From 5ff7b4aa7821c5d1142a5e78ba67fc4027ad311a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 14:10:38 +0000 Subject: [PATCH 64/91] Added single header --- single_include/kompute/Kompute.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 572f0e4da..9b41e1ead 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -2001,7 +2001,7 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t nrOfTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager From 6fd19b9d05fb2de7fbc545f4f7144266f98c98d1 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 14:11:32 +0000 Subject: [PATCH 65/91] Fixed conflicts --- python/src/docstrings.hpp | 75 +++++++++++++++++++++++++++------------ python/src/main.cpp | 12 ------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/python/src/docstrings.hpp b/python/src/docstrings.hpp index a5bda0a4d..d4593edb8 100644 --- a/python/src/docstrings.hpp +++ b/python/src/docstrings.hpp @@ -252,7 +252,11 @@ nrOfTimestamps The maximum number of timestamps to allocate. If zero (default), disables latching of timestamps. @returns Shared pointer with initialised sequence)doc"; -static const char *__doc_kp_Manager_tensor = +static const char *__doc_kp_Manager_tensor = R"doc()doc"; + +static const char *__doc_kp_Manager_tensor_2 = R"doc()doc"; + +static const char *__doc_kp_Manager_tensorT = R"doc(Create a managed tensor that will be destroyed by this manager if it hasn't been destroyed by its reference count going to zero. @@ -679,6 +683,20 @@ across GPUs. Each tensor would have a respective Vulkan memory and buffer, which would be used to store their respective data. The tensors can be used for GPU data storage or transfer.)doc"; +static const char *__doc_kp_TensorT = R"doc()doc"; + +static const char *__doc_kp_TensorT_TensorT = R"doc()doc"; + +static const char *__doc_kp_TensorT_data = R"doc()doc"; + +static const char *__doc_kp_TensorT_dataType = R"doc()doc"; + +static const char *__doc_kp_TensorT_operator_array = R"doc()doc"; + +static const char *__doc_kp_TensorT_setData = R"doc()doc"; + +static const char *__doc_kp_TensorT_vector = R"doc()doc"; + static const char *__doc_kp_Tensor_Tensor = R"doc(Constructor with data provided which would be used to create the respective vulkan buffer and memory. @@ -689,6 +707,18 @@ respective vulkan buffer and memory. tensor @param tensorTypes Type for the tensor which is of type TensorTypes)doc"; +static const char *__doc_kp_Tensor_TensorDataTypes = R"doc()doc"; + +static const char *__doc_kp_Tensor_TensorDataTypes_eBool = R"doc()doc"; + +static const char *__doc_kp_Tensor_TensorDataTypes_eDouble = R"doc()doc"; + +static const char *__doc_kp_Tensor_TensorDataTypes_eFloat = R"doc()doc"; + +static const char *__doc_kp_Tensor_TensorDataTypes_eInt = R"doc()doc"; + +static const char *__doc_kp_Tensor_TensorDataTypes_eUnsignedInt = R"doc()doc"; + static const char *__doc_kp_Tensor_TensorTypes = R"doc(Type for tensors created: Device allows memory to be transferred from staging buffers. Staging are host memory visible. Storage are device @@ -714,13 +744,14 @@ without exposing it. static const char *__doc_kp_Tensor_createBuffer = R"doc()doc"; -static const char *__doc_kp_Tensor_data = -R"doc(Returns the vector of data currently contained by the Tensor. It is -important to ensure that there is no out-of-sync data with the GPU -memory. +static const char *__doc_kp_Tensor_data = R"doc()doc"; -@return Reference to vector of elements representing the data in the -tensor.)doc"; +static const char *__doc_kp_Tensor_dataType = +R"doc(Retrieve the underlying data type of the Tensor + +@return Data type of tensor of type kp::Tensor::TensorDataTypes)doc"; + +static const char *__doc_kp_Tensor_dataTypeMemorySize = R"doc()doc"; static const char *__doc_kp_Tensor_destroy = R"doc(Destroys and frees the GPU resources which include the buffer and @@ -740,7 +771,9 @@ resources. @returns Boolean stating whether tensor is initialized)doc"; -static const char *__doc_kp_Tensor_mData = R"doc()doc"; +static const char *__doc_kp_Tensor_mDataType = R"doc()doc"; + +static const char *__doc_kp_Tensor_mDataTypeMemorySize = R"doc()doc"; static const char *__doc_kp_Tensor_mDevice = R"doc()doc"; @@ -758,29 +791,21 @@ static const char *__doc_kp_Tensor_mPrimaryBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_mPrimaryMemory = R"doc()doc"; +static const char *__doc_kp_Tensor_mRawData = R"doc()doc"; + +static const char *__doc_kp_Tensor_mSize = R"doc()doc"; + static const char *__doc_kp_Tensor_mStagingBuffer = R"doc()doc"; static const char *__doc_kp_Tensor_mStagingMemory = R"doc()doc"; static const char *__doc_kp_Tensor_mTensorType = R"doc()doc"; -static const char *__doc_kp_Tensor_mapDataFromHostMemory = -R"doc(Maps data from the Host Visible GPU memory into the data vector. It -requires the Tensor to be of staging type for it to work.)doc"; - -static const char *__doc_kp_Tensor_mapDataIntoHostMemory = -R"doc(Maps data from the data vector into the Host Visible GPU memory. It -requires the tensor to be of staging type for it to work.)doc"; +static const char *__doc_kp_Tensor_mapRawData = R"doc()doc"; static const char *__doc_kp_Tensor_memorySize = R"doc()doc"; -static const char *__doc_kp_Tensor_operator_array = -R"doc(Overrides the subscript operator to expose the underlying data's -subscript operator which in this case would be its underlying -vector's. - -@param i The index where the element will be returned from. @return -Returns the element in the position requested.)doc"; +static const char *__doc_kp_Tensor_rawData = R"doc()doc"; static const char *__doc_kp_Tensor_rebuild = R"doc(Function to trigger reinitialisation of the tensor buffer and memory @@ -829,7 +854,7 @@ would only be relevant for kp::Tensors of type eDevice. @param createBarrier Whether to create a barrier that ensures the data is copied before further operations. Default is true.)doc"; -static const char *__doc_kp_Tensor_setData = +static const char *__doc_kp_Tensor_setRawData = R"doc(Sets / resets the vector data of the tensor. This function does not perform any copies into GPU memory and is only performed on the host.)doc"; @@ -844,6 +869,10 @@ R"doc(Retrieve the tensor type of the Tensor @return Tensor type of tensor)doc"; +static const char *__doc_kp_Tensor_unmapRawData = R"doc()doc"; + +static const char *__doc_kp_Tensor_vector = R"doc()doc"; + #if defined(__GNUG__) #pragma GCC diagnostic pop #endif diff --git a/python/src/main.cpp b/python/src/main.cpp index 495d0ed0c..9e065c213 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -131,7 +131,6 @@ PYBIND11_MODULE(kp, m) { DOC(kp, Sequence, evalAwait)) .def("eval_await", [](kp::Sequence& self, uint32_t wait) { return self.evalAwait(wait); }, DOC(kp, Sequence, evalAwait)) -<<<<<<< HEAD .def("is_recording", &kp::Sequence::isRecording, DOC(kp, Sequence, isRecording)) .def("is_running", &kp::Sequence::isRunning, @@ -150,17 +149,6 @@ PYBIND11_MODULE(kp, m) { py::class_>(m, "Manager", DOC(kp, Manager)) .def(py::init(), DOC(kp, Manager, Manager)) .def(py::init(), DOC(kp, Manager, Manager_2)) -======= - .def("is_recording", &kp::Sequence::isRecording, DOC(kp, Sequence, isRecording)) - .def("is_running", &kp::Sequence::isRunning, DOC(kp, Sequence, isRunning)) - .def("is_init", &kp::Sequence::isInit, DOC(kp, Sequence, isInit)) - .def("clear", &kp::Sequence::clear, DOC(kp, Sequence, clear)) - .def("destroy", &kp::Sequence::destroy, DOC(kp, Sequence, destroy)); - - py::class_>(m, "Manager") - .def(py::init()) - .def(py::init()) ->>>>>>> cc1a6cc (Updated tests and rebased) .def(py::init&,const std::vector&>(), DOC(kp, Manager, Manager_2), py::arg("device") = 0, From 2e1022410baf5c8971b9c4b97c33f53d2cc31181 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 14:20:31 +0000 Subject: [PATCH 66/91] Updated compile_shader to compileShader --- test/TestSequence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index 090a6317b..ca3b9a485 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -117,7 +117,7 @@ TEST(TestSequence, SequenceTimestamps) pa[index] = pa[index] + 1; })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); auto seq = mgr.sequence(0, 100); //100 timestamps seq->record({ tensorA }) From 1d1018fa0c24d4270d53540169efbed72c3b3c4c Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 7 Mar 2021 14:45:31 +0000 Subject: [PATCH 67/91] Reformat --- docs/overview/advanced-examples.rst | 8 +-- src/Algorithm.cpp | 23 +++--- src/Manager.cpp | 41 +++++++---- src/OpTensorCopy.cpp | 13 ++-- src/OpTensorSyncDevice.cpp | 1 - src/Sequence.cpp | 65 +++++++++-------- src/Shader.cpp | 8 +-- src/Tensor.cpp | 30 ++++---- src/include/kompute/Algorithm.hpp | 74 ++++++++++--------- src/include/kompute/Manager.hpp | 21 ++++-- src/include/kompute/Sequence.hpp | 30 ++++---- src/include/kompute/Shader.hpp | 1 - src/include/kompute/Tensor.hpp | 96 ++++++++++++------------- test/TestDestroy.cpp | 2 +- test/TestLogisticRegression.cpp | 24 ++++--- test/TestMultipleAlgoExecutions.cpp | 8 ++- test/TestOpShadersFromStringAndFile.cpp | 6 +- test/TestPushConstant.cpp | 34 +++++---- test/TestSequence.cpp | 38 +++++----- test/TestShaderResources.cpp | 43 ++++++----- test/TestSpecializationConstant.cpp | 6 +- test/TestWorkgroup.cpp | 13 ++-- 22 files changed, 326 insertions(+), 259 deletions(-) diff --git a/docs/overview/advanced-examples.rst b/docs/overview/advanced-examples.rst index 90066e8cc..0ddf3e32a 100644 --- a/docs/overview/advanced-examples.rst +++ b/docs/overview/advanced-examples.rst @@ -55,7 +55,7 @@ The example below shows how you can enable the "VK_EXT_shader_atomic_float" exte atomicAdd(pa[2], pcs.z); })"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr sq = nullptr; @@ -102,7 +102,7 @@ We also provide tools that allow you to `convert shaders into C++ headers spirv = kp::Shader::compile_source(R"( + std::vector spirv = kp::Shader::compileSource(R"( #version 450 layout(set = 0, binding = 0) buffer tensorLhs { @@ -215,7 +215,7 @@ In this case we create a shader that should take a couple of milliseconds to run } )"); - auto algo = mgr.algorithm({tensor}, kp::Shader::compile_source(shader)); + auto algo = mgr.algorithm({tensor}, kp::Shader::compileSource(shader)); Now we are able to run the await function on the default sequence. @@ -361,7 +361,7 @@ Similar to the asyncrhonous usecase above, we can still run synchronous commands } )"); - std::vector spirv = kp::Shader::compile_source(shader); + std::vector spirv = kp::Shader::compileSource(shader); std::shared_ptr algo = mgr.algorithm({tensorA, tenssorB}, spirv); diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index d5263628b..1b34b35e6 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -20,7 +20,8 @@ Algorithm::Algorithm(std::shared_ptr device, "spirv size: {}", tensors.size(), spirv.size()); - this->rebuild(tensors, spirv, workgroup, specializationConstants, pushConstants); + this->rebuild( + tensors, spirv, workgroup, specializationConstants, pushConstants); } else { KP_LOG_INFO("Kompute Algorithm constructor with empty tensors and or " "spirv so not rebuilding vulkan components"); @@ -425,15 +426,18 @@ Algorithm::setWorkgroup(const Workgroup& workgroup, uint32_t minSize) } void -Algorithm::setPush(const Constants& pushConstants) { +Algorithm::setPush(const Constants& pushConstants) +{ - if (pushConstants.size() != this->mPushConstants.size()) { - throw std::runtime_error(fmt::format("Kompute Algorithm push " - "constant provided is size {} but expected size {}", - pushConstants.size(), this->mPushConstants.size())); - } + if (pushConstants.size() != this->mPushConstants.size()) { + throw std::runtime_error( + fmt::format("Kompute Algorithm push " + "constant provided is size {} but expected size {}", + pushConstants.size(), + this->mPushConstants.size())); + } - this->mPushConstants = pushConstants; + this->mPushConstants = pushConstants; } const Workgroup& @@ -449,7 +453,8 @@ Algorithm::getSpecializationConstants() } const Constants& -Algorithm::getPush() { +Algorithm::getPush() +{ return this->mPushConstants; } diff --git a/src/Manager.cpp b/src/Manager.cpp index 5d6bf4cd4..cdc896332 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -1,8 +1,8 @@ -#include -#include -#include #include +#include +#include +#include #include "kompute/Manager.hpp" @@ -39,7 +39,8 @@ Manager::Manager(uint32_t physicalDeviceIndex, this->mManageResources = true; this->createInstance(); - this->createDevice(familyQueueIndices, physicalDeviceIndex, desiredExtensions); + this->createDevice( + familyQueueIndices, physicalDeviceIndex, desiredExtensions); } Manager::Manager(std::shared_ptr instance, @@ -177,7 +178,8 @@ Manager::createInstance() }; std::vector envLayerNames; const char* envLayerNamesVal = std::getenv("KOMPUTE_ENV_DEBUG_LAYERS"); - KP_LOG_DEBUG("Kompute Manager adding environment layers: {}", envLayerNamesVal); + KP_LOG_DEBUG("Kompute Manager adding environment layers: {}", + envLayerNamesVal); if (envLayerNamesVal != NULL && *envLayerNamesVal != '\0') { std::istringstream iss(envLayerNamesVal); std::istream_iterator beg(iss), end; @@ -206,13 +208,15 @@ Manager::createInstance() } if (validLayerNames.size() > 0) { - KP_LOG_DEBUG("Kompute Manager Initializing instance with valid layers: {}", validLayerNames); + KP_LOG_DEBUG( + "Kompute Manager Initializing instance with valid layers: {}", + validLayerNames); computeInstanceCreateInfo.enabledLayerCount = (uint32_t)validLayerNames.size(); computeInstanceCreateInfo.ppEnabledLayerNames = validLayerNames.data(); - } - else { - KP_LOG_WARN("Kompute Manager no valid layer names found from desired layer names"); + } else { + KP_LOG_WARN("Kompute Manager no valid layer names found from desired " + "layer names"); } #endif #endif @@ -347,16 +351,19 @@ Manager::createDevice(const std::vector& familyQueueIndices, deviceQueueCreateInfos.push_back(deviceQueueCreateInfo); } - KP_LOG_DEBUG("Kompute Manager desired extension layers {}", desiredExtensions); + KP_LOG_DEBUG("Kompute Manager desired extension layers {}", + desiredExtensions); - std::vector deviceExtensions = this->mPhysicalDevice->enumerateDeviceExtensionProperties(); + std::vector deviceExtensions = + this->mPhysicalDevice->enumerateDeviceExtensionProperties(); std::set uniqueExtensionNames; for (const vk::ExtensionProperties& ext : deviceExtensions) { std::string extName(ext.extensionName.data()); uniqueExtensionNames.insert(extName); } - KP_LOG_DEBUG("Kompute Manager available extensions {}", uniqueExtensionNames); + KP_LOG_DEBUG("Kompute Manager available extensions {}", + uniqueExtensionNames); std::vector validExtensions; for (std::string ext : desiredExtensions) { if (uniqueExtensionNames.count(ext) != 0) { @@ -364,7 +371,8 @@ Manager::createDevice(const std::vector& familyQueueIndices, } } if (desiredExtensions.size() != validExtensions.size()) { - KP_LOG_ERROR("Kompute Manager not all extensions were added: {}", validExtensions); + KP_LOG_ERROR("Kompute Manager not all extensions were added: {}", + validExtensions); } vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), @@ -406,7 +414,12 @@ Manager::algorithm(const std::vector>& tensors, KP_LOG_DEBUG("Kompute Manager algorithm creation triggered"); std::shared_ptr algorithm{ new kp::Algorithm( - this->mDevice, tensors, spirv, workgroup, specializationConstants, pushConstants) }; + this->mDevice, + tensors, + spirv, + workgroup, + specializationConstants, + pushConstants) }; if (this->mManageResources) { this->mManagedAlgorithms.push_back(algorithm); diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index c93830902..33b9eb838 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -18,13 +18,16 @@ OpTensorCopy::OpTensorCopy(const std::vector>& tensors) uint32_t size = this->mTensors[0]->size(); for (const std::shared_ptr& tensor : tensors) { if (tensor->dataType() != dataType) { - throw std::runtime_error(fmt::format("Attempting to copy tensors of different types from {} to {}", - dataType, tensor->dataType())); + throw std::runtime_error(fmt::format( + "Attempting to copy tensors of different types from {} to {}", + dataType, + tensor->dataType())); } if (tensor->size() != size) { - throw std::runtime_error(fmt::format("Attempting to copy tensors of different sizes from {} to {}", - size, tensor->size())); - + throw std::runtime_error(fmt::format( + "Attempting to copy tensors of different sizes from {} to {}", + size, + tensor->size())); } } } diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index 4dbfaec83..cd887c148 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -40,7 +40,6 @@ void OpTensorSyncDevice::preEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorSyncDevice preEval called"); - } void diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 6e379eb92..4db458288 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -18,8 +18,9 @@ Sequence::Sequence(std::shared_ptr physicalDevice, this->createCommandPool(); this->createCommandBuffer(); - if(totalTimestamps>0) - this->createTimestampQueryPool(totalTimestamps+1); //+1 for the first one + if (totalTimestamps > 0) + this->createTimestampQueryPool(totalTimestamps + + 1); //+1 for the first one } Sequence::~Sequence() @@ -48,12 +49,12 @@ Sequence::begin() this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); this->mRecording = true; - //latch the first timestamp before any commands are submitted - if(this->timestampQueryPool) + // latch the first timestamp before any commands are submitted + if (this->timestampQueryPool) this->mCommandBuffer->writeTimestamp( - vk::PipelineStageFlagBits::eAllCommands, - *this->timestampQueryPool, 0 - ); + vk::PipelineStageFlagBits::eAllCommands, + *this->timestampQueryPool, + 0); } void @@ -246,12 +247,12 @@ Sequence::destroy() this->mOperations.clear(); } - if(this->timestampQueryPool){ + if (this->timestampQueryPool) { KP_LOG_INFO("Destroying QueryPool"); this->mDevice->destroy( - *this->timestampQueryPool, - (vk::Optional)nullptr); - + *this->timestampQueryPool, + (vk::Optional)nullptr); + this->timestampQueryPool = nullptr; KP_LOG_DEBUG("Kompute Sequence Destroyed QueryPool"); } @@ -281,12 +282,12 @@ Sequence::record(std::shared_ptr op) this->mOperations.push_back(op); - if(this->timestampQueryPool) - this->mCommandBuffer->writeTimestamp( - vk::PipelineStageFlagBits::eAllCommands, - *this->timestampQueryPool, this->mOperations.size() - ); - + if (this->timestampQueryPool) + this->mCommandBuffer->writeTimestamp( + vk::PipelineStageFlagBits::eAllCommands, + *this->timestampQueryPool, + this->mOperations.size()); + return shared_from_this(); } @@ -339,7 +340,8 @@ Sequence::createTimestampQueryPool(uint32_t totalTimestamps) { KP_LOG_DEBUG("Kompute Sequence creating query pool"); if (!this->isInit()) { - throw std::runtime_error("createTimestampQueryPool() called on uninitialized Sequence"); + throw std::runtime_error( + "createTimestampQueryPool() called on uninitialized Sequence"); } if (!this->mPhysicalDevice) { throw std::runtime_error("Kompute Sequence physical device is null"); @@ -347,16 +349,16 @@ Sequence::createTimestampQueryPool(uint32_t totalTimestamps) vk::PhysicalDeviceProperties physicalDeviceProperties = this->mPhysicalDevice->getProperties(); - - if(physicalDeviceProperties.limits.timestampComputeAndGraphics){ + + if (physicalDeviceProperties.limits.timestampComputeAndGraphics) { vk::QueryPoolCreateInfo queryPoolInfo; queryPoolInfo.setQueryCount(totalTimestamps); queryPoolInfo.setQueryType(vk::QueryType::eTimestamp); - this->timestampQueryPool = std::make_shared(this->mDevice->createQueryPool(queryPoolInfo)); + this->timestampQueryPool = std::make_shared( + this->mDevice->createQueryPool(queryPoolInfo)); KP_LOG_DEBUG("Query pool for timestamps created"); - } - else{ + } else { throw std::runtime_error("Device does not support timestamps"); } } @@ -364,14 +366,19 @@ Sequence::createTimestampQueryPool(uint32_t totalTimestamps) std::vector Sequence::getTimestamps() { - if(!this->timestampQueryPool) + if (!this->timestampQueryPool) throw std::runtime_error("Timestamp latching not enabled"); - - const auto n = this->mOperations.size()+1; + + const auto n = this->mOperations.size() + 1; std::vector timestamps(n, 0); - this->mDevice->getQueryPoolResults(*this->timestampQueryPool, - 0, n, timestamps.size()*sizeof(std::uint64_t), timestamps.data(), - sizeof(uint64_t), vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait); + this->mDevice->getQueryPoolResults( + *this->timestampQueryPool, + 0, + n, + timestamps.size() * sizeof(std::uint64_t), + timestamps.data(), + sizeof(uint64_t), + vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait); return timestamps; } diff --git a/src/Shader.cpp b/src/Shader.cpp index bedac0165..293752a9a 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -99,10 +99,10 @@ Shader::compileSource( const TBuiltInResource& resource) { return compileSources({ source }, - std::vector({}), - entryPoint, - definitions, - resource); + std::vector({}), + entryPoint, + definitions, + resource); } const TBuiltInResource Shader::defaultResource = { diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 947714693..90c21fc8a 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -64,13 +64,10 @@ Tensor::tensorType() bool Tensor::isInit() { - return this->mDevice - && this->mPrimaryBuffer - && this->mPrimaryMemory - && this->mRawData; + return this->mDevice && this->mPrimaryBuffer && this->mPrimaryMemory && + this->mRawData; } - void Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, std::shared_ptr copyFromTensor, @@ -175,7 +172,8 @@ Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, vk::DescriptorBufferInfo Tensor::constructDescriptorBufferInfo() { - KP_LOG_DEBUG("Kompute Tensor construct descriptor buffer info size {}", this->memorySize()); + KP_LOG_DEBUG("Kompute Tensor construct descriptor buffer info size {}", + this->memorySize()); vk::DeviceSize bufferSize = this->memorySize(); return vk::DescriptorBufferInfo(*this->mPrimaryBuffer, 0, // offset @@ -213,7 +211,7 @@ Tensor::getPrimaryMemoryPropertyFlags() break; case TensorTypes::eHost: return vk::MemoryPropertyFlagBits::eHostVisible | - vk::MemoryPropertyFlagBits::eHostCoherent; + vk::MemoryPropertyFlagBits::eHostCoherent; break; case TensorTypes::eStorage: return vk::MemoryPropertyFlagBits::eDeviceLocal; @@ -363,7 +361,8 @@ Tensor::destroy() { KP_LOG_DEBUG("Kompute Tensor started destroy()"); - // Setting raw data to null regardless whether device is available to invalidate Tensor + // Setting raw data to null regardless whether device is available to + // invalidate Tensor this->mRawData = nullptr; this->mSize = 0; this->mDataTypeMemorySize = 0; @@ -442,31 +441,36 @@ Tensor::destroy() template<> Tensor::TensorDataTypes -TensorT::dataType() { +TensorT::dataType() +{ return Tensor::TensorDataTypes::eBool; } template<> Tensor::TensorDataTypes -TensorT::dataType() { +TensorT::dataType() +{ return Tensor::TensorDataTypes::eInt; } template<> Tensor::TensorDataTypes -TensorT::dataType() { +TensorT::dataType() +{ return Tensor::TensorDataTypes::eUnsignedInt; } template<> Tensor::TensorDataTypes -TensorT::dataType() { +TensorT::dataType() +{ return Tensor::TensorDataTypes::eFloat; } template<> Tensor::TensorDataTypes -TensorT::dataType() { +TensorT::dataType() +{ return Tensor::TensorDataTypes::eDouble; } diff --git a/src/include/kompute/Algorithm.hpp b/src/include/kompute/Algorithm.hpp index fae9cfd4b..ecbd36aa6 100644 --- a/src/include/kompute/Algorithm.hpp +++ b/src/include/kompute/Algorithm.hpp @@ -18,15 +18,17 @@ class Algorithm * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param tensors (optional) The tensors to use to create the descriptor resources + * @param tensors (optional) The tensors to use to create the descriptor + * resources * @param spirv (optional) The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -36,18 +38,19 @@ class Algorithm const Constants& pushConstants = {}); /** - * Rebuild function to reconstruct algorithm with configuration parameters to create - * the underlying resources. + * Rebuild function to reconstruct algorithm with configuration parameters + * to create the underlying resources. * * @param tensors The tensors to use to create the descriptor resources * @param spirv The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -70,25 +73,26 @@ class Algorithm void recordDispatch(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the "core" algorithm components which consist of - * binding the pipeline and binding the descriptorsets. + * Records command that binds the "core" algorithm components which consist + * of binding the pipeline and binding the descriptorsets. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindCore(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the push constants to the command buffer provided - * - it is required that the pushConstants provided are of the same size as the - * ones provided during initialization. + * Records command that binds the push constants to the command buffer + * provided + * - it is required that the pushConstants provided are of the same size as + * the ones provided during initialization. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindPush(const vk::CommandBuffer& commandBuffer); /** - * function that checks all the gpu resource components to verify if these have - * been created and returns true if all are valid. + * function that checks all the gpu resource components to verify if these + * have been created and returns true if all are valid. * * @returns returns true if the algorithm is currently initialized. */ @@ -97,26 +101,28 @@ class Algorithm /** * Sets the work group to use in the recordDispatch * - * @param workgroup The kp::Workgroup value to use to update the algorithm. It - * must have a value greater than 1 on the x value (index 1) otherwise it will - * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + * @param workgroup The kp::Workgroup value to use to update the algorithm. + * It must have a value greater than 1 on the x value (index 1) otherwise it + * will be initialized on the size of the first tensor (ie. + * this->mTensor[0]->size()) */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); /** - * Sets the push constants to the new value provided to use in the next bindPush() + * Sets the push constants to the new value provided to use in the next + * bindPush() * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ void setPush(const Constants& pushConstants); /** * Gets the current workgroup from the algorithm. * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ const Workgroup& getWorkgroup(); /** diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 6eb2042eb..6b06b83fd 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -24,13 +24,14 @@ class Manager Manager(); /** - * Similar to base constructor but allows for further configuration to use when - * creating the Vulkan resources. + * Similar to base constructor but allows for further configuration to use + * when creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param desiredExtensions The desired extensions to load from physicalDevice + * @param desiredExtensions The desired extensions to load from + * physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -64,7 +65,8 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, + uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager @@ -74,7 +76,7 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - template + template std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) @@ -105,8 +107,13 @@ class Manager const Tensor::TensorDataTypes& dataType, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { - std::shared_ptr tensor{ new kp::Tensor( - this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + std::shared_ptr tensor{ new kp::Tensor(this->mPhysicalDevice, + this->mDevice, + data, + elementTotalCount, + elementMemorySize, + dataType, + tensorType) }; if (this->mManageResources) { this->mManagedTensors.push_back(tensor); diff --git a/src/include/kompute/Sequence.hpp b/src/include/kompute/Sequence.hpp index d29f6aaf0..6eeb265c1 100644 --- a/src/include/kompute/Sequence.hpp +++ b/src/include/kompute/Sequence.hpp @@ -2,8 +2,8 @@ #include "kompute/Core.hpp" -#include "kompute/operations/OpBase.hpp" #include "kompute/operations/OpAlgoDispatch.hpp" +#include "kompute/operations/OpBase.hpp" namespace kp { @@ -40,8 +40,8 @@ class Sequence : public std::enable_shared_from_this * function also requires the Sequence to be recording, otherwise it will * not be able to add the operation. * - * @param op Object derived from kp::BaseOp that will be recoreded by the sequence - * which will be used when the operation is evaluated. + * @param op Object derived from kp::BaseOp that will be recoreded by the + * sequence which will be used when the operation is evaluated. * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -59,7 +59,8 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, TArgs&&... params) + std::vector> tensors, + TArgs&&... params) { std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); @@ -94,8 +95,9 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(); /** - * Resets all the recorded and stored operations, records the operation - * provided and submits into the gpu as a submit job synchronously (with a barrier). + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a + * barrier). * * @return shared_ptr of the Sequence class itself */ @@ -138,16 +140,18 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); /** * Clears currnet operations to record provided one in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ @@ -241,9 +245,9 @@ class Sequence : public std::enable_shared_from_this bool isInit(); /** - * Clears command buffer and triggers re-record of all the current operations - * saved, which is useful if the underlying kp::Tensors or kp::Algorithms - * are modified and need to be re-recorded. + * Clears command buffer and triggers re-record of all the current + * operations saved, which is useful if the underlying kp::Tensors or + * kp::Algorithms are modified and need to be re-recorded. */ void rerecord(); diff --git a/src/include/kompute/Shader.hpp b/src/include/kompute/Shader.hpp index 9ecab24cd..2cd240424 100644 --- a/src/include/kompute/Shader.hpp +++ b/src/include/kompute/Shader.hpp @@ -18,7 +18,6 @@ namespace kp { class Shader { public: - // The default resource limit for the GLSL compiler, can be overwritten // Has been adopted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 0194e208f..dc4a4f51a 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -160,41 +160,33 @@ class Tensor * @return Unsigned integer representing the total number of elements */ // TODO: move to cpp - uint32_t size() { - return this->mSize; - } + uint32_t size() { return this->mSize; } // TODO: move to cpp - uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } + uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } // TODO: move to cpp - uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } + uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } /** * Retrieve the underlying data type of the Tensor * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { - return this->mDataType; - } + TensorDataTypes dataType() { return this->mDataType; } - void* rawData() { - return this->mRawData; - } + void* rawData() { return this->mRawData; } // TODO: move to cpp - template - T* data() { + template + T* data() + { return (T*)this->mRawData; } - template - std::vector vector() { + template + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } @@ -202,9 +194,9 @@ class Tensor * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. */ - void setRawData(const void* data) + void setRawData(const void* data) { - // Copy data + // Copy data memcpy(this->mRawData, data, this->memorySize()); } @@ -217,7 +209,8 @@ class Tensor void* mRawData; private: - void mapRawData() { + void mapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -235,14 +228,17 @@ class Tensor vk::DeviceSize bufferSize = this->memorySize(); - // Given we request coherent host memory we don't need to invalidate / flush + // Given we request coherent host memory we don't need to invalidate / + // flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + vk::MappedMemoryRange mappedMemoryRange( + *hostVisibleMemory, 0, bufferSize); } - void unmapRawData() { + void unmapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -296,49 +292,46 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - }; // TODO: Limit T to be only float, bool, double, etc -template -class TensorT: public Tensor +template +class TensorT : public Tensor { public: TensorT(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, - device, - (void*)data.data(), - data.size(), - sizeof(T), - this->dataType(), - tensorType) + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", + data.size()); } - ~TensorT() { - KP_LOG_DEBUG("Kompute TensorT destructor"); - } + ~TensorT() { KP_LOG_DEBUG("Kompute TensorT destructor"); } - T* data() { - return (T*)this->mRawData; - } + T* data() { return (T*)this->mRawData; } - std::vector vector() { + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - T& operator[](int index) { - return *(((T*)this->mRawData) + index); - } + T& operator[](int index) { return *(((T*)this->mRawData) + index); } - void setData(const std::vector& data) { + void setData(const std::vector& data) + { - KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", + data.size()); if (data.size() != this->mSize) { throw std::runtime_error( @@ -349,7 +342,6 @@ class TensorT: public Tensor } TensorDataTypes dataType(); - }; } // End namespace kp diff --git a/test/TestDestroy.cpp b/test/TestDestroy.cpp index defd40998..36127f84e 100644 --- a/test/TestDestroy.cpp +++ b/test/TestDestroy.cpp @@ -7,7 +7,7 @@ TEST(TestDestroy, TestDestroyTensorSingle) { std::shared_ptr> tensorA = nullptr; - std::string shader(R"( + std::string shader(R"( #version 450 layout (local_size_x = 1) in; layout(set = 0, binding = 0) buffer a { float pa[]; }; diff --git a/test/TestLogisticRegression.cpp b/test/TestLogisticRegression.cpp index a4402637f..71eeaafde 100644 --- a/test/TestLogisticRegression.cpp +++ b/test/TestLogisticRegression.cpp @@ -20,13 +20,17 @@ TEST(TestLogisticRegression, TestMainLogisticRegression) std::shared_ptr> y = mgr.tensor({ 0, 0, 0, 1, 1 }); std::shared_ptr> wIn = mgr.tensor({ 0.001, 0.001 }); - std::shared_ptr> wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr> wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutI = + mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutJ = + mgr.tensor({ 0, 0, 0, 0, 0 }); std::shared_ptr> bIn = mgr.tensor({ 0 }); - std::shared_ptr> bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> bOut = + mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr> lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> lOut = + mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, @@ -95,14 +99,18 @@ TEST(TestLogisticRegression, TestMainLogisticRegressionManualCopy) std::shared_ptr> wIn = mgr.tensor({ 0.001, 0.001 }, kp::Tensor::TensorTypes::eHost); - std::shared_ptr> wOutI = mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr> wOutJ = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutI = + mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> wOutJ = + mgr.tensor({ 0, 0, 0, 0, 0 }); std::shared_ptr> bIn = mgr.tensor({ 0 }, kp::Tensor::TensorTypes::eHost); - std::shared_ptr> bOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> bOut = + mgr.tensor({ 0, 0, 0, 0, 0 }); - std::shared_ptr> lOut = mgr.tensor({ 0, 0, 0, 0, 0 }); + std::shared_ptr> lOut = + mgr.tensor({ 0, 0, 0, 0, 0 }); std::vector> params = { xI, xJ, y, wIn, wOutI, wOutJ, diff --git a/test/TestMultipleAlgoExecutions.cpp b/test/TestMultipleAlgoExecutions.cpp index f9e066f47..932661dd4 100644 --- a/test/TestMultipleAlgoExecutions.cpp +++ b/test/TestMultipleAlgoExecutions.cpp @@ -50,8 +50,11 @@ TEST(TestMultipleAlgoExecutions, TestEndToEndFunctionality) kp::Constants pushConstsA({ 2.0 }); kp::Constants pushConstsB({ 3.0 }); - auto algorithm = mgr.algorithm( - params, kp::Shader::compileSource(shader), workgroup, specConsts, pushConstsA); + auto algorithm = mgr.algorithm(params, + kp::Shader::compileSource(shader), + workgroup, + specConsts, + pushConstsA); // 3. Run operation with string shader synchronously mgr.sequence() @@ -202,4 +205,3 @@ TEST(TestMultipleAlgoExecutions, SingleRecordMultipleEval) EXPECT_EQ(tensorA->vector(), std::vector({ 3, 3, 3 })); } - diff --git a/test/TestOpShadersFromStringAndFile.cpp b/test/TestOpShadersFromStringAndFile.cpp index a1f8eda99..bf2ed8587 100644 --- a/test/TestOpShadersFromStringAndFile.cpp +++ b/test/TestOpShadersFromStringAndFile.cpp @@ -71,9 +71,9 @@ TEST(TestOpAlgoCreate, ShaderCompiledDataFromConstructor) //{ // kp::Manager mgr; // -// std::shared_ptr> tensorA{ new kp::Tensor({ 3, 4, 5 }) }; -// std::shared_ptr> tensorB{ new kp::Tensor({ 0, 0, 0 }) }; -// mgr.rebuild({ tensorA, tensorB }); +// std::shared_ptr> tensorA{ new kp::Tensor({ 3, 4, 5 }) +// }; std::shared_ptr> tensorB{ new kp::Tensor({ 0, 0, 0 +// }) }; mgr.rebuild({ tensorA, tensorB }); // // mgr.evalOpDefault( // { tensorA, tensorB }, diff --git a/test/TestPushConstant.cpp b/test/TestPushConstant.cpp index 9599596ed..66f798afe 100644 --- a/test/TestPushConstant.cpp +++ b/test/TestPushConstant.cpp @@ -29,15 +29,17 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchOverride) { kp::Manager mgr; - std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = + mgr.tensor({ 0, 0, 0 }); - std::shared_ptr algo = - mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); + std::shared_ptr algo = mgr.algorithm( + { tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0, 0.0, 0.0 }); sq = mgr.sequence()->eval({ tensor }); // We need to run this in sequence to avoid race condition - // We can't use atomicAdd as swiftshader doesn't support it for float + // We can't use atomicAdd as swiftshader doesn't support it for + // float sq->eval(algo, kp::Constants{ 0.1, 0.2, 0.3 }); sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); sq->eval({ tensor }); @@ -72,15 +74,17 @@ TEST(TestPushConstants, TestConstantsAlgoDispatchNoOverride) { kp::Manager mgr; - std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = + mgr.tensor({ 0, 0, 0 }); - std::shared_ptr algo = - mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.1, 0.2, 0.3 }); + std::shared_ptr algo = mgr.algorithm( + { tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.1, 0.2, 0.3 }); sq = mgr.sequence()->eval({ tensor }); // We need to run this in sequence to avoid race condition - // We can't use atomicAdd as swiftshader doesn't support it for float + // We can't use atomicAdd as swiftshader doesn't support it for + // float sq->eval(algo); sq->eval(algo, kp::Constants{ 0.3, 0.2, 0.1 }); sq->eval({ tensor }); @@ -115,15 +119,17 @@ TEST(TestPushConstants, TestConstantsWrongSize) { kp::Manager mgr; - std::shared_ptr> tensor = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensor = + mgr.tensor({ 0, 0, 0 }); - std::shared_ptr algo = - mgr.algorithm({ tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0 }); + std::shared_ptr algo = mgr.algorithm( + { tensor }, spirv, kp::Workgroup({ 1 }), {}, { 0.0 }); - sq = mgr.sequence() - ->record({ tensor }); + sq = mgr.sequence()->record({ tensor }); - EXPECT_THROW(sq->record(algo, kp::Constants{ 0.1, 0.2, 0.3 }), std::runtime_error); + EXPECT_THROW(sq->record( + algo, kp::Constants{ 0.1, 0.2, 0.3 }), + std::runtime_error); } } } diff --git a/test/TestSequence.cpp b/test/TestSequence.cpp index ca3b9a485..19d96c893 100644 --- a/test/TestSequence.cpp +++ b/test/TestSequence.cpp @@ -60,9 +60,9 @@ TEST(TestSequence, RerecordSequence) 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}); + 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 }); @@ -83,25 +83,24 @@ TEST(TestSequence, RerecordSequence) )"); std::shared_ptr algo = - mgr.algorithm({tensorA, tensorB, tensorOut}, spirv); + mgr.algorithm({ tensorA, tensorB, tensorOut }, spirv); - sq->record(algo) - ->record({tensorA, tensorB, tensorOut}); + sq->record(algo)->record( + { tensorA, tensorB, tensorOut }); sq->eval(); - EXPECT_EQ(tensorOut->vector(), std::vector({2, 4, 6})); + EXPECT_EQ(tensorOut->vector(), std::vector({ 2, 4, 6 })); - algo->rebuild({tensorOut, tensorA, tensorB}, spirv); + algo->rebuild({ tensorOut, tensorA, tensorB }, spirv); // Refresh and trigger a rerecord sq->rerecord(); sq->eval(); - EXPECT_EQ(tensorB->vector(), std::vector({2, 8, 18})); + EXPECT_EQ(tensorB->vector(), std::vector({ 2, 8, 18 })); } - TEST(TestSequence, SequenceTimestamps) { kp::Manager mgr; @@ -118,15 +117,16 @@ TEST(TestSequence, SequenceTimestamps) })"); std::vector spirv = kp::Shader::compileSource(shader); - - auto seq = mgr.sequence(0, 100); //100 timestamps + + auto seq = mgr.sequence(0, 100); // 100 timestamps seq->record({ tensorA }) - ->record(mgr.algorithm({ tensorA }, spirv)) - ->record(mgr.algorithm({ tensorA }, spirv)) - ->record(mgr.algorithm({ tensorA }, spirv)) - ->record({ tensorA }) - ->eval(); + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record(mgr.algorithm({ tensorA }, spirv)) + ->record({ tensorA }) + ->eval(); const std::vector timestamps = seq->getTimestamps(); - - EXPECT_EQ(timestamps.size(), 6); //1 timestamp at start + 1 after each operation + + EXPECT_EQ(timestamps.size(), + 6); // 1 timestamp at start + 1 after each operation } diff --git a/test/TestShaderResources.cpp b/test/TestShaderResources.cpp index 536f4ca0c..6faddb39e 100644 --- a/test/TestShaderResources.cpp +++ b/test/TestShaderResources.cpp @@ -24,34 +24,43 @@ static const std::string shaderString = (R"( } )"); -void compileShaderWithGivenResources(const std::string shaderString, const TBuiltInResource resources) { - kp::Shader::compileSource(shaderString, std::string("main"), std::vector>({}), resources); +void +compileShaderWithGivenResources(const std::string shaderString, + const TBuiltInResource resources) +{ + kp::Shader::compileSource( + shaderString, + std::string("main"), + std::vector>({}), + resources); } - - TEST(TestShaderResources, TestNoMaxLight) { TBuiltInResource noMaxLightResources = kp::Shader::defaultResource; - noMaxLightResources.maxLights=0; - - EXPECT_NO_THROW(compileShaderWithGivenResources(shaderString, noMaxLightResources)); -} + noMaxLightResources.maxLights = 0; + EXPECT_NO_THROW( + compileShaderWithGivenResources(shaderString, noMaxLightResources)); +} TEST(TestShaderResources, TestSmallComputeWorkGroupSizeX) { - TBuiltInResource smallComputeWorkGroupSizeXResources = kp::Shader::defaultResource; - smallComputeWorkGroupSizeXResources.maxComputeWorkGroupSizeX=0; - - ASSERT_THROW(compileShaderWithGivenResources(shaderString, smallComputeWorkGroupSizeXResources), std::runtime_error); -} + TBuiltInResource smallComputeWorkGroupSizeXResources = + kp::Shader::defaultResource; + smallComputeWorkGroupSizeXResources.maxComputeWorkGroupSizeX = 0; + ASSERT_THROW(compileShaderWithGivenResources( + shaderString, smallComputeWorkGroupSizeXResources), + std::runtime_error); +} TEST(TestShaderResources, TestNoWhileLoopLimit) { TBuiltInResource noWhileLoopLimitResources = kp::Shader::defaultResource; - noWhileLoopLimitResources.limits.whileLoops=0; - - ASSERT_THROW(compileShaderWithGivenResources(shaderString, noWhileLoopLimitResources), std::runtime_error); -} + noWhileLoopLimitResources.limits.whileLoops = 0; + + ASSERT_THROW( + compileShaderWithGivenResources(shaderString, noWhileLoopLimitResources), + std::runtime_error); +} diff --git a/test/TestSpecializationConstant.cpp b/test/TestSpecializationConstant.cpp index fe40fb5ea..7654c8aaf 100644 --- a/test/TestSpecializationConstant.cpp +++ b/test/TestSpecializationConstant.cpp @@ -25,8 +25,10 @@ TEST(TestSpecializationConstants, TestTwoConstants) { kp::Manager mgr; - std::shared_ptr> tensorA = mgr.tensor({ 0, 0, 0 }); - std::shared_ptr> tensorB = mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorA = + mgr.tensor({ 0, 0, 0 }); + std::shared_ptr> tensorB = + mgr.tensor({ 0, 0, 0 }); std::vector> params = { tensorA, tensorB }; diff --git a/test/TestWorkgroup.cpp b/test/TestWorkgroup.cpp index 8836840a6..baa5e14da 100644 --- a/test/TestWorkgroup.cpp +++ b/test/TestWorkgroup.cpp @@ -52,12 +52,13 @@ TEST(TestWorkgroup, TestSimpleWorkgroup) }; std::vector expectedB = { - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, - 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, - 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, - 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, - 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, + 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, + 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, + 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; EXPECT_EQ(tensorA->vector(), expectedA); From 263f392cbb3385421f989b8ff21ec7955a3c6c63 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Tue, 9 Mar 2021 08:06:27 +0000 Subject: [PATCH 68/91] Updated memory barriers to include staging buffers --- single_include/kompute/Kompute.hpp | 271 +++++++++++++++-------------- src/OpAlgoDispatch.cpp | 2 +- src/OpTensorCopy.cpp | 2 +- src/OpTensorSyncDevice.cpp | 3 +- src/OpTensorSyncLocal.cpp | 10 +- src/Tensor.cpp | 65 ++++--- src/include/kompute/Tensor.hpp | 52 +++--- 7 files changed, 230 insertions(+), 175 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 9b41e1ead..fc7d697fb 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -741,7 +741,6 @@ namespace kp { class Shader { public: - // The default resource limit for the GLSL compiler, can be overwritten // Has been adopted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp @@ -888,12 +887,9 @@ class Tensor * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param copyFromTensor Tensor to copy the data from - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ void recordCopyFrom(const vk::CommandBuffer& commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier); + std::shared_ptr copyFromTensor); /** * Records a copy from the internal staging memory to the device memory @@ -901,11 +897,8 @@ class Tensor * only be relevant for kp::Tensors of type eDevice. * * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ - void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer); /** * Records a copy from the internal device memory to the staging memory @@ -913,14 +906,11 @@ class Tensor * only be relevant for kp::Tensors of type eDevice. * * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ - void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer); /** - * Records the buffer memory barrier into the command buffer which + * Records the buffer memory barrier into the primary buffer and command buffer which * ensures that relevant data transfers are carried out correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into @@ -929,11 +919,26 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + /** + * Records the buffer memory barrier into the staging buffer and command buffer which + * ensures that relevant data transfers are carried out correctly. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param srcAccessMask Access flags for source access mask + * @param dstAccessMask Access flags for destination access mask + * @param scrStageMask Pipeline stage flags for source stage mask + * @param dstStageMask Pipeline stage flags for destination stage mask + */ + void recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -951,41 +956,33 @@ class Tensor * @return Unsigned integer representing the total number of elements */ // TODO: move to cpp - uint32_t size() { - return this->mSize; - } + uint32_t size() { return this->mSize; } // TODO: move to cpp - uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } + uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } // TODO: move to cpp - uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } + uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } /** * Retrieve the underlying data type of the Tensor * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { - return this->mDataType; - } + TensorDataTypes dataType() { return this->mDataType; } - void* rawData() { - return this->mRawData; - } + void* rawData() { return this->mRawData; } // TODO: move to cpp - template - T* data() { + template + T* data() + { return (T*)this->mRawData; } - template - std::vector vector() { + template + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } @@ -993,9 +990,9 @@ class Tensor * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. */ - void setRawData(const void* data) + void setRawData(const void* data) { - // Copy data + // Copy data memcpy(this->mRawData, data, this->memorySize()); } @@ -1008,7 +1005,8 @@ class Tensor void* mRawData; private: - void mapRawData() { + void mapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1026,14 +1024,17 @@ class Tensor vk::DeviceSize bufferSize = this->memorySize(); - // Given we request coherent host memory we don't need to invalidate / flush + // Given we request coherent host memory we don't need to invalidate / + // flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + vk::MappedMemoryRange mappedMemoryRange( + *hostVisibleMemory, 0, bufferSize); } - void unmapRawData() { + void unmapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1079,57 +1080,59 @@ class Tensor std::shared_ptr bufferFrom, std::shared_ptr bufferTo, vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); + vk::BufferCopy copyRegion); + void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + const vk::Buffer& buffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); // Private util functions vk::BufferUsageFlags getPrimaryBufferUsageFlags(); vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - }; // TODO: Limit T to be only float, bool, double, etc -template -class TensorT: public Tensor +template +class TensorT : public Tensor { public: TensorT(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, - device, - (void*)data.data(), - data.size(), - sizeof(T), - this->dataType(), - tensorType) + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", + data.size()); } - ~TensorT() { - KP_LOG_DEBUG("Kompute TensorT destructor"); - } + ~TensorT() { KP_LOG_DEBUG("Kompute TensorT destructor"); } - T* data() { - return (T*)this->mRawData; - } + T* data() { return (T*)this->mRawData; } - std::vector vector() { + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - T& operator[](int index) { - return *(((T*)this->mRawData) + index); - } + T& operator[](int index) { return *(((T*)this->mRawData) + index); } - void setData(const std::vector& data) { + void setData(const std::vector& data) + { - KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", + data.size()); if (data.size() != this->mSize) { throw std::runtime_error( @@ -1140,7 +1143,6 @@ class TensorT: public Tensor } TensorDataTypes dataType(); - }; } // End namespace kp @@ -1159,15 +1161,17 @@ class Algorithm * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param tensors (optional) The tensors to use to create the descriptor resources + * @param tensors (optional) The tensors to use to create the descriptor + * resources * @param spirv (optional) The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -1177,18 +1181,19 @@ class Algorithm const Constants& pushConstants = {}); /** - * Rebuild function to reconstruct algorithm with configuration parameters to create - * the underlying resources. + * Rebuild function to reconstruct algorithm with configuration parameters + * to create the underlying resources. * * @param tensors The tensors to use to create the descriptor resources * @param spirv The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -1211,25 +1216,26 @@ class Algorithm void recordDispatch(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the "core" algorithm components which consist of - * binding the pipeline and binding the descriptorsets. + * Records command that binds the "core" algorithm components which consist + * of binding the pipeline and binding the descriptorsets. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindCore(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the push constants to the command buffer provided - * - it is required that the pushConstants provided are of the same size as the - * ones provided during initialization. + * Records command that binds the push constants to the command buffer + * provided + * - it is required that the pushConstants provided are of the same size as + * the ones provided during initialization. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindPush(const vk::CommandBuffer& commandBuffer); /** - * function that checks all the gpu resource components to verify if these have - * been created and returns true if all are valid. + * function that checks all the gpu resource components to verify if these + * have been created and returns true if all are valid. * * @returns returns true if the algorithm is currently initialized. */ @@ -1238,26 +1244,28 @@ class Algorithm /** * Sets the work group to use in the recordDispatch * - * @param workgroup The kp::Workgroup value to use to update the algorithm. It - * must have a value greater than 1 on the x value (index 1) otherwise it will - * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + * @param workgroup The kp::Workgroup value to use to update the algorithm. + * It must have a value greater than 1 on the x value (index 1) otherwise it + * will be initialized on the size of the first tensor (ie. + * this->mTensor[0]->size()) */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); /** - * Sets the push constants to the new value provided to use in the next bindPush() + * Sets the push constants to the new value provided to use in the next + * bindPush() * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ void setPush(const Constants& pushConstants); /** * Gets the current workgroup from the algorithm. * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ const Workgroup& getWorkgroup(); /** @@ -1690,8 +1698,8 @@ class Sequence : public std::enable_shared_from_this * function also requires the Sequence to be recording, otherwise it will * not be able to add the operation. * - * @param op Object derived from kp::BaseOp that will be recoreded by the sequence - * which will be used when the operation is evaluated. + * @param op Object derived from kp::BaseOp that will be recoreded by the + * sequence which will be used when the operation is evaluated. * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -1709,7 +1717,8 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, TArgs&&... params) + std::vector> tensors, + TArgs&&... params) { std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); @@ -1744,8 +1753,9 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(); /** - * Resets all the recorded and stored operations, records the operation - * provided and submits into the gpu as a submit job synchronously (with a barrier). + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a + * barrier). * * @return shared_ptr of the Sequence class itself */ @@ -1788,16 +1798,18 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); /** * Clears currnet operations to record provided one in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ @@ -1891,9 +1903,9 @@ class Sequence : public std::enable_shared_from_this bool isInit(); /** - * Clears command buffer and triggers re-record of all the current operations - * saved, which is useful if the underlying kp::Tensors or kp::Algorithms - * are modified and need to be re-recorded. + * Clears command buffer and triggers re-record of all the current + * operations saved, which is useful if the underlying kp::Tensors or + * kp::Algorithms are modified and need to be re-recorded. */ void rerecord(); @@ -1961,13 +1973,14 @@ class Manager Manager(); /** - * Similar to base constructor but allows for further configuration to use when - * creating the Vulkan resources. + * Similar to base constructor but allows for further configuration to use + * when creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param desiredExtensions The desired extensions to load from physicalDevice + * @param desiredExtensions The desired extensions to load from + * physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -2001,7 +2014,8 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, + uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager @@ -2011,7 +2025,7 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - template + template std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) @@ -2042,8 +2056,13 @@ class Manager const Tensor::TensorDataTypes& dataType, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { - std::shared_ptr tensor{ new kp::Tensor( - this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + std::shared_ptr tensor{ new kp::Tensor(this->mPhysicalDevice, + this->mDevice, + data, + elementTotalCount, + elementMemorySize, + dataType, + tensorType) }; if (this->mManageResources) { this->mManagedTensors.push_back(tensor); diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 44908adb3..94502cc76 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -26,7 +26,7 @@ OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) // Barrier to ensure the data is finished writing to buffer memory for (const std::shared_ptr& tensor : this->mAlgorithm->getTensors()) { - tensor->recordBufferMemoryBarrier( + tensor->recordPrimaryBufferMemoryBarrier( commandBuffer, vk::AccessFlagBits::eHostWrite, vk::AccessFlagBits::eShaderRead, diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 33b9eb838..13e189a58 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -45,7 +45,7 @@ OpTensorCopy::record(const vk::CommandBuffer& commandBuffer) // We iterate from the second tensor onwards and record a copy to all for (size_t i = 1; i < this->mTensors.size(); i++) { this->mTensors[i]->recordCopyFrom( - commandBuffer, this->mTensors[0], false); + commandBuffer, this->mTensors[0]); } } diff --git a/src/OpTensorSyncDevice.cpp b/src/OpTensorSyncDevice.cpp index cd887c148..ad071d8d3 100644 --- a/src/OpTensorSyncDevice.cpp +++ b/src/OpTensorSyncDevice.cpp @@ -30,8 +30,7 @@ OpTensorSyncDevice::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordCopyFromStagingToDevice(commandBuffer, - false); + this->mTensors[i]->recordCopyFromStagingToDevice(commandBuffer); } } } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index f7e15ffd5..70642b9de 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -30,8 +30,14 @@ OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer, - true); + + this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer); + + this->mTensors[i]->recordStagingBufferMemoryBarrier(commandBuffer, + vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eHostRead, + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eHost); } } } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 90c21fc8a..7e7af4ca4 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -70,8 +70,7 @@ Tensor::isInit() void Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier) + std::shared_ptr copyFromTensor) { vk::DeviceSize bufferSize(this->memorySize()); @@ -83,13 +82,11 @@ Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, copyFromTensor->mPrimaryBuffer, this->mPrimaryBuffer, bufferSize, - copyRegion, - createBarrier); + copyRegion); } void -Tensor::recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, - bool createBarrier) +Tensor::recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer) { vk::DeviceSize bufferSize(this->memorySize()); vk::BufferCopy copyRegion(0, 0, bufferSize); @@ -100,13 +97,11 @@ Tensor::recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, this->mStagingBuffer, this->mPrimaryBuffer, bufferSize, - copyRegion, - createBarrier); + copyRegion); } void -Tensor::recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, - bool createBarrier) +Tensor::recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer) { vk::DeviceSize bufferSize(this->memorySize()); vk::BufferCopy copyRegion(0, 0, bufferSize); @@ -117,8 +112,7 @@ Tensor::recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, this->mPrimaryBuffer, this->mStagingBuffer, bufferSize, - copyRegion, - createBarrier); + copyRegion); } void @@ -126,24 +120,49 @@ Tensor::recordCopyBuffer(const vk::CommandBuffer& commandBuffer, std::shared_ptr bufferFrom, std::shared_ptr bufferTo, vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier) + vk::BufferCopy copyRegion) { commandBuffer.copyBuffer(*bufferFrom, *bufferTo, copyRegion); +} - if (createBarrier) { - // Buffer to ensure wait until data is copied to staging buffer - this->recordBufferMemoryBarrier(commandBuffer, - vk::AccessFlagBits::eTransferWrite, - vk::AccessFlagBits::eHostRead, - vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eHost); - } +void +Tensor::recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask) +{ + KP_LOG_DEBUG("Kompute Tensor recording PRIMARY buffer memory barrier"); + + this->recordBufferMemoryBarrier(commandBuffer, + *this->mPrimaryBuffer, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask); +} + +void +Tensor::recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask) +{ + KP_LOG_DEBUG("Kompute Tensor recording PRIMARY buffer memory barrier"); + + this->recordBufferMemoryBarrier(commandBuffer, + *this->mStagingBuffer, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask); } void Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + const vk::Buffer& buffer, vk::AccessFlagBits srcAccessMask, vk::AccessFlagBits dstAccessMask, vk::PipelineStageFlagBits srcStageMask, @@ -154,7 +173,7 @@ Tensor::recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, vk::DeviceSize bufferSize = this->memorySize(); vk::BufferMemoryBarrier bufferMemoryBarrier; - bufferMemoryBarrier.buffer = *this->mPrimaryBuffer; + bufferMemoryBarrier.buffer = buffer; bufferMemoryBarrier.size = bufferSize; bufferMemoryBarrier.srcAccessMask = srcAccessMask; bufferMemoryBarrier.dstAccessMask = dstAccessMask; diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index dc4a4f51a..6cd75996a 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -97,12 +97,9 @@ class Tensor * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param copyFromTensor Tensor to copy the data from - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ void recordCopyFrom(const vk::CommandBuffer& commandBuffer, - std::shared_ptr copyFromTensor, - bool createBarrier); + std::shared_ptr copyFromTensor); /** * Records a copy from the internal staging memory to the device memory @@ -110,11 +107,8 @@ class Tensor * only be relevant for kp::Tensors of type eDevice. * * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ - void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromStagingToDevice(const vk::CommandBuffer& commandBuffer); /** * Records a copy from the internal device memory to the staging memory @@ -122,14 +116,11 @@ class Tensor * only be relevant for kp::Tensors of type eDevice. * * @param commandBuffer Vulkan Command Buffer to record the commands into - * @param createBarrier Whether to create a barrier that ensures the data is - * copied before further operations. Default is true. */ - void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer, - bool createBarrier); + void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer); /** - * Records the buffer memory barrier into the command buffer which + * Records the buffer memory barrier into the primary buffer and command buffer which * ensures that relevant data transfers are carried out correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into @@ -138,11 +129,27 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + /** + * Records the buffer memory barrier into the staging buffer and command buffer which + * ensures that relevant data transfers are carried out correctly. + * + * @param commandBuffer Vulkan Command Buffer to record the commands into + * @param srcAccessMask Access flags for source access mask + * @param dstAccessMask Access flags for destination access mask + * @param scrStageMask Pipeline stage flags for source stage mask + * @param dstStageMask Pipeline stage flags for destination stage mask + */ + void recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); + /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -284,8 +291,13 @@ class Tensor std::shared_ptr bufferFrom, std::shared_ptr bufferTo, vk::DeviceSize bufferSize, - vk::BufferCopy copyRegion, - bool createBarrier); + vk::BufferCopy copyRegion); + void recordBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, + const vk::Buffer& buffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); // Private util functions vk::BufferUsageFlags getPrimaryBufferUsageFlags(); From 0ea3a7912375253eb2e51030c946ba462b4ac48a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Tue, 9 Mar 2021 08:52:43 +0000 Subject: [PATCH 69/91] Updated access masks for the pipeline barriers --- src/OpAlgoDispatch.cpp | 4 ++-- src/OpTensorSyncLocal.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpAlgoDispatch.cpp b/src/OpAlgoDispatch.cpp index 94502cc76..517a70d52 100644 --- a/src/OpAlgoDispatch.cpp +++ b/src/OpAlgoDispatch.cpp @@ -28,9 +28,9 @@ OpAlgoDispatch::record(const vk::CommandBuffer& commandBuffer) this->mAlgorithm->getTensors()) { tensor->recordPrimaryBufferMemoryBarrier( commandBuffer, - vk::AccessFlagBits::eHostWrite, + vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eShaderRead, - vk::PipelineStageFlagBits::eHost, + vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eComputeShader); } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 70642b9de..8a99c780b 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -31,13 +31,13 @@ OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer); + this->mTensors[i]->recordPrimaryBufferMemoryBarrier(commandBuffer, + vk::AccessFlagBits::eShaderWrite, + vk::AccessFlagBits::eTransferRead, + vk::PipelineStageFlagBits::eComputeShader, + vk::PipelineStageFlagBits::eTransfer); - this->mTensors[i]->recordStagingBufferMemoryBarrier(commandBuffer, - vk::AccessFlagBits::eTransferWrite, - vk::AccessFlagBits::eHostRead, - vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eHost); + this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer); } } } From c9bd406c8b2fb268459da5e8716bb59c25b0f95b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Tue, 9 Mar 2021 10:00:33 +0000 Subject: [PATCH 70/91] Added barrier --- src/OpTensorSyncLocal.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 8a99c780b..5e653154a 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -38,6 +38,12 @@ OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) vk::PipelineStageFlagBits::eTransfer); this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer); + + this->mTensors[i]->recordPrimaryBufferMemoryBarrier(commandBuffer, + vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eHostRead, + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eHost); } } } From d7b98f149b41f3877e59f5b8dc4863cfff43d3c9 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Tue, 9 Mar 2021 22:11:54 +0000 Subject: [PATCH 71/91] Added OpMemoryBarrier to the available operations --- single_include/AggregateHeaders.cpp | 1 + single_include/kompute/Kompute.hpp | 71 +++++++++++++++++ src/OpMemoryBarrier.cpp | 69 ++++++++++++++++ .../kompute/operations/OpMemoryBarrier.hpp | 78 +++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 src/OpMemoryBarrier.cpp create mode 100644 src/include/kompute/operations/OpMemoryBarrier.hpp diff --git a/single_include/AggregateHeaders.cpp b/single_include/AggregateHeaders.cpp index 25dc04edb..e4d1014e9 100644 --- a/single_include/AggregateHeaders.cpp +++ b/single_include/AggregateHeaders.cpp @@ -6,6 +6,7 @@ #include "kompute/Tensor.hpp" #include "kompute/Algorithm.hpp" #include "kompute/operations/OpBase.hpp" +#include "kompute/operations/OpMemoryBarrier.hpp" #include "kompute/operations/OpTensorCopy.hpp" #include "kompute/operations/OpTensorSyncDevice.hpp" #include "kompute/operations/OpTensorSyncLocal.hpp" diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index fc7d697fb..2353f62b8 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1388,6 +1388,77 @@ class OpBase namespace kp { +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * It exposes the pipeline barrier functionality specifically for memory + * barriers that can be configured through the respective source and destination + * masks + */ +class OpMemoryBarrier : public OpBase +{ + public: + + /** + * Constructor that stores tensors as well as memory barrier parameters to be + * used to create a pipeline barrier on the respective primary or staging tensor. + * + * @param tensors The tensors to apply the memory barriers on + * @param srcAccessMask The kp::AccessFlagBits for the source access mask + * @param dstAccessMask The kp::AccessFlagBits for the destination access mask + * @param srcStageMask The kp::PipelineStageFlagBits for the source stage mask + * @param dstStageMask The kp::PipelineStageFlagBits for the destination stage mask + * @param barrierOnPrimary Boolean to select primary or secondary buffers on tensors + */ + OpMemoryBarrier( + const std::vector>& tensors, + const vk::AccessFlagBits& srcAccessMask, + const vk::AccessFlagBits& dstAccessMask, + const vk::PipelineStageFlagBits& srcStageMask, + const vk::PipelineStageFlagBits& dstStageMask, + bool barrierOnPrimary = true); + + /** + * Default destructor, which is in charge of destroying the reference to the tensors + * and all the relevant access / stage masks created + */ + virtual ~OpMemoryBarrier() override; + + /** + * This records the memory barrier with the access and stage masks provided + * across all relevant tensors. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + +private: + const vk::AccessFlagBits mSrcAccessMask; + const vk::AccessFlagBits mDstAccessMask; + const vk::PipelineStageFlagBits mSrcStageMask; + const vk::PipelineStageFlagBits mDstStageMask; + const bool mBarrierOnPrimary; + const std::vector> mTensors; +}; + +} // End namespace kp + +namespace kp { + /** * Operation that copies the data from the first tensor to the rest of the tensors * provided, using a record command for all the vectors. This operation does not diff --git a/src/OpMemoryBarrier.cpp b/src/OpMemoryBarrier.cpp new file mode 100644 index 000000000..9e45d0c4e --- /dev/null +++ b/src/OpMemoryBarrier.cpp @@ -0,0 +1,69 @@ +#pragma once + +#include "kompute/operations/OpMemoryBarrier.hpp" + +namespace kp { + +OpMemoryBarrier::OpMemoryBarrier( + const std::vector>& tensors, + const vk::AccessFlagBits& srcAccessMask, + const vk::AccessFlagBits& dstAccessMask, + const vk::PipelineStageFlagBits& srcStageMask, + const vk::PipelineStageFlagBits& dstStageMask, + bool barrierOnPrimary) + : mTensors(tensors), + mSrcAccessMask(srcAccessMask), + mDstAccessMask(dstAccessMask), + mSrcStageMask(srcStageMask), + mDstStageMask(dstStageMask), + mBarrierOnPrimary(barrierOnPrimary) +{ + KP_LOG_DEBUG("Kompute OpMemoryBarrier constructor"); + +} + +OpMemoryBarrier::~OpMemoryBarrier() +{ + KP_LOG_DEBUG("Kompute OpMemoryBarrier destructor started"); +} + +void +OpMemoryBarrier::record(const vk::CommandBuffer& commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpMemoryBarrier record called"); + + // Barrier to ensure the data is finished writing to buffer memory + if (this->mBarrierOnPrimary) { + for (const std::shared_ptr& tensor : this->mTensors) { + tensor->recordPrimaryBufferMemoryBarrier( + commandBuffer, + this->mSrcAccessMask, + this->mDstAccessMask, + this->mSrcStageMask, + this->mDstStageMask); + } + } else { + for (const std::shared_ptr& tensor : this->mTensors) { + tensor->recordStagingBufferMemoryBarrier( + commandBuffer, + this->mSrcAccessMask, + this->mDstAccessMask, + this->mSrcStageMask, + this->mDstStageMask); + } + } +} + +void +OpMemoryBarrier::preEval(const vk::CommandBuffer& commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpMemoryBarrier preEval called"); +} + +void +OpMemoryBarrier::postEval(const vk::CommandBuffer& commandBuffer) +{ + KP_LOG_DEBUG("Kompute OpMemoryBarrier postSubmit called"); +} + +} diff --git a/src/include/kompute/operations/OpMemoryBarrier.hpp b/src/include/kompute/operations/OpMemoryBarrier.hpp new file mode 100644 index 000000000..6ae7100db --- /dev/null +++ b/src/include/kompute/operations/OpMemoryBarrier.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "kompute/Core.hpp" +#include "kompute/Algorithm.hpp" +#include "kompute/Tensor.hpp" +#include "kompute/operations/OpBase.hpp" + +namespace kp { + +/** + * Operation that provides a general abstraction that simplifies the use of + * algorithm and parameter components which can be used with shaders. + * It exposes the pipeline barrier functionality specifically for memory + * barriers that can be configured through the respective source and destination + * masks + */ +class OpMemoryBarrier : public OpBase +{ + public: + + /** + * Constructor that stores tensors as well as memory barrier parameters to be + * used to create a pipeline barrier on the respective primary or staging tensor. + * + * @param tensors The tensors to apply the memory barriers on + * @param srcAccessMask The kp::AccessFlagBits for the source access mask + * @param dstAccessMask The kp::AccessFlagBits for the destination access mask + * @param srcStageMask The kp::PipelineStageFlagBits for the source stage mask + * @param dstStageMask The kp::PipelineStageFlagBits for the destination stage mask + * @param barrierOnPrimary Boolean to select primary or secondary buffers on tensors + */ + OpMemoryBarrier( + const std::vector>& tensors, + const vk::AccessFlagBits& srcAccessMask, + const vk::AccessFlagBits& dstAccessMask, + const vk::PipelineStageFlagBits& srcStageMask, + const vk::PipelineStageFlagBits& dstStageMask, + bool barrierOnPrimary = true); + + /** + * Default destructor, which is in charge of destroying the reference to the tensors + * and all the relevant access / stage masks created + */ + virtual ~OpMemoryBarrier() override; + + /** + * This records the memory barrier with the access and stage masks provided + * across all relevant tensors. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void record(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any preEval commands. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void preEval(const vk::CommandBuffer& commandBuffer) override; + + /** + * Does not perform any postEval commands. + * + * @param commandBuffer The command buffer to record the command into. + */ + virtual void postEval(const vk::CommandBuffer& commandBuffer) override; + +private: + const vk::AccessFlagBits mSrcAccessMask; + const vk::AccessFlagBits mDstAccessMask; + const vk::PipelineStageFlagBits mSrcStageMask; + const vk::PipelineStageFlagBits mDstStageMask; + const bool mBarrierOnPrimary; + const std::vector> mTensors; +}; + +} // End namespace kp + From 70678e44ae1293227939979e02a978200d0bd493 Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Thu, 11 Mar 2021 20:08:18 +0100 Subject: [PATCH 72/91] too many warnings --- src/Sequence.cpp | 6 ++++-- src/Tensor.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 4db458288..cc54acc8b 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -27,7 +27,8 @@ Sequence::~Sequence() { KP_LOG_DEBUG("Kompute Sequence Destructor started"); - this->destroy(); + if (this->mDevice) + this->destroy(); } void @@ -81,7 +82,8 @@ void Sequence::clear() { KP_LOG_DEBUG("Kompute Sequence calling clear"); - this->end(); + if (this->isRecording()) + this->end(); } std::shared_ptr diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 90c21fc8a..0a16702d4 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -28,7 +28,8 @@ Tensor::~Tensor() KP_LOG_DEBUG("Kompute Tensor destructor started. Type: {}", this->tensorType()); - this->destroy(); + if (this->mDevice) + this->destroy(); KP_LOG_DEBUG("Kompute Tensor destructor success"); } From c6c89ac75af216449b206558c2da72341521c38e Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Thu, 11 Mar 2021 20:30:30 +0100 Subject: [PATCH 73/91] { } --- src/Sequence.cpp | 6 ++++-- src/Tensor.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Sequence.cpp b/src/Sequence.cpp index cc54acc8b..501e965d0 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -27,8 +27,9 @@ Sequence::~Sequence() { KP_LOG_DEBUG("Kompute Sequence Destructor started"); - if (this->mDevice) + if (this->mDevice) { this->destroy(); + } } void @@ -82,8 +83,9 @@ void Sequence::clear() { KP_LOG_DEBUG("Kompute Sequence calling clear"); - if (this->isRecording()) + if (this->isRecording()) { this->end(); + } } std::shared_ptr diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 0a16702d4..a7b433a74 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -28,8 +28,9 @@ Tensor::~Tensor() KP_LOG_DEBUG("Kompute Tensor destructor started. Type: {}", this->tensorType()); - if (this->mDevice) + if (this->mDevice) { this->destroy(); + } KP_LOG_DEBUG("Kompute Tensor destructor success"); } From f52efcef0f4c585250d3b8663888078aff356c41 Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Fri, 12 Mar 2021 08:32:51 +0100 Subject: [PATCH 74/91] Device Properties --- python/src/main.cpp | 21 ++- single_include/kompute/Kompute.hpp | 240 ++++++++++++++++------------- src/Manager.cpp | 13 ++ src/include/kompute/Manager.hpp | 23 +++ test/TestManager.cpp | 8 + 5 files changed, 198 insertions(+), 107 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 9e065c213..33f9d02ae 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -217,7 +217,26 @@ PYBIND11_MODULE(kp, m) { py::arg("spirv"), py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), - py::arg("push_consts") = kp::Constants()); + py::arg("push_consts") = kp::Constants()) + .def("get_device_properties", &kp::Manager::getDeviceProperties, "Return a struct containing information about the device"); + + py::class_(m, "DeviceProperties") + .def_readonly("device_name", &kp::DeviceProperties::deviceName) + .def_readonly("max_work_group_count", &kp::DeviceProperties::maxWorkGroupCount) + .def_readonly("max_work_group_invocations", &kp::DeviceProperties::maxWorkGroupInvocations) + .def_readonly("max_work_group_size", &kp::DeviceProperties::maxWorkGroupSize) + .def_readonly("timestamps_supported", &kp::DeviceProperties::timestampsSupported) + .def("__repr__", [](const kp::DeviceProperties &p) { + return "Device Name: " + p.deviceName + "\n" + +"Maximum Workgroup Count: " + std::to_string(p.maxWorkGroupCount[0]) + ", " + + std::to_string(p.maxWorkGroupCount[1]) + ", " + + std::to_string(p.maxWorkGroupCount[2]) + "\n" + +"Maximum Workgroup Invocations: " + std::to_string(p.maxWorkGroupInvocations) + "\n" + +"Maximum Workgroup Size: " + std::to_string(p.maxWorkGroupSize[0]) + ", " + + std::to_string(p.maxWorkGroupSize[1]) + ", " + + std::to_string(p.maxWorkGroupSize[2]) + "\n" + +"Timestamps Supported: " + (p.timestampsSupported? "True" : "False") + "\n"; + }); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 9b41e1ead..474faee14 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -741,7 +741,6 @@ namespace kp { class Shader { public: - // The default resource limit for the GLSL compiler, can be overwritten // Has been adopted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp @@ -951,41 +950,33 @@ class Tensor * @return Unsigned integer representing the total number of elements */ // TODO: move to cpp - uint32_t size() { - return this->mSize; - } + uint32_t size() { return this->mSize; } // TODO: move to cpp - uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } + uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } // TODO: move to cpp - uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } + uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } /** * Retrieve the underlying data type of the Tensor * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { - return this->mDataType; - } + TensorDataTypes dataType() { return this->mDataType; } - void* rawData() { - return this->mRawData; - } + void* rawData() { return this->mRawData; } // TODO: move to cpp - template - T* data() { + template + T* data() + { return (T*)this->mRawData; } - template - std::vector vector() { + template + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } @@ -993,9 +984,9 @@ class Tensor * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. */ - void setRawData(const void* data) + void setRawData(const void* data) { - // Copy data + // Copy data memcpy(this->mRawData, data, this->memorySize()); } @@ -1008,7 +999,8 @@ class Tensor void* mRawData; private: - void mapRawData() { + void mapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1026,14 +1018,17 @@ class Tensor vk::DeviceSize bufferSize = this->memorySize(); - // Given we request coherent host memory we don't need to invalidate / flush + // Given we request coherent host memory we don't need to invalidate / + // flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + vk::MappedMemoryRange mappedMemoryRange( + *hostVisibleMemory, 0, bufferSize); } - void unmapRawData() { + void unmapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1087,49 +1082,46 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - }; // TODO: Limit T to be only float, bool, double, etc -template -class TensorT: public Tensor +template +class TensorT : public Tensor { public: TensorT(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, - device, - (void*)data.data(), - data.size(), - sizeof(T), - this->dataType(), - tensorType) + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", + data.size()); } - ~TensorT() { - KP_LOG_DEBUG("Kompute TensorT destructor"); - } + ~TensorT() { KP_LOG_DEBUG("Kompute TensorT destructor"); } - T* data() { - return (T*)this->mRawData; - } + T* data() { return (T*)this->mRawData; } - std::vector vector() { + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - T& operator[](int index) { - return *(((T*)this->mRawData) + index); - } + T& operator[](int index) { return *(((T*)this->mRawData) + index); } - void setData(const std::vector& data) { + void setData(const std::vector& data) + { - KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", + data.size()); if (data.size() != this->mSize) { throw std::runtime_error( @@ -1140,7 +1132,6 @@ class TensorT: public Tensor } TensorDataTypes dataType(); - }; } // End namespace kp @@ -1159,15 +1150,17 @@ class Algorithm * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param tensors (optional) The tensors to use to create the descriptor resources + * @param tensors (optional) The tensors to use to create the descriptor + * resources * @param spirv (optional) The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -1177,18 +1170,19 @@ class Algorithm const Constants& pushConstants = {}); /** - * Rebuild function to reconstruct algorithm with configuration parameters to create - * the underlying resources. + * Rebuild function to reconstruct algorithm with configuration parameters + * to create the underlying resources. * * @param tensors The tensors to use to create the descriptor resources * @param spirv The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -1211,25 +1205,26 @@ class Algorithm void recordDispatch(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the "core" algorithm components which consist of - * binding the pipeline and binding the descriptorsets. + * Records command that binds the "core" algorithm components which consist + * of binding the pipeline and binding the descriptorsets. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindCore(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the push constants to the command buffer provided - * - it is required that the pushConstants provided are of the same size as the - * ones provided during initialization. + * Records command that binds the push constants to the command buffer + * provided + * - it is required that the pushConstants provided are of the same size as + * the ones provided during initialization. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindPush(const vk::CommandBuffer& commandBuffer); /** - * function that checks all the gpu resource components to verify if these have - * been created and returns true if all are valid. + * function that checks all the gpu resource components to verify if these + * have been created and returns true if all are valid. * * @returns returns true if the algorithm is currently initialized. */ @@ -1238,26 +1233,28 @@ class Algorithm /** * Sets the work group to use in the recordDispatch * - * @param workgroup The kp::Workgroup value to use to update the algorithm. It - * must have a value greater than 1 on the x value (index 1) otherwise it will - * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + * @param workgroup The kp::Workgroup value to use to update the algorithm. + * It must have a value greater than 1 on the x value (index 1) otherwise it + * will be initialized on the size of the first tensor (ie. + * this->mTensor[0]->size()) */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); /** - * Sets the push constants to the new value provided to use in the next bindPush() + * Sets the push constants to the new value provided to use in the next + * bindPush() * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ void setPush(const Constants& pushConstants); /** * Gets the current workgroup from the algorithm. * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ const Workgroup& getWorkgroup(); /** @@ -1690,8 +1687,8 @@ class Sequence : public std::enable_shared_from_this * function also requires the Sequence to be recording, otherwise it will * not be able to add the operation. * - * @param op Object derived from kp::BaseOp that will be recoreded by the sequence - * which will be used when the operation is evaluated. + * @param op Object derived from kp::BaseOp that will be recoreded by the + * sequence which will be used when the operation is evaluated. * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -1709,7 +1706,8 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, TArgs&&... params) + std::vector> tensors, + TArgs&&... params) { std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); @@ -1744,8 +1742,9 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(); /** - * Resets all the recorded and stored operations, records the operation - * provided and submits into the gpu as a submit job synchronously (with a barrier). + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a + * barrier). * * @return shared_ptr of the Sequence class itself */ @@ -1788,16 +1787,18 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); /** * Clears currnet operations to record provided one in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ @@ -1891,9 +1892,9 @@ class Sequence : public std::enable_shared_from_this bool isInit(); /** - * Clears command buffer and triggers re-record of all the current operations - * saved, which is useful if the underlying kp::Tensors or kp::Algorithms - * are modified and need to be re-recorded. + * Clears command buffer and triggers re-record of all the current + * operations saved, which is useful if the underlying kp::Tensors or + * kp::Algorithms are modified and need to be re-recorded. */ void rerecord(); @@ -1948,6 +1949,21 @@ class Sequence : public std::enable_shared_from_this namespace kp { +struct DeviceProperties +{ + //Name of the device + const std::string deviceName; + //Maximum number of workgroups that can be dispatched per shader + const std::array maxWorkGroupCount; + //Maximum number of shader invocations per local workgroup + //i.e. the product of maxWorkGroupSize must not exceed this value + const uint32_t maxWorkGroupInvocations; + //Maximum number of shader invocations per local workgroup + const std::array maxWorkGroupSize; + //Whether timestamping is supported by this device or not + const bool timestampsSupported; +}; + /** Base orchestrator which creates and manages device and child components */ @@ -1961,13 +1977,14 @@ class Manager Manager(); /** - * Similar to base constructor but allows for further configuration to use when - * creating the Vulkan resources. + * Similar to base constructor but allows for further configuration to use + * when creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param desiredExtensions The desired extensions to load from physicalDevice + * @param desiredExtensions The desired extensions to load from + * physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -2001,7 +2018,8 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, + uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager @@ -2011,7 +2029,7 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - template + template std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) @@ -2042,8 +2060,13 @@ class Manager const Tensor::TensorDataTypes& dataType, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { - std::shared_ptr tensor{ new kp::Tensor( - this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + std::shared_ptr tensor{ new kp::Tensor(this->mPhysicalDevice, + this->mDevice, + data, + elementTotalCount, + elementMemorySize, + dataType, + tensorType) }; if (this->mManageResources) { this->mManagedTensors.push_back(tensor); @@ -2083,6 +2106,11 @@ class Manager **/ void clear(); + /** + * Return a struct containing information about the device. + **/ + DeviceProperties getDeviceProperties() const; + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; diff --git a/src/Manager.cpp b/src/Manager.cpp index cdc896332..996548db3 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -447,4 +447,17 @@ Manager::sequence(uint32_t queueIndex, uint32_t totalTimestamps) return sq; } +DeviceProperties Manager::getDeviceProperties() const +{ + const vk::PhysicalDeviceProperties properties = this->mPhysicalDevice->getProperties(); + const DeviceProperties output{ + std::string(properties.deviceName.data()), + properties.limits.maxComputeWorkGroupCount, + properties.limits.maxComputeWorkGroupInvocations, + properties.limits.maxComputeWorkGroupSize, + properties.limits.timestampComputeAndGraphics, + }; + return output; +} + } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 6b06b83fd..b168e3727 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -11,6 +11,23 @@ namespace kp { + +struct DeviceProperties +{ + //Name of the device + const std::string deviceName; + //Maximum number of workgroups that can be dispatched per shader + const std::array maxWorkGroupCount; + //Maximum number of shader invocations per local workgroup + //i.e. the product of maxWorkGroupSize must not exceed this value + const uint32_t maxWorkGroupInvocations; + //Maximum number of shader invocations per local workgroup + const std::array maxWorkGroupSize; + //Whether timestamping is supported by this device or not + const bool timestampsSupported; +}; + + /** Base orchestrator which creates and manages device and child components */ @@ -153,6 +170,11 @@ class Manager **/ void clear(); + /** + * Return a struct containing information about the device. + **/ + DeviceProperties getDeviceProperties() const; + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; @@ -185,4 +207,5 @@ class Manager const std::vector& desiredExtensions = {}); }; + } // End namespace kp diff --git a/test/TestManager.cpp b/test/TestManager.cpp index f759208aa..608bb8dfc 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -62,3 +62,11 @@ TEST(TestManager, TestMultipleSequences) EXPECT_EQ(tensorOutput->vector(), std::vector({ 0, 4, 12 })); } + +TEST(TestManager, TestDeviceProperties) +{ + kp::Manager mgr; + const auto properties = mgr.getDeviceProperties(); + + EXPECT_GT(properties.deviceName.size(), 0); +} From 8ec1f8000be1d22f00192c1f3d04e13c0c0cd15b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Fri, 12 Mar 2021 07:38:18 +0000 Subject: [PATCH 75/91] Fixed references in the docs --- docs/assets/custom.css | 3 + docs/overview/reference.rst | 13 +- single_include/kompute/Kompute.hpp | 220 +++++++++++++++-------------- 3 files changed, 127 insertions(+), 109 deletions(-) diff --git a/docs/assets/custom.css b/docs/assets/custom.css index 06b5f8ef3..48acff843 100644 --- a/docs/assets/custom.css +++ b/docs/assets/custom.css @@ -46,6 +46,9 @@ a:hover { .md-nav__item a:hover { color: #0091ea; } +.md-nav__item a[data-md-state="blur"] { + color: #1a7c80; +} .md-source { color: #fff; diff --git a/docs/overview/reference.rst b/docs/overview/reference.rst index 13f6934c4..51704ac5c 100644 --- a/docs/overview/reference.rst +++ b/docs/overview/reference.rst @@ -64,15 +64,15 @@ The :class:`kp::OpBase` provides a top level class for an operation in Kompute, .. doxygenclass:: kp::OpBase :members: -OpAlgoBase +OpAlgoDispatch ------- -The vk::OpAlgoBase extends the vk::OpBase class, and provides the base for shader-based operations. Besides of consisting of one or more vk::Tensor as per the vk::OpBase, it also contains a unique vk::Algorithm. +The `vk::OpAlgoDispatch` extends the `vk::OpBase` class, and provides the base for shader-based operations. Besides of consisting of one or more `vk::Tensor` as per the `vk::OpBase`, it also contains a unique `vk::Algorithm`. .. image:: ../images/kompute-vulkan-architecture-opmult.jpg :width: 100% -.. doxygenclass:: kp::OpAlgoBase +.. doxygenclass:: kp::OpAlgoDispatch :members: OpMult @@ -111,6 +111,13 @@ The :class:`kp::OpTensorSyncDevice` is a tensor only operation that maps the dat .. doxygenclass:: kp::OpTensorSyncDevice :members: +OpMemoryBarrier +------- + +The :class:`kp::OpMemoryBarrier` is a tensor only operation which adds memory barriers to the tensors provided with the access and stage masks provided. + +.. doxygenclass:: kp::OpTensorSyncDevice + :members: Shader -------- diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 9b41e1ead..547793ccf 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -741,7 +741,6 @@ namespace kp { class Shader { public: - // The default resource limit for the GLSL compiler, can be overwritten // Has been adopted by: // https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp @@ -951,41 +950,33 @@ class Tensor * @return Unsigned integer representing the total number of elements */ // TODO: move to cpp - uint32_t size() { - return this->mSize; - } + uint32_t size() { return this->mSize; } // TODO: move to cpp - uint32_t dataTypeMemorySize() { - return this->mDataTypeMemorySize; - } + uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } // TODO: move to cpp - uint32_t memorySize() { - return this->mSize * this->mDataTypeMemorySize; - } + uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } /** * Retrieve the underlying data type of the Tensor * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { - return this->mDataType; - } + TensorDataTypes dataType() { return this->mDataType; } - void* rawData() { - return this->mRawData; - } + void* rawData() { return this->mRawData; } // TODO: move to cpp - template - T* data() { + template + T* data() + { return (T*)this->mRawData; } - template - std::vector vector() { + template + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } @@ -993,9 +984,9 @@ class Tensor * Sets / resets the vector data of the tensor. This function does not * perform any copies into GPU memory and is only performed on the host. */ - void setRawData(const void* data) + void setRawData(const void* data) { - // Copy data + // Copy data memcpy(this->mRawData, data, this->memorySize()); } @@ -1008,7 +999,8 @@ class Tensor void* mRawData; private: - void mapRawData() { + void mapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1026,14 +1018,17 @@ class Tensor vk::DeviceSize bufferSize = this->memorySize(); - // Given we request coherent host memory we don't need to invalidate / flush + // Given we request coherent host memory we don't need to invalidate / + // flush this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); + vk::MappedMemoryRange mappedMemoryRange( + *hostVisibleMemory, 0, bufferSize); } - void unmapRawData() { + void unmapRawData() + { KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); @@ -1087,49 +1082,46 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); - }; // TODO: Limit T to be only float, bool, double, etc -template -class TensorT: public Tensor +template +class TensorT : public Tensor { public: TensorT(std::shared_ptr physicalDevice, - std::shared_ptr device, - const std::vector& data, - const TensorTypes& tensorType = TensorTypes::eDevice) - : Tensor(physicalDevice, - device, - (void*)data.data(), - data.size(), - sizeof(T), - this->dataType(), - tensorType) + std::shared_ptr device, + const std::vector& data, + const TensorTypes& tensorType = TensorTypes::eDevice) + : Tensor(physicalDevice, + device, + (void*)data.data(), + data.size(), + sizeof(T), + this->dataType(), + tensorType) { - KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT constructor with data size {}", + data.size()); } - ~TensorT() { - KP_LOG_DEBUG("Kompute TensorT destructor"); - } + ~TensorT() { KP_LOG_DEBUG("Kompute TensorT destructor"); } - T* data() { - return (T*)this->mRawData; - } + T* data() { return (T*)this->mRawData; } - std::vector vector() { + std::vector vector() + { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - T& operator[](int index) { - return *(((T*)this->mRawData) + index); - } + T& operator[](int index) { return *(((T*)this->mRawData) + index); } - void setData(const std::vector& data) { + void setData(const std::vector& data) + { - KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", data.size()); + KP_LOG_DEBUG("Kompute TensorT setting data with data size {}", + data.size()); if (data.size() != this->mSize) { throw std::runtime_error( @@ -1140,7 +1132,6 @@ class TensorT: public Tensor } TensorDataTypes dataType(); - }; } // End namespace kp @@ -1159,15 +1150,17 @@ class Algorithm * the underlying resources. * * @param device The Vulkan device to use for creating resources - * @param tensors (optional) The tensors to use to create the descriptor resources + * @param tensors (optional) The tensors to use to create the descriptor + * resources * @param spirv (optional) The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ Algorithm(std::shared_ptr device, const std::vector>& tensors = {}, @@ -1177,18 +1170,19 @@ class Algorithm const Constants& pushConstants = {}); /** - * Rebuild function to reconstruct algorithm with configuration parameters to create - * the underlying resources. + * Rebuild function to reconstruct algorithm with configuration parameters + * to create the underlying resources. * * @param tensors The tensors to use to create the descriptor resources * @param spirv The spirv code to use to create the algorithm - * @param workgroup (optional) The kp::Workgroup to use for the dispatch which defaults to - * kp::Workgroup(tensor[0].size(), 1, 1) if not set. - * @param specializationConstants (optional) The kp::Constants to use to initialize - * the specialization constants which cannot be changed once set. - * @param pushConstants (optional) The kp::Constants to use when initializing the - * pipeline, which set the size of the push constants - these can be modified but - * all new values must have the same vector size as this initial value. + * @param workgroup (optional) The kp::Workgroup to use for the dispatch + * which defaults to kp::Workgroup(tensor[0].size(), 1, 1) if not set. + * @param specializationConstants (optional) The kp::Constants to use to + * initialize the specialization constants which cannot be changed once set. + * @param pushConstants (optional) The kp::Constants to use when + * initializing the pipeline, which set the size of the push constants - + * these can be modified but all new values must have the same vector size + * as this initial value. */ void rebuild(const std::vector>& tensors, const std::vector& spirv, @@ -1211,25 +1205,26 @@ class Algorithm void recordDispatch(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the "core" algorithm components which consist of - * binding the pipeline and binding the descriptorsets. + * Records command that binds the "core" algorithm components which consist + * of binding the pipeline and binding the descriptorsets. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindCore(const vk::CommandBuffer& commandBuffer); /** - * Records command that binds the push constants to the command buffer provided - * - it is required that the pushConstants provided are of the same size as the - * ones provided during initialization. + * Records command that binds the push constants to the command buffer + * provided + * - it is required that the pushConstants provided are of the same size as + * the ones provided during initialization. * * @param commandBuffer Command buffer to record the algorithm resources to */ void recordBindPush(const vk::CommandBuffer& commandBuffer); /** - * function that checks all the gpu resource components to verify if these have - * been created and returns true if all are valid. + * function that checks all the gpu resource components to verify if these + * have been created and returns true if all are valid. * * @returns returns true if the algorithm is currently initialized. */ @@ -1238,26 +1233,28 @@ class Algorithm /** * Sets the work group to use in the recordDispatch * - * @param workgroup The kp::Workgroup value to use to update the algorithm. It - * must have a value greater than 1 on the x value (index 1) otherwise it will - * be initialized on the size of the first tensor (ie. this->mTensor[0]->size()) + * @param workgroup The kp::Workgroup value to use to update the algorithm. + * It must have a value greater than 1 on the x value (index 1) otherwise it + * will be initialized on the size of the first tensor (ie. + * this->mTensor[0]->size()) */ void setWorkgroup(const Workgroup& workgroup, uint32_t minSize = 1); /** - * Sets the push constants to the new value provided to use in the next bindPush() + * Sets the push constants to the new value provided to use in the next + * bindPush() * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ void setPush(const Constants& pushConstants); /** * Gets the current workgroup from the algorithm. * - * @param The kp::Constant to use to set the push constants to use in the next - * bindPush(...) calls. The constants provided must be of the same size as the - * ones created during initialization. + * @param The kp::Constant to use to set the push constants to use in the + * next bindPush(...) calls. The constants provided must be of the same size + * as the ones created during initialization. */ const Workgroup& getWorkgroup(); /** @@ -1690,8 +1687,8 @@ class Sequence : public std::enable_shared_from_this * function also requires the Sequence to be recording, otherwise it will * not be able to add the operation. * - * @param op Object derived from kp::BaseOp that will be recoreded by the sequence - * which will be used when the operation is evaluated. + * @param op Object derived from kp::BaseOp that will be recoreded by the + * sequence which will be used when the operation is evaluated. * @return shared_ptr of the Sequence class itself */ std::shared_ptr record(std::shared_ptr op); @@ -1709,7 +1706,8 @@ class Sequence : public std::enable_shared_from_this */ template std::shared_ptr record( - std::vector> tensors, TArgs&&... params) + std::vector> tensors, + TArgs&&... params) { std::shared_ptr op{ new T(tensors, std::forward(params)...) }; return this->record(op); @@ -1744,8 +1742,9 @@ class Sequence : public std::enable_shared_from_this std::shared_ptr eval(); /** - * Resets all the recorded and stored operations, records the operation - * provided and submits into the gpu as a submit job synchronously (with a barrier). + * Resets all the recorded and stored operations, records the operation + * provided and submits into the gpu as a submit job synchronously (with a + * barrier). * * @return shared_ptr of the Sequence class itself */ @@ -1788,16 +1787,18 @@ class Sequence : public std::enable_shared_from_this /** * Eval Async sends all the recorded and stored operations in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ std::shared_ptr evalAsync(); /** * Clears currnet operations to record provided one in the vector of - * operations into the gpu as a submit job without a barrier. EvalAwait() must - * ALWAYS be called after to ensure the sequence is terminated correctly. + * operations into the gpu as a submit job without a barrier. EvalAwait() + * must ALWAYS be called after to ensure the sequence is terminated + * correctly. * * @return Boolean stating whether execution was successful. */ @@ -1891,9 +1892,9 @@ class Sequence : public std::enable_shared_from_this bool isInit(); /** - * Clears command buffer and triggers re-record of all the current operations - * saved, which is useful if the underlying kp::Tensors or kp::Algorithms - * are modified and need to be re-recorded. + * Clears command buffer and triggers re-record of all the current + * operations saved, which is useful if the underlying kp::Tensors or + * kp::Algorithms are modified and need to be re-recorded. */ void rerecord(); @@ -1961,13 +1962,14 @@ class Manager Manager(); /** - * Similar to base constructor but allows for further configuration to use when - * creating the Vulkan resources. + * Similar to base constructor but allows for further configuration to use + * when creating the Vulkan resources. * * @param physicalDeviceIndex The index of the physical device to use * @param familyQueueIndices (Optional) List of queue indices to add for * explicit allocation - * @param desiredExtensions The desired extensions to load from physicalDevice + * @param desiredExtensions The desired extensions to load from + * physicalDevice */ Manager(uint32_t physicalDeviceIndex, const std::vector& familyQueueIndices = {}, @@ -2001,7 +2003,8 @@ class Manager * If zero (default), disables latching of timestamps. * @returns Shared pointer with initialised sequence */ - std::shared_ptr sequence(uint32_t queueIndex = 0, uint32_t totalTimestamps = 0); + std::shared_ptr sequence(uint32_t queueIndex = 0, + uint32_t totalTimestamps = 0); /** * Create a managed tensor that will be destroyed by this manager @@ -2011,7 +2014,7 @@ class Manager * @param tensorType The type of tensor to initialize * @returns Shared pointer with initialised tensor */ - template + template std::shared_ptr> tensorT( const std::vector& data, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) @@ -2042,8 +2045,13 @@ class Manager const Tensor::TensorDataTypes& dataType, Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) { - std::shared_ptr tensor{ new kp::Tensor( - this->mPhysicalDevice, this->mDevice, data, elementTotalCount, elementMemorySize, dataType, tensorType) }; + std::shared_ptr tensor{ new kp::Tensor(this->mPhysicalDevice, + this->mDevice, + data, + elementTotalCount, + elementMemorySize, + dataType, + tensorType) }; if (this->mManageResources) { this->mManagedTensors.push_back(tensor); From d71d169886ee9f4defdc7bce4dc6414827fd652e Mon Sep 17 00:00:00 2001 From: alexander-g <3867427+alexander-g@users.noreply.github.com> Date: Fri, 12 Mar 2021 09:51:31 +0100 Subject: [PATCH 76/91] raw vk::deviceproperties and python dict --- python/src/main.cpp | 38 +++++++++++++++--------------- single_include/kompute/Kompute.hpp | 17 +------------ src/Manager.cpp | 13 +++------- src/include/kompute/Manager.hpp | 20 +--------------- test/TestManager.cpp | 1 - 5 files changed, 24 insertions(+), 65 deletions(-) diff --git a/python/src/main.cpp b/python/src/main.cpp index 33f9d02ae..acf4308aa 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -9,6 +9,8 @@ #include "docstrings.hpp" namespace py = pybind11; +using namespace pybind11::literals; // for the `_a` literal + //used in Core.hpp py::object kp_debug, kp_info, kp_warning, kp_error; @@ -218,25 +220,23 @@ PYBIND11_MODULE(kp, m) { py::arg("workgroup") = kp::Workgroup(), py::arg("spec_consts") = kp::Constants(), py::arg("push_consts") = kp::Constants()) - .def("get_device_properties", &kp::Manager::getDeviceProperties, "Return a struct containing information about the device"); - - py::class_(m, "DeviceProperties") - .def_readonly("device_name", &kp::DeviceProperties::deviceName) - .def_readonly("max_work_group_count", &kp::DeviceProperties::maxWorkGroupCount) - .def_readonly("max_work_group_invocations", &kp::DeviceProperties::maxWorkGroupInvocations) - .def_readonly("max_work_group_size", &kp::DeviceProperties::maxWorkGroupSize) - .def_readonly("timestamps_supported", &kp::DeviceProperties::timestampsSupported) - .def("__repr__", [](const kp::DeviceProperties &p) { - return "Device Name: " + p.deviceName + "\n" - +"Maximum Workgroup Count: " + std::to_string(p.maxWorkGroupCount[0]) + ", " - + std::to_string(p.maxWorkGroupCount[1]) + ", " - + std::to_string(p.maxWorkGroupCount[2]) + "\n" - +"Maximum Workgroup Invocations: " + std::to_string(p.maxWorkGroupInvocations) + "\n" - +"Maximum Workgroup Size: " + std::to_string(p.maxWorkGroupSize[0]) + ", " - + std::to_string(p.maxWorkGroupSize[1]) + ", " - + std::to_string(p.maxWorkGroupSize[2]) + "\n" - +"Timestamps Supported: " + (p.timestampsSupported? "True" : "False") + "\n"; - }); + .def("get_device_properties", [](kp::Manager& self){ + const auto properties = self.getDeviceProperties(); + py::dict py_props( + "device_name"_a = std::string(properties.deviceName.data()), + "max_work_group_count"_a = py::make_tuple(properties.limits.maxComputeWorkGroupCount[0], + properties.limits.maxComputeWorkGroupCount[1], + properties.limits.maxComputeWorkGroupCount[2]), + "max_work_group_invocations"_a = properties.limits.maxComputeWorkGroupInvocations, + "max_work_group_size"_a = py::make_tuple(properties.limits.maxComputeWorkGroupSize[0], + properties.limits.maxComputeWorkGroupSize[1], + properties.limits.maxComputeWorkGroupSize[2]), + "timestamps_supported"_a = (bool)properties.limits.timestampComputeAndGraphics + ); + + return py_props; + }, "Return a dict containing information about the device"); + #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 474faee14..44689d868 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -1949,21 +1949,6 @@ class Sequence : public std::enable_shared_from_this namespace kp { -struct DeviceProperties -{ - //Name of the device - const std::string deviceName; - //Maximum number of workgroups that can be dispatched per shader - const std::array maxWorkGroupCount; - //Maximum number of shader invocations per local workgroup - //i.e. the product of maxWorkGroupSize must not exceed this value - const uint32_t maxWorkGroupInvocations; - //Maximum number of shader invocations per local workgroup - const std::array maxWorkGroupSize; - //Whether timestamping is supported by this device or not - const bool timestampsSupported; -}; - /** Base orchestrator which creates and manages device and child components */ @@ -2109,7 +2094,7 @@ class Manager /** * Return a struct containing information about the device. **/ - DeviceProperties getDeviceProperties() const; + vk::PhysicalDeviceProperties getDeviceProperties() const; private: // -------------- OPTIONALLY OWNED RESOURCES diff --git a/src/Manager.cpp b/src/Manager.cpp index 996548db3..807d4832f 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -447,17 +447,10 @@ Manager::sequence(uint32_t queueIndex, uint32_t totalTimestamps) return sq; } -DeviceProperties Manager::getDeviceProperties() const +vk::PhysicalDeviceProperties +Manager::getDeviceProperties() const { - const vk::PhysicalDeviceProperties properties = this->mPhysicalDevice->getProperties(); - const DeviceProperties output{ - std::string(properties.deviceName.data()), - properties.limits.maxComputeWorkGroupCount, - properties.limits.maxComputeWorkGroupInvocations, - properties.limits.maxComputeWorkGroupSize, - properties.limits.timestampComputeAndGraphics, - }; - return output; + return this->mPhysicalDevice->getProperties(); } } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index b168e3727..e9e284155 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -11,23 +11,6 @@ namespace kp { - -struct DeviceProperties -{ - //Name of the device - const std::string deviceName; - //Maximum number of workgroups that can be dispatched per shader - const std::array maxWorkGroupCount; - //Maximum number of shader invocations per local workgroup - //i.e. the product of maxWorkGroupSize must not exceed this value - const uint32_t maxWorkGroupInvocations; - //Maximum number of shader invocations per local workgroup - const std::array maxWorkGroupSize; - //Whether timestamping is supported by this device or not - const bool timestampsSupported; -}; - - /** Base orchestrator which creates and manages device and child components */ @@ -173,7 +156,7 @@ class Manager /** * Return a struct containing information about the device. **/ - DeviceProperties getDeviceProperties() const; + vk::PhysicalDeviceProperties getDeviceProperties() const; private: // -------------- OPTIONALLY OWNED RESOURCES @@ -207,5 +190,4 @@ class Manager const std::vector& desiredExtensions = {}); }; - } // End namespace kp diff --git a/test/TestManager.cpp b/test/TestManager.cpp index 608bb8dfc..6d6756d41 100644 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -67,6 +67,5 @@ TEST(TestManager, TestDeviceProperties) { kp::Manager mgr; const auto properties = mgr.getDeviceProperties(); - EXPECT_GT(properties.deviceName.size(), 0); } From 4e86562cff4c3fc3737160c93a9f9aebe41f089f Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 07:40:20 +0000 Subject: [PATCH 77/91] Updated staging memory barrier to ensure it's always coherent --- src/Tensor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 7e7af4ca4..6ea92ccd6 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -258,7 +258,8 @@ Tensor::getStagingMemoryPropertyFlags() { switch (this->mTensorType) { case TensorTypes::eDevice: - return vk::MemoryPropertyFlagBits::eHostVisible; + return vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent; break; default: throw std::runtime_error("Kompute Tensor invalid tensor type"); From 1088ee893e923d27a292e2569bdbe2a4b568a7c1 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 17:00:29 +0000 Subject: [PATCH 78/91] Moved all functions from hpp to cpp in tensor --- src/Tensor.cpp | 89 ++++++++++++++++++++++++ src/include/kompute/Tensor.hpp | 123 ++++++++++++++------------------- 2 files changed, 139 insertions(+), 73 deletions(-) diff --git a/src/Tensor.cpp b/src/Tensor.cpp index c1d391fdd..aa3584dcd 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -70,6 +70,95 @@ Tensor::isInit() this->mRawData; } +uint32_t +Tensor::size() +{ + return this->mSize; +} + +uint32_t +Tensor::dataTypeMemorySize() +{ + return this->mDataTypeMemorySize; +} + +uint32_t +Tensor::memorySize() +{ + return this->mSize * this->mDataTypeMemorySize; +} + +kp::Tensor::TensorDataTypes +Tensor::dataType() +{ + return this->mDataType; +} + +void* +Tensor::rawData() +{ + return this->mRawData; +} + +void +Tensor::setRawData(const void* data) +{ + memcpy(this->mRawData, data, this->memorySize()); +} + +void +Tensor::mapRawData() +{ + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + + // Given we request coherent host memory we don't need to invalidate / + // flush + this->mRawData = this->mDevice->mapMemory( + *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); + + vk::MappedMemoryRange mappedMemoryRange( + *hostVisibleMemory, 0, bufferSize); +} + +void +Tensor::unmapRawData() +{ + + KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); + + std::shared_ptr hostVisibleMemory = nullptr; + + if (this->mTensorType == TensorTypes::eHost) { + hostVisibleMemory = this->mPrimaryMemory; + } else if (this->mTensorType == TensorTypes::eDevice) { + hostVisibleMemory = this->mStagingMemory; + } else { + KP_LOG_WARN( + "Kompute Tensor mapping data not supported on storage tensor"); + return; + } + + vk::DeviceSize bufferSize = this->memorySize(); + vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); + this->mDevice->flushMappedMemoryRanges(1, &mappedRange); + this->mDevice->unmapMemory(*hostVisibleMemory); +} + void Tensor::recordCopyFrom(const vk::CommandBuffer& commandBuffer, std::shared_ptr copyFromTensor) diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index 6cd75996a..ebc9b43b4 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -166,47 +166,72 @@ class Tensor * * @return Unsigned integer representing the total number of elements */ - // TODO: move to cpp - uint32_t size() { return this->mSize; } - - // TODO: move to cpp - uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } - - // TODO: move to cpp - uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } + uint32_t size(); /** - * Retrieve the underlying data type of the Tensor + * Returns the total size of a single element of the respective data type + * that this tensor holds. + * + * @return Unsigned integer representing the memory of a single element of the + * respective data type. + */ + uint32_t dataTypeMemorySize(); + + /** + * Returns the total memory size of the data contained by the Tensor object which + * would equate to (this->size() * this->dataTypeMemorySize()) + * + * @return Unsigned integer representing the memory of a single element of the + * respective data type. + */ + uint32_t memorySize(); + + /** + * Retrieve the data type of the tensor (host, device, storage) * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { return this->mDataType; } + TensorDataTypes dataType(); - void* rawData() { return this->mRawData; } + /** + * Retrieve the raw data via the pointer to the memory that contains the raw memory + * of this current tensor. This tensor gets changed to a nullptr when the Tensor is + * removed. + * + * @return Pointer to raw memory containing raw bytes data of Tensor. + */ + void* rawData(); - // TODO: move to cpp + /** + * Sets / resets the data of the tensor which is directly done on the GPU host visible + * memory available by the tensor. + */ + void setRawData(const void* data); + + /** + * Template to return the pointer data converted by specific type, which would be + * any of the supported types including float, double, int32, uint32 and bool. + * + * @return Pointer to raw memory containing raw bytes data of Tensor. + */ template T* data() { return (T*)this->mRawData; } + /** + * Template to get the data of the current tensor as a vector of specific type, which would be + * any of the supported types including float, double, int32, uint32 and bool. + * + * @return Vector of type provided by template. + */ template std::vector vector() { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setRawData(const void* data) - { - // Copy data - memcpy(this->mRawData, data, this->memorySize()); - } - protected: // -------------- ALWAYS OWNED RESOURCES TensorTypes mTensorType; @@ -216,56 +241,6 @@ class Tensor void* mRawData; private: - void mapRawData() - { - - KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - - // Given we request coherent host memory we don't need to invalidate / - // flush - this->mRawData = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - - vk::MappedMemoryRange mappedMemoryRange( - *hostVisibleMemory, 0, bufferSize); - } - - void unmapRawData() - { - - KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->flushMappedMemoryRanges(1, &mappedRange); - this->mDevice->unmapMemory(*hostVisibleMemory); - } // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; @@ -304,9 +279,11 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + + void mapRawData(); + void unmapRawData(); }; -// TODO: Limit T to be only float, bool, double, etc template class TensorT : public Tensor { From 53ae5c2d0a67d31b321a1728330b28ccc99fdb5c Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 17:02:06 +0000 Subject: [PATCH 79/91] Updated kompute --- single_include/kompute/Kompute.hpp | 123 ++++++++++++----------------- 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 17f6cb240..fa93d229d 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -955,47 +955,72 @@ class Tensor * * @return Unsigned integer representing the total number of elements */ - // TODO: move to cpp - uint32_t size() { return this->mSize; } - - // TODO: move to cpp - uint32_t dataTypeMemorySize() { return this->mDataTypeMemorySize; } - - // TODO: move to cpp - uint32_t memorySize() { return this->mSize * this->mDataTypeMemorySize; } + uint32_t size(); /** - * Retrieve the underlying data type of the Tensor + * Returns the total size of a single element of the respective data type + * that this tensor holds. + * + * @return Unsigned integer representing the memory of a single element of the + * respective data type. + */ + uint32_t dataTypeMemorySize(); + + /** + * Returns the total memory size of the data contained by the Tensor object which + * would equate to (this->size() * this->dataTypeMemorySize()) + * + * @return Unsigned integer representing the memory of a single element of the + * respective data type. + */ + uint32_t memorySize(); + + /** + * Retrieve the data type of the tensor (host, device, storage) * * @return Data type of tensor of type kp::Tensor::TensorDataTypes */ - TensorDataTypes dataType() { return this->mDataType; } + TensorDataTypes dataType(); - void* rawData() { return this->mRawData; } + /** + * Retrieve the raw data via the pointer to the memory that contains the raw memory + * of this current tensor. This tensor gets changed to a nullptr when the Tensor is + * removed. + * + * @return Pointer to raw memory containing raw bytes data of Tensor. + */ + void* rawData(); - // TODO: move to cpp + /** + * Sets / resets the data of the tensor which is directly done on the GPU host visible + * memory available by the tensor. + */ + void setRawData(const void* data); + + /** + * Template to return the pointer data converted by specific type, which would be + * any of the supported types including float, double, int32, uint32 and bool. + * + * @return Pointer to raw memory containing raw bytes data of Tensor. + */ template T* data() { return (T*)this->mRawData; } + /** + * Template to get the data of the current tensor as a vector of specific type, which would be + * any of the supported types including float, double, int32, uint32 and bool. + * + * @return Vector of type provided by template. + */ template std::vector vector() { return { (T*)this->mRawData, ((T*)this->mRawData) + this->size() }; } - /** - * Sets / resets the vector data of the tensor. This function does not - * perform any copies into GPU memory and is only performed on the host. - */ - void setRawData(const void* data) - { - // Copy data - memcpy(this->mRawData, data, this->memorySize()); - } - protected: // -------------- ALWAYS OWNED RESOURCES TensorTypes mTensorType; @@ -1005,56 +1030,6 @@ class Tensor void* mRawData; private: - void mapRawData() - { - - KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - - // Given we request coherent host memory we don't need to invalidate / - // flush - this->mRawData = this->mDevice->mapMemory( - *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - - vk::MappedMemoryRange mappedMemoryRange( - *hostVisibleMemory, 0, bufferSize); - } - - void unmapRawData() - { - - KP_LOG_DEBUG("Kompute Tensor mapping data from host buffer"); - - std::shared_ptr hostVisibleMemory = nullptr; - - if (this->mTensorType == TensorTypes::eHost) { - hostVisibleMemory = this->mPrimaryMemory; - } else if (this->mTensorType == TensorTypes::eDevice) { - hostVisibleMemory = this->mStagingMemory; - } else { - KP_LOG_WARN( - "Kompute Tensor mapping data not supported on storage tensor"); - return; - } - - vk::DeviceSize bufferSize = this->memorySize(); - vk::MappedMemoryRange mappedRange(*hostVisibleMemory, 0, bufferSize); - this->mDevice->flushMappedMemoryRanges(1, &mappedRange); - this->mDevice->unmapMemory(*hostVisibleMemory); - } // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; @@ -1093,9 +1068,11 @@ class Tensor vk::MemoryPropertyFlags getPrimaryMemoryPropertyFlags(); vk::BufferUsageFlags getStagingBufferUsageFlags(); vk::MemoryPropertyFlags getStagingMemoryPropertyFlags(); + + void mapRawData(); + void unmapRawData(); }; -// TODO: Limit T to be only float, bool, double, etc template class TensorT : public Tensor { From ea3a3039845c7b5c46d5cb290110feedccb82a57 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 17:02:23 +0000 Subject: [PATCH 80/91] Reformat --- src/OpMemoryBarrier.cpp | 47 ++++++++++++------------ src/OpTensorCopy.cpp | 3 +- src/OpTensorSyncLocal.cpp | 22 ++++++------ src/Tensor.cpp | 39 ++++++++++---------- src/include/kompute/Tensor.hpp | 66 ++++++++++++++++++---------------- 5 files changed, 89 insertions(+), 88 deletions(-) diff --git a/src/OpMemoryBarrier.cpp b/src/OpMemoryBarrier.cpp index 9e45d0c4e..09a91f065 100644 --- a/src/OpMemoryBarrier.cpp +++ b/src/OpMemoryBarrier.cpp @@ -5,21 +5,20 @@ namespace kp { OpMemoryBarrier::OpMemoryBarrier( - const std::vector>& tensors, - const vk::AccessFlagBits& srcAccessMask, - const vk::AccessFlagBits& dstAccessMask, - const vk::PipelineStageFlagBits& srcStageMask, - const vk::PipelineStageFlagBits& dstStageMask, - bool barrierOnPrimary) - : mTensors(tensors), - mSrcAccessMask(srcAccessMask), - mDstAccessMask(dstAccessMask), - mSrcStageMask(srcStageMask), - mDstStageMask(dstStageMask), - mBarrierOnPrimary(barrierOnPrimary) + const std::vector>& tensors, + const vk::AccessFlagBits& srcAccessMask, + const vk::AccessFlagBits& dstAccessMask, + const vk::PipelineStageFlagBits& srcStageMask, + const vk::PipelineStageFlagBits& dstStageMask, + bool barrierOnPrimary) + : mTensors(tensors) + , mSrcAccessMask(srcAccessMask) + , mDstAccessMask(dstAccessMask) + , mSrcStageMask(srcStageMask) + , mDstStageMask(dstStageMask) + , mBarrierOnPrimary(barrierOnPrimary) { KP_LOG_DEBUG("Kompute OpMemoryBarrier constructor"); - } OpMemoryBarrier::~OpMemoryBarrier() @@ -35,21 +34,19 @@ OpMemoryBarrier::record(const vk::CommandBuffer& commandBuffer) // Barrier to ensure the data is finished writing to buffer memory if (this->mBarrierOnPrimary) { for (const std::shared_ptr& tensor : this->mTensors) { - tensor->recordPrimaryBufferMemoryBarrier( - commandBuffer, - this->mSrcAccessMask, - this->mDstAccessMask, - this->mSrcStageMask, - this->mDstStageMask); + tensor->recordPrimaryBufferMemoryBarrier(commandBuffer, + this->mSrcAccessMask, + this->mDstAccessMask, + this->mSrcStageMask, + this->mDstStageMask); } } else { for (const std::shared_ptr& tensor : this->mTensors) { - tensor->recordStagingBufferMemoryBarrier( - commandBuffer, - this->mSrcAccessMask, - this->mDstAccessMask, - this->mSrcStageMask, - this->mDstStageMask); + tensor->recordStagingBufferMemoryBarrier(commandBuffer, + this->mSrcAccessMask, + this->mDstAccessMask, + this->mSrcStageMask, + this->mDstStageMask); } } } diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index 13e189a58..b78dd520b 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -44,8 +44,7 @@ OpTensorCopy::record(const vk::CommandBuffer& commandBuffer) // We iterate from the second tensor onwards and record a copy to all for (size_t i = 1; i < this->mTensors.size(); i++) { - this->mTensors[i]->recordCopyFrom( - commandBuffer, this->mTensors[0]); + this->mTensors[i]->recordCopyFrom(commandBuffer, this->mTensors[0]); } } diff --git a/src/OpTensorSyncLocal.cpp b/src/OpTensorSyncLocal.cpp index 5e653154a..fc3e0b93b 100644 --- a/src/OpTensorSyncLocal.cpp +++ b/src/OpTensorSyncLocal.cpp @@ -31,19 +31,21 @@ OpTensorSyncLocal::record(const vk::CommandBuffer& commandBuffer) for (size_t i = 0; i < this->mTensors.size(); i++) { if (this->mTensors[i]->tensorType() == Tensor::TensorTypes::eDevice) { - this->mTensors[i]->recordPrimaryBufferMemoryBarrier(commandBuffer, - vk::AccessFlagBits::eShaderWrite, - vk::AccessFlagBits::eTransferRead, - vk::PipelineStageFlagBits::eComputeShader, - vk::PipelineStageFlagBits::eTransfer); + this->mTensors[i]->recordPrimaryBufferMemoryBarrier( + commandBuffer, + vk::AccessFlagBits::eShaderWrite, + vk::AccessFlagBits::eTransferRead, + vk::PipelineStageFlagBits::eComputeShader, + vk::PipelineStageFlagBits::eTransfer); this->mTensors[i]->recordCopyFromDeviceToStaging(commandBuffer); - this->mTensors[i]->recordPrimaryBufferMemoryBarrier(commandBuffer, - vk::AccessFlagBits::eTransferWrite, - vk::AccessFlagBits::eHostRead, - vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eHost); + this->mTensors[i]->recordPrimaryBufferMemoryBarrier( + commandBuffer, + vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eHostRead, + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eHost); } } } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index aa3584dcd..601d2f624 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -131,8 +131,7 @@ Tensor::mapRawData() this->mRawData = this->mDevice->mapMemory( *hostVisibleMemory, 0, bufferSize, vk::MemoryMapFlags()); - vk::MappedMemoryRange mappedMemoryRange( - *hostVisibleMemory, 0, bufferSize); + vk::MappedMemoryRange mappedMemoryRange(*hostVisibleMemory, 0, bufferSize); } void @@ -219,36 +218,36 @@ Tensor::recordCopyBuffer(const vk::CommandBuffer& commandBuffer, void Tensor::recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask) + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask) { KP_LOG_DEBUG("Kompute Tensor recording PRIMARY buffer memory barrier"); this->recordBufferMemoryBarrier(commandBuffer, - *this->mPrimaryBuffer, - srcAccessMask, - dstAccessMask, - srcStageMask, - dstStageMask); + *this->mPrimaryBuffer, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask); } void Tensor::recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask) + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask) { KP_LOG_DEBUG("Kompute Tensor recording PRIMARY buffer memory barrier"); this->recordBufferMemoryBarrier(commandBuffer, - *this->mStagingBuffer, - srcAccessMask, - dstAccessMask, - srcStageMask, - dstStageMask); + *this->mStagingBuffer, + srcAccessMask, + dstAccessMask, + srcStageMask, + dstStageMask); } void diff --git a/src/include/kompute/Tensor.hpp b/src/include/kompute/Tensor.hpp index ebc9b43b4..db2745172 100644 --- a/src/include/kompute/Tensor.hpp +++ b/src/include/kompute/Tensor.hpp @@ -120,8 +120,9 @@ class Tensor void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer); /** - * Records the buffer memory barrier into the primary buffer and command buffer which - * ensures that relevant data transfers are carried out correctly. + * Records the buffer memory barrier into the primary buffer and command + * buffer which ensures that relevant data transfers are carried out + * correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param srcAccessMask Access flags for source access mask @@ -129,14 +130,16 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordPrimaryBufferMemoryBarrier( + const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** - * Records the buffer memory barrier into the staging buffer and command buffer which - * ensures that relevant data transfers are carried out correctly. + * Records the buffer memory barrier into the staging buffer and command + * buffer which ensures that relevant data transfers are carried out + * correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param srcAccessMask Access flags for source access mask @@ -144,12 +147,12 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); - + void recordStagingBufferMemoryBarrier( + const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -172,17 +175,17 @@ class Tensor * Returns the total size of a single element of the respective data type * that this tensor holds. * - * @return Unsigned integer representing the memory of a single element of the - * respective data type. + * @return Unsigned integer representing the memory of a single element of + * the respective data type. */ uint32_t dataTypeMemorySize(); /** - * Returns the total memory size of the data contained by the Tensor object which - * would equate to (this->size() * this->dataTypeMemorySize()) + * Returns the total memory size of the data contained by the Tensor object + * which would equate to (this->size() * this->dataTypeMemorySize()) * - * @return Unsigned integer representing the memory of a single element of the - * respective data type. + * @return Unsigned integer representing the memory of a single element of + * the respective data type. */ uint32_t memorySize(); @@ -194,23 +197,24 @@ class Tensor TensorDataTypes dataType(); /** - * Retrieve the raw data via the pointer to the memory that contains the raw memory - * of this current tensor. This tensor gets changed to a nullptr when the Tensor is - * removed. + * Retrieve the raw data via the pointer to the memory that contains the raw + * memory of this current tensor. This tensor gets changed to a nullptr when + * the Tensor is removed. * * @return Pointer to raw memory containing raw bytes data of Tensor. */ void* rawData(); /** - * Sets / resets the data of the tensor which is directly done on the GPU host visible - * memory available by the tensor. + * Sets / resets the data of the tensor which is directly done on the GPU + * host visible memory available by the tensor. */ void setRawData(const void* data); /** - * Template to return the pointer data converted by specific type, which would be - * any of the supported types including float, double, int32, uint32 and bool. + * Template to return the pointer data converted by specific type, which + * would be any of the supported types including float, double, int32, + * uint32 and bool. * * @return Pointer to raw memory containing raw bytes data of Tensor. */ @@ -221,8 +225,9 @@ class Tensor } /** - * Template to get the data of the current tensor as a vector of specific type, which would be - * any of the supported types including float, double, int32, uint32 and bool. + * Template to get the data of the current tensor as a vector of specific + * type, which would be any of the supported types including float, double, + * int32, uint32 and bool. * * @return Vector of type provided by template. */ @@ -241,7 +246,6 @@ class Tensor void* mRawData; private: - // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; From 156e3b4964ebd5b2968f564477a11697f2f96106 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 17:03:41 +0000 Subject: [PATCH 81/91] Reformat kompute --- single_include/kompute/Kompute.hpp | 65 ++++++++++++++++-------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index fa93d229d..cd313e6e7 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -910,8 +910,9 @@ class Tensor void recordCopyFromDeviceToStaging(const vk::CommandBuffer& commandBuffer); /** - * Records the buffer memory barrier into the primary buffer and command buffer which - * ensures that relevant data transfers are carried out correctly. + * Records the buffer memory barrier into the primary buffer and command + * buffer which ensures that relevant data transfers are carried out + * correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param srcAccessMask Access flags for source access mask @@ -919,14 +920,16 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordPrimaryBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordPrimaryBufferMemoryBarrier( + const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** - * Records the buffer memory barrier into the staging buffer and command buffer which - * ensures that relevant data transfers are carried out correctly. + * Records the buffer memory barrier into the staging buffer and command + * buffer which ensures that relevant data transfers are carried out + * correctly. * * @param commandBuffer Vulkan Command Buffer to record the commands into * @param srcAccessMask Access flags for source access mask @@ -934,11 +937,12 @@ class Tensor * @param scrStageMask Pipeline stage flags for source stage mask * @param dstStageMask Pipeline stage flags for destination stage mask */ - void recordStagingBufferMemoryBarrier(const vk::CommandBuffer& commandBuffer, - vk::AccessFlagBits srcAccessMask, - vk::AccessFlagBits dstAccessMask, - vk::PipelineStageFlagBits srcStageMask, - vk::PipelineStageFlagBits dstStageMask); + void recordStagingBufferMemoryBarrier( + const vk::CommandBuffer& commandBuffer, + vk::AccessFlagBits srcAccessMask, + vk::AccessFlagBits dstAccessMask, + vk::PipelineStageFlagBits srcStageMask, + vk::PipelineStageFlagBits dstStageMask); /** * Constructs a vulkan descriptor buffer info which can be used to specify @@ -961,17 +965,17 @@ class Tensor * Returns the total size of a single element of the respective data type * that this tensor holds. * - * @return Unsigned integer representing the memory of a single element of the - * respective data type. + * @return Unsigned integer representing the memory of a single element of + * the respective data type. */ uint32_t dataTypeMemorySize(); /** - * Returns the total memory size of the data contained by the Tensor object which - * would equate to (this->size() * this->dataTypeMemorySize()) + * Returns the total memory size of the data contained by the Tensor object + * which would equate to (this->size() * this->dataTypeMemorySize()) * - * @return Unsigned integer representing the memory of a single element of the - * respective data type. + * @return Unsigned integer representing the memory of a single element of + * the respective data type. */ uint32_t memorySize(); @@ -983,23 +987,24 @@ class Tensor TensorDataTypes dataType(); /** - * Retrieve the raw data via the pointer to the memory that contains the raw memory - * of this current tensor. This tensor gets changed to a nullptr when the Tensor is - * removed. + * Retrieve the raw data via the pointer to the memory that contains the raw + * memory of this current tensor. This tensor gets changed to a nullptr when + * the Tensor is removed. * * @return Pointer to raw memory containing raw bytes data of Tensor. */ void* rawData(); /** - * Sets / resets the data of the tensor which is directly done on the GPU host visible - * memory available by the tensor. + * Sets / resets the data of the tensor which is directly done on the GPU + * host visible memory available by the tensor. */ void setRawData(const void* data); /** - * Template to return the pointer data converted by specific type, which would be - * any of the supported types including float, double, int32, uint32 and bool. + * Template to return the pointer data converted by specific type, which + * would be any of the supported types including float, double, int32, + * uint32 and bool. * * @return Pointer to raw memory containing raw bytes data of Tensor. */ @@ -1010,8 +1015,9 @@ class Tensor } /** - * Template to get the data of the current tensor as a vector of specific type, which would be - * any of the supported types including float, double, int32, uint32 and bool. + * Template to get the data of the current tensor as a vector of specific + * type, which would be any of the supported types including float, double, + * int32, uint32 and bool. * * @return Vector of type provided by template. */ @@ -1030,7 +1036,6 @@ class Tensor void* mRawData; private: - // -------------- NEVER OWNED RESOURCES std::shared_ptr mPhysicalDevice; std::shared_ptr mDevice; From 7d2c7825ffe3e8eb7c1880e68e40622b094b2663 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sat, 13 Mar 2021 17:06:37 +0000 Subject: [PATCH 82/91] Removed unused code in optensorcopy --- src/OpTensorCopy.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/OpTensorCopy.cpp b/src/OpTensorCopy.cpp index b78dd520b..4438f8d6a 100644 --- a/src/OpTensorCopy.cpp +++ b/src/OpTensorCopy.cpp @@ -59,10 +59,6 @@ OpTensorCopy::postEval(const vk::CommandBuffer& commandBuffer) { KP_LOG_DEBUG("Kompute OpTensorCopy postEval called"); - // TODO: Simplify with a copyRawData - uint32_t size = this->mTensors[0]->size(); - uint32_t dataTypeMemSize = this->mTensors[0]->dataTypeMemorySize(); - uint32_t memSize = size * dataTypeMemSize; void* data = this->mTensors[0]->rawData(); // Copy the data from the first tensor into all the tensors From b0c23f4085175870df811b1e3868f810ce5f4550 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 08:37:27 +0000 Subject: [PATCH 83/91] Updated version string --- CMakeLists.txt | 2 +- VERSION | 2 +- docs/conf.py | 2 +- setup.py | 2 +- vcpkg.json.opt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bebb2a545..0a317c68e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4.1) -project(kompute VERSION 0.6.0) +project(kompute VERSION 0.7.0) set(CMAKE_CXX_STANDARD 14) diff --git a/VERSION b/VERSION index a918a2aa1..faef31a43 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.0 +0.7.0 diff --git a/docs/conf.py b/docs/conf.py index f5910ed56..28d2704e2 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ html_title = "Vulkan Kompute Documentation (Python & C++)" author = 'Alejandro Saucedo' # The full version, including alpha/beta/rc tags -release = '0.6.0' +release = '0.7.0' # -- General configuration --------------------------------------------------- diff --git a/setup.py b/setup.py index 733c4c185..bb44423c9 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ class CMakeBuild(build_ext): setup( name='kp', - version='0.6.0', + version='0.7.0', author='Alejandro Saucedo', description='Vulkan Kompute: Blazing fast, mobile-enabled, asynchronous, and optimized for advanced GPU processing usecases.', long_description=long_description, diff --git a/vcpkg.json.opt b/vcpkg.json.opt index b8da49533..e2db75236 100644 --- a/vcpkg.json.opt +++ b/vcpkg.json.opt @@ -1,6 +1,6 @@ { "name": "example", - "version-string": "0.6.0", + "version-string": "0.7.0", "dependencies": [ "fmt", "spdlog", From 282326e8b86be6f8d1f4d91e4520a00be57c059c Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 08:44:52 +0000 Subject: [PATCH 84/91] Updated changelog and added further documentation on release --- CHANGELOG.md | 74 ++++++++++++++++++++++++++++++++++++-- Makefile | 2 +- docs/overview/ci-tests.rst | 5 +-- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f98612d3c..b85f4e1de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,76 @@ # Changelog -## [v0.6.0](https://github.com/EthicalML/vulkan-kompute/tree/v0.6.0) +## [v0.7.0](https://github.com/EthicalML/vulkan-kompute/tree/v0.7.0) + +[Full Changelog](https://github.com/EthicalML/vulkan-kompute/compare/v0.6.0...v0.7.0) + +**Implemented enhancements:** + +- Extend non-spdlog print functions to use std::format [\#158](https://github.com/EthicalML/vulkan-kompute/issues/158) +- Add code coverage reports with codecov [\#145](https://github.com/EthicalML/vulkan-kompute/issues/145) +- Explore removing `std::vector mData;` completely from Tensor in favour of always storing data in hostVisible buffer memory \(TBC\) [\#144](https://github.com/EthicalML/vulkan-kompute/issues/144) +- Update all examples to match breaking changes in 0.7.0 [\#141](https://github.com/EthicalML/vulkan-kompute/issues/141) +- Avoid copy when returning python numpy / array [\#139](https://github.com/EthicalML/vulkan-kompute/issues/139) +- Cover all Python & C++ tests in CI [\#121](https://github.com/EthicalML/vulkan-kompute/issues/121) +- Add C++ Test for Simple Work Groups Example [\#117](https://github.com/EthicalML/vulkan-kompute/issues/117) +- Expose push constants in OpAlgo [\#54](https://github.com/EthicalML/vulkan-kompute/issues/54) +- Expose ability to create barriers in OpTensor operations [\#45](https://github.com/EthicalML/vulkan-kompute/issues/45) +- Create delete function in manager to free / destroy sequence [\#36](https://github.com/EthicalML/vulkan-kompute/issues/36) +- Make specialisation data extensible [\#12](https://github.com/EthicalML/vulkan-kompute/issues/12) +- Support multiple types for Kompute Tensors [\#2](https://github.com/EthicalML/vulkan-kompute/issues/2) +- Added re-record sequence functionality and updated docs [\#171](https://github.com/EthicalML/vulkan-kompute/pull/171) ([axsaucedo](https://github.com/axsaucedo)) +- Extend non-spdlog print functions to use fmt::format / fmt::print [\#159](https://github.com/EthicalML/vulkan-kompute/pull/159) ([axsaucedo](https://github.com/axsaucedo)) +- Added support for custom SpecializedConstants and removed KomputeWorkgroup class [\#151](https://github.com/EthicalML/vulkan-kompute/pull/151) ([axsaucedo](https://github.com/axsaucedo)) +- Added destroy functions for tensors and sequences \(named and object\) [\#146](https://github.com/EthicalML/vulkan-kompute/pull/146) ([axsaucedo](https://github.com/axsaucedo)) + +**Fixed bugs:** + +- push\_constant not working in my case? [\#168](https://github.com/EthicalML/vulkan-kompute/issues/168) +- DescriptorPool set is not being freed [\#155](https://github.com/EthicalML/vulkan-kompute/issues/155) +- Updated memory barriers to include staging buffers [\#182](https://github.com/EthicalML/vulkan-kompute/pull/182) ([axsaucedo](https://github.com/axsaucedo)) +- Adds push const ranges in pipelinelayout to fix \#168 [\#174](https://github.com/EthicalML/vulkan-kompute/pull/174) ([axsaucedo](https://github.com/axsaucedo)) +- Added destructor for staging tensors [\#134](https://github.com/EthicalML/vulkan-kompute/pull/134) ([axsaucedo](https://github.com/axsaucedo)) + +**Closed issues:** + +- Update memory barriers to align with tensor staging/primary memory revamp [\#181](https://github.com/EthicalML/vulkan-kompute/issues/181) +- Move shader defaultResource inside kp::Shader class [\#175](https://github.com/EthicalML/vulkan-kompute/issues/175) +- Reach at least 90% code coverage on tests [\#170](https://github.com/EthicalML/vulkan-kompute/issues/170) +- Add functionality to re-record sequence as now it's possible to update the underlying algorithm [\#169](https://github.com/EthicalML/vulkan-kompute/issues/169) +- Use numpy arrays as default return value [\#166](https://github.com/EthicalML/vulkan-kompute/issues/166) +- Update all shared\_ptr value passes to be by ref or const ref [\#161](https://github.com/EthicalML/vulkan-kompute/issues/161) +- Amend memory hierarchy for kp::Operations so they can be created separately [\#160](https://github.com/EthicalML/vulkan-kompute/issues/160) +- Customise theme of documentation [\#156](https://github.com/EthicalML/vulkan-kompute/issues/156) +- Remove KomputeWorkgroup class in favour of std::array\ [\#152](https://github.com/EthicalML/vulkan-kompute/issues/152) +- Passing raw GLSL string to Shader Module depricated so remove this method from supported approach [\#150](https://github.com/EthicalML/vulkan-kompute/issues/150) +- Add python backwards compatibility for eval\_tensor\_create\_def [\#147](https://github.com/EthicalML/vulkan-kompute/issues/147) +- Document breaking changes for 0.7.0 [\#140](https://github.com/EthicalML/vulkan-kompute/issues/140) +- Tensor memory management and memory hierarchy redesign [\#136](https://github.com/EthicalML/vulkan-kompute/issues/136) +- Staging tensor GPU memory is not freed as part of OpCreateTensor removal [\#133](https://github.com/EthicalML/vulkan-kompute/issues/133) +- eStorage Tensors are currently unusable as OpTensorCreate calls mapDataIntoHostMemory [\#132](https://github.com/EthicalML/vulkan-kompute/issues/132) +- 0.6.0 Release [\#126](https://github.com/EthicalML/vulkan-kompute/issues/126) +- java.lang.UnsatisfiedLinkError: dlopen failed: library "libkompute-jni.so" not found [\#125](https://github.com/EthicalML/vulkan-kompute/issues/125) +- Initial exploration: Include explicit GLSL to SPIRV compilation [\#107](https://github.com/EthicalML/vulkan-kompute/issues/107) +- Add support for push constants [\#106](https://github.com/EthicalML/vulkan-kompute/issues/106) + +**Merged pull requests:** + +- Resolve moving all functions from tensor HPP to CPP [\#186](https://github.com/EthicalML/vulkan-kompute/pull/186) ([axsaucedo](https://github.com/axsaucedo)) +- Device Properties [\#184](https://github.com/EthicalML/vulkan-kompute/pull/184) ([alexander-g](https://github.com/alexander-g)) +- Too many warnings [\#183](https://github.com/EthicalML/vulkan-kompute/pull/183) ([alexander-g](https://github.com/alexander-g)) +- Add support for bool, double, int32, uint32 and float32 on Tensors via TensorT [\#177](https://github.com/EthicalML/vulkan-kompute/pull/177) ([axsaucedo](https://github.com/axsaucedo)) +- Support for Timestamping [\#176](https://github.com/EthicalML/vulkan-kompute/pull/176) ([alexander-g](https://github.com/alexander-g)) +- Test for ShaderResources [\#165](https://github.com/EthicalML/vulkan-kompute/pull/165) ([aliPMPAINT](https://github.com/aliPMPAINT)) +- Amend memory hierarchy to enable for push constants and functional interface for more flexible operations [\#164](https://github.com/EthicalML/vulkan-kompute/pull/164) ([axsaucedo](https://github.com/axsaucedo)) +- made changes for include paths for complete installation [\#163](https://github.com/EthicalML/vulkan-kompute/pull/163) ([aliPMPAINT](https://github.com/aliPMPAINT)) +- Added dark mode on docs [\#157](https://github.com/EthicalML/vulkan-kompute/pull/157) ([axsaucedo](https://github.com/axsaucedo)) +- Glslang implementation for online shader compilation [\#154](https://github.com/EthicalML/vulkan-kompute/pull/154) ([axsaucedo](https://github.com/axsaucedo)) +- Adding test code coverage using gcov and lcov [\#149](https://github.com/EthicalML/vulkan-kompute/pull/149) ([axsaucedo](https://github.com/axsaucedo)) +- Added temporary backwards compatibility for eval\_tensor\_create\_def function [\#148](https://github.com/EthicalML/vulkan-kompute/pull/148) ([axsaucedo](https://github.com/axsaucedo)) +- Amend memory ownership hierarchy to have Tensor owned by Manager instead of OpCreateTensor / OpBase [\#138](https://github.com/EthicalML/vulkan-kompute/pull/138) ([axsaucedo](https://github.com/axsaucedo)) +- Removed Staging Tensors in favour of having two buffer & memory in a Tensor to minimise data transfer [\#137](https://github.com/EthicalML/vulkan-kompute/pull/137) ([axsaucedo](https://github.com/axsaucedo)) + +## [v0.6.0](https://github.com/EthicalML/vulkan-kompute/tree/v0.6.0) (2021-01-31) [Full Changelog](https://github.com/EthicalML/vulkan-kompute/compare/v0.5.1...v0.6.0) @@ -49,7 +119,6 @@ - Remove the template params from OpAlgoBase for dispatch layout [\#57](https://github.com/EthicalML/vulkan-kompute/issues/57) - Enable layout to be configured dynamically within shaders [\#26](https://github.com/EthicalML/vulkan-kompute/issues/26) - replaced "static unsigned const" to "static const unsigned" to avoid SWIG parsing error. [\#95](https://github.com/EthicalML/vulkan-kompute/pull/95) ([0x0f0f0f](https://github.com/0x0f0f0f)) -- Added python bindings with kp as python module [\#88](https://github.com/EthicalML/vulkan-kompute/pull/88) ([axsaucedo](https://github.com/axsaucedo)) **Closed issues:** @@ -69,6 +138,7 @@ - Adding Python package for Kompute [\#87](https://github.com/EthicalML/vulkan-kompute/issues/87) - Python shader extension [\#91](https://github.com/EthicalML/vulkan-kompute/pull/91) ([axsaucedo](https://github.com/axsaucedo)) - Enhanced python build [\#89](https://github.com/EthicalML/vulkan-kompute/pull/89) ([axsaucedo](https://github.com/axsaucedo)) +- Added python bindings with kp as python module [\#88](https://github.com/EthicalML/vulkan-kompute/pull/88) ([axsaucedo](https://github.com/axsaucedo)) **Closed issues:** diff --git a/Makefile b/Makefile index 9fdcbdcbe..8a81663bf 100644 --- a/Makefile +++ b/Makefile @@ -199,4 +199,4 @@ format: build_changelog: docker run --rm -it -v "$(PWD)":/usr/local/src/your-app -e CHANGELOG_GITHUB_TOKEN=${CHANGELOG_GITHUB_TOKEN} ferrarimarco/github-changelog-generator:1.15.2 -u EthicalML -p vulkan-kompute chmod 664 CHANGELOG.md # (Read+Write, Read+Write, Read) - sed -i -e 's/\(HEAD\|Unreleased\)/v0.6.0/g' CHANGELOG.md # Replacing unreleased version with latest tag + sed -i -e 's/\(HEAD\|Unreleased\)/v${VERSION}/g' CHANGELOG.md # Replacing unreleased version with latest tag diff --git a/docs/overview/ci-tests.rst b/docs/overview/ci-tests.rst index abda9479b..4afc1f0ce 100644 --- a/docs/overview/ci-tests.rst +++ b/docs/overview/ci-tests.rst @@ -81,6 +81,7 @@ Performing Release In order to perform the release the following steps need to be carried out: * Build changelog + * Create branch called `v-release` * Generate latest changelog `make build_changelog` * Update latest tag in new CHANGELOG.md to be the vesion to release * Python Release @@ -98,7 +99,3 @@ In order to perform the release the following steps need to be carried out: * Ensure all tests pass in GPU and CPU: `python -m pytest` -``` -``` - - From 6bab776b0389febd05d2cc6ed3a3650ffa68cbb0 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 09:41:17 +0000 Subject: [PATCH 85/91] Added options to build dependencies as shared or static lib --- CMakeLists.txt | 12 +++++++++++- docs/overview/build-system.rst | 4 ++++ src/CMakeLists.txt | 12 +++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a317c68e..8f23fc622 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ option(KOMPUTE_OPT_REPO_SUBMODULE_BUILD, "Use the submodule repos instead of ext option(KOMPUTE_OPT_ANDOID_BUILD "Enable android compilation flags required" 0) option(KOMPUTE_OPT_DISABLE_VK_DEBUG_LAYERS "Explicitly disable debug layers even on debug" 0) option(KOMPUTE_OPT_DISABLE_SHADER_UTILS "Remove shader util code and dependencies including glslang" 0) +option(KOMPUTE_OPT_DEPENDENCIES_SHARED_LIBS "Whether to use shared libraries for dependencies for install" 0) +option(KOMPUTE_OPT_BUILD_AS_SHARED_LIB "Whether to build kompute as shared library" 0) # Build flags set(KOMPUTE_EXTRA_CXX_FLAGS "" CACHE STRING "Extra compile flags for Kompute, see docs for full list") @@ -29,6 +31,10 @@ if(KOMPUTE_OPT_ENABLE_SPDLOG) if(KOMPUTE_OPT_INSTALL) # Enable install parameters for spdlog (overrides parameters passed) set(SPDLOG_INSTALL ON CACHE BOOL "Enables install of spdlot" FORCE) + + if(KOMPUTE_OPT_DEPENDENCIES_SHARED_LIBS) + set(SPDLOG_BUILD_SHARED ON CACHE BOOL "Enables build of shared libraries" FORCE) + endif() endif() endif() @@ -54,7 +60,11 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) # Enable install parameters for glslang (overrides parameters passed) # When install is enabled the glslang libraries become shared set(ENABLE_GLSLANG_INSTALL ON CACHE BOOL "Enables install of glslang" FORCE) - set(BUILD_SHARED_LIBS ON CACHE BOOL "Enables build of shared libraries" FORCE) + + # By default we enable shared library based installation + if(KOMPUTE_OPT_DEPENDENCIES_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON CACHE BOOL "Enables build of shared libraries" FORCE) + endif() endif() else() set(KOMPUTE_EXTRA_CXX_FLAGS "${KOMPUTE_EXTRA_CXX_FLAGS} -DKOMPUTE_DISABLE_SHADER_UTILS=1") diff --git a/docs/overview/build-system.rst b/docs/overview/build-system.rst index 620711cc4..cb6933ba4 100644 --- a/docs/overview/build-system.rst +++ b/docs/overview/build-system.rst @@ -33,6 +33,10 @@ This by default configures without any of the extra build tasks (such as buildin - Disables the install step in the cmake file (useful for android build) * - -DKOMPUTE_OPT_ANDROID_BUILD=1 - Enables android build which includes and excludes relevant libraries + * - -DKOMPUTE_OPT_DEPENDENCIES_SHARED_LIBS=1 + - Ensures dependencies are referenced as shared libraries for kompute install + * - -DKOMPUTE_OPT_BUILD_AS_SHARED_LIB=1 + - Whether to build Kompute as shared lib instead of static Compile Flags diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 950f95896..102d9527d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,9 +39,15 @@ if(KOMPUTE_OPT_ANDOID_BUILD) ${PROJECT_SOURCE_DIR}/vk_ndk_wrapper_include/kompute_vk_ndk_wrapper.cpp) endif() -add_library( - kompute STATIC - ${kompute_CPP}) +if(NOT KOMPUTE_OPT_BUILD_AS_SHARED_LIB) + add_library( + kompute STATIC + ${kompute_CPP}) +else() + add_library( + kompute SHARED + ${kompute_CPP}) +endif() target_include_directories( kompute PUBLIC From 953dc54cb5443048f367130dfbcee2fade8493c4 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 09:56:47 +0000 Subject: [PATCH 86/91] Updated scope of the dependencies to include in install --- src/CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 102d9527d..7280dc88d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,7 @@ target_include_directories( if(NOT KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute + kompute PRIVATE Vulkan::Vulkan ) else() @@ -87,7 +87,7 @@ else() endif() target_link_libraries( - kompute + kompute PUBLIC fmt::fmt ) @@ -103,7 +103,7 @@ if(KOMPUTE_OPT_ENABLE_SPDLOG) endif() target_link_libraries( - kompute + kompute PUBLIC spdlog::spdlog ) endif() @@ -114,7 +114,7 @@ endif() if(KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute + kompute PRIVATE kompute_vk_ndk_wrapper log android @@ -152,7 +152,8 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${PROJECT_SOURCE_DIR}/external/glslang) - target_link_libraries(kompute + target_link_libraries( + kompute PUBLIC # Not including hlsl support # HLSL # glslang includes OGLCompiler, OSDependent, MachineIndependent @@ -165,7 +166,8 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${GLSLANG_GENERATED_INCLUDEDIR}) - target_link_libraries(kompute + target_link_libraries( + kompute PUBLIC # Not including hlsl support # glslang::HLSL # Adding explicit dependencies to match above From 4e1ef1fae8424d2e30efc00c4892c75cf397a21d Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 10:08:32 +0000 Subject: [PATCH 87/91] Removed scope classifier for library dependencies --- src/CMakeLists.txt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7280dc88d..3488c1bd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,7 @@ target_include_directories( if(NOT KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute PRIVATE + kompute Vulkan::Vulkan ) else() @@ -87,7 +87,7 @@ else() endif() target_link_libraries( - kompute PUBLIC + kompute fmt::fmt ) @@ -103,7 +103,7 @@ if(KOMPUTE_OPT_ENABLE_SPDLOG) endif() target_link_libraries( - kompute PUBLIC + kompute spdlog::spdlog ) endif() @@ -114,7 +114,7 @@ endif() if(KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute PRIVATE + kompute kompute_vk_ndk_wrapper log android @@ -152,8 +152,7 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${PROJECT_SOURCE_DIR}/external/glslang) - target_link_libraries( - kompute PUBLIC + target_link_libraries(kompute # Not including hlsl support # HLSL # glslang includes OGLCompiler, OSDependent, MachineIndependent @@ -166,8 +165,7 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${GLSLANG_GENERATED_INCLUDEDIR}) - target_link_libraries( - kompute PUBLIC + target_link_libraries(kompute # Not including hlsl support # glslang::HLSL # Adding explicit dependencies to match above From 25fc03cb726ae96ffd4300a93630125c67176943 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 11:10:04 +0000 Subject: [PATCH 88/91] Added spdlog, fmt and glslang as public targets --- src/CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3488c1bd8..7280dc88d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,7 @@ target_include_directories( if(NOT KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute + kompute PRIVATE Vulkan::Vulkan ) else() @@ -87,7 +87,7 @@ else() endif() target_link_libraries( - kompute + kompute PUBLIC fmt::fmt ) @@ -103,7 +103,7 @@ if(KOMPUTE_OPT_ENABLE_SPDLOG) endif() target_link_libraries( - kompute + kompute PUBLIC spdlog::spdlog ) endif() @@ -114,7 +114,7 @@ endif() if(KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute + kompute PRIVATE kompute_vk_ndk_wrapper log android @@ -152,7 +152,8 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${PROJECT_SOURCE_DIR}/external/glslang) - target_link_libraries(kompute + target_link_libraries( + kompute PUBLIC # Not including hlsl support # HLSL # glslang includes OGLCompiler, OSDependent, MachineIndependent @@ -165,7 +166,8 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${GLSLANG_GENERATED_INCLUDEDIR}) - target_link_libraries(kompute + target_link_libraries( + kompute PUBLIC # Not including hlsl support # glslang::HLSL # Adding explicit dependencies to match above From d60e9ee86b2d6207fa305c7ba3ebc4064cdf27f0 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 14:45:22 +0000 Subject: [PATCH 89/91] Updated to private library include --- src/CMakeLists.txt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7280dc88d..3488c1bd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,7 @@ target_include_directories( if(NOT KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute PRIVATE + kompute Vulkan::Vulkan ) else() @@ -87,7 +87,7 @@ else() endif() target_link_libraries( - kompute PUBLIC + kompute fmt::fmt ) @@ -103,7 +103,7 @@ if(KOMPUTE_OPT_ENABLE_SPDLOG) endif() target_link_libraries( - kompute PUBLIC + kompute spdlog::spdlog ) endif() @@ -114,7 +114,7 @@ endif() if(KOMPUTE_OPT_ANDOID_BUILD) target_link_libraries( - kompute PRIVATE + kompute kompute_vk_ndk_wrapper log android @@ -152,8 +152,7 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${PROJECT_SOURCE_DIR}/external/glslang) - target_link_libraries( - kompute PUBLIC + target_link_libraries(kompute # Not including hlsl support # HLSL # glslang includes OGLCompiler, OSDependent, MachineIndependent @@ -166,8 +165,7 @@ if(NOT KOMPUTE_OPT_DISABLE_SHADER_UTILS) kompute PRIVATE ${GLSLANG_GENERATED_INCLUDEDIR}) - target_link_libraries( - kompute PUBLIC + target_link_libraries(kompute # Not including hlsl support # glslang::HLSL # Adding explicit dependencies to match above From ab7cf079a99ee4cbcf6c7f26f255ce7a6131a77b Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 15:05:52 +0000 Subject: [PATCH 90/91] Updated include directories to always include both the full headers and the single include --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3488c1bd8..d67af1c01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,8 +51,8 @@ endif() target_include_directories( kompute PUBLIC - $ - $ + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/single_include ) if(NOT KOMPUTE_OPT_ANDOID_BUILD) From 21f4337d551506e9debddc63a53311af5ba3689a Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 14 Mar 2021 15:35:32 +0000 Subject: [PATCH 91/91] Fixed skip test check --- python/test/test_tensor_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/test/test_tensor_types.py b/python/test/test_tensor_types.py index b1d90fe03..91866f885 100644 --- a/python/test/test_tensor_types.py +++ b/python/test/test_tensor_types.py @@ -82,7 +82,7 @@ def test_type_float_double_incorrect(): assert np.all(tensor_out.data() != arr_in_a * arr_in_b) -@pytest.mark.skipif("swiftshader" in os.environ.get("VK_ICD_FILENAMES"), +@pytest.mark.skipif("swiftshader" in os.environ.get("VK_ICD_FILENAMES", ""), reason="Swiftshader doesn't support double") def test_type_double():