Initial checkpoint with reasonable workflow

This commit is contained in:
Alejandro Saucedo 2021-02-24 08:39:09 +00:00
parent 5db9abd06e
commit 9aae5d69db
46 changed files with 1158 additions and 695 deletions

View file

@ -0,0 +1,77 @@
#pragma once
#include <fstream>
#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<std::shared_ptr<Tensor>> tensors,
std::shared_ptr<kp::Algorithm> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> 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

View file

@ -1,14 +1,8 @@
#pragma once
#include <fstream>
#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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>>& tensors,
const Workgroup& komputeWorkgroup = {},
const Constants& specializationConstants = {});
OpAlgoDispatch(std::vector<std::shared_ptr<Tensor>> tensors,
std::shared_ptr<kp::Algorithm> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>>& tensors,
std::string shaderFilePath,
const Workgroup& komputeWorkgroup = {},
const Constants& specializationConstants = {});
OpAlgoDispatch(std::vector<std::shared_ptr<Tensor>>& tensors,
std::shared_ptr<kp::Algorithm>& 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>>& tensors,
const std::vector<uint32_t>& shaderDataRaw,
const Workgroup& komputeWorkgroup = {},
const Constants& specializationConstants = {});
OpAlgoDispatch(std::vector<std::shared_ptr<Tensor>>& tensors,
std::shared_ptr<kp::Algorithm>& algorithm,
const std::vector<uint32_t>& 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> commandBuffer) override;
/**
@ -123,21 +101,6 @@ class OpAlgoBase : public OpBase
*/
virtual void postEval() override;
protected:
// -------------- NEVER OWNED RESOURCES
// -------------- OPTIONALLY OWNED RESOURCES
std::shared_ptr<Algorithm> 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<uint32_t> mShaderDataRaw; ///< Optional member variable which can be provided to contain either the raw shader content or the spirv binary content
virtual std::vector<uint32_t> fetchSpirvBinaryData();
};
} // End namespace kp

View file

@ -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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>> tensors,
const Workgroup& komputeWorkgroup = {});
OpAlgoLhsRhsOut(std::vector<std::shared_ptr<Tensor>>& tensors,
std::shared_ptr<Algorithm> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> commandBuffer) override;
/**
* Executes after the recorded commands are submitted, and performs a copy

View file

@ -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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>>& tensors)
OpBase(std::vector<std::shared_ptr<Tensor>>& tensors,
std::shared_ptr<Algorithm> 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<kp::Algorithm> algorithm() {
return this->mAlgorithm;
}
if (this->mFreeTensors) {
KP_LOG_DEBUG("Kompute OpBase freeing tensors");
for (std::shared_ptr<Tensor> 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<std::shared_ptr<kp::Tensor>> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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> 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>& 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<vk::CommandBuffer> 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<vk::PhysicalDevice>
mPhysicalDevice; ///< Vulkan Physical Device
std::shared_ptr<vk::Device> mDevice; ///< Vulkan Logical Device
std::shared_ptr<vk::CommandBuffer>
mCommandBuffer; ///< Vulkan Command Buffer
// -------------- OPTIONALLY OWNED RESOURCES
std::vector<std::shared_ptr<Tensor>>
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<std::shared_ptr<Tensor>> mTensors;
bool mManagesTensors = false;
std::shared_ptr<kp::Algorithm> mAlgorithm;
bool mManagesAlgorithm = false;
// -------------- ALWAYS OWNED RESOURCES
bool mIsInit;
};
} // End namespace kp

View file

@ -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<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>> tensors,
const Workgroup& komputeWorkgroup = {})
: OpAlgoBase(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup)
: OpAlgoCreate(physicalDevice, device, commandBuffer, tensors, "", komputeWorkgroup)
{
KP_LOG_DEBUG("Kompute OpMult constructor with params");

View file

@ -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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>> tensors);
OpTensorCopy(std::vector<std::shared_ptr<Tensor>> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> commandBuffer) override;
/**
* Does not perform any preEval commands.

View file

@ -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<std::shared_ptr<Tensor>>& 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> 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

View file

@ -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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>> tensors);
OpTensorSyncDevice(std::vector<std::shared_ptr<Tensor>> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> commandBuffer) override;
/**
* Does not perform any preEval commands.

View file

@ -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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> device,
std::shared_ptr<vk::CommandBuffer> commandBuffer,
std::vector<std::shared_ptr<Tensor>> tensors);
OpTensorSyncLocal(std::vector<std::shared_ptr<Tensor>> 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<vk::PhysicalDevice> physicalDevice,
std::shared_ptr<vk::Device> 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<vk::CommandBuffer> commandBuffer) override;
/**
* Does not perform any preEval commands.