mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Implement shader byte loading and caching in GuiRenderer and PipelineService
This commit is contained in:
@@ -667,6 +667,29 @@ void GuiRenderer::GenerateGuiGeometry(const std::vector<GuiCommand>& commands, u
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& GuiRenderer::LoadShaderBytes(const std::filesystem::path& path,
|
||||
VkShaderStageFlagBits stage) {
|
||||
const std::string key = path.string();
|
||||
auto cached = shaderSpirvCache_.find(key);
|
||||
if (cached != shaderSpirvCache_.end()) {
|
||||
if (logger_) {
|
||||
logger_->Trace("GuiRenderer", "LoadShaderBytes",
|
||||
"cacheHit=true, path=" + key +
|
||||
", bytes=" + std::to_string(cached->second.size()));
|
||||
}
|
||||
return cached->second;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> shaderBytes = ReadShaderFile(path, stage, logger_.get());
|
||||
auto inserted = shaderSpirvCache_.emplace(key, std::move(shaderBytes));
|
||||
if (logger_) {
|
||||
logger_->Trace("GuiRenderer", "LoadShaderBytes",
|
||||
"cacheHit=false, path=" + key +
|
||||
", bytes=" + std::to_string(inserted.first->second.size()));
|
||||
}
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
void GuiRenderer::CreatePipeline(VkRenderPass renderPass, VkExtent2D extent) {
|
||||
// Load shader modules
|
||||
const std::filesystem::path vertexShaderPath =
|
||||
@@ -681,8 +704,8 @@ void GuiRenderer::CreatePipeline(VkRenderPass renderPass, VkExtent2D extent) {
|
||||
", fragmentShader=" + fragmentShaderPath.string());
|
||||
}
|
||||
|
||||
auto vertShaderCode = ReadShaderFile(vertexShaderPath, VK_SHADER_STAGE_VERTEX_BIT, logger_.get());
|
||||
auto fragShaderCode = ReadShaderFile(fragmentShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT, logger_.get());
|
||||
const auto& vertShaderCode = LoadShaderBytes(vertexShaderPath, VK_SHADER_STAGE_VERTEX_BIT);
|
||||
const auto& fragShaderCode = LoadShaderBytes(fragmentShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
VkShaderModuleCreateInfo vertModuleInfo{};
|
||||
vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
|
||||
@@ -57,6 +57,8 @@ private:
|
||||
void CleanupBuffers();
|
||||
void UpdateFormat(VkFormat format);
|
||||
void GenerateGuiGeometry(const std::vector<GuiCommand>& commands, uint32_t width, uint32_t height);
|
||||
const std::vector<uint8_t>& LoadShaderBytes(const std::filesystem::path& path,
|
||||
VkShaderStageFlagBits stage);
|
||||
|
||||
VkDevice device_;
|
||||
VkPhysicalDevice physicalDevice_;
|
||||
@@ -83,6 +85,7 @@ private:
|
||||
uint32_t viewportWidth_ = 0;
|
||||
uint32_t viewportHeight_ = 0;
|
||||
std::unordered_map<std::string, ParsedSvg> svgCache_;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> shaderSpirvCache_;
|
||||
std::shared_ptr<IBufferService> bufferService_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
};
|
||||
|
||||
@@ -94,6 +94,7 @@ void PipelineService::RecreatePipelines(VkRenderPass renderPass, VkExtent2D exte
|
||||
void PipelineService::Cleanup() {
|
||||
logger_->Trace("PipelineService", "Cleanup");
|
||||
CleanupPipelines();
|
||||
shaderSpirvCache_.clear();
|
||||
|
||||
auto device = deviceService_->GetDevice();
|
||||
|
||||
@@ -306,7 +307,7 @@ void PipelineService::CreatePipelinesInternal(VkRenderPass renderPass, VkExtent2
|
||||
};
|
||||
|
||||
auto addStage = [&](VkShaderStageFlagBits stage, const std::string& path) {
|
||||
auto shaderCode = ReadShaderFile(path, stage);
|
||||
const auto& shaderCode = ReadShaderFile(path, stage);
|
||||
VkShaderModule shaderModule = CreateShaderModule(shaderCode);
|
||||
shaderModules.push_back(shaderModule);
|
||||
|
||||
@@ -407,7 +408,7 @@ bool PipelineService::HasShaderSource(const std::string& path) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> PipelineService::ReadShaderFile(const std::string& path, VkShaderStageFlagBits stage) {
|
||||
const std::vector<char>& PipelineService::ReadShaderFile(const std::string& path, VkShaderStageFlagBits stage) {
|
||||
logger_->Trace("PipelineService", "ReadShaderFile",
|
||||
"path=" + path + ", stage=" + std::to_string(static_cast<int>(stage)));
|
||||
|
||||
@@ -435,6 +436,16 @@ std::vector<char> PipelineService::ReadShaderFile(const std::string& path, VkSha
|
||||
throw std::runtime_error("Path is not a regular file: " + shaderPath.string());
|
||||
}
|
||||
|
||||
const std::string cacheKey = shaderPath.string() + "|" +
|
||||
std::to_string(static_cast<int>(stage));
|
||||
auto cached = shaderSpirvCache_.find(cacheKey);
|
||||
if (cached != shaderSpirvCache_.end()) {
|
||||
logger_->Trace("PipelineService", "ReadShaderFile",
|
||||
"cacheHit=true, bytes=" + std::to_string(cached->second.size()));
|
||||
return cached->second;
|
||||
}
|
||||
|
||||
std::vector<char> buffer;
|
||||
if (IsSpirvPath(shaderPath)) {
|
||||
std::ifstream file(shaderPath, std::ios::ate | std::ios::binary);
|
||||
if (!file) {
|
||||
@@ -443,7 +454,7 @@ std::vector<char> PipelineService::ReadShaderFile(const std::string& path, VkSha
|
||||
}
|
||||
|
||||
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||
std::vector<char> buffer(fileSize);
|
||||
buffer.resize(fileSize);
|
||||
|
||||
file.seekg(0);
|
||||
file.read(buffer.data(), static_cast<std::streamsize>(fileSize));
|
||||
@@ -451,38 +462,41 @@ std::vector<char> PipelineService::ReadShaderFile(const std::string& path, VkSha
|
||||
|
||||
logger_->Debug("Read shader file: " + shaderPath.string() +
|
||||
" (" + std::to_string(fileSize) + " bytes)");
|
||||
return buffer;
|
||||
} else {
|
||||
std::ifstream sourceFile(shaderPath);
|
||||
if (!sourceFile) {
|
||||
throw std::runtime_error("Failed to open shader source: " + shaderPath.string());
|
||||
}
|
||||
std::string source((std::istreambuf_iterator<char>(sourceFile)),
|
||||
std::istreambuf_iterator<char>());
|
||||
sourceFile.close();
|
||||
|
||||
shaderc::Compiler compiler;
|
||||
shaderc::CompileOptions options;
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||
|
||||
shaderc_shader_kind kind = ShadercKindFromStage(stage);
|
||||
auto result = compiler.CompileGlslToSpv(source, kind, shaderPath.string().c_str(), options);
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
std::string error = result.GetErrorMessage();
|
||||
logger_->Error("Shader compilation failed: " + shaderPath.string() + "\n" + error);
|
||||
throw std::runtime_error("Shader compilation failed: " + shaderPath.string() + "\n" + error);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> spirv(result.cbegin(), result.cend());
|
||||
buffer.resize(spirv.size() * sizeof(uint32_t));
|
||||
if (!buffer.empty()) {
|
||||
std::memcpy(buffer.data(), spirv.data(), buffer.size());
|
||||
}
|
||||
|
||||
logger_->Debug("Compiled shader: " + shaderPath.string() +
|
||||
" (" + std::to_string(buffer.size()) + " bytes)");
|
||||
}
|
||||
|
||||
std::ifstream sourceFile(shaderPath);
|
||||
if (!sourceFile) {
|
||||
throw std::runtime_error("Failed to open shader source: " + shaderPath.string());
|
||||
}
|
||||
std::string source((std::istreambuf_iterator<char>(sourceFile)),
|
||||
std::istreambuf_iterator<char>());
|
||||
sourceFile.close();
|
||||
|
||||
shaderc::Compiler compiler;
|
||||
shaderc::CompileOptions options;
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||
|
||||
shaderc_shader_kind kind = ShadercKindFromStage(stage);
|
||||
auto result = compiler.CompileGlslToSpv(source, kind, shaderPath.string().c_str(), options);
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
std::string error = result.GetErrorMessage();
|
||||
logger_->Error("Shader compilation failed: " + shaderPath.string() + "\n" + error);
|
||||
throw std::runtime_error("Shader compilation failed: " + shaderPath.string() + "\n" + error);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> spirv(result.cbegin(), result.cend());
|
||||
std::vector<char> buffer(spirv.size() * sizeof(uint32_t));
|
||||
if (!buffer.empty()) {
|
||||
std::memcpy(buffer.data(), spirv.data(), buffer.size());
|
||||
}
|
||||
|
||||
logger_->Debug("Compiled shader: " + shaderPath.string() +
|
||||
" (" + std::to_string(buffer.size()) + " bytes)");
|
||||
return buffer;
|
||||
auto inserted = shaderSpirvCache_.emplace(cacheKey, std::move(buffer));
|
||||
logger_->Trace("PipelineService", "ReadShaderFile",
|
||||
"cacheHit=false, bytes=" + std::to_string(inserted.first->second.size()));
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
|
||||
@@ -52,11 +52,13 @@ private:
|
||||
|
||||
// Helper methods
|
||||
VkShaderModule CreateShaderModule(const std::vector<char>& code);
|
||||
std::vector<char> ReadShaderFile(const std::string& path, VkShaderStageFlagBits stage);
|
||||
const std::vector<char>& ReadShaderFile(const std::string& path, VkShaderStageFlagBits stage);
|
||||
bool HasShaderSource(const std::string& path) const;
|
||||
void CreatePipelineLayout();
|
||||
void CreatePipelinesInternal(VkRenderPass renderPass, VkExtent2D extent);
|
||||
void CleanupPipelines();
|
||||
|
||||
std::unordered_map<std::string, std::vector<char>> shaderSpirvCache_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
|
||||
@@ -27,8 +27,8 @@ end
|
||||
function get_shader_paths()
|
||||
return {
|
||||
test = {
|
||||
vertex = "shaders/test.vert.spv",
|
||||
fragment = "shaders/test.frag.spv",
|
||||
vertex = "shaders/test.vert",
|
||||
fragment = "shaders/test.frag",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
@@ -97,8 +97,8 @@ int main() {
|
||||
auto testEntry = shaderMap.find("test");
|
||||
Assert(testEntry != shaderMap.end(), "shader map missing test entry", failures);
|
||||
if (testEntry != shaderMap.end()) {
|
||||
Assert(testEntry->second.vertex == "shaders/test.vert.spv", "vertex shader path", failures);
|
||||
Assert(testEntry->second.fragment == "shaders/test.frag.spv", "fragment shader path", failures);
|
||||
Assert(testEntry->second.vertex == "shaders/test.vert", "vertex shader path", failures);
|
||||
Assert(testEntry->second.fragment == "shaders/test.frag", "fragment shader path", failures);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "exception during tests: " << ex.what() << '\n';
|
||||
|
||||
Reference in New Issue
Block a user