Skip to content

Commit

Permalink
Feat: Vulkan shader compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
brenocq committed Nov 30, 2023
1 parent 30356e8 commit 7cb62e5
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 69 deletions.
2 changes: 2 additions & 0 deletions src/atta/cmakeConfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

#ifdef ATTA_OS_WEB
#define ATTA_DIR "/"
#define ATTA_BUILD_DIR "/"
#else
#define ATTA_DIR "@CMAKE_SOURCE_DIR@"
#define ATTA_BUILD_DIR "@CMAKE_BINARY_DIR@"
#endif

#define ATTA_VERSION "@CMAKE_PROJECT_VERSION@"
Expand Down
2 changes: 2 additions & 0 deletions src/atta/file/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ fs::path solveResourcePath(fs::path relativePath, bool mustExist) { return Manag

std::vector<fs::path> getResourcePaths() { return Manager::getInstance().getResourcePathsImpl(); }

fs::path getBuildPath() { return Manager::getInstance().getBuildPathImpl(); }

std::vector<fs::path> getDirectoryFilesRecursive(fs::path directory) { return Manager::getInstance().getDirectoryFilesRecursiveImpl(directory); }

fs::path getDefaultProjectFolder() { return Manager::getInstance()._defaultProjectFolder; }
Expand Down
1 change: 1 addition & 0 deletions src/atta/file/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ std::shared_ptr<Project> getProject();
// The return is the absolute resource path
fs::path solveResourcePath(fs::path relativePath, bool mustExist = true);
std::vector<fs::path> getResourcePaths();
fs::path getBuildPath();
std::vector<fs::path> getDirectoryFilesRecursive(fs::path directory);
fs::path getDefaultProjectFolder();

Expand Down
20 changes: 11 additions & 9 deletions src/atta/file/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ bool Manager::openProjectImpl(fs::path projectFile) {
_projectSerializer = std::make_shared<ProjectSerializer>(_project);

// Create .atta file if it does not exists yet
if(!fs::exists(projectFile))
if (!fs::exists(projectFile))
_projectSerializer->serialize();

// Clear components and read project file
Expand Down Expand Up @@ -167,6 +167,8 @@ std::vector<fs::path> Manager::getResourcePathsImpl() const {
return {fs::path(ATTA_DIR) / "resources"};
}

fs::path Manager::getBuildPathImpl() const { return fs::path(ATTA_BUILD_DIR); }

std::vector<fs::path> Manager::getDirectoryFilesRecursiveImpl(fs::path directory) {
std::vector<fs::path> files;
#ifndef ATTA_OS_WEB
Expand Down Expand Up @@ -203,14 +205,14 @@ std::vector<fs::path> Manager::getDirectoryFilesRecursiveImpl(fs::path directory

void Manager::onSimulationStateChange(event::Event& event) {
switch (event.getType()) {
case event::SimulationStart::type:
_simulationRunning = true;
break;
case event::SimulationStop::type:
_simulationRunning = false;
break;
default:
LOG_WARN("file::Manager", "Unknown simulation event");
case event::SimulationStart::type:
_simulationRunning = true;
break;
case event::SimulationStop::type:
_simulationRunning = false;
break;
default:
LOG_WARN("file::Manager", "Unknown simulation event");
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/atta/file/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Manager final {
friend std::shared_ptr<Project> getProject();
friend fs::path solveResourcePath(fs::path relativePath, bool mustExist);
friend std::vector<fs::path> getResourcePaths();
friend fs::path getBuildPath();
friend std::vector<fs::path> getDirectoryFilesRecursive(fs::path directory);
friend fs::path getDefaultProjectFolder();
friend void update();
Expand All @@ -44,6 +45,7 @@ class Manager final {

fs::path solveResourcePathImpl(fs::path relativePath, bool mustExist);
std::vector<fs::path> getResourcePathsImpl() const;
fs::path getBuildPathImpl() const;
std::vector<fs::path> getDirectoryFilesRecursiveImpl(fs::path directory);

// Handle events
Expand Down
154 changes: 114 additions & 40 deletions src/atta/graphics/apis/vulkan/shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <atta/graphics/apis/vulkan/shader.h>

#include <atta/file/manager.h>
#include <regex>

namespace atta::graphics::vk {

Expand All @@ -17,8 +18,9 @@ Shader::Shader(const fs::path& file) : gfx::Shader(file), _device(common::getDev
}

Shader::~Shader() {
if (_shader != VK_NULL_HANDLE)
vkDestroyShaderModule(_device->getHandle(), _shader, nullptr);
for (auto& [type, shader] : _shaders)
if (shader != VK_NULL_HANDLE)
vkDestroyShaderModule(_device->getHandle(), shader, nullptr);
}

void Shader::setBool(const char* name, bool b) {}
Expand All @@ -41,33 +43,105 @@ std::vector<VkPipelineShaderStageCreateInfo> Shader::getShaderStages() const {
// return result;
}

std::string Shader::generateApiCode(ShaderType type, std::string iCode) { return iCode; }
std::string Shader::generateApiCode(ShaderType type, std::string iCode) {
std::string apiCode;

// GLSL version
apiCode += "#version 450\n";

// Uniform buffer
if (!_uniformLayout.getElements().empty()) {
apiCode += "layout(set = 0, binding = 0) uniform UniformBufferObject {\n";
for (const BufferLayout::Element& element : _uniformLayout.getElements())
if (element.type != BufferLayout::Element::Type::SAMPLER_2D && element.type != BufferLayout::Element::Type::SAMPLER_CUBE)
apiCode += std::string(" ") + BufferLayout::Element::typeToString(element.type) + " " + element.name + ";\n";
apiCode += "} ubo;\n";
}

// TODO Uniform image sampler

// Remove uniform declarations from iCode
std::regex pattern(R"(^uniform .*\n?)", std::regex_constants::multiline);
std::string iCodeFix = std::regex_replace(iCode, pattern, "");

// Append "ubo." when using uniform in iCode
for (const BufferLayout::Element& element : _uniformLayout.getElements()) {
if (element.type != BufferLayout::Element::Type::SAMPLER_2D && element.type != BufferLayout::Element::Type::SAMPLER_CUBE) {
std::string patternStr = "\\b" + element.name + "\\b";
std::regex pattern(patternStr);
iCodeFix = std::regex_replace(iCodeFix, pattern, "ubo." + element.name);
}
}

// Add corrected iCode to apiCode
apiCode += iCodeFix;

// Add location to in/out
std::istringstream iss(apiCode);
std::ostringstream oss;
std::string line;
int inNum = 0, outNum = 0;
while (std::getline(iss, line)) {
if (std::regex_search(line, std::regex(R"(^in\s)"))) {
oss << "layout(location = " << inNum++ << ") " << line << "\n";
} else if (std::regex_search(line, std::regex(R"(^out\s)"))) {
oss << "layout(location = " << outNum++ << ") " << line << "\n";
} else {
oss << line << "\n";
}
}
apiCode = oss.str();

// Add location to out

// LOG_DEBUG("gfx::vk::Shader", "[y]$0\n[r]iCode:\n[w]$1\n[r]apiCode:\n[w]$2", type, iCode, apiCode);

return apiCode;
}

void Shader::compile() {
// fs::path filepath = file::solveResourcePath(_filepath);
// std::string in = fs::absolute(filepath).string();
// std::string out = in + ".spv";
// LOG_DEBUG("sf", "$0 $1", in, out);
// if (!runCommand("glslc " + in + " -o " + out))
// return;

//// Parse GLSL
// parseGlsl(readFile(fs::path(in)));

//// Create vulkan
// std::string spirvCode = readFile(fs::path(out));
// VkShaderModuleCreateInfo createInfo{};
// createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
// createInfo.codeSize = spirvCode.size();
// createInfo.pCode = (const uint32_t*)spirvCode.data();

// if (vkCreateShaderModule(_device->getHandle(), &createInfo, nullptr, &_shader) != VK_SUCCESS)
// LOG_ERROR("gfx::vk::Shader", "Failed to create shader!");
for (const auto& [type, shaderCode] : _shaderCodes) {
// Save shader
std::array<std::string, 3> ext = {".vert", ".geom", ".frag"};
fs::path in = file::getBuildPath() / "shaders" / (_file.stem().string() + ext[int(type)]);
fs::create_directories(in.parent_path());
std::ofstream file(in);
if (file.is_open()) {
file << shaderCode.apiCode;
file.close();
LOG_DEBUG("shader", "Saved $0 code", in);
} else {
LOG_ERROR("gfx::vk::Shader", "Failed to save shader code to [w]$0[] when compiling [w]$1[]", in, _file.string());
continue;
}

// Compile shader
fs::path out = in.string() + ".spv";
LOG_DEBUG("Shader", "in $0 out $1", in, out);
if (!runCommand("glslc " + in.string() + " -o " + out.string()))
return;

// Load shader and create shader module
std::string spirvCode = readFile(fs::path(out));
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = spirvCode.size();
createInfo.pCode = (const uint32_t*)spirvCode.data();

VkShaderModule shader;
if (vkCreateShaderModule(_device->getHandle(), &createInfo, nullptr, &shader) != VK_SUCCESS)
LOG_ERROR("gfx::vk::Shader", "Failed to create shader!");
else
_shaders[type] = shader;
}
}

void Shader::bind() {}

VkShaderModule Shader::getHandle() const { return _shader; }
VkShaderModule Shader::getHandle() const {
// TODO
return {};
}

VkPipelineShaderStageCreateInfo Shader::getShaderStage() const {
// VkShaderStageFlagBits stage = convertFileToShaderStage(_filepath);
Expand Down Expand Up @@ -109,27 +183,27 @@ bool Shader::runCommand(std::string cmd) {
output += buffer.data();

if (output != "") {
// LOG_ERROR("gfx::vk::Shader", "Failed to compile shader [w]$0[]: $1", _filepath.string(), output);
LOG_ERROR("gfx::vk::Shader", "Failed to compile shader [w]$0[]:\n$1", _file.string(), output);
return false;
}
return true;
}

// std::string Shader::readFile(const fs::path& file) {
// std::ifstream f(file, std::ios::ate | std::ios::binary);
//
// if (!f.is_open()) {
// LOG_ERROR("gfx::vk::Shader", "Failed to open file: [w]$0[]!", file.string());
// return {};
// }
//
// size_t fileSize = (size_t)f.tellg();
// std::vector<char> buffer(fileSize);
// f.seekg(0);
// f.read(buffer.data(), fileSize);
// f.close();
//
// return std::string(buffer.begin(), buffer.end());
// }
std::string Shader::readFile(const fs::path& file) {
std::ifstream f(file, std::ios::ate | std::ios::binary);

if (!f.is_open()) {
LOG_ERROR("gfx::vk::Shader", "Failed to open file: [w]$0[]!", file.string());
return {};
}

size_t fileSize = (size_t)f.tellg();
std::vector<char> buffer(fileSize);
f.seekg(0);
f.read(buffer.data(), fileSize);
f.close();

return std::string(buffer.begin(), buffer.end());
}

} // namespace atta::graphics::vk
4 changes: 2 additions & 2 deletions src/atta/graphics/apis/vulkan/shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ class Shader final : public gfx::Shader {
static VkShaderStageFlagBits convertFileToShaderStage(const fs::path& filepath);

bool runCommand(std::string cmd);
// std::string readFile(const fs::path& file);
std::string readFile(const fs::path& file);

std::shared_ptr<Device> _device;
VkShaderModule _shader;
std::map<ShaderType, VkShaderModule> _shaders;
};

} // namespace atta::graphics::vk
Expand Down
2 changes: 1 addition & 1 deletion src/atta/graphics/shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void Shader::processASL() {
ShaderCode shaderCode;
shaderCode.iCode = removeUnusedFunctions(generateICode(type, _aslCode));
shaderCode.apiCode = "";
LOG_DEBUG("Shader", "[y]$0\n[w]$1", type, shaderCode.iCode);
// LOG_DEBUG("gfx::Shader", "[y]$0\n[w]$1", type, shaderCode.iCode);
_shaderCodes[type] = shaderCode;
}
}
Expand Down
36 changes: 19 additions & 17 deletions src/atta/graphics/shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ class Shader {
std::string apiCode; ///< Graphics API specific code
};

/**
* @brief Process intermediate code to generate API specific code.
*
* It will mainly substitute placeholders with Graphics API specific code.
*
* @param type Shader type
* @param iCode Intermediate code
*
* @return API specific code
*/
virtual std::string generateApiCode(ShaderType type, std::string iCode) = 0;

/**
* @brief Compile graphics API specific code
*/
virtual void compile() = 0;

private:
/**
* @brief Process .asl file to generate ICode
*
Expand Down Expand Up @@ -111,23 +129,7 @@ class Shader {
*/
void populateUniformLayout();

/**
* @brief Process intermediate code to generate API specific code.
*
* It will mainly substitute placeholders with Graphics API specific code.
*
* @param type Shader type
* @param iCode Intermediate code
*
* @return API specific code
*/
virtual std::string generateApiCode(ShaderType type, std::string iCode) = 0;

/**
* @brief Compile graphics API specific code
*/
virtual void compile() = 0;

protected:
fs::path _file;
std::string _aslCode;

Expand Down

0 comments on commit 7cb62e5

Please sign in to comment.