From 08d06dbe6073fe5785190aef71f53c4e927921ea Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Wed, 7 Jan 2026 20:17:56 +0000 Subject: [PATCH] feat(shader): Refactor shader compilation process to use BgfxShaderCompiler and streamline error handling --- CMakeLists.txt | 2 + src/bgfx_tools/shaderc/shaderc_mem.cpp | 64 +- src/bgfx_tools/shaderc/shaderc_metal.cpp | 16 +- src/bgfx_tools/shaderc/shaderc_spirv.cpp | 36 +- src/bgfx_tools/shaderc/shaderc_utils.cpp | 72 +++ src/bgfx_tools/src/shader.h | 73 +++ src/bgfx_tools/src/shader_spirv.h | 659 ++++++++++++++++++++ src/services/impl/bgfx_graphics_backend.cpp | 66 +- 8 files changed, 894 insertions(+), 94 deletions(-) create mode 100644 src/bgfx_tools/src/shader.h create mode 100644 src/bgfx_tools/src/shader_spirv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 35f6c5a..0bf4ef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,8 @@ target_include_directories(shaderc_local PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src/bgfx_deps/glslang" "${CMAKE_CURRENT_SOURCE_DIR}/src/bgfx_deps/glslang/Include" "${CMAKE_CURRENT_SOURCE_DIR}/src/bgfx_deps/glslang/glslang/Public") +target_include_directories(shaderc_local PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src") if(TARGET bgfx::bx) target_link_libraries(shaderc_local PUBLIC bgfx::bx) endif() diff --git a/src/bgfx_tools/shaderc/shaderc_mem.cpp b/src/bgfx_tools/shaderc/shaderc_mem.cpp index dff3eb0..35cd3f4 100644 --- a/src/bgfx_tools/shaderc/shaderc_mem.cpp +++ b/src/bgfx_tools/shaderc/shaderc_mem.cpp @@ -10,6 +10,11 @@ namespace { +constexpr uint8_t kShaderBinVersion = 11; +constexpr uint32_t kChunkMagicVsh = BX_MAKEFOURCC('V', 'S', 'H', kShaderBinVersion); +constexpr uint32_t kChunkMagicFsh = BX_MAKEFOURCC('F', 'S', 'H', kShaderBinVersion); +constexpr uint32_t kChunkMagicCsh = BX_MAKEFOURCC('C', 'S', 'H', kShaderBinVersion); + // Writer that captures binary output into a std::vector class MemoryWriter : public bx::FileWriter { @@ -30,6 +35,28 @@ private: std::vector m_buffer; }; +uint32_t ChunkMagicForType(char shaderType) { + switch (shaderType) { + case 'v': + return kChunkMagicVsh; + case 'f': + return kChunkMagicFsh; + case 'c': + return kChunkMagicCsh; + default: + return kChunkMagicFsh; + } +} + +void WriteShaderHeader(MemoryWriter& writer, char shaderType) { + bx::ErrorAssert err; + const uint32_t magic = ChunkMagicForType(shaderType); + const uint32_t hash = 0; + bx::write(&writer, magic, &err); + bx::write(&writer, hash, &err); + bx::write(&writer, hash, &err); +} + } // anonymous int shaderc_compile_from_memory(const char* source, size_t source_len, const char* profile, @@ -53,25 +80,27 @@ int shaderc_compile_from_memory(const char* source, size_t source_len, const cha opts.shaderType = 'f'; } + opts.profile = "glsl"; // Use SPIR-V target by default (matches CLI's typical default for desktop) const uint32_t spirvVersion = 1010; - MemoryWriter writer; - bx::ErrorAssert err; - bx::WriterI* messageWriter = &writer; // reuse writer for messages as well + MemoryWriter shaderWriter; + MemoryWriter messageWriter; + bx::WriterI* messageOut = &messageWriter; const std::string code(source, source_len); - bool ok = bgfx::compileSPIRVShader(opts, spirvVersion, code, &writer, messageWriter); + WriteShaderHeader(shaderWriter, opts.shaderType); + bool ok = bgfx::compileSPIRVShader(opts, spirvVersion, code, &shaderWriter, messageOut); if (!ok) { // capture any message buffer - const auto& msg = writer.data(); + const auto& msg = messageWriter.data(); std::string s(msg.begin(), msg.end()); if (out_error) *out_error = strdup(s.c_str()); return -1; } - const auto& out = writer.data(); + const auto& out = shaderWriter.data(); if (out.empty()) { if (out_error) *out_error = strdup("shaderc: empty output"); return -1; @@ -120,20 +149,27 @@ int shaderc_compile_from_memory_with_target(const char* source, size_t source_le opts.shaderType = 'f'; } - MemoryWriter writer; - bx::ErrorAssert err; - bx::WriterI* messageWriter = &writer; + if (0 == std::strcmp(target, "spirv")) { + opts.profile = "glsl"; + } + + MemoryWriter shaderWriter; + MemoryWriter messageWriter; + bx::WriterI* messageOut = &messageWriter; const std::string code(source, source_len); bool ok = false; uint32_t version = 1010; // Dispatch based on target language (in-process supports SPIR-V/MSL/HLSL only). if (0 == std::strcmp(target, "spirv")) { - ok = bgfx::compileSPIRVShader(opts, version, code, &writer, messageWriter); + WriteShaderHeader(shaderWriter, opts.shaderType); + ok = bgfx::compileSPIRVShader(opts, version, code, &shaderWriter, messageOut); } else if (0 == std::strcmp(target, "msl") || 0 == std::strcmp(target, "metal")) { - ok = bgfx::compileMetalShader(opts, version, code, &writer, messageWriter); + WriteShaderHeader(shaderWriter, opts.shaderType); + ok = bgfx::compileMetalShader(opts, version, code, &shaderWriter, messageOut); } else if (0 == std::strcmp(target, "hlsl")) { - ok = bgfx::compileHLSLShader(opts, version, code, &writer, messageWriter); + WriteShaderHeader(shaderWriter, opts.shaderType); + ok = bgfx::compileHLSLShader(opts, version, code, &shaderWriter, messageOut); } else { if (out_error) { *out_error = strdup("shaderc: target not supported by in-process compiler"); @@ -142,13 +178,13 @@ int shaderc_compile_from_memory_with_target(const char* source, size_t source_le } if (!ok) { - const auto& msg = writer.data(); + const auto& msg = messageWriter.data(); std::string s(msg.begin(), msg.end()); if (out_error) *out_error = strdup(s.c_str()); return -1; } - const auto& out = writer.data(); + const auto& out = shaderWriter.data(); if (out.empty()) { if (out_error) *out_error = strdup("shaderc: empty output"); return -1; diff --git a/src/bgfx_tools/shaderc/shaderc_metal.cpp b/src/bgfx_tools/shaderc/shaderc_metal.cpp index 38ea79a..c199493 100644 --- a/src/bgfx_tools/shaderc/shaderc_metal.cpp +++ b/src/bgfx_tools/shaderc/shaderc_metal.cpp @@ -27,7 +27,7 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u #include BX_PRAGMA_DIAGNOSTIC_POP() -namespace bgfx +namespace shaderc_local { struct TinyStlAllocator { @@ -35,16 +35,16 @@ namespace bgfx static void static_deallocate(void* _ptr, size_t /*_bytes*/); }; -} // namespace bgfx +} // namespace shaderc_local -#define TINYSTL_ALLOCATOR bgfx::TinyStlAllocator -#include -#include -#include -#include +#define TINYSTL_ALLOCATOR shaderc_local::TinyStlAllocator +#include +#include +#include +#include namespace stl = tinystl; -#include "../../src/shader.h" +#include "../src/shader.h" namespace bgfx { namespace metal { diff --git a/src/bgfx_tools/shaderc/shaderc_spirv.cpp b/src/bgfx_tools/shaderc/shaderc_spirv.cpp index bad39c2..c29d8a1 100644 --- a/src/bgfx_tools/shaderc/shaderc_spirv.cpp +++ b/src/bgfx_tools/shaderc/shaderc_spirv.cpp @@ -27,10 +27,10 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u #include BX_PRAGMA_DIAGNOSTIC_POP() -namespace bgfx +namespace shaderc_local { static bx::DefaultAllocator s_allocator; - bx::AllocatorI* g_allocator = &s_allocator; + static bx::AllocatorI* g_allocator = &s_allocator; struct TinyStlAllocator { @@ -50,18 +50,18 @@ namespace bgfx bx::free(g_allocator, _ptr); } } -} // namespace bgfx +} // namespace shaderc_local -#define TINYSTL_ALLOCATOR bgfx::TinyStlAllocator -#include -#include -#include -#include +#define TINYSTL_ALLOCATOR shaderc_local::TinyStlAllocator +#include +#include +#include +#include namespace stl = tinystl; -#include "../../src/shader.h" -#include "../../src/shader_spirv.h" -#include "../../3rdparty/khronos/vulkan-local/vulkan.h" +#include "../src/shader.h" +#include "../src/shader_spirv.h" +#include "../../bgfx_deps/khronos/vulkan-local/vulkan.h" namespace bgfx { namespace spirv { @@ -462,20 +462,25 @@ namespace bgfx { namespace spirv return false; } + const bool useHlslInput = (0 == bx::strCmp(_options.profile.c_str(), "hlsl")); + glslang::TProgram* program = new glslang::TProgram; glslang::TShader* shader = new glslang::TShader(stage); EShMessages messages = EShMessages(0 | EShMsgDefault - | EShMsgReadHlsl | EShMsgVulkanRules | EShMsgSpvRules | EShMsgDebugInfo + | (useHlslInput ? EShMsgReadHlsl : 0) ); shader->setEntryPoint("main"); shader->setAutoMapBindings(true); - shader->setEnvInput(glslang::EShSourceHlsl, stage, glslang::EShClientVulkan, s_GLSL_VULKAN_CLIENT_VERSION); + shader->setEnvInput(useHlslInput ? glslang::EShSourceHlsl : glslang::EShSourceGlsl, + stage, + glslang::EShClientVulkan, + s_GLSL_VULKAN_CLIENT_VERSION); shader->setEnvClient(glslang::EShClientVulkan, getGlslangTargetVulkanVersion(_version, _messageWriter)); shader->setEnvTarget(glslang::EShTargetSpv, getGlslangTargetSpirvVersion(_version, _messageWriter)); @@ -554,7 +559,7 @@ namespace bgfx { namespace spirv { program->buildReflection(); - if (_firstPass) + if (_firstPass && useHlslInput) { // first time through, we just find unused uniforms and get rid of them std::string output; @@ -896,7 +901,8 @@ namespace bgfx { namespace spirv bool compileSPIRVShader(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _shaderWriter, bx::WriterI* _messageWriter) { - return spirv::compile(_options, _version, _code, _shaderWriter, _messageWriter, true); + const bool useHlslInput = (0 == bx::strCmp(_options.profile.c_str(), "hlsl")); + return spirv::compile(_options, _version, _code, _shaderWriter, _messageWriter, useHlslInput); } } // namespace bgfx diff --git a/src/bgfx_tools/shaderc/shaderc_utils.cpp b/src/bgfx_tools/shaderc/shaderc_utils.cpp index 5632192..45acf2f 100644 --- a/src/bgfx_tools/shaderc/shaderc_utils.cpp +++ b/src/bgfx_tools/shaderc/shaderc_utils.cpp @@ -9,6 +9,78 @@ namespace bgfx { bool g_verbose = false; +Options::Options() + : shaderType(' ') + , disasm(false) + , raw(false) + , preprocessOnly(false) + , depends(false) + , debugInformation(false) + , avoidFlowControl(false) + , noPreshader(false) + , partialPrecision(false) + , preferFlowControl(false) + , backwardsCompatibility(false) + , warningsAreErrors(false) + , keepIntermediate(false) + , optimize(false) + , optimizationLevel(3) { +} + +void Options::dump() { + BX_TRACE("Options:\n" + "\t shaderType: %c\n" + "\t platform: %s\n" + "\t profile: %s\n" + "\t inputFile: %s\n" + "\t outputFile: %s\n" + "\t disasm: %s\n" + "\t raw: %s\n" + "\t preprocessOnly: %s\n" + "\t depends: %s\n" + "\t debugInformation: %s\n" + "\t avoidFlowControl: %s\n" + "\t noPreshader: %s\n" + "\t partialPrecision: %s\n" + "\t preferFlowControl: %s\n" + "\t backwardsCompatibility: %s\n" + "\t warningsAreErrors: %s\n" + "\t keepIntermediate: %s\n" + "\t optimize: %s\n" + "\t optimizationLevel: %d\n", + shaderType, + platform.c_str(), + profile.c_str(), + inputFilePath.c_str(), + outputFilePath.c_str(), + disasm ? "true" : "false", + raw ? "true" : "false", + preprocessOnly ? "true" : "false", + depends ? "true" : "false", + debugInformation ? "true" : "false", + avoidFlowControl ? "true" : "false", + noPreshader ? "true" : "false", + partialPrecision ? "true" : "false", + preferFlowControl ? "true" : "false", + backwardsCompatibility ? "true" : "false", + warningsAreErrors ? "true" : "false", + keepIntermediate ? "true" : "false", + optimize ? "true" : "false", + optimizationLevel); + + for (size_t ii = 0; ii < includeDirs.size(); ++ii) { + BX_TRACE("\t include :%s\n", includeDirs[ii].c_str()); + } + + for (size_t ii = 0; ii < defines.size(); ++ii) { + BX_TRACE("\t define :%s\n", defines[ii].c_str()); + } + + for (size_t ii = 0; ii < dependencies.size(); ++ii) { + BX_TRACE("\t dependency :%s\n", dependencies[ii].c_str()); + } +} + int32_t writef(bx::WriterI* _writer, const char* _format, ...) { va_list argList; va_start(argList, _format); diff --git a/src/bgfx_tools/src/shader.h b/src/bgfx_tools/src/shader.h new file mode 100644 index 0000000..8f51775 --- /dev/null +++ b/src/bgfx_tools/src/shader.h @@ -0,0 +1,73 @@ +/* + * Copyright 2011-2025 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE + */ + +#ifndef BGFX_SHADER_H +#define BGFX_SHADER_H + +#include + +namespace bgfx +{ + BX_ERROR_RESULT(kShaderInvalidHeader, BX_MAKEFOURCC('S', 'H', 0, 1) ); + BX_ERROR_RESULT(kShaderInvalidInstruction, BX_MAKEFOURCC('S', 'H', 0, 2) ); + + struct DescriptorType + { + enum Enum + { + StorageBuffer, + StorageImage, + + Count + }; + }; + + DescriptorType::Enum idToDescriptorType(uint16_t _id); + uint16_t descriptorTypeToId(DescriptorType::Enum _type); + + struct TextureComponentType + { + enum Enum + { + Float, + Int, + Uint, + Depth, + UnfilterableFloat, + + Count + }; + }; + + TextureComponentType::Enum idToTextureComponentType(uint8_t _id); + uint8_t textureComponentTypeToId(TextureComponentType::Enum _type); + + struct TextureDimension + { + enum Enum + { + Dimension1D, + Dimension2D, + Dimension2DArray, + DimensionCube, + DimensionCubeArray, + Dimension3D, + + Count + }; + }; + + TextureDimension::Enum idToTextureDimension(uint8_t _id); + uint8_t textureDimensionToId(TextureDimension::Enum _dim); + + /// + void disassemble(bx::WriterI* _writer, bx::ReaderSeekerI* _reader, bx::Error* _err = NULL); + + /// + void disassemble(bx::WriterI* _writer, const void* _data, uint32_t _size, bx::Error* _err = NULL); + +} // namespace bgfx + +#endif // BGFX_SHADER_H diff --git a/src/bgfx_tools/src/shader_spirv.h b/src/bgfx_tools/src/shader_spirv.h new file mode 100644 index 0000000..73b5596 --- /dev/null +++ b/src/bgfx_tools/src/shader_spirv.h @@ -0,0 +1,659 @@ +/* + * Copyright 2011-2025 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE + */ + +#ifndef BGFX_SHADER_SPIRV_H +#define BGFX_SHADER_SPIRV_H + +#include + +#define SPV_CHUNK_HEADER BX_MAKEFOURCC(0x03, 0x02, 0x23, 0x07) + +namespace bgfx +{ + constexpr uint8_t kSpirvVertexBinding = 0; + constexpr uint8_t kSpirvFragmentBinding = 1; + constexpr uint8_t kSpirvBindShift = 2; + constexpr uint8_t kSpirvSamplerShift = 16; + + constexpr uint8_t kSpirvOldVertexBinding = 0; + constexpr uint8_t kSpirvOldFragmentBinding = 48; + constexpr uint8_t kSpirvOldFragmentShift = 48; + constexpr uint8_t kSpirvOldBufferShift = 16; + constexpr uint8_t kSpirvOldImageShift = 32; + constexpr uint8_t kSpirvOldTextureShift = 16; + + // Reference(s): + // - https://web.archive.org/web/20181126035927/https://www.khronos.org/registry/spir-v/specs/1.0/SPIRV.html + // + struct SpvOpcode + { + enum Enum + { + Nop, + Undef, + SourceContinued, + Source, + SourceExtension, + Name, + MemberName, + String, + Line, + Invalid9, + Extension, + ExtInstImport, + ExtInst, + Invalid13, + MemoryModel, + EntryPoint, + ExecutionMode, + Capability, + Invalid18, + TypeVoid, + TypeBool, + TypeInt, + TypeFloat, + TypeVector, + TypeMatrix, + TypeImage, + TypeSampler, + TypeSampledImage, + TypeArray, + TypeRuntimeArray, + TypeStruct, + TypeOpaque, + TypePointer, + TypeFunction, + TypeEvent, + TypeDeviceEvent, + TypeReserveId, + TypeQueue, + TypePipe, + TypeForwardPointer, + Invalid40, + ConstantTrue, + ConstantFalse, + Constant, + ConstantComposite, + ConstantSampler, + ConstantNull, + Invalid47, + SpecConstantTrue, + SpecConstantFalse, + SpecConstant, + SpecConstantComposite, + SpecConstantOp, + Invalid53, + Function, + FunctionParameter, + FunctionEnd, + FunctionCall, + Invalid58, + Variable, + ImageTexelPointer, + Load, + Store, + CopyMemory, + CopyMemorySized, + AccessChain, + InBoundsAccessChain, + PtrAccessChain, + ArrayLength, + GenericPtrMemSemantics, + InBoundsPtrAccessChain, + Decorate, + MemberDecorate, + DecorationGroup, + GroupDecorate, + GroupMemberDecorate, + Invalid76, + VectorExtractDynamic, + VectorInsertDynamic, + VectorShuffle, + CompositeConstruct, + CompositeExtract, + CompositeInsert, + CopyObject, + Transpose, + Invalid85, + SampledImage, + ImageSampleImplicitLod, + ImageSampleExplicitLod, + ImageSampleDrefImplicitLod, + ImageSampleDrefExplicitLod, + ImageSampleProjImplicitLod, + ImageSampleProjExplicitLod, + ImageSampleProjDrefImplicitLod, + ImageSampleProjDrefExplicitLod, + ImageFetch, + ImageGather, + ImageDrefGather, + ImageRead, + ImageWrite, + Image, + ImageQueryFormat, + ImageQueryOrder, + ImageQuerySizeLod, + ImageQuerySize, + ImageQueryLod, + ImageQueryLevels, + ImageQuerySamples, + Invalid108, + ConvertFToU, + ConvertFToS, + ConvertSToF, + ConvertUToF, + UConvert, + SConvert, + FConvert, + QuantizeToF16, + ConvertPtrToU, + SatConvertSToU, + SatConvertUToS, + ConvertUToPtr, + PtrCastToGeneric, + GenericCastToPtr, + GenericCastToPtrExplicit, + Bitcast, + Invalid125, + SNegate, + FNegate, + IAdd, + FAdd, + ISub, + FSub, + IMul, + FMul, + UDiv, + SDiv, + FDiv, + UMod, + SRem, + SMod, + FRem, + FMod, + VectorTimesScalar, + MatrixTimesScalar, + VectorTimesMatrix, + MatrixTimesVector, + MatrixTimesMatrix, + OuterProduct, + Dot, + IAddCarry, + ISubBorrow, + UMulExtended, + SMulExtended, + Invalid153, + Any, + All, + IsNan, + IsInf, + IsFinite, + IsNormal, + SignBitSet, + LessOrGreater, + Ordered, + Unordered, + LogicalEqual, + LogicalNotEqual, + LogicalOr, + LogicalAnd, + LogicalNot, + Select, + IEqual, + INotEqual, + UGreaterThan, + SGreaterThan, + UGreaterThanEqual, + SGreaterThanEqual, + ULessThan, + SLessThan, + ULessThanEqual, + SLessThanEqual, + FOrdEqual, + FUnordEqual, + FOrdNotEqual, + FUnordNotEqual, + FOrdLessThan, + FUnordLessThan, + FOrdGreaterThan, + FUnordGreaterThan, + FOrdLessThanEqual, + FUnordLessThanEqual, + FOrdGreaterThanEqual, + FUnordGreaterThanEqual, + Invalid192, + Invalid193, + ShiftRightLogical, + ShiftRightArithmetic, + ShiftLeftLogical, + BitwiseOr, + BitwiseXor, + BitwiseAnd, + Not, + BitFieldInsert, + BitFieldSExtract, + BitFieldUExtract, + BitReverse, + BitCount, + Invalid206, + DPdx, + DPdy, + Fwidth, + DPdxFine, + DPdyFine, + FwidthFine, + DPdxCoarse, + DPdyCoarse, + FwidthCoarse, + Invalid216, + Invalid217, + EmitVertex, + EndPrimitive, + EmitStreamVertex, + EndStreamPrimitive, + Invalid222, + Invalid223, + ControlBarrier, + MemoryBarrier, + Invalid226, + AtomicLoad, + AtomicStore, + AtomicExchange, + AtomicCompareExchange, + AtomicCompareExchangeWeak, + AtomicIIncrement, + AtomicIDecrement, + AtomicIAdd, + AtomicISub, + AtomicSMin, + AtomicUMin, + AtomicSMax, + AtomicUMax, + AtomicAnd, + AtomicOr, + AtomicXor, + Invalid243, + Invalid244, + Phi, + LoopMerge, + SelectionMerge, + Label, + Branch, + BranchConditional, + Switch, + Kill, + Return, + ReturnValue, + Unreachable, + LifetimeStart, + LifetimeStop, + Invalid258, + GroupAsyncCopy, + GroupWaitEvents, + GroupAll, + GroupAny, + GroupBroadcast, + GroupIAdd, + GroupFAdd, + GroupFMin, + GroupUMin, + GroupSMin, + GroupFMax, + GroupUMax, + GroupSMax, + Invalid272, + Invalid273, + ReadPipe, + WritePipe, + ReservedReadPipe, + ReservedWritePipe, + ReserveReadPipePackets, + ReserveWritePipePackets, + CommitReadPipe, + CommitWritePipe, + IsValidReserveId, + GetNumPipePackets, + GetMaxPipePackets, + GroupReserveReadPipePackets, + GroupReserveWritePipePackets, + GroupCommitReadPipe, + GroupCommitWritePipe, + Invalid289, + Invalid290, + EnqueueMarker, + EnqueueKernel, + GetKernelNDrangeSubGroupCount, + GetKernelNDrangeMaxSubGroupSize, + GetKernelWorkGroupSize, + GetKernelPreferredWorkGroupSizeMultiple, + RetainEvent, + ReleaseEvent, + CreateUserEvent, + IsValidEvent, + SetUserEventStatus, + CaptureEventProfilingInfo, + GetDefaultQueue, + BuildNDRange, + ImageSparseSampleImplicitLod, + ImageSparseSampleExplicitLod, + ImageSparseSampleDrefImplicitLod, + ImageSparseSampleDrefExplicitLod, + ImageSparseSampleProjImplicitLod, + ImageSparseSampleProjExplicitLod, + ImageSparseSampleProjDrefImplicitLod, + ImageSparseSampleProjDrefExplicitLod, + ImageSparseFetch, + ImageSparseGather, + ImageSparseDrefGather, + ImageSparseTexelsResident, + NoLine, + AtomicFlagTestAndSet, + AtomicFlagClear, + ImageSparseRead, + + Count + }; + }; + + const char* getName(SpvOpcode::Enum _opcode); + + struct SpvBuiltin + { + enum Enum + { + Position, + PointSize, + ClipDistance, + CullDistance, + VertexId, + InstanceId, + PrimitiveId, + InvocationId, + Layer, + ViewportIndex, + TessLevelOuter, + TessLevelInner, + TessCoord, + PatchVertices, + FragCoord, + PointCoord, + FrontFacing, + SampleId, + SamplePosition, + SampleMask, + FragDepth, + HelperInvocation, + NumWorkgroups, + WorkgroupSize, + WorkgroupId, + LocalInvocationId, + GlobalInvocationId, + LocalInvocationIndex, + WorkDim, + GlobalSize, + EnqueuedWorkgroupSize, + GlobalOffset, + GlobalLinearId, + SubgroupSize, + SubgroupMaxSize, + NumSubgroups, + NumEnqueuedSubgroups, + SubgroupId, + SubgroupLocalInvocationId, + VertexIndex, + InstanceIndex, + + Count + }; + }; + + const char* getName(SpvBuiltin::Enum _enum); + + struct SpvExecutionModel + { + enum Enum + { + Vertex, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment, + GLCompute, + Kernel, + + Count + }; + }; + + struct SpvAddressingModel + { + enum Enum + { + Logical, + Physical32, + Physical64, + + Count + }; + }; + + struct SpvMemoryModel + { + enum Enum + { + Simple, + GLSL450, + OpenCL, + + Count + }; + }; + + struct SpvStorageClass + { + enum Enum + { + UniformConstant, + Input, + Uniform, + Output, + Workgroup, + CrossWorkgroup, + Private, + Function, + Generic, + PushConstant, + AtomicCounter, + Image, + + Count + }; + }; + + const char* getName(SpvStorageClass::Enum _enum); + + struct SpvResourceDim + { + enum Enum + { + Texture1D, + Texture2D, + Texture3D, + TextureCube, + TextureRect, + Buffer, + SubpassData, + }; + }; + + struct SpvDecoration + { + enum Enum + { + RelaxedPrecision, + SpecId, + Block, + BufferBlock, + RowMajor, + ColMajor, + ArrayStride, + MatrixStride, + GLSLShared, + GLSLPacked, + CPacked, + BuiltIn, + Unknown12, + NoPerspective, + Flat, + Patch, + Centroid, + Sample, + Invariant, + Restrict, + Aliased, + Volatile, + Constant, + Coherent, + NonWritable, + NonReadable, + Uniform, + Unknown27, + SaturatedConversion, + Stream, + Location, + Component, + Index, + Binding, + DescriptorSet, + Offset, + XfbBuffer, + XfbStride, + FuncParamAttr, + FPRoundingMode, + FPFastMathMode, + LinkageAttributes, + NoContraction, + InputAttachmentIndex, + Alignment, + + Count + }; + }; + + const char* getName(SpvDecoration::Enum _enum); + + struct SpvOperand + { + SpvOperand() { /* not pod */ } + + enum Enum + { + AccessQualifier, + AddressingModel, + Base, + Capability, + Component, + ComponentType, + Composite, + Condition, + Coordinate, + Decoration, + Dim, + Dref, + ElementType, + ExecutionModel, + Function, + FunctionControl, + Id, + IdRep, + ImageFormat, + ImageOperands, + LinkageType, + LiteralNumber, + LiteralRep, + LiteralString, + Matrix, + MemoryAccess, + MemoryModel, + Object, + Pointer, + SampledType, + SampledImage, + SamplerAddressingMode, + SamplerFilterMode, + Scalar, + SourceLanguage, + StorageClass, + StructureType, + Vector, + + Count + }; + + Enum type; + uint32_t data; + + stl::string literalString; + }; + + struct SpvInstruction + { + SpvInstruction() { /* not pod */ } + + SpvOpcode::Enum opcode; + uint16_t length; + uint16_t numOperands; + + uint32_t type; + uint32_t result; + bool hasType; + bool hasResult; + + SpvOperand operand[32]; + }; + + int32_t read(bx::ReaderI* _reader, SpvInstruction& _instruction, bx::Error* _err); + int32_t write(bx::WriterI* _writer, const SpvInstruction& _instruction, bx::Error* _err); + int32_t toString(char* _out, int32_t _size, const SpvInstruction& _instruction); + + struct SpvShader + { + SpvShader() { /* not pod */ } + + stl::vector byteCode; + }; + + int32_t read(bx::ReaderSeekerI* _reader, SpvShader& _shader, bx::Error* _err); + int32_t write(bx::WriterI* _writer, const SpvShader& _shader, bx::Error* _err); + + typedef bool (*SpvParseFn)(uint32_t _offset, const SpvInstruction& _instruction, void* _userData); + void parse(const SpvShader& _src, SpvParseFn _fn, void* _userData, bx::Error* _err = NULL); + + typedef void (*SpvFilterFn)(SpvInstruction& _instruction, void* _userData); + void filter(SpvShader& _dst, const SpvShader& _src, SpvFilterFn _fn, void* _userData, bx::Error* _err = NULL); + + struct SpirV + { + SpirV() { /* not pod */ } + + struct Header + { + uint32_t magic; + uint32_t version; + uint32_t generator; + uint32_t bound; + uint32_t schema; + }; + + Header header; + SpvShader shader; + }; + + int32_t read(bx::ReaderSeekerI* _reader, SpirV& _spirv, bx::Error* _err); + int32_t write(bx::WriterSeekerI* _writer, const SpirV& _spirv, bx::Error* _err); + +} // namespace bgfx + +#endif // BGFX_SHADER_SPIRV_H diff --git a/src/services/impl/bgfx_graphics_backend.cpp b/src/services/impl/bgfx_graphics_backend.cpp index 7108af9..b01fa70 100644 --- a/src/services/impl/bgfx_graphics_backend.cpp +++ b/src/services/impl/bgfx_graphics_backend.cpp @@ -1,4 +1,5 @@ #include "bgfx_graphics_backend.hpp" +#include "bgfx_shader_compiler.hpp" #include "../interfaces/i_pipeline_compiler_service.hpp" #include @@ -677,67 +678,18 @@ bgfx::ShaderHandle BgfxGraphicsBackend::CreateShader(const std::string& label, logger_->Trace("BgfxGraphicsBackend", "CreateShader", "label=" + label + ", renderer=" + RendererTypeName(rendererType) + - ", sourceLength=" + std::to_string(source.size())); + ", sourceLength=" + std::to_string(source.size()) + + ", compiler=BgfxShaderCompiler"); } - // 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(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; - } - - - // Use PipelineCompilerService to compile shader via bgfx_tools - std::string tempInputPath = "/tmp/" + label + (isVertex ? ".vert.glsl" : ".frag.glsl"); - std::string tempOutputPath = "/tmp/" + label + (isVertex ? ".vert.bin" : ".frag.bin"); - // Write source to tempInputPath - { - std::ofstream ofs(tempInputPath); - ofs << source; - } - std::vector args; - // Add any required args for bgfx_tools/shaderc here (e.g., profile, macros) - bool success = pipelineCompiler_ && pipelineCompiler_->Compile(tempInputPath, tempOutputPath, args); - if (!success) { - std::string error = pipelineCompiler_ ? pipelineCompiler_->GetLastError().value_or("") : "No compiler service"; + BgfxShaderCompiler compiler(logger_, pipelineCompiler_); + bgfx::ShaderHandle handle = compiler.CompileShader(label, source, isVertex, {}, {}); + if (!bgfx::isValid(handle)) { if (logger_) { - logger_->Error("PipelineCompilerService failed: " + label + "\n" + error); + logger_->Error("BgfxGraphicsBackend::CreateShader failed for " + label + + " (renderer=" + RendererTypeName(rendererType) + ")"); } - throw std::runtime_error("PipelineCompilerService failed: " + label + "\n" + error); - } - // Read compiled binary - std::ifstream ifs(tempOutputPath, std::ios::binary | std::ios::ate); - if (!ifs) { - if (logger_) { - logger_->Error("Failed to read compiled shader: " + tempOutputPath); - } - throw std::runtime_error("Failed to read compiled shader: " + tempOutputPath); - } - std::streamsize size = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - std::vector buffer(size); - if (!ifs.read(buffer.data(), size)) { - if (logger_) { - logger_->Error("Failed to read compiled shader data: " + tempOutputPath); - } - throw std::runtime_error("Failed to read compiled shader data: " + tempOutputPath); - } - const bgfx::Memory* mem = bgfx::copy(buffer.data(), static_cast(size)); - bgfx::ShaderHandle handle = bgfx::createShader(mem); - if (!bgfx::isValid(handle) && logger_) { - logger_->Error("bgfx::createShader failed for " + label + " (renderer=" + RendererTypeName(rendererType) + ", binSize=" + std::to_string(size) + ")"); + throw std::runtime_error("BgfxGraphicsBackend::CreateShader failed for " + label); } return handle; }