#include "shadercompiler/icompiler.h" #include "slang.h" #include "tier0/mem.h" #include "tier1/utlstring.h" #include "tier2/ifilesystem.h" #include "vulkan/vulkan.h" #include "materialsystem/vulkan_shadermeta.h" using namespace slang; static IGlobalSession *s_pGlobalSession; static ISession *s_pSession; class CSlangVulkanSpirvShaderCompiler: public IShaderCompiler { public: virtual void Init() override; virtual void Shutdown() override; virtual void CompileShader( const char *szInput, CCompiledShader *pShader ) override; private: IBlob *m_pDiagnostics = NULL; void CheckDiagnostics(); void CompileShaderStage( EShaderStage eStage, const char *szMain, ISession *pSession, IModule *pModule, CCompiledShader *pShader ); void ReadChildren( DeclReflection::IteratedList pChildren ) { for (auto *c: pChildren) { if (c->getKind() == DeclReflection::Kind::Unsupported) continue; if (c->getKind() == DeclReflection::Kind::Namespace) { V_printf("Namespace: %s\n", c->getName()); ReadChildren(c->getChildren()); } if (c->getKind() == DeclReflection::Kind::Variable) { ReadChildren(c->getChildren()); } if (c->getKind() == DeclReflection::Kind::Struct) { V_printf("Struct: %s\n", c->getName()); } } } }; EXPOSE_INTERFACE(CSlangVulkanSpirvShaderCompiler, IShaderCompiler, SLANG_SHADER_COMPILER_SPIRV_VULKAN) void CSlangVulkanSpirvShaderCompiler::CompileShaderStage( EShaderStage eStage, const char *szMain, ISession *pSession, IModule *pModule, CCompiledShader *pShader ) { SlangStage eSlangStage; IEntryPoint *pEntryPoint = NULL; IComponentType *pLinked = NULL; IBlob *pBinary = NULL; ShaderObject_t *pShaderObject = NULL; ProgramLayout *pProgramLayout = NULL; switch (eStage) { case SHADER_STAGE_VERTEX: eSlangStage = SLANG_STAGE_VERTEX; break; case SHADER_STAGE_PIXEL: eSlangStage = SLANG_STAGE_PIXEL; break; case SHADER_STAGE_CALLABLE: eSlangStage = SLANG_STAGE_CALLABLE; break; default: break; } pProgramLayout = pModule->getLayout(); uint32_t uCount = pProgramLayout->getParameterCount(); // we want to get all inputs // just for the sake of making vulkan simple CUtlVector inputs; VulkanDescriptor_t input = {}; for ( uint32_t u = 0; u < uCount; u++ ) { VariableLayoutReflection *pVar = pProgramLayout->getParameterByIndex(u); input = {}; input.uCount = 1; V_printf("%s %i\n", pVar->getName(), pVar->getType()->getKind()); TypeReflection *pType = pVar->getType(); trygetkind: V_printf("%s %i\n", pType->getName(), pType->getKind()); switch(pType->getKind()) { case slang::TypeReflection::Kind::ConstantBuffer: input.eDescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; break; V_printf("%s: %i\n", pType->getName(), pType->getResourceShape()); switch(pType->getResourceShape()) { case SLANG_TEXTURE_2D: input.eDescriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; break; case SLANG_STRUCTURED_BUFFER: input.eDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; break; default: break; } break; case slang::TypeReflection::Kind::Array: { size_t uCount = pType->getTotalArrayElementCount(); if (uCount == 0) input.uCount = 128; else input.uCount = uCount; pType = pType->getElementType(); } goto trygetkind; case slang::TypeReflection::Kind::SamplerState: input.eDescriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; break; default: break; } V_strncpy(input.szName, pVar->getName(), 32); input.uBinding = pVar->getBindingIndex(); input.uSet = pVar->getBindingSpace(); V_printf("%i\n", input.uCount); inputs.AppendTail(input); } pModule->findAndCheckEntryPoint(szMain, eSlangStage, &pEntryPoint, &m_pDiagnostics); // stage was not defined if (pEntryPoint == NULL) { // fool it m_pDiagnostics = NULL; return; } CheckDiagnostics(); pEntryPoint->link(&pLinked, &m_pDiagnostics); CheckDiagnostics(); if (pLinked == NULL) { pEntryPoint->release(); return; } pLinked->getEntryPointCode(0, 0, &pBinary, &m_pDiagnostics); CheckDiagnostics(); if (pBinary == NULL) { pEntryPoint->release(); return; } pShaderObject = pShader->AllocateShader(); pShaderObject->m_eBackend = SHADER_BACKEND_SPIRV_VULKAN; pShaderObject->m_eStage = eStage; pShaderObject->m_nDataLump = pShader->AllocateLump(pBinary->getBufferSize()); VulkanInputMetaData_t stMetadata = {}; stMetadata.nDescriptorsCount = uCount; stMetadata.pDescriptorSets = pShader->AllocateLump(sizeof(VulkanDescriptor_t)*inputs.GetSize()); V_memcpy(pShader->GetLumpPtr(stMetadata.pDescriptorSets), inputs.GetData(), sizeof(VulkanDescriptor_t)*inputs.GetSize()); pShaderObject->m_nMetadataLump = pShader->AllocateLump(sizeof(VulkanInputMetaData_t)); V_memcpy(pShader->GetLumpPtr(pShaderObject->m_nDataLump), pBinary->getBufferPointer(), pBinary->getBufferSize()); V_memcpy(pShader->GetLumpPtr(pShaderObject->m_nMetadataLump), &stMetadata, sizeof(VulkanInputMetaData_t)); if (eStage == SHADER_STAGE_CALLABLE) { IFileHandle *ph = filesystem->Open("a.txt", FILEMODE_WRITE); filesystem->Write(ph, pBinary->getBufferPointer(), pBinary->getBufferSize()); filesystem->Close(ph); } pEntryPoint->release(); } void CSlangVulkanSpirvShaderCompiler::CompileShader( const char *szInput, CCompiledShader *pShader ) { SessionDesc stSessionDesc = {}; TargetDesc stTargetDesc = {}; PreprocessorMacroDesc stStageMacroDesc = {}; const char *szMainName; IModule *pModule = NULL; ISession *pSession = NULL; IBlob *pShaderSourceBlob = NULL; if (filesystem == NULL) { CreateInterfaceFn pFilesystemFactory = Sys_GetFactory("filesystem_std"); filesystem = (IFileSystem*)pFilesystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL); filesystem->Init(); } IFileHandle *pFile = filesystem->Open(szInput, FILEMODE_READ); const char *szShaderSource = filesystem->ReadString(pFile); int i = 0; pShaderSourceBlob = slang_createBlob(szShaderSource, filesystem->Size(pFile)); filesystem->Close(pFile); printf(" SLANG %s\n", szInput); stSessionDesc.targetCount = 1; stSessionDesc.targets = &stTargetDesc; stTargetDesc.format = SLANG_SPIRV; stTargetDesc.profile = s_pGlobalSession->findProfile("spirv_1_6"); for ( i = 0; icreateSession(stSessionDesc, &pSession); pModule = pSession->loadModuleFromSource("main", szInput, pShaderSourceBlob, &m_pDiagnostics); CheckDiagnostics(); CompileShaderStage((EShaderStage)i, szMainName, pSession, pModule, pShader); pModule->release(); // ah yes free(): corrupted unsorted chunks //pSession->release(); } V_free((void*)szShaderSource); } void CSlangVulkanSpirvShaderCompiler::CheckDiagnostics() { if (m_pDiagnostics) { V_printf("%s\n",(const char*)m_pDiagnostics->getBufferPointer()); } m_pDiagnostics = NULL; }; void CSlangVulkanSpirvShaderCompiler::Init() { createGlobalSession(&s_pGlobalSession); } void CSlangVulkanSpirvShaderCompiler::Shutdown() { s_pGlobalSession->Release(); }