feat(shader): Enhance shader creation for OpenGL and Vulkan by adding SPIRV wrapping and improved error logging

This commit is contained in:
2026-01-07 10:27:51 +00:00
parent c7a5e77bea
commit 36d8a40fcc
4 changed files with 105 additions and 24 deletions

View File

@@ -18,7 +18,6 @@
}
],
"include": [
"build/build/Release/generators/CMakePresets.json",
"build-ninja/build/Release/generators/CMakePresets.json"
]
}

0
scripts/dev_commands.py Normal file → Executable file
View File

View File

@@ -668,36 +668,79 @@ std::vector<uint8_t> BgfxGraphicsBackend::ReadShaderSource(const std::string& pa
bgfx::ShaderHandle BgfxGraphicsBackend::CreateShader(const std::string& label,
const std::string& source,
bool isVertex) const {
const bgfx::RendererType::Enum rendererType = bgfx::getRendererType();
if (logger_) {
logger_->Trace("BgfxGraphicsBackend", "CreateShader",
"label=" + label +
", renderer=" + RendererTypeName(rendererType) +
", sourceLength=" + std::to_string(source.size()));
}
// For OpenGL, bgfx expects raw GLSL source (no wrapper needed)
// For Vulkan/Metal/DX, bgfx expects SPIRV wrapped in binary format
const bool isOpenGL = (rendererType == bgfx::RendererType::OpenGL ||
rendererType == bgfx::RendererType::OpenGLES);
if (isOpenGL) {
// For OpenGL: Just copy GLSL source directly
const uint32_t sourceSize = static_cast<uint32_t>(source.size());
const bgfx::Memory* mem = bgfx::copy(source.c_str(), sourceSize + 1); // +1 for null terminator
bgfx::ShaderHandle handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle) && logger_) {
logger_->Error("bgfx::createShader failed for " + label +
" (renderer=" + RendererTypeName(rendererType) + ")");
}
return handle;
}
// For Vulkan/Metal/DX: Compile to SPIRV and wrap in bgfx binary format
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1);
options.SetAutoBindUniforms(true);
options.SetAutoMapLocations(true);
if (logger_) {
logger_->Trace("BgfxGraphicsBackend", "CreateShader",
"label=" + label +
", renderer=" + RendererTypeName(bgfx::getRendererType()) +
", sourceLength=" + std::to_string(source.size()));
}
shaderc_shader_kind kind = isVertex ? shaderc_vertex_shader : shaderc_fragment_shader;
auto result = compiler.CompileGlslToSpv(source, kind, label.c_str(), options);
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
std::string error = result.GetErrorMessage();
if (logger_) {
logger_->Error("Bgfx shader compilation failed: " + label + "\n" + error);
logger_->Error("Shader GLSL->SPIRV compilation failed: " + label + "\n" + error);
}
throw std::runtime_error("Bgfx shader compilation failed: " + label + "\n" + error);
throw std::runtime_error("Shader GLSL->SPIRV compilation failed: " + label + "\n" + error);
}
std::vector<uint32_t> spirv(result.cbegin(), result.cend());
const bgfx::Memory* mem = bgfx::copy(spirv.data(),
static_cast<uint32_t>(spirv.size() * sizeof(uint32_t)));
// Wrap SPIRV with bgfx binary format: magic + hashes + uniformCount + spirvSize + spirv + nul
constexpr uint8_t kBgfxShaderVersion = 11;
constexpr uint32_t kMagicVSH = ('V') | ('S' << 8) | ('H' << 16) | (kBgfxShaderVersion << 24);
constexpr uint32_t kMagicFSH = ('F') | ('S' << 8) | ('H' << 16) | (kBgfxShaderVersion << 24);
const uint32_t magic = isVertex ? kMagicVSH : kMagicFSH;
const uint32_t inputHash = static_cast<uint32_t>(std::hash<std::string>{}(source));
const uint32_t spirvSize = static_cast<uint32_t>(spirv.size() * sizeof(uint32_t));
const uint16_t uniformCount = 0;
const uint32_t totalSize = 4 + 4 + 4 + 2 + 4 + spirvSize + 1;
const bgfx::Memory* mem = bgfx::alloc(totalSize);
uint8_t* data = mem->data;
uint32_t offset = 0;
std::memcpy(data + offset, &magic, 4); offset += 4;
std::memcpy(data + offset, &inputHash, 4); offset += 4;
std::memcpy(data + offset, &inputHash, 4); offset += 4;
std::memcpy(data + offset, &uniformCount, 2); offset += 2;
std::memcpy(data + offset, &spirvSize, 4); offset += 4;
std::memcpy(data + offset, spirv.data(), spirvSize); offset += spirvSize;
data[offset] = 0;
bgfx::ShaderHandle handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle) && logger_) {
logger_->Error("BgfxGraphicsBackend::CreateShader: Failed to create shader handle for " + label);
logger_->Error("bgfx::createShader failed for " + label +
" (renderer=" + RendererTypeName(rendererType) + ", spirvSize=" + std::to_string(spirv.size()) + " words)");
}
return handle;
}

