mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Add support for additional shader types and enhance shader path handling
This commit is contained in:
@@ -37,6 +37,8 @@ class SDL3CppConan(ConanFile):
|
||||
self.requires("vulkan-headers/1.3.243.0")
|
||||
self.requires("vulkan-memory-allocator/3.3.0")
|
||||
self.requires("vulkan-validationlayers/1.3.243.0")
|
||||
self.requires("spirv-tools/1.4.313.0")
|
||||
self.requires("spirv-headers/1.4.313.0")
|
||||
self.requires("cpptrace/1.0.4")
|
||||
self.requires("ogg/1.3.5")
|
||||
self.requires("theora/1.1.1")
|
||||
|
||||
@@ -284,7 +284,7 @@ def msvc_quick(args: argparse.Namespace) -> None:
|
||||
def _compile_shaders(dry_run: bool) -> None:
|
||||
"""
|
||||
Compile GLSL shaders to SPIR-V format using glslangValidator.
|
||||
Compiles all .vert and .frag files in the shaders directory.
|
||||
Compiles .vert, .frag, .geom, .tesc, .tese, and .comp files in the shaders directory.
|
||||
"""
|
||||
shaders_dir = Path("shaders")
|
||||
if not shaders_dir.exists():
|
||||
@@ -309,7 +309,14 @@ def _compile_shaders(dry_run: bool) -> None:
|
||||
return
|
||||
|
||||
print("\n=== Compiling Shaders ===")
|
||||
shader_files = list(shaders_dir.glob("*.vert")) + list(shaders_dir.glob("*.frag"))
|
||||
shader_files = (
|
||||
list(shaders_dir.glob("*.vert"))
|
||||
+ list(shaders_dir.glob("*.frag"))
|
||||
+ list(shaders_dir.glob("*.geom"))
|
||||
+ list(shaders_dir.glob("*.tesc"))
|
||||
+ list(shaders_dir.glob("*.tese"))
|
||||
+ list(shaders_dir.glob("*.comp"))
|
||||
)
|
||||
|
||||
for shader_file in shader_files:
|
||||
output_file = shader_file.with_suffix(shader_file.suffix + ".spv")
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
@@ -248,42 +249,96 @@ void PipelineService::CreatePipelinesInternal(VkRenderPass renderPass, VkExtent2
|
||||
"\n\nPlease ensure shader files are compiled and present in the shaders directory.");
|
||||
}
|
||||
|
||||
auto vertShaderCode = ReadShaderFile(paths.vertex);
|
||||
auto fragShaderCode = ReadShaderFile(paths.fragment);
|
||||
bool hasGeometry = !paths.geometry.empty();
|
||||
bool hasTessControl = !paths.tessControl.empty();
|
||||
bool hasTessEval = !paths.tessEval.empty();
|
||||
|
||||
VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
|
||||
VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertStageInfo{};
|
||||
vertStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertStageInfo.module = vertShaderModule;
|
||||
vertStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragStageInfo{};
|
||||
fragStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragStageInfo.module = fragShaderModule;
|
||||
fragStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[] = {vertStageInfo, fragStageInfo};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = pipelineInfo;
|
||||
pipelineCreateInfo.stageCount = 2;
|
||||
pipelineCreateInfo.pStages = shaderStages;
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr,
|
||||
&pipeline) != VK_SUCCESS) {
|
||||
vkDestroyShaderModule(device, fragShaderModule, nullptr);
|
||||
vkDestroyShaderModule(device, vertShaderModule, nullptr);
|
||||
throw std::runtime_error("Failed to create graphics pipeline for shader: " + key);
|
||||
if (hasGeometry && !std::filesystem::exists(paths.geometry)) {
|
||||
throw std::runtime_error(
|
||||
"Geometry shader not found: " + paths.geometry +
|
||||
"\n\nShader key: " + key +
|
||||
"\n\nPlease ensure shader files are compiled and present in the shaders directory.");
|
||||
}
|
||||
if (hasTessControl != hasTessEval) {
|
||||
throw std::runtime_error(
|
||||
"Tessellation shaders require both 'tesc' and 'tese' paths. Shader key: " + key);
|
||||
}
|
||||
if (hasTessControl && !std::filesystem::exists(paths.tessControl)) {
|
||||
throw std::runtime_error(
|
||||
"Tessellation control shader not found: " + paths.tessControl +
|
||||
"\n\nShader key: " + key +
|
||||
"\n\nPlease ensure shader files are compiled and present in the shaders directory.");
|
||||
}
|
||||
if (hasTessEval && !std::filesystem::exists(paths.tessEval)) {
|
||||
throw std::runtime_error(
|
||||
"Tessellation evaluation shader not found: " + paths.tessEval +
|
||||
"\n\nShader key: " + key +
|
||||
"\n\nPlease ensure shader files are compiled and present in the shaders directory.");
|
||||
}
|
||||
|
||||
pipelines_[key] = pipeline;
|
||||
std::vector<VkShaderModule> shaderModules;
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
|
||||
shaderStages.reserve(2 + (hasGeometry ? 1 : 0) + (hasTessControl ? 2 : 0));
|
||||
|
||||
vkDestroyShaderModule(device, fragShaderModule, nullptr);
|
||||
vkDestroyShaderModule(device, vertShaderModule, nullptr);
|
||||
auto destroyShaderModules = [&]() {
|
||||
for (VkShaderModule module : shaderModules) {
|
||||
vkDestroyShaderModule(device, module, nullptr);
|
||||
}
|
||||
shaderModules.clear();
|
||||
};
|
||||
|
||||
auto addStage = [&](VkShaderStageFlagBits stage, const std::string& path) {
|
||||
auto shaderCode = ReadShaderFile(path);
|
||||
VkShaderModule shaderModule = CreateShaderModule(shaderCode);
|
||||
shaderModules.push_back(shaderModule);
|
||||
|
||||
VkPipelineShaderStageCreateInfo stageInfo{};
|
||||
stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stageInfo.stage = stage;
|
||||
stageInfo.module = shaderModule;
|
||||
stageInfo.pName = "main";
|
||||
shaderStages.push_back(stageInfo);
|
||||
};
|
||||
|
||||
try {
|
||||
addStage(VK_SHADER_STAGE_VERTEX_BIT, paths.vertex);
|
||||
if (hasTessControl) {
|
||||
addStage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, paths.tessControl);
|
||||
addStage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, paths.tessEval);
|
||||
}
|
||||
if (hasGeometry) {
|
||||
addStage(VK_SHADER_STAGE_GEOMETRY_BIT, paths.geometry);
|
||||
}
|
||||
addStage(VK_SHADER_STAGE_FRAGMENT_BIT, paths.fragment);
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = inputAssembly;
|
||||
VkPipelineTessellationStateCreateInfo tessellationState{};
|
||||
bool useTessellation = hasTessControl && hasTessEval;
|
||||
if (useTessellation) {
|
||||
inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
||||
tessellationState.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
tessellationState.patchControlPoints = 3;
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = pipelineInfo;
|
||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||
pipelineCreateInfo.pTessellationState = useTessellation ? &tessellationState : nullptr;
|
||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||
pipelineCreateInfo.pStages = shaderStages.data();
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr,
|
||||
&pipeline) != VK_SUCCESS) {
|
||||
destroyShaderModules();
|
||||
throw std::runtime_error("Failed to create graphics pipeline for shader: " + key);
|
||||
}
|
||||
|
||||
pipelines_[key] = pipeline;
|
||||
destroyShaderModules();
|
||||
} catch (...) {
|
||||
destroyShaderModules();
|
||||
throw;
|
||||
}
|
||||
|
||||
logger_->Debug("Created pipeline: " + key);
|
||||
}
|
||||
|
||||
@@ -82,30 +82,52 @@ ShaderPaths ShaderScriptService::ReadShaderPathsTable(lua_State* L, int index) c
|
||||
ShaderPaths paths;
|
||||
int absIndex = lua_absindex(L, index);
|
||||
|
||||
lua_getfield(L, absIndex, "vertex");
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (logger_) {
|
||||
logger_->Error("Shader path 'vertex' must be a string");
|
||||
auto readRequiredPath = [&](const char* field, std::string& target) {
|
||||
lua_getfield(L, absIndex, field);
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (logger_) {
|
||||
logger_->Error("Shader path '" + std::string(field) + "' must be a string");
|
||||
}
|
||||
throw std::runtime_error("Shader path '" + std::string(field) + "' must be a string");
|
||||
}
|
||||
throw std::runtime_error("Shader path 'vertex' must be a string");
|
||||
}
|
||||
paths.vertex = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, absIndex, "fragment");
|
||||
if (!lua_isstring(L, -1)) {
|
||||
target = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (logger_) {
|
||||
logger_->Error("Shader path 'fragment' must be a string");
|
||||
}
|
||||
throw std::runtime_error("Shader path 'fragment' must be a string");
|
||||
}
|
||||
paths.fragment = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
};
|
||||
|
||||
paths.vertex = ResolveShaderPath(paths.vertex);
|
||||
paths.fragment = ResolveShaderPath(paths.fragment);
|
||||
auto readOptionalPath = [&](const char* field, std::string& target) {
|
||||
lua_getfield(L, absIndex, field);
|
||||
if (lua_isstring(L, -1)) {
|
||||
target = lua_tostring(L, -1);
|
||||
} else if (!lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (logger_) {
|
||||
logger_->Error("Shader path '" + std::string(field) + "' must be a string when provided");
|
||||
}
|
||||
throw std::runtime_error("Shader path '" + std::string(field) + "' must be a string when provided");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
};
|
||||
|
||||
readRequiredPath("vertex", paths.vertex);
|
||||
readRequiredPath("fragment", paths.fragment);
|
||||
readOptionalPath("geometry", paths.geometry);
|
||||
readOptionalPath("tesc", paths.tessControl);
|
||||
readOptionalPath("tese", paths.tessEval);
|
||||
readOptionalPath("compute", paths.compute);
|
||||
|
||||
auto resolveIfPresent = [&](std::string& value) {
|
||||
if (!value.empty()) {
|
||||
value = ResolveShaderPath(value);
|
||||
}
|
||||
};
|
||||
|
||||
resolveIfPresent(paths.vertex);
|
||||
resolveIfPresent(paths.fragment);
|
||||
resolveIfPresent(paths.geometry);
|
||||
resolveIfPresent(paths.tessControl);
|
||||
resolveIfPresent(paths.tessEval);
|
||||
resolveIfPresent(paths.compute);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ struct GraphicsConfig {
|
||||
struct ShaderPaths {
|
||||
std::string vertex;
|
||||
std::string fragment;
|
||||
std::string geometry;
|
||||
std::string tessControl;
|
||||
std::string tessEval;
|
||||
std::string compute;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -35,4 +39,4 @@ struct RenderCommand {
|
||||
std::array<float, 16> modelMatrix;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
} // namespace sdl3cpp::services
|
||||
|
||||
Reference in New Issue
Block a user