feat(shader): Refactor shader compilation process to use BgfxShaderCompiler and streamline error handling

This commit is contained in:
2026-01-07 20:17:56 +00:00
parent 99e35573d2
commit 08d06dbe60
8 changed files with 894 additions and 94 deletions

View File

@@ -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()

View File

@@ -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<uint8_t>
class MemoryWriter : public bx::FileWriter
{
@@ -30,6 +35,28 @@ private:
std::vector<uint8_t> 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;

View File

@@ -27,7 +27,7 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u
#include <spirv-tools/optimizer.hpp>
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 <tinystl/allocator.h>
#include <tinystl/string.h>
#include <tinystl/unordered_map.h>
#include <tinystl/vector.h>
#define TINYSTL_ALLOCATOR shaderc_local::TinyStlAllocator
#include <TINYSTL/allocator.h>
#include <TINYSTL/string.h>
#include <TINYSTL/unordered_map.h>
#include <TINYSTL/vector.h>
namespace stl = tinystl;
#include "../../src/shader.h"
#include "../src/shader.h"
namespace bgfx { namespace metal
{

View File

@@ -27,10 +27,10 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u
#include <spirv-tools/optimizer.hpp>
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 <tinystl/allocator.h>
#include <tinystl/string.h>
#include <tinystl/unordered_map.h>
#include <tinystl/vector.h>
#define TINYSTL_ALLOCATOR shaderc_local::TinyStlAllocator
#include <TINYSTL/allocator.h>
#include <TINYSTL/string.h>
#include <TINYSTL/unordered_map.h>
#include <TINYSTL/vector.h>
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

View File

@@ -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);

View File

@@ -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 <bx/readerwriter.h>
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

View File

@@ -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 <bx/readerwriter.h>
#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<uint8_t> 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

View File

@@ -1,4 +1,5 @@
#include "bgfx_graphics_backend.hpp"
#include "bgfx_shader_compiler.hpp"
#include "../interfaces/i_pipeline_compiler_service.hpp"
#include <stb_image.h>
@@ -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<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;
}
// 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<std::string> 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<char> 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<uint32_t>(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;
}