View File

@@ -855,19 +855,37 @@ bgfx::ProgramHandle BgfxGuiService::CreateProgram(const char* vertexSource,
bgfx::ShaderHandle BgfxGuiService::CreateShader(const std::string& label,
const std::string& source,
bool isVertex) const {
const bgfx::RendererType::Enum rendererType = bgfx::getRendererType();
if (logger_) {
logger_->Trace("BgfxGuiService", "CreateShader",
"label=" + label +
", renderer=" + std::string(RendererTypeName(rendererType)) +
", sourceLength=" + std::to_string(source.size()));
}
const bool isOpenGL = (rendererType == bgfx::RendererType::OpenGL ||
rendererType == bgfx::RendererType::OpenGLES);
if (isOpenGL) {
// For OpenGL: Just copy GLSL source directly
const uint32_t sourceSize = static_cast<uint32_t>(source.size());
const bgfx::Memory* mem = bgfx::copy(source.c_str(), sourceSize + 1);
bgfx::ShaderHandle handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle) && logger_) {
logger_->Error("BgfxGuiService: Failed to create shader handle for " + label);
}
return handle;
}
// For Vulkan/Metal/DX: Compile to SPIRV and wrap in bgfx binary format
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1);
options.SetAutoBindUniforms(true);
options.SetAutoMapLocations(true);
if (logger_) {
logger_->Trace("BgfxGuiService", "CreateShader",
"label=" + label +
", renderer=" + std::string(RendererTypeName(bgfx::getRendererType())) +
", sourceLength=" + std::to_string(source.size()));
}
shaderc_shader_kind kind = isVertex ? shaderc_vertex_shader : shaderc_fragment_shader;
auto result = compiler.CompileGlslToSpv(source, kind, label.c_str(), options);
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
@@ -878,11 +896,32 @@ bgfx::ShaderHandle BgfxGuiService::CreateShader(const std::string& label,
}
std::vector<uint32_t> spirv(result.cbegin(), result.cend());
const bgfx::Memory* mem = bgfx::copy(spirv.data(),
static_cast<uint32_t>(spirv.size() * sizeof(uint32_t)));
// Wrap SPIRV with bgfx binary format
constexpr uint8_t kBgfxShaderVersion = 11;
constexpr uint32_t kMagicVSH = ('V') | ('S' << 8) | ('H' << 16) | (kBgfxShaderVersion << 24);
constexpr uint32_t kMagicFSH = ('F') | ('S' << 8) | ('H' << 16) | (kBgfxShaderVersion << 24);
const uint32_t magic = isVertex ? kMagicVSH : kMagicFSH;
const uint32_t inputHash = static_cast<uint32_t>(std::hash<std::string>{}(source));
const uint32_t spirvSize = static_cast<uint32_t>(spirv.size() * sizeof(uint32_t));
const uint16_t uniformCount = 0;
const uint32_t totalSize = 4 + 4 + 4 + 2 + 4 + spirvSize + 1;
const bgfx::Memory* mem = bgfx::alloc(totalSize);
uint8_t* data = mem->data;
uint32_t offset = 0;
std::memcpy(data + offset, &magic, 4); offset += 4;
std::memcpy(data + offset, &inputHash, 4); offset += 4;
std::memcpy(data + offset, &inputHash, 4); offset += 4;
std::memcpy(data + offset, &uniformCount, 2); offset += 2;
std::memcpy(data + offset, &spirvSize, 4); offset += 4;
std::memcpy(data + offset, spirv.data(), spirvSize); offset += spirvSize;
data[offset] = 0;
bgfx::ShaderHandle handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle) && logger_) {
logger_->Error("BgfxGuiService::CreateShader: Failed to create shader handle for " + label);
logger_->Error("BgfxGuiService: Failed to create shader handle for " + label);
}
return handle;
}