├── .gitmodules ├── source └── thusc │ ├── LLVMSpecificProperties-Release.props │ ├── LLVMSpecificProperties-Debug.props │ ├── ClangHelperFunctions.h │ ├── thusc.vcxproj.filters │ ├── TypeHelpers.h │ ├── LLVMSpecificProperties.props │ ├── ThuscShaderIR.cpp │ ├── ThuscBackendUE4.h │ ├── ThuscCppToHLSLTranslator.h │ ├── ThuscCommon.h │ ├── ThuscBackend.h │ ├── ThuscBackendHLSL.h │ ├── ThuscFrontendAction.h │ ├── ThuscCppToHLSLTranslator.cpp │ ├── thusc.vcxproj │ ├── thusc.cpp │ ├── ThuscShaderIR.h │ ├── ThuscBackendHLSL.cpp │ ├── ThuscBackendUE4.cpp │ └── ThuscFrontendAction.cpp ├── thusc.sln ├── CITATION.cff ├── LICENSE.txt ├── README.md ├── include └── thusc.h └── .gitignore /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/hlslpp"] 2 | path = external/hlslpp 3 | url = git@github.com:kseitz/hlslpp.git 4 | -------------------------------------------------------------------------------- /source/thusc/LLVMSpecificProperties-Release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $(LLVMBuildDir)\Release\lib; 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /source/thusc/LLVMSpecificProperties-Debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $(LLVMBuildDir)\RelWithDebInfo\lib; 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /thusc.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "thusc", "source\thusc\thusc.vcxproj", "{9988EB8D-EB65-4BAB-AECA-845341448507}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Debug|x64.ActiveCfg = Debug|x64 17 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Debug|x64.Build.0 = Debug|x64 18 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Debug|x86.ActiveCfg = Debug|Win32 19 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Debug|x86.Build.0 = Debug|Win32 20 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Release|x64.ActiveCfg = Release|x64 21 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Release|x64.Build.0 = Release|x64 22 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Release|x86.ActiveCfg = Release|Win32 23 | {9988EB8D-EB65-4BAB-AECA-845341448507}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {9144C82C-C073-40A6-BC4A-5EDB143DBDAD} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /source/thusc/ClangHelperFunctions.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | DISABLE_WARNINGS_LLVM 32 | #include "clang/Basic/Diagnostic.h" 33 | ENABLE_WARNINGS_LLVM 34 | 35 | template 36 | constexpr void diagError(clang::DiagnosticsEngine& diagEngine, clang::SourceLocation loc, const char(&msg)[N]) { 37 | const unsigned diagID = diagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error, msg); 38 | diagEngine.Report(loc, diagID); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite the associated technical paper from the preferred-citation." 3 | title: "Supporting Unified Shader Specialization by Co-opting C++ Features" 4 | authors: 5 | - given-names: "Kerry A." 6 | family-names: "Seitz" 7 | name-suffix: "Jr." 8 | email: "kaseitz@ucdavis.edu" 9 | affiliation: "University of California, Davis" 10 | orcid: "https://orcid.org/0000-0003-2424-9495" 11 | - given-names: "Theresa" 12 | family-names: "Foley" 13 | email: "tfoley@nvidia.com" 14 | affiliation: "NVIDIA" 15 | orcid: "https://orcid.org/0000-0002-2381-3544" 16 | - given-names: "Serban D." 17 | family-names: "Porumbescu" 18 | email: "sdporumbescu@ucdavis.edu" 19 | affiliation: "University of California, Davis" 20 | orcid: "https://orcid.org/0000-0003-1523-9199" 21 | - given-names: "John D." 22 | family-names: "Owens" 23 | email: "jowens@ece.ucdavis.edu" 24 | affiliation: "University of California, Davis" 25 | orcid: "https://orcid.org/0000-0001-6582-8237" 26 | repository-code: "https://github.com/owensgroup/UnifiedShaderSpecialization" 27 | preferred-citation: 28 | type: article 29 | title: "Supporting Unified Shader Specialization by Co-opting C++ Features" 30 | authors: 31 | - given-names: "Kerry A." 32 | family-names: "Seitz" 33 | name-suffix: "Jr." 34 | email: "kaseitz@ucdavis.edu" 35 | affiliation: "University of California, Davis" 36 | orcid: "https://orcid.org/0000-0003-2424-9495" 37 | - given-names: "Theresa" 38 | family-names: "Foley" 39 | email: "tfoley@nvidia.com" 40 | affiliation: "NVIDIA" 41 | orcid: "https://orcid.org/0000-0002-2381-3544" 42 | - given-names: "Serban D." 43 | family-names: "Porumbescu" 44 | email: "sdporumbescu@ucdavis.edu" 45 | affiliation: "University of California, Davis" 46 | orcid: "https://orcid.org/0000-0003-1523-9199" 47 | - given-names: "John D." 48 | family-names: "Owens" 49 | email: "jowens@ece.ucdavis.edu" 50 | affiliation: "University of California, Davis" 51 | orcid: "https://orcid.org/0000-0001-6582-8237" 52 | journal: "Proceedings of the ACM on Computer Graphics and Interactive Techniques" 53 | year: 2022 54 | volume: 5 55 | issue: 3 56 | start: "25:1" 57 | end: "25:17" 58 | month: 7 59 | doi: 10.1145/3543866 60 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Excluding the "external" directory, this code is licensed under 2 | the 3-clause BSD license as follows: 3 | 4 | Copyright (c) 2022, The Regents of the University of California, 5 | Davis campus. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither any name of The Regents of the University of California nor 16 | the names of its contributors may be used to endorse or promote 17 | products derived from this software without specific prior written 18 | permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | For code contained in the "external" directory and its subdirectories, 34 | please refer to the specific copyright and licensing information 35 | contained therein. 36 | 37 | This code relies on LLVM and Clang (distributed separately). LLVM and 38 | Clang are subject to their own terms and conditions that can be found 39 | at llvm.org. 40 | 41 | The output generated from this code interfaces with Unreal Engine 4. 42 | Unreal Engine 4 is not owned by The Regents of the University of 43 | California, and it requires a separate license. Users of this code 44 | are responsible for obtaining their own licenses to use Unreal Engine 4. 45 | -------------------------------------------------------------------------------- /source/thusc/thusc.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | -------------------------------------------------------------------------------- /source/thusc/TypeHelpers.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscCommon.h" 32 | 33 | DISABLE_WARNINGS_LLVM 34 | #include "clang/AST/ASTContext.h" 35 | #include "clang/AST/Type.h" 36 | #include "llvm/ADT/StringRef.h" 37 | ENABLE_WARNINGS_LLVM 38 | 39 | #include 40 | #include 41 | 42 | inline 43 | bool isPrimitiveType(const llvm::StringRef typeName) { 44 | const std::string typeNames[]{ 45 | "float", "float2", "float3", "float4", 46 | "int", "int2", "int3", "int4", 47 | // TODO add others 48 | }; 49 | 50 | for (const auto& name : typeNames) { 51 | if (name == typeName) 52 | return true; 53 | } 54 | 55 | return false; 56 | } 57 | 58 | // Map from UE4 types to HLSL types 59 | inline 60 | std::string getGpuTypeName(const clang::QualType& type, const clang::ASTContext& context) { 61 | std::pair typeNames[] = { 62 | std::make_pair("FVector2D", "float2"), 63 | std::make_pair("FVector4", "float4"), 64 | std::make_pair("FIntPoint", "int2"), 65 | std::make_pair("thusc::Texture2DBase", "Texture2D"), 66 | // TODO add others 67 | }; 68 | 69 | std::string typeName = type.getDesugaredType(context).getAsString(context.getLangOpts()); 70 | 71 | for (const auto& namePair : typeNames) { 72 | if (typeName == namePair.first) 73 | return namePair.second; 74 | } 75 | 76 | return typeName; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /source/thusc/LLVMSpecificProperties.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(SolutionDir)\..\llvm\ 6 | $(LLVMDir)\llvm-project 7 | $(LLVMDir)\build-11.0.0 8 | 9 | 10 | <_PropertySheetDisplayName>LLVMSpecificProperties 11 | 12 | 13 | 14 | LLVMXRay.lib;LLVMWindowsManifest.lib;LLVMTestingSupport.lib;LLVMTableGen.lib;LLVMSymbolize.lib;LLVMDebugInfoPDB.lib;LLVMOrcJIT.lib;LLVMOrcError.lib;LLVMJITLink.lib;LLVMObjectYAML.lib;LLVMMCA.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMObjCARCOpts.lib;LLVMExtensions.lib;LLVMLineEditor.lib;LLVMLibDriver.lib;LLVMInterpreter.lib;gtest_main.lib;gtest.lib;LLVMFuzzMutate.lib;LLVMFrontendOpenMP.lib;LLVMMCJIT.lib;LLVMExecutionEngine.lib;LLVMDWARFLinker.lib;LLVMDlltoolDriver.lib;LLVMOption.lib;LLVMDebugInfoGSYM.lib;LLVMCoverage.lib;LLVMCoroutines.lib;LLVMX86Disassembler.lib;LLVMX86AsmParser.lib;LLVMX86CodeGen.lib;LLVMX86Desc.lib;LLVMX86Info.lib;LLVMMIRParser.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMVectorize.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMAsmParser.lib;LLVMMCDisassembler.lib;LLVMCFGuard.lib;LLVMGlobalISel.lib;LLVMSelectionDAG.lib;LLVMAsmPrinter.lib;LLVMDebugInfoDWARF.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMAggressiveInstCombine.lib;LLVMTransformUtils.lib;LLVMBitWriter.lib;LLVMAnalysis.lib;LLVMProfileData.lib;LLVMObject.lib;LLVMTextAPI.lib;LLVMBitReader.lib;LLVMCore.lib;LLVMRemarks.lib;LLVMBitstreamReader.lib;LLVMMCParser.lib;LLVMMC.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoMSF.lib;LLVMBinaryFormat.lib;LLVMSupport.lib;LLVMDemangle.lib;clangAnalysis.lib;clangARCMigrate.lib;clangAST.lib;clangASTMatchers.lib;clangBasic.lib;clangCodeGen.lib;clangCrossTU.lib;clangDependencyScanning.lib;clangDirectoryWatcher.lib;clangDriver.lib;clangDynamicASTMatchers.lib;clangEdit.lib;clangFormat.lib;clangFrontend.lib;clangFrontendTool.lib;clangHandleCXX.lib;clangHandleLLVM.lib;clangIndex.lib;clangLex.lib;clangParse.lib;clangRewrite.lib;clangRewriteFrontend.lib;clangSema.lib;clangSerialization.lib;clangStaticAnalyzerCheckers.lib;clangStaticAnalyzerCore.lib;clangStaticAnalyzerFrontend.lib;clangTooling.lib;clangToolingASTDiff.lib;clangToolingCore.lib;clangToolingInclusions.lib;clangToolingRefactoring.lib;clangToolingSyntax.lib;clangTransformer.lib;Version.lib;%(AdditionalDependencies) 15 | 16 | 17 | $(LLVMSrcDir)\clang\include;$(LLVMSrcDir)\llvm\include;$(LLVMBuildDir)\include;$(LLVMBuildDir)\tools\clang\include; 18 | 19 | 20 | 21 | 22 | $(LLVMDir) 23 | 24 | 25 | $(LLVMSrcDir) 26 | true 27 | 28 | 29 | $(LLVMBuildDir) 30 | true 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/thusc/ThuscShaderIR.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include "ThuscShaderIR.h" 30 | 31 | using namespace clang; 32 | using namespace llvm; 33 | using namespace thusc; 34 | 35 | const char* thusc::shaderImplClassPrefix = "ThuscImpl_"; 36 | 37 | // PermutationShaderClass 38 | PermutationShaderClass::PermutationShaderClass(const clang::FieldDecl* fieldDecl, llvm::StringRef name, 39 | const ShaderClass* shaderClassType, const ShaderIR* shaderIR) 40 | : fieldDecl(fieldDecl) 41 | , name(name) 42 | , shaderClassType(shaderClassType) 43 | { 44 | validShaderClasses.push_back(shaderClassType); 45 | 46 | for (const auto& shaderClass : shaderIR->getShaderClasses()) { 47 | const ShaderClass* baseShaderClass = shaderClass->baseShaderClass; 48 | while (baseShaderClass) { 49 | if (baseShaderClass == shaderClassType) { 50 | validShaderClasses.push_back(shaderClass.get()); 51 | break; 52 | } 53 | baseShaderClass = baseShaderClass->baseShaderClass; 54 | } 55 | } 56 | } 57 | 58 | // ImplClass 59 | std::string ImplClass::generateImplClassName(const ShaderClass* parent, const std::vector& selections) { 60 | std::string name = (shaderImplClassPrefix + parent->getName()).str(); 61 | for (const auto& s : selections) { 62 | name += "_" + s.implClass->getName().str(); 63 | } 64 | return name; 65 | } 66 | 67 | 68 | // ShaderClass 69 | void ShaderClass::addGPUMethod(std::unique_ptr gpuFunc) { 70 | // If this function overrides a function that's already in the ShaderClass, 71 | // then remove the overridden functions 72 | if (const auto* methodDecl = dyn_cast(gpuFunc->getFunctionDecl())) { 73 | for (const auto* overriddenMethod : methodDecl->overridden_methods()) { 74 | gpuMethods.erase(std::remove_if(gpuMethods.begin(), gpuMethods.end(), 75 | [overriddenMethod](std::unique_ptr& f) { 76 | return (f->getFunctionDecl() == overriddenMethod); 77 | }), gpuMethods.end()); 78 | } 79 | } 80 | 81 | gpuMethods.push_back(std::move(gpuFunc)); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /source/thusc/ThuscBackendUE4.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscBackend.h" 32 | #include "ThuscBackendHLSL.h" 33 | #include "ThuscShaderIR.h" 34 | 35 | class ThuscBackendUE4Host : public ThuscBackendHost { 36 | public: 37 | ThuscBackendUE4Host(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, llvm::StringRef gpuOutputDirectory) 38 | : ThuscBackendHost(shaderIR, rewriter, gpuOutputDirectory) 39 | {} 40 | 41 | virtual bool Output(const clang::FileID currentFileID) override; 42 | 43 | private: 44 | bool OutputShaderClass(const thusc::ShaderClass* shaderClss); 45 | bool OutputGPUFunction(const thusc::GPUFunction* gpuFunc); 46 | 47 | void OutputUniformFieldStruct(const thusc::UniformField* uniform, llvm::raw_ostream& stream) const; 48 | void OutputUniformField(const thusc::UniformField* uniform, llvm::raw_ostream& stream) const; 49 | void OutputUniformFields(const thusc::ShaderClass* shaderClass, const thusc::ImplClass* implClass, llvm::raw_ostream& stream) const; 50 | 51 | void OutputPermutationErrorChecking(const thusc::PermutationField& perm, llvm::raw_ostream& stream) const; 52 | void OutputPermutationFields(const thusc::ShaderClass* shaderClass, const thusc::ImplClass* implClass, llvm::raw_ostream& stream) const; 53 | 54 | void OutputComputeShaderEntry(const thusc::ComputeEntryPoint* entryPoint, const thusc::ShaderClass* shaderClass, llvm::raw_ostream& stream) const; 55 | 56 | bool OutputImplClass(const thusc::ShaderClass* shaderClass, const thusc::ImplClass* implClass, llvm::raw_ostream& stream) const; 57 | 58 | 59 | const char* permutationClassPrefix = "ThuscPermutationClass_"; 60 | const char* thuscTypeIDPrefix = "Thusc_getTypeID_"; 61 | const char* thuscgetImplClassIDFunctionName = "Thusc_getImplClassID"; 62 | }; 63 | 64 | class ThuscBackendUE4 : public ThuscBackend { 65 | public: 66 | ThuscBackendUE4(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, 67 | llvm::StringRef gpuOutputDirectory, llvm::StringRef gpuOutputDirectoryVirtual) 68 | : ThuscBackend(shaderIR, rewriter, gpuOutputDirectory, gpuOutputDirectoryVirtual) 69 | {} 70 | }; 71 | 72 | -------------------------------------------------------------------------------- /source/thusc/ThuscCppToHLSLTranslator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscCommon.h" 32 | #include "ThuscShaderIR.h" 33 | 34 | DISABLE_WARNINGS_LLVM 35 | #include "clang/AST/RecursiveASTVisitor.h" 36 | #include "clang/Basic/Diagnostic.h" 37 | #include "clang/Basic/SourceManager.h" 38 | #include "clang/Rewrite/Core/Rewriter.h" 39 | ENABLE_WARNINGS_LLVM 40 | 41 | #include 42 | 43 | namespace thusc { 44 | 45 | class CppToHLSLVisitor : public clang::RecursiveASTVisitor { 46 | public: 47 | CppToHLSLVisitor(const llvm::DenseMap& clangFuncToThuscFunc, 48 | const ShaderClass* shaderClass, 49 | clang::Rewriter& rewriter) 50 | : clangFuncToThuscFunc(clangFuncToThuscFunc) 51 | , shaderClass(shaderClass) 52 | , context(shaderClass->classDecl->getASTContext()) 53 | , rewriter(rewriter) 54 | , diagEngine(rewriter.getSourceMgr().getDiagnostics()) 55 | {} 56 | 57 | bool VisitStmt(clang::Stmt* stmt); 58 | bool VisitDeclRefExpr(clang::DeclRefExpr* declRefExpr); 59 | bool VisitCallExpr(clang::CallExpr* callExpr); 60 | bool VisitMemberExpr(clang::MemberExpr* memberExpr); 61 | 62 | void pushNestedShaderClass(const ShaderClass* shaderClassParam, llvm::StringRef prefix) { 63 | shaderClassStack.push_back(std::make_pair(shaderClass, implicitThisOverride)); 64 | shaderClass = shaderClassParam; 65 | implicitThisOverride = prefix.str(); 66 | } 67 | 68 | void popNestedShaderClass() { 69 | shaderClass = shaderClassStack.back().first; 70 | implicitThisOverride = shaderClassStack.back().second; 71 | shaderClassStack.pop_back(); 72 | } 73 | 74 | void undoRewrites(); 75 | 76 | private: 77 | // Helper methods 78 | void replaceTextWithUndo(llvm::StringRef newText, std::function f); 79 | bool rewriteUniformMemberAccess(const clang::MemberExpr* memberExpr); 80 | 81 | const llvm::DenseMap& clangFuncToThuscFunc; 82 | const ShaderClass* shaderClass; 83 | const clang::ASTContext& context; 84 | clang::Rewriter& rewriter; 85 | clang::DiagnosticsEngine& diagEngine; 86 | 87 | std::vector> shaderClassStack; 88 | std::string implicitThisOverride = ""; 89 | 90 | std::stack, unsigned, std::string>> undoStack; 91 | }; 92 | 93 | }; // namespace thusc 94 | -------------------------------------------------------------------------------- /source/thusc/ThuscCommon.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #define DISABLE_WARNINGS_LLVM \ 32 | __pragma(warning(push, 0)) /* Disable all warnings before including LLVM/Clang headers */ \ 33 | __pragma(warning(disable : 4146)) /* Disable this specific warning too (which is reported as an error) */ 34 | 35 | #define ENABLE_WARNINGS_LLVM \ 36 | __pragma(warning(pop)) /* Reenable warnings */ 37 | 38 | #define THUSC_H_PATH "G:\\workspace\\thusc\\include\\thusc.h" 39 | 40 | #define THUSC_FOWRARDING_FUNCTION(functionName) "template static auto " functionName "(Args&&... args) -> decltype(thusc::" functionName "(Forward(args)...)) { return " functionName "(Forward(args)...); }" 41 | 42 | #define HLSLPP_FOWRARDING_FUNCTION(functionName) "template static auto " functionName "(Args&&... args) -> decltype(hlslpp::" functionName "(Forward(args)...)) { return " functionName "(Forward(args)...); }" 43 | 44 | #define THUSC_TYPE_ADAPTERS \ 45 | THUSC_FOWRARDING_FUNCTION("sincos") \ 46 | THUSC_FOWRARDING_FUNCTION("max") \ 47 | THUSC_FOWRARDING_FUNCTION("min") \ 48 | HLSLPP_FOWRARDING_FUNCTION("all") \ 49 | HLSLPP_FOWRARDING_FUNCTION("any") \ 50 | "" 51 | 52 | #define THUSC_SHADER_SPELLING "ThuscShader" 53 | 54 | #define THUSC_UNIFORM_PREFIX "ThuscUniform-" 55 | #define THUSC_UNIFORM_SPELLING THUSC_UNIFORM_PREFIX 56 | 57 | #define THUSC_PERMUTATION_PREFIX "ThuscPermutation-" 58 | #define THUSC_PERMUTATION_ENUM_SPELLING THUSC_PERMUTATION_PREFIX "Enum" 59 | #define THUSC_PERMUTATION_BOOL_SPELLING THUSC_PERMUTATION_PREFIX "Bool" 60 | #define THUSC_PERMUTATION_INT_SPELLING THUSC_PERMUTATION_PREFIX "Int" 61 | #define THUSC_PERMUTATION_SPARSE_INT_SPELLING THUSC_PERMUTATION_PREFIX "SparseInt" 62 | #define THUSC_PERMUTATION_SHADERCLASS_SPELLING THUSC_PERMUTATION_PREFIX "ShaderClass" 63 | 64 | #define THUSC_HLSL_SEMANTIC_PREFIX "ThuscHLSLSemantic-" 65 | 66 | #define THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX THUSC_HLSL_SEMANTIC_PREFIX "ComputeShader-" 67 | #define THUSC_SV_DISPATCH_THREAD_ID_SPELLING THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX "SV_DispatchThreadID" 68 | #define THUSC_SV_GROUP_THREAD_ID_SPELLING THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX "SV_GroupThreadID" 69 | #define THUSC_SV_GROUP_ID_SPELLING THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX "SV_GroupID" 70 | #define THUSC_SV_GROUP_INDEX_SPELLING THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX "SV_GroupIndex" 71 | 72 | #define THUSC_SHADER_ENTRY_PREFIX "ThuscShaderEntry-" 73 | #define THUSC_ENTRY_COMPUTE_SPELLING THUSC_SHADER_ENTRY_PREFIX "Compute" 74 | #define THUSC_ENTRY_PIXEL_SPELLING THUSC_SHADER_ENTRY_PREFIX "Pixel" 75 | 76 | #define THUSC_GPU_FN_SPELLING "Thusc-GPU" 77 | #define THUSC_HLSL_PRECODE_SPELLING "Thusc-PrecodeHLSL" 78 | #define THUSC_HLSL_POSTCODE_SPELLING "Thusc-PostcodeHLSL" 79 | #define THUSC_HLSL_INOUT_SPELLING "Thusc-inout" 80 | 81 | -------------------------------------------------------------------------------- /source/thusc/ThuscBackend.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscCommon.h" 32 | #include "ThuscShaderIR.h" 33 | 34 | DISABLE_WARNINGS_LLVM 35 | #include "clang/AST/DeclCXX.h" 36 | #include "clang/Basic/Diagnostic.h" 37 | #include "clang/Basic/LangOptions.h" 38 | #include "clang/Basic/SourceManager.h" 39 | #include "clang/Rewrite/Core/Rewriter.h" 40 | 41 | #include "llvm/Support/MemoryBuffer.h" 42 | ENABLE_WARNINGS_LLVM 43 | 44 | #define WRITELN(X) X << ((X.empty()) ? "" : "\n") 45 | 46 | class ThuscBackendBase { 47 | public: 48 | ThuscBackendBase(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter) 49 | : shaderIR(shaderIR) 50 | , sourceMgr(rewriter.getSourceMgr()) 51 | , langOpts(rewriter.getLangOpts()) 52 | , diagEngine(rewriter.getSourceMgr().getDiagnostics()) 53 | , rewriter(rewriter) 54 | {} 55 | 56 | virtual bool Output(const clang::FileID currentFileID) = 0; 57 | 58 | protected: 59 | const thusc::ShaderIR& shaderIR; 60 | const clang::SourceManager& sourceMgr; 61 | const clang::LangOptions& langOpts; 62 | clang::DiagnosticsEngine& diagEngine; 63 | clang::Rewriter& rewriter; 64 | }; 65 | 66 | class ThuscBackendHost : public ThuscBackendBase { 67 | public: 68 | ThuscBackendHost(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, llvm::StringRef gpuOutputDirectory) 69 | : ThuscBackendBase(shaderIR, rewriter) 70 | , gpuOutputDir(gpuOutputDirectory) 71 | {} 72 | 73 | protected: 74 | std::string gpuOutputDir; 75 | }; 76 | 77 | class ThuscBackendGPU : public ThuscBackendBase { 78 | public: 79 | ThuscBackendGPU(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, llvm::StringRef gpuOutputDirectory) 80 | : ThuscBackendBase(shaderIR, rewriter) 81 | , gpuOutputDir(gpuOutputDirectory) 82 | {} 83 | 84 | protected: 85 | std::string gpuOutputDir; 86 | }; 87 | 88 | template 89 | class ThuscBackend: public ThuscBackendBase { 90 | public: 91 | ThuscBackend(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, 92 | llvm::StringRef gpuOutputDirectory, llvm::StringRef gpuOutputDirectoryVirtual) 93 | : ThuscBackendBase(shaderIR, rewriter) 94 | , hostBackend(shaderIR, rewriter, gpuOutputDirectoryVirtual) 95 | , gpuBackend(shaderIR, rewriter, gpuOutputDirectory) 96 | {} 97 | 98 | virtual bool Output(const clang::FileID currentFileID) override { 99 | // GPU backend function must be called first 100 | // The host backend rewrites the source code, including deleting functions, 101 | // so the GPU backend must be called first so it has the chance to use the unaltered code 102 | return gpuBackend.Output(currentFileID) && hostBackend.Output(currentFileID); 103 | } 104 | 105 | protected: 106 | HostBackendType hostBackend; 107 | GPUBackendType gpuBackend; 108 | }; 109 | 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thusc 2 | Translator for Heterogeneous Unified Shaders in C++ 3 | 4 | This repository contains source code supporting the High Performance Graphics 2022 paper: 5 | 6 | **Supporting Unified Shader Specialization by Co-opting C++ Features**\ 7 | [Kerry A. Seitz, Jr.](https://seitz.tech/), Theresa Foley, [Serban D. Porumbescu](http://graphics.cs.ucdavis.edu/~porumbes), and [John D. Owens](https://www.ece.ucdavis.edu/~jowens/)\ 8 | Proceedings of the ACM on Computer Graphics and Interactive Techniques (PACMCGIT)\ 9 | Volume 5 Issue 3, July 2022\ 10 | Article No. 25 11 | 12 | DOI: [https://doi.org/10.1145/3543866](https://doi.org/10.1145/3543866)\ 13 | Code: [https://github.com/owensgroup/UnifiedShaderSpecialization](https://github.com/owensgroup/UnifiedShaderSpecialization) 14 | 15 | 16 | Getting Started 17 | =============== 18 | 19 | **Note:** This code is currently tested only on 64-bit Windows. 20 | 21 | Required Software 22 | ================= 23 | 24 | 1) Microsoft Visual Studio 2019: [https://visualstudio.microsoft.com/vs/older-downloads/](https://visualstudio.microsoft.com/vs/older-downloads/) 25 | 26 | **Note:** Also known as Microsoft Visual Studio 16.0 27 | 28 | 2) LLVM and Clang version 11.0.0: [https://llvm.org/](https://llvm.org/) 29 | 30 | **Note:** We recommend building LLVM 11 from source with the following CMake arguments: 31 | 32 | * `-DLLVM_ENABLE_PROJECTS=clang` 33 | * `-DLLVM_TARGETS_TO_BUILD=X86` 34 | * `-DLLVM_USE_CRT_RELWITHDEBINFO=MDd` 35 | * `-DMSVC_DIA_SDK_DIR=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/DIA SDK` 36 | * (Modify path appropriately for your install of Visual Studio 2019) 37 | 38 | **Note 2:** We recommend building the `Release` and `RelWithDebInfo` builds of LLVM and Clang, which are used with thusc's `Release` and `Debug` builds, respectively. However, you can change this by modifying `$THSUC/source/thusc/LLVMSpecificProperties-Release.props` and `$THUSC/source/thusc/LLVMSpecificProperties-Debug.props` to build against different LLVM build types. 39 | 40 | 41 | Building 42 | ======== 43 | 44 | 1) Clone the thusc repository. We'll call the directory into which you cloned the repository: `$THUSC` 45 | 46 | ```Shell 47 | # Make sure to clone with --recursive 48 | git clone --recursive https://github.com/owensgroup/UnifiedShaderSpecialization.git 49 | ``` 50 | 51 | **Note:** If you didn't clone with the `--recursive` flag, then you need to manually clone the submodules: 52 | 53 | ```Shell 54 | cd $THUSC 55 | git submodule update --init --recursive 56 | ``` 57 | 58 | 2) Modify `$THUSC/source/thusc/ThuscCommon.h` as follows: 59 | 60 | * Modify line 38 `#define THUSC_H_PATH "G:\\workspace\\thusc\\include\\thusc.h"` to point to your cloned copy of the `$THUSC/include/thusc.h` file. 61 | 62 | 3) Modify `$THUSC/source/thusc/LLVMSpecificProperties.props` as follows: 63 | 64 | * (Optional) Modify line 5 `$(SolutionDir)\..\llvm\` to point to the location of the parent directory to your LLVM source and build directories. 65 | * Modify line 6 `$(LLVMDir)\llvm-project` to point to the location of your LLVM top-level source directory. This directory should contain subdirectories for `llvm` and `clang`. 66 | * Modify line 7 `$(LLVMDir)\build-11.0.0` to point to the location of your LLVM build directory. 67 | 68 | 4) Build thusc by opening `thusc.sln` and building via Visual Studio 2019. The solution provides both `Debug` and `Release` builds. 69 | 70 | 5) You should now be able to run the translator tool. Run with `-h` to see the command-line arguments. 71 | 72 | 73 | Tips for use with Unreal Engine 4 (UE4) 74 | ======================================= 75 | 76 | In the paper, the C++ attribute `[[ShaderClass]]` is used to denote a ShaderClass. However, UE4 uses this identifier already. Instead, use `[[ThuscShader]]` to denote a ShaderClass. 77 | 78 | 79 | If you are writing code that `#includes` UE4 header files, we recommend generating a [Clang Compilation Database](https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) from UE4. The Unreal Build Tool can generate this database for you: 80 | 81 | 1) Unreal Build Tool expects LLVM and Clang to be installed at `C:\Program Files\LLVM` (it searches for `bin\clang-cl.exe` at that location). If you did not install LLVM and Clang to this location, you can symbolically link `C:\Program Files\LLVM` to your build. 82 | 83 | 2) You can then generate the compilation database by running this command: 84 | 85 | ```Shell 86 | UnrealEngine\Engine\Build\BatchFiles\Build.bat -mode=GenerateClangDatabase -Target="UE4Editor Win64 Development" -WaitMutex 87 | ``` 88 | 89 | 3) This generated file is very large, and the translator tool takes a long time to parse it. We recommend making a copy and editing it to contain just the portions you need. 90 | -------------------------------------------------------------------------------- /source/thusc/ThuscBackendHLSL.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscBackend.h" 32 | 33 | #include "ThuscCppToHLSLTranslator.h" 34 | 35 | DISABLE_WARNINGS_LLVM 36 | #include "clang/AST/RecursiveASTVisitor.h" 37 | ENABLE_WARNINGS_LLVM 38 | 39 | namespace thusc { 40 | 41 | // TODO Fix this hack. It's a quick-and-dirty way to get the corresponding command line option to this file 42 | extern bool outputAllGPUFuncs; 43 | 44 | } // namespace thusc 45 | 46 | class ThuscBackendHLSL : public ThuscBackendGPU { 47 | public: 48 | ThuscBackendHLSL(const thusc::ShaderIR& shaderIR, clang::Rewriter& rewriter, llvm::StringRef gpuOutputDirectory) 49 | : ThuscBackendGPU(shaderIR, rewriter, gpuOutputDirectory) 50 | { 51 | for (const auto& gpuFunc : shaderIR.gpuFunctions) { 52 | clangFuncToThuscFunc.insert(std::make_pair(gpuFunc->getFunctionDecl(), gpuFunc.get())); 53 | } 54 | 55 | for (const auto& shaderClass : shaderIR.getShaderClasses()) { 56 | for (const auto& gpuMethod : shaderClass->getGPUMethods()) { 57 | clangFuncToThuscFunc.insert(std::make_pair(gpuMethod->getFunctionDecl(), gpuMethod.get())); 58 | } 59 | } 60 | } 61 | 62 | virtual bool Output(const clang::FileID currentFileID) override; 63 | 64 | private: 65 | bool OutputShaderClass(const thusc::ShaderClass* shaderClass); 66 | 67 | void OutputPermutationShaderClassFields(const thusc::ShaderClass* shaderClass, llvm::raw_ostream& stream) const; 68 | void OutputPermutationFields(const thusc::ShaderClass* shaderClass, llvm::raw_ostream& stream) const; 69 | void OutputUniformFields(const thusc::ShaderClass*, llvm::raw_ostream& stream, llvm::StringRef prefix = "") const; 70 | 71 | void OutputSelection(const thusc::PermutationShaderClassSelection& selection, llvm::raw_ostream& stream, llvm::StringRef prevPrefix = "" ) const; 72 | 73 | void OutputUniformFieldSimple(const thusc::UniformField* uniform, llvm::raw_ostream& stream, const llvm::Twine& prefix) const; 74 | void OutputFlattenedUniformStruct(const clang::CXXRecordDecl* classDecl, llvm::raw_ostream& stream, const llvm::Twine& prefix) const; 75 | void OutputUniformFieldStruct(const thusc::UniformField* uniform, llvm::raw_ostream& stream, const llvm::Twine& prefix) const; 76 | void OutputUniformField(const thusc::UniformField* uniform, llvm::raw_ostream& stream, const llvm::Twine& prefix) const; 77 | 78 | void OutputFunctionDeclaration(const thusc::GPUFunctionBase* gpuFunc, llvm::raw_ostream& stream, bool shouldOutputSemantics, llvm::StringRef prefix = "") const; 79 | void OutputFunctionBody(const clang::FunctionDecl* funcDecl, llvm::raw_ostream& stream) const; 80 | 81 | void OutputGPUFunction(const thusc::GPUFunction* gpuFunc, llvm::raw_ostream& stream, llvm::StringRef prefix = "") const; 82 | 83 | void OutputShaderEntryCommon(const thusc::EntryPoint* entryPoint, llvm::raw_ostream& stream) const; 84 | void OutputComputeShaderEntry(const thusc::ComputeEntryPoint* entryPoint, llvm::raw_ostream& stream) const; 85 | 86 | llvm::DenseMap clangFuncToThuscFunc; 87 | std::unique_ptr translator = nullptr; 88 | }; 89 | 90 | -------------------------------------------------------------------------------- /include/thusc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #include "RenderGraphResources.h" 34 | 35 | __pragma(warning(push, 0)) 36 | __pragma(warning(disable : 4582)) 37 | __pragma(warning(disable : 4668)) 38 | #define HLSLPP_SCALAR 39 | #include "../external/hlslpp/include/hlsl++.h" 40 | __pragma(warning(pop)) 41 | 42 | #define BRANCH 43 | #define UNROLL 44 | 45 | 46 | namespace thusc { 47 | template 48 | T1 max(T1 f1, T2 f2) { return (f2 > f1) ? f2 : f1; } 49 | 50 | template 51 | T1 min(T1 f1, T2 f2) { return (f2 < f1) ? f2 : f1; } 52 | 53 | template 54 | T rcp(T f) { return hlslpp::rcp(f); } 55 | 56 | template 57 | void sincos(const hlslpp::float1& f, hlslpp::swizzle1& s, hlslpp::swizzle1& c) { 58 | hlslpp::float1 sinFloat = s; 59 | hlslpp::float1 cosFloat = c; 60 | hlslpp::sincos(f, sinFloat, cosFloat); 61 | s = sinFloat; 62 | c = cosFloat; 63 | } 64 | 65 | template 66 | void sincos(const T& f, T& s, T& c) { hlslpp::sincos(f, s, c); } 67 | 68 | class SamplerState { 69 | public: 70 | SamplerState operator=(FRHISamplerState* rhs) { 71 | data = rhs; 72 | return *this; 73 | } 74 | 75 | operator FRHISamplerState*() const { 76 | return data; 77 | } 78 | 79 | private: 80 | FRHISamplerState* data; 81 | }; 82 | 83 | template 84 | class Texture2DBase { 85 | public: 86 | Texture2DBase& operator=(const UE4Type& rhs) { 87 | data = rhs; 88 | return *this; 89 | } 90 | 91 | operator UE4Type() const { 92 | return data; 93 | } 94 | 95 | // GPU-only functions 96 | #ifndef THUSC_HOST 97 | T SampleLevel(SamplerState samplerState, hlslpp::float2 location, float lod, hlslpp::int2 offset = hlslpp::int2(0)) const { 98 | return T(0); 99 | } 100 | 101 | T Load(hlslpp::int3 in) const { 102 | return T(0); 103 | } 104 | #endif // ndef THUSC_HOST 105 | 106 | private: 107 | UE4Type data; 108 | }; 109 | 110 | template 111 | class RWTexture2D { 112 | public: 113 | RWTexture2D& operator=(const FRDGTextureUAVRef& rhs) { 114 | data = rhs; 115 | return *this; 116 | } 117 | 118 | operator FRDGTextureUAVRef() const { 119 | return data; 120 | } 121 | 122 | // GPU-only functions 123 | #ifndef THUSC_HOST 124 | T operator[](const hlslpp::uint2 pos) const { 125 | return T(); 126 | } 127 | #endif // ndef THUSC_HOST 128 | 129 | 130 | private: 131 | FRDGTextureUAVRef data; 132 | }; 133 | 134 | template 135 | #ifdef THUSC_HOST 136 | class GlobalUniformBuffer { 137 | #else 138 | class GlobalUniformBuffer : public T { 139 | #endif // THUSC_HOST 140 | public: 141 | GlobalUniformBuffer& operator=(const TUniformBufferRef& rhs) { 142 | data = rhs; 143 | return *this; 144 | } 145 | 146 | operator TUniformBufferRef() const { 147 | return data; 148 | } 149 | 150 | private: 151 | TUniformBufferRef data; 152 | }; 153 | } // namespace thusc 154 | 155 | using float2 = hlslpp::float2; 156 | using float3 = hlslpp::float3; 157 | using float4 = hlslpp::float4; 158 | using int1 = hlslpp::int1; 159 | using int2 = hlslpp::int2; 160 | using int3 = hlslpp::int3; 161 | using int4 = hlslpp::int4; 162 | using uint = unsigned int; 163 | using uint2 = hlslpp::uint2; 164 | using uint3 = hlslpp::uint3; 165 | using uint4 = hlslpp::uint4; 166 | using bool2 = hlslpp::uint2; 167 | 168 | #define THUSC_FOWRARDING_FUNCTION(functionName) template static auto functionName (Args&&... args) -> decltype(thusc:: functionName (Forward(args)...)) { return functionName (Forward(args)...); } 169 | 170 | #define HLSLPP_FOWRARDING_FUNCTION(functionName) template static auto functionName (Args&&... args) -> decltype(hlslpp:: functionName (Forward(args)...)) { return functionName (Forward(args)...); } 171 | 172 | THUSC_FOWRARDING_FUNCTION(sincos) 173 | THUSC_FOWRARDING_FUNCTION(max) 174 | THUSC_FOWRARDING_FUNCTION(min) 175 | THUSC_FOWRARDING_FUNCTION(rcp) 176 | //HLSLPP_FOWRARDING_FUNCTION(all) 177 | //HLSLPP_FOWRARDING_FUNCTION(any) 178 | 179 | //template 180 | //using min = thusc::min; 181 | // 182 | //template 183 | //using max = thusc::max; 184 | 185 | using SamplerState = thusc::SamplerState; 186 | 187 | using Texture2D = thusc::Texture2DBase; 188 | 189 | template 190 | using Texture2DSRV = thusc::Texture2DBase; 191 | 192 | template 193 | using RWTexture2D = thusc::RWTexture2D; 194 | 195 | template 196 | using GlobalUniformBuffer = thusc::GlobalUniformBuffer; 197 | 198 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Bb]uild/ 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | ## 6 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 7 | 8 | # User-specific files 9 | *.rsuser 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Mono auto generated files 19 | mono_crash.* 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | x64/ 27 | x86/ 28 | [Aa][Rr][Mm]/ 29 | [Aa][Rr][Mm]64/ 30 | bld/ 31 | [Bb]in/ 32 | [Oo]bj/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # StyleCop 67 | StyleCopReport.xml 68 | 69 | # Files built by Visual Studio 70 | *_i.c 71 | *_p.c 72 | *_h.h 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.iobj 77 | *.pch 78 | *.pdb 79 | *.ipdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *_wpftmp.csproj 90 | *.log 91 | *.vspscc 92 | *.vssscc 93 | .builds 94 | *.pidb 95 | *.svclog 96 | *.scc 97 | 98 | # Chutzpah Test files 99 | _Chutzpah* 100 | 101 | # Visual C++ cache files 102 | ipch/ 103 | *.aps 104 | *.ncb 105 | *.opendb 106 | *.opensdf 107 | *.sdf 108 | *.cachefile 109 | *.VC.db 110 | *.VC.VC.opendb 111 | 112 | # Visual Studio profiler 113 | *.psess 114 | *.vsp 115 | *.vspx 116 | *.sap 117 | 118 | # Visual Studio Trace Files 119 | *.e2e 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | -------------------------------------------------------------------------------- /source/thusc/ThuscFrontendAction.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscBackend.h" 32 | #include "ThuscCommon.h" 33 | #include "ThuscShaderIR.h" 34 | 35 | DISABLE_WARNINGS_LLVM 36 | #include "clang/AST/ASTContext.h" 37 | #include "clang/AST/DeclCXX.h" 38 | #include "clang/AST/RecursiveASTVisitor.h" 39 | #include "clang/Frontend/ASTConsumers.h" 40 | #include "clang/Frontend/CompilerInstance.h" 41 | #include "clang/Frontend/FrontendAction.h" 42 | #include "clang/Rewrite/Core/Rewriter.h" 43 | #include "clang/Tooling/Tooling.h" 44 | 45 | #include "llvm/ADT/StringRef.h" 46 | ENABLE_WARNINGS_LLVM 47 | 48 | class ThuscASTVisitor : public clang::RecursiveASTVisitor { 49 | public: 50 | ThuscASTVisitor(clang::Rewriter& r) 51 | : rewriter(r) 52 | , sourceMgr(r.getSourceMgr()) 53 | , langOpts(r.getLangOpts()) 54 | , diagEngine(r.getSourceMgr().getDiagnostics()) 55 | , printingPolicy(langOpts) 56 | { 57 | printingPolicy.SuppressUnwrittenScope = 1; 58 | } 59 | 60 | void setShaderIR(thusc::ShaderIR* IR) { 61 | shaderIR = IR; 62 | } 63 | 64 | bool VisitCXXRecordDecl(clang::CXXRecordDecl* classDecl); 65 | bool VisitFunctionDecl(clang::FunctionDecl* funcDecl); 66 | bool VisitDecl(clang::Decl* Decl); 67 | 68 | private: 69 | bool HandleUniformFields(clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 70 | bool HandlePermutationFields(clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 71 | bool HandleShaderEntryFunctions(const clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 72 | 73 | bool HandleOtherGPUDecls(const clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 74 | bool HandleGPUMethods(const clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 75 | bool HandleHLSLPrecodePostcode(const clang::CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass); 76 | 77 | // Helper functions 78 | std::unique_ptr CreateGPUFunctionIR(const clang::FunctionDecl* funcDecl); 79 | thusc::ShaderParameterSemanticKind getComputeEntryParameterSemantic(const clang::ParmVarDecl* param, 80 | const clang::CXXRecordDecl* shaderClassDecl) const; 81 | 82 | clang::Rewriter& rewriter; 83 | thusc::ShaderIR* shaderIR = nullptr; 84 | 85 | const clang::SourceManager& sourceMgr; 86 | const clang::LangOptions& langOpts; 87 | clang::DiagnosticsEngine& diagEngine; 88 | clang::PrintingPolicy printingPolicy; 89 | }; 90 | 91 | template 92 | class ThuscASTConsumer : public clang::ASTConsumer { 93 | public: 94 | ThuscASTConsumer(clang::Rewriter& r, llvm::StringRef gpuOutputDirectory, llvm::StringRef gpuOutputDirectoryVirtual) 95 | : visitor(r) 96 | , rewriter(r) 97 | , gpuOutputDir(gpuOutputDirectory) 98 | , gpuOutputDirVirtual(gpuOutputDirectoryVirtual) 99 | {} 100 | 101 | virtual void HandleTranslationUnit(clang::ASTContext& context) override { 102 | thusc::ShaderIR shaderIR; 103 | visitor.setShaderIR(&shaderIR); 104 | 105 | visitor.TraverseDecl(context.getTranslationUnitDecl()); 106 | 107 | BackendType backend(shaderIR, rewriter, gpuOutputDir, gpuOutputDirVirtual); 108 | backend.Output(rewriter.getSourceMgr().getMainFileID()); 109 | } 110 | 111 | private: 112 | ThuscASTVisitor visitor; 113 | clang::Rewriter& rewriter; 114 | llvm::StringRef gpuOutputDir; 115 | llvm::StringRef gpuOutputDirVirtual; 116 | }; 117 | 118 | template 119 | class ThuscFrontendAction : public clang::ASTFrontendAction { 120 | public: 121 | ThuscFrontendAction(llvm::raw_ostream& outputStream, llvm::StringRef gpuOutputDirectory, llvm::StringRef gpuOutputDirectoryVirtual) 122 | : outstream(outputStream), gpuOutputDir(gpuOutputDirectory), gpuOutputDirVirtual(gpuOutputDirectoryVirtual) {} 123 | 124 | void EndSourceFileAction() override { 125 | clang::SourceManager& sourceMgr = rewriter.getSourceMgr(); 126 | const clang::FileID fileID = sourceMgr.getMainFileID(); 127 | const clang::FileEntry* fileEntry = sourceMgr.getFileEntryForID(sourceMgr.getMainFileID()); 128 | 129 | outstream << "// Generated from " << fileEntry->getName() << "\n\n"; 130 | outstream << "#define THUSC_HOST\n"; 131 | outstream << "#include \"" THUSC_H_PATH "\"\n"; 132 | outstream << "#undef THUSC_HOST\n\n"; 133 | rewriter.getEditBuffer(fileID).write(outstream); 134 | } 135 | 136 | std::unique_ptr CreateASTConsumer( 137 | clang::CompilerInstance& compilerInstance, llvm::StringRef inFile) override { 138 | 139 | rewriter.setSourceMgr(compilerInstance.getSourceManager(), compilerInstance.getLangOpts()); 140 | 141 | return std::make_unique>(rewriter, gpuOutputDir, gpuOutputDirVirtual); 142 | } 143 | 144 | private: 145 | llvm::raw_ostream& outstream; 146 | llvm::StringRef gpuOutputDir; 147 | llvm::StringRef gpuOutputDirVirtual; 148 | 149 | clang::Rewriter rewriter; 150 | }; 151 | 152 | template 153 | class ThuscFrontendActionFactory : public clang::tooling::FrontendActionFactory { 154 | public: 155 | ThuscFrontendActionFactory(llvm::raw_ostream& outputStream, llvm::StringRef gpuOutputDirectory, llvm::StringRef gpuOutputDirectoryVirtual) 156 | : outstream(outputStream), gpuOutputDir(gpuOutputDirectory), gpuOutputDirVirtual(gpuOutputDirectoryVirtual) {} 157 | 158 | std::unique_ptr create() override { 159 | // TODO Clang tools are often meant to be run on multiple files simultaneously, 160 | // so using the same output stream for each instance of the FrontendAction created 161 | // is probably a bad idea. However, it's fine for now, since thusc.cpp's main() 162 | // guarantees that only one source file will be processed per invocation of main(). 163 | return std::make_unique< ThuscFrontendAction >(outstream, gpuOutputDir, gpuOutputDirVirtual); 164 | } 165 | 166 | private: 167 | llvm::raw_ostream& outstream; 168 | llvm::StringRef gpuOutputDir; 169 | llvm::StringRef gpuOutputDirVirtual; 170 | }; 171 | 172 | -------------------------------------------------------------------------------- /source/thusc/ThuscCppToHLSLTranslator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include "ThuscCppToHLSLTranslator.h" 30 | 31 | #include "ClangHelperFunctions.h" 32 | 33 | using namespace clang; 34 | using namespace llvm; 35 | using namespace thusc; 36 | 37 | // CppToHLSLVisitor methods 38 | 39 | void CppToHLSLVisitor::replaceTextWithUndo(StringRef newText, std::function f) { 40 | std::string origText = rewriter.getRewrittenText(f()); 41 | undoStack.push(std::make_tuple(f, unsigned(newText.size()), origText)); 42 | 43 | rewriter.ReplaceText(f(), newText); 44 | } 45 | 46 | void CppToHLSLVisitor::undoRewrites() { 47 | while (!undoStack.empty()) { 48 | auto elem = undoStack.top(); 49 | undoStack.pop(); 50 | 51 | rewriter.ReplaceText(std::get<0>(elem)().getBegin(), std::get<1>(elem), std::get<2>(elem)); 52 | } 53 | } 54 | 55 | bool CppToHLSLVisitor::VisitStmt(Stmt* stmt) { 56 | //llvm::errs() << "============================================\n"; 57 | //stmt->dump(); 58 | //stmt->dumpPretty(context); 59 | 60 | return true; 61 | } 62 | 63 | bool CppToHLSLVisitor::VisitDeclRefExpr(DeclRefExpr* declRefExpr) { 64 | // Replace references to enum values (Foo::Bar) with the underscore version used in the output HLSL (Foo_Bar) 65 | // Currently only operates on enums that were used as permutation fields 66 | // TODO Make this work for non-permutation enums 67 | if (const auto* enumConstantDecl = dyn_cast(declRefExpr->getDecl())) { 68 | const auto* enumDecl = cast(enumConstantDecl->getDeclContext()); 69 | 70 | for (const auto& perm : shaderClass->permutationFields) { 71 | if (const auto* enumType = dyn_cast(perm.fieldDecl->getType())) { 72 | if (enumType->getDecl() == enumDecl) { 73 | replaceTextWithUndo(perm.type + "_" + declRefExpr->getDecl()->getNameAsString(), [declRefExpr]() { return declRefExpr->getSourceRange(); }); 74 | return true; 75 | } 76 | } 77 | } 78 | 79 | diagError(diagEngine, declRefExpr->getLocation(), "References to enum values must correspond to an enum used as a permutation parameter"); 80 | return true; 81 | } 82 | 83 | return true; 84 | } 85 | 86 | bool CppToHLSLVisitor::VisitCallExpr(CallExpr* callExpr) { 87 | const FunctionDecl* funcDecl = callExpr->getDirectCallee(); 88 | if (funcDecl == nullptr) 89 | return true; 90 | 91 | // TODO Rather than just searching for calls to known GPU functions, 92 | // we should also search for calls to builtin functions. 93 | // Then, if we find a CallExpr that calls a non-GPU and non-builtin 94 | // function, we can report an error. 95 | 96 | const auto funcMapEntry = clangFuncToThuscFunc.find(funcDecl); 97 | if (funcMapEntry != clangFuncToThuscFunc.end()) { 98 | const GPUFunction* gpuFunc = funcMapEntry->second; 99 | 100 | std::string argString = "("; 101 | if (callExpr->getNumArgs() == 0) { 102 | argString.append(")"); 103 | } 104 | else { 105 | const SourceRange argRange(callExpr->getArg(0)->getBeginLoc(), callExpr->getRParenLoc()); 106 | argString.append(rewriter.getRewrittenText(argRange)); 107 | } 108 | 109 | std::string newText = (gpuFunc->getMangledName() + argString).str(); 110 | 111 | // Replace function call with a call to the name-mangled version 112 | if (const auto* cxxMemberCallExpr = dyn_cast(callExpr)) { 113 | replaceTextWithUndo(newText, [cxxMemberCallExpr]() { return SourceRange(cxxMemberCallExpr->getExprLoc(), cxxMemberCallExpr->getRParenLoc()); }); 114 | } 115 | else { 116 | replaceTextWithUndo(newText, [callExpr]() { return callExpr->getSourceRange(); }); 117 | } 118 | 119 | } 120 | 121 | return true; 122 | } 123 | 124 | bool CppToHLSLVisitor::VisitMemberExpr(MemberExpr* memberExpr) { 125 | bool shouldContinue = rewriteUniformMemberAccess(memberExpr); 126 | 127 | const auto* base = memberExpr->getBase()->IgnoreImpCasts(); 128 | 129 | // For implicit this expressions, replace with the implicitThisOverride prior to the access 130 | if (const auto* thisExpr = dyn_cast(base)) { 131 | bool isPermutationFieldOrOtherGPUDecl = 132 | shaderClass->isPermutationField(memberExpr->getMemberDecl()) || shaderClass->isOtherGPUDecl(memberExpr->getMemberDecl()); 133 | 134 | if (!(isPermutationFieldOrOtherGPUDecl || implicitThisOverride.empty())) { 135 | std::string memberExprString = rewriter.getRewrittenText(memberExpr->getSourceRange()); 136 | replaceTextWithUndo(implicitThisOverride + memberExprString, [memberExpr]() { return memberExpr->getSourceRange(); }); 137 | } 138 | } 139 | // For accesses to member of ShaderClass permutation fields, change -> to _ 140 | else if (const auto* baseMemberExpr = dyn_cast(base)) { 141 | if (shaderClass->isPermutationShaderClassField(baseMemberExpr->getMemberDecl())) { 142 | replaceTextWithUndo("_", [memberExpr]() { return SourceRange(memberExpr->getOperatorLoc(), memberExpr->getOperatorLoc()); }); 143 | } 144 | } 145 | 146 | return shouldContinue; 147 | } 148 | 149 | 150 | // CppToHLSLVisitor helper methods 151 | 152 | // In UE4, struct uniform variables are flattened into individual uniforms. 153 | // So, if the MemberExpr has a base (somewhere up the chain) that is a 154 | // uniform, replace the MemberExpr with an access to the flattened uniform. 155 | bool CppToHLSLVisitor::rewriteUniformMemberAccess(const MemberExpr* memberExpr) { 156 | const Expr* base = memberExpr->getBase()->IgnoreCasts(); 157 | 158 | bool foundUniformStruct = false; 159 | while (const auto* baseMemberExpr = dyn_cast(base)) { 160 | if (baseMemberExpr->getBase()->isImplicitCXXThis()) { 161 | if (const auto* fieldDecl = dyn_cast(baseMemberExpr->getMemberDecl())) { 162 | foundUniformStruct = shaderClass->isUniformField(fieldDecl, UniformField::Kind::Struct); 163 | } 164 | break; 165 | } 166 | 167 | base = baseMemberExpr->getBase()->IgnoreCasts(); 168 | } 169 | 170 | if (!foundUniformStruct) { 171 | return true; 172 | } 173 | 174 | assert(!memberExpr->isArrow() && "Access to uniform parameter using arrow syntax (->) is not supported"); 175 | 176 | // Replace the member access dot operator with an underscore 177 | replaceTextWithUndo("_", [memberExpr]() { return SourceRange(memberExpr->getOperatorLoc(), memberExpr->getOperatorLoc()); }); 178 | 179 | return true; 180 | } 181 | 182 | -------------------------------------------------------------------------------- /source/thusc/thusc.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {9988eb8d-eb65-4bab-aeca-845341448507} 25 | thusc 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | $(SolutionDir)\build\bin\$(Platform)\$(Configuration)\ 80 | $(SolutionDir)\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 81 | 82 | 83 | false 84 | $(SolutionDir)\build\bin\$(Platform)\$(Configuration)\ 85 | $(SolutionDir)\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 86 | 87 | 88 | true 89 | $(SolutionDir)\build\bin\$(Platform)\$(Configuration)\ 90 | $(SolutionDir)\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 91 | 92 | 93 | false 94 | $(SolutionDir)\build\bin\$(Platform)\$(Configuration)\ 95 | $(SolutionDir)\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 96 | 97 | 98 | 99 | Level3 100 | true 101 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 102 | true 103 | 104 | 105 | Console 106 | true 107 | 108 | 109 | 110 | 111 | Level3 112 | true 113 | true 114 | true 115 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 116 | true 117 | 118 | 119 | Console 120 | true 121 | true 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 130 | true 131 | true 132 | 133 | 134 | Console 135 | true 136 | %(AdditionalDependencies) 137 | 138 | 139 | 140 | 141 | Level3 142 | true 143 | true 144 | true 145 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 146 | true 147 | true 148 | 149 | 150 | Console 151 | true 152 | true 153 | true 154 | %(AdditionalDependencies) 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /source/thusc/thusc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | // thusc.cpp 30 | 31 | #include "ThuscBackendUE4.h" 32 | #include "ThuscCommon.h" 33 | #include "ThuscFrontendAction.h" 34 | 35 | #include 36 | #include 37 | 38 | DISABLE_WARNINGS_LLVM 39 | #include "clang/Tooling/CommonOptionsParser.h" 40 | #include "clang/Tooling/CompilationDatabase.h" 41 | #include "clang/Tooling/Tooling.h" 42 | 43 | #include "llvm/Support/CommandLine.h" 44 | #include "llvm/Support/FileSystem.h" 45 | #include "llvm/Support/raw_ostream.h" 46 | #include "llvm/Support/ToolOutputFile.h" 47 | ENABLE_WARNINGS_LLVM 48 | 49 | using namespace clang::tooling; 50 | using namespace llvm; 51 | 52 | static cl::OptionCategory thuscOptionsCategory("thusc options"); 53 | 54 | static cl::opt hostOutputFilename("o", cl::desc("Output filename for Host code"), cl::value_desc("filename"), 55 | cl::cat(thuscOptionsCategory), cl::Required); 56 | 57 | static cl::opt gpuOutputDirectory("gpuOutDir", cl::desc("Output directory for GPU code"), cl::value_desc("directory"), 58 | cl::cat(thuscOptionsCategory), cl::Required); 59 | 60 | static cl::opt gpuOutputDirectoryVirtual("gpuOutDirVirtual", cl::desc("Corresponding virtual path for directory specified in gpuOutDir (UE4 backend only)"), cl::value_desc("directory"), 61 | cl::cat(thuscOptionsCategory), cl::Required); 62 | 63 | static cl::opt disableNameManglingOpt("disableNameMangling", cl::desc("Disables name mangling of GPU functions"), cl::cat(thuscOptionsCategory)); 64 | bool thusc::disableNameMangling = false; 65 | 66 | static cl::opt outputAllGPUFuncsOpt("outputAllGPUFuncs", cl::desc("Output all GPU functions to the HLSL file, whether or not they are used"), cl::cat(thuscOptionsCategory)); 67 | bool thusc::outputAllGPUFuncs = false; 68 | 69 | static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); 70 | 71 | #define ADD_ANNOTATION_OPTION(macroName, spelling, extraArgs) \ 72 | "--extra-arg=-D" macroName "=clang::annotate(\"" spelling "\"" extraArgs ")" 73 | 74 | void addExtraClangArgs(std::vector* args) { 75 | // TODO These extra arguments probably be specified alongside the files to compile and parsed in, 76 | // rather than be hardcoded here. 77 | static const char* extraArgs[] = { 78 | "--extra-arg=-DUNREAL_CODE_ANALYZER=1", 79 | "--extra-arg=-Wno-nonportable-include-path", 80 | "--extra-arg=-Wno-inconsistent-missing-override", 81 | "--extra-arg=-Wno-microsoft-unqualified-friend", 82 | "--extra-arg=-Wno-gnu-string-literal-operator-template", 83 | "--extra-arg=-Wno-ignored-attributes", 84 | "--extra-arg=-Wno-switch", 85 | "--extra-arg=-Wno-tautological-undefined-compare", 86 | "--extra-arg=-Wno-invalid-offsetof", 87 | 88 | // TODO This path is hardcoded. Figure out how to get this path in a more portable way. 89 | "--extra-arg=-include" THUSC_H_PATH, 90 | 91 | // Thusc-specific #defines 92 | ADD_ANNOTATION_OPTION("ThuscShader", THUSC_SHADER_SPELLING, ""), 93 | 94 | // Uniforms 95 | ADD_ANNOTATION_OPTION("uniform", THUSC_UNIFORM_SPELLING, ""), 96 | 97 | // Permutations / Specializations 98 | ADD_ANNOTATION_OPTION("permutation_enum", THUSC_PERMUTATION_ENUM_SPELLING, ""), 99 | ADD_ANNOTATION_OPTION("permutation_bool", THUSC_PERMUTATION_BOOL_SPELLING, ""), 100 | ADD_ANNOTATION_OPTION("permutation_int(x)", THUSC_PERMUTATION_INT_SPELLING, " #x"), 101 | ADD_ANNOTATION_OPTION("permutation_sparseInt(...)", THUSC_PERMUTATION_SPARSE_INT_SPELLING, " #__VA_ARGS__"), 102 | ADD_ANNOTATION_OPTION("permutation_ShaderClass", THUSC_PERMUTATION_SHADERCLASS_SPELLING, ""), 103 | 104 | ADD_ANNOTATION_OPTION("specialization_Enum", THUSC_PERMUTATION_ENUM_SPELLING, ""), 105 | ADD_ANNOTATION_OPTION("specialization_Bool", THUSC_PERMUTATION_BOOL_SPELLING, ""), 106 | ADD_ANNOTATION_OPTION("specialization_Int(x)", THUSC_PERMUTATION_INT_SPELLING, " #x"), 107 | ADD_ANNOTATION_OPTION("specialization_SparseInt(...)", THUSC_PERMUTATION_SPARSE_INT_SPELLING, " #__VA_ARGS__"), 108 | ADD_ANNOTATION_OPTION("specialization_ShaderClass", THUSC_PERMUTATION_SHADERCLASS_SPELLING, ""), 109 | 110 | // Function parameters 111 | ADD_ANNOTATION_OPTION("SV_DispatchThreadID", THUSC_SV_DISPATCH_THREAD_ID_SPELLING, ""), 112 | ADD_ANNOTATION_OPTION("SV_GroupThreadID", THUSC_SV_GROUP_THREAD_ID_SPELLING, ""), 113 | ADD_ANNOTATION_OPTION("SV_GroupID", THUSC_SV_GROUP_ID_SPELLING, ""), 114 | ADD_ANNOTATION_OPTION("SV_GroupIndex", THUSC_SV_GROUP_INDEX_SPELLING, ""), 115 | ADD_ANNOTATION_OPTION("inout", THUSC_HLSL_INOUT_SPELLING, ""), 116 | 117 | // Shader entry functions 118 | ADD_ANNOTATION_OPTION("entry_ComputeShader(x, y, z)", THUSC_ENTRY_COMPUTE_SPELLING, " #x \",\" #y \",\" #z"), 119 | ADD_ANNOTATION_OPTION("entry_PixelShader", THUSC_ENTRY_PIXEL_SPELLING, ""), 120 | 121 | // GPU functions 122 | ADD_ANNOTATION_OPTION("gpu", THUSC_GPU_FN_SPELLING, ""), 123 | ADD_ANNOTATION_OPTION("PrecodeHLSL", THUSC_HLSL_PRECODE_SPELLING, ""), 124 | ADD_ANNOTATION_OPTION("PostcodeHLSL", THUSC_HLSL_POSTCODE_SPELLING, ""), 125 | 126 | // Backend-specific #defines 127 | // TODO move these to a backend-specific file 128 | "--extra-arg=-DTHUSC_SHADER(TY, SUPERTYPE)=" 129 | "void ThuscShaderStubFunc() {};" 130 | "void AddComputePass(FRDGBuilder& GraphBuilder, FRDGEventName&& PassName, const FGlobalShaderMap* ShaderMap, FIntVector GroupCount) { }" 131 | THUSC_TYPE_ADAPTERS 132 | }; 133 | 134 | args->insert(args->end(), std::begin(extraArgs), std::end(extraArgs)); 135 | } 136 | 137 | // Returns false if the arguments did not produce a valid source path and corresponding compile command 138 | // Returns true otherwise 139 | bool validateArguments(CommonOptionsParser& optionsParser) { 140 | if (optionsParser.getSourcePathList().size() == 0) { 141 | // If no input soruce file is found, report an error 142 | llvm::errs() << "Error: No source file found for the specified arguments.\n"; 143 | return false; 144 | } 145 | else if (optionsParser.getSourcePathList().size() != 1) { 146 | // thusc should be run on one input file at a time, to produce one source-to-source translation of that file. 147 | // The translation will be written to the output file specified by -o (or stdout by default). 148 | // If multiple source paths are found, report an error. 149 | llvm::errs() << "Error: Multiple source files found for the specified arguments.\n"; 150 | return false; 151 | } 152 | 153 | const std::string sourcePath = optionsParser.getSourcePathList()[0]; 154 | const std::vector commands = optionsParser.getCompilations().getCompileCommands(sourcePath); 155 | 156 | if (commands.size() == 0) { 157 | // If there are no valid compile commands for the input file, report an error. 158 | llvm::errs() << "Error: No compile command found for input file <" << sourcePath << ">.\n"; 159 | return false; 160 | } 161 | else if (commands.size() != 1) { 162 | // Since thusc produces one source-to-source translation per input file, it cannot handle the case where 163 | // there are mutliple entries found for that file in the compilation database. So report an error. 164 | // (This may change later.) 165 | llvm::errs() << "Error: Multiple compile commands found for input file <" << sourcePath << ">. " 166 | "(The compilation database may contain multiple entries for this file, which is currently unsupported.)\n"; 167 | return false; 168 | } 169 | else { 170 | CompileCommand cc = commands[0]; 171 | 172 | if (cc.Heuristic != std::string("")) { 173 | // The heuristic used to determine compile commands when an exact match isn't found in the compilation database 174 | // does not seem to do a great job. For now, report an error if a heuristic was used to find the compile command. 175 | llvm::errs() << "Error: The compile command was determined by a heuristic, " 176 | "which might mean that the exact input file could not be found in the compilation database. " 177 | "The reported heuristic is: " << cc.Heuristic << "\n"; 178 | return false; 179 | } 180 | } 181 | 182 | // If all of the checks have passed, then the arguments have produced a valid source path and compile command 183 | return true; 184 | } 185 | 186 | int main(int argc, const char** argv) { 187 | std::vector clangArgs(argv, argv + argc); 188 | addExtraClangArgs(&clangArgs); 189 | 190 | int clangArgsCount = (int)clangArgs.size(); 191 | CommonOptionsParser optionsParser(clangArgsCount, clangArgs.data(), thuscOptionsCategory, cl::NumOccurrencesFlag::Required); 192 | 193 | if (!validateArguments(optionsParser)) { 194 | return EXIT_FAILURE; 195 | } 196 | 197 | if (clangArgsCount != clangArgs.size()) { 198 | // TODO I'm not sure of the implications if the argument count is changed, so report an error for now. 199 | llvm::errs() << "Error: CommonOptionsParser changed the argument count.\n"; 200 | return EXIT_FAILURE; 201 | } 202 | 203 | std::error_code ec; 204 | llvm::ToolOutputFile hostOutputFile(hostOutputFilename, ec, llvm::sys::fs::OpenFlags::OF_None); 205 | if (ec) { 206 | llvm::errs() << "Error: Could not open output file '" << hostOutputFilename << "': " << ec.message() << "\n"; 207 | return EXIT_FAILURE; 208 | } 209 | 210 | bool isDirectory = false; 211 | ec = llvm::sys::fs::is_directory(gpuOutputDirectory, isDirectory); 212 | if (ec) { 213 | llvm::errs() << "Error: Could not access GPU output directory '" << gpuOutputDirectory << "': " << ec.message() << "\n"; 214 | return EXIT_FAILURE; 215 | } 216 | else if (!isDirectory) { 217 | llvm::errs() << "Error: Specified GPU output directory '" << gpuOutputDirectory << "' is not a directory\n"; 218 | return EXIT_FAILURE; 219 | } 220 | 221 | 222 | // TODO Other backends? 223 | ThuscFrontendActionFactory factory(hostOutputFile.os(), gpuOutputDirectory, gpuOutputDirectoryVirtual); 224 | thusc::disableNameMangling = disableNameManglingOpt; 225 | thusc::outputAllGPUFuncs = outputAllGPUFuncsOpt; 226 | 227 | ClangTool tool(optionsParser.getCompilations(), optionsParser.getSourcePathList()); 228 | int toolExitCode = tool.run(&factory); 229 | 230 | if (toolExitCode) { 231 | return toolExitCode; 232 | } 233 | 234 | hostOutputFile.keep(); 235 | 236 | return toolExitCode; 237 | } 238 | 239 | -------------------------------------------------------------------------------- /source/thusc/ThuscShaderIR.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #pragma once 30 | 31 | #include "ThuscCommon.h" 32 | 33 | #include 34 | 35 | DISABLE_WARNINGS_LLVM 36 | #include "clang/AST/Decl.h" 37 | #include "clang/AST/DeclCXX.h" 38 | #include "llvm/Support/Casting.h" 39 | ENABLE_WARNINGS_LLVM 40 | 41 | namespace thusc { 42 | 43 | // TODO Fix this hack. It's a quick-and-dirty way to get the corresponding command line option to this file 44 | extern bool disableNameMangling; 45 | 46 | // Constants 47 | extern const char* shaderImplClassPrefix; 48 | 49 | // Forward declaration 50 | class ImplClass; 51 | struct ShaderClass; 52 | struct ShaderIR; 53 | 54 | 55 | inline 56 | std::string generateMangledName(const clang::NamedDecl* decl) { 57 | std::string ret = decl->getName().str(); 58 | 59 | if (disableNameMangling) 60 | return ret; 61 | 62 | const clang::DeclContext* context = decl->getDeclContext(); 63 | 64 | while (const auto* namedDecl = llvm::dyn_cast(context)) { 65 | ret = (namedDecl->getName() + "_" + ret).str(); 66 | context = namedDecl->getDeclContext(); 67 | } 68 | 69 | ret = "thusc_" + ret; 70 | 71 | return ret; 72 | } 73 | 74 | struct UniformField { 75 | enum class Kind { 76 | Sampler, 77 | Texture, 78 | TextureSRV, 79 | TextureUAV, 80 | Primitive, 81 | Array, 82 | Struct, 83 | GlobalUniformBuffer, 84 | Unknown, 85 | }; 86 | 87 | UniformField(const clang::FieldDecl* decl, llvm::StringRef name, llvm::StringRef type, Kind kind) 88 | : fieldDecl(decl) 89 | , name(name) 90 | , type(type) 91 | , kind(kind) 92 | { 93 | assert(kind != Kind::Unknown && "Creating a UniformField with Unknown kind"); 94 | } 95 | 96 | Kind getKind() const { return kind; } 97 | 98 | const clang::FieldDecl* fieldDecl; 99 | const std::string name; 100 | const std::string type; 101 | 102 | private: 103 | Kind kind; 104 | }; 105 | 106 | class PermutationShaderClass { 107 | public: 108 | PermutationShaderClass() = delete; 109 | 110 | PermutationShaderClass(const clang::FieldDecl* fieldDecl, llvm::StringRef name, 111 | const ShaderClass* shaderClassType, const ShaderIR* shaderIR); 112 | 113 | const llvm::StringRef getFieldName() const { return name; } 114 | 115 | const clang::FieldDecl* getFieldDecl() const { return fieldDecl; } 116 | 117 | const ShaderClass* getShaderClassType() const { return shaderClassType; } 118 | 119 | const std::vector& getValdShaderClasses() const { return validShaderClasses; } 120 | 121 | private: 122 | const clang::FieldDecl* const fieldDecl; 123 | const std::string name; 124 | const ShaderClass* shaderClassType; 125 | std::vector validShaderClasses; 126 | }; 127 | 128 | struct PermutationField { 129 | enum class Kind { 130 | Enum, 131 | Bool, 132 | Int, 133 | SparseInt, 134 | Unknown, 135 | }; 136 | 137 | PermutationField() = delete; 138 | 139 | PermutationField(const clang::FieldDecl* decl, llvm::StringRef name, llvm::StringRef type, 140 | llvm::ArrayRef valueOptions, Kind kind) 141 | : fieldDecl(decl) 142 | , name(name) 143 | , type(type) 144 | , valueOptions(valueOptions) 145 | , kind(kind) 146 | { 147 | assert(kind != Kind::Unknown && "Creating a PermutationField with Unknown kind"); 148 | } 149 | 150 | Kind getKind() const { return kind; } 151 | 152 | const clang::FieldDecl* const fieldDecl = nullptr; 153 | const std::string name; 154 | const std::string type; 155 | const std::vector valueOptions; 156 | const Kind kind; 157 | }; 158 | 159 | enum class ShaderParameterSemanticKind { 160 | // Compute shader semantics 161 | SV_DispatchThreadID, 162 | SV_GroupThreadID, 163 | SV_GroupID, 164 | SV_GroupIndex, 165 | 166 | None, 167 | Unknown, 168 | }; 169 | 170 | 171 | class GPUFunctionBase { 172 | public: 173 | struct Parameter { 174 | Parameter(llvm::StringRef name, llvm::StringRef type, ShaderParameterSemanticKind kind, bool isInout) 175 | : name(name) 176 | , type(type) 177 | , semantic(kind) 178 | , isInout(isInout) 179 | { 180 | assert(kind != ShaderParameterSemanticKind::Unknown && "Creating a GPU function parameter with Unknown kind"); 181 | } 182 | 183 | const std::string name; 184 | const std::string type; 185 | const ShaderParameterSemanticKind semantic; 186 | const bool isInout; 187 | }; 188 | 189 | const clang::FunctionDecl* getFunctionDecl() const { 190 | return funcDecl; 191 | } 192 | 193 | llvm::StringRef getSourceName() const { 194 | return sourceName; 195 | } 196 | 197 | llvm::StringRef getMangledName() const { 198 | return mangledName; 199 | } 200 | 201 | llvm::StringRef getReturnType() const { 202 | return returnType; 203 | } 204 | 205 | const std::vector& getParameters() const { 206 | return parameters; 207 | } 208 | 209 | protected: 210 | GPUFunctionBase(const clang::FunctionDecl* funcDecl, const llvm::StringRef name, const llvm::StringRef returnType) 211 | : funcDecl(funcDecl) 212 | , sourceName(name) 213 | , mangledName(generateMangledName(funcDecl)) 214 | , returnType(returnType) 215 | {} 216 | 217 | void addParameter(llvm::StringRef name, llvm::StringRef type, ShaderParameterSemanticKind semantic, bool isInout = false) { 218 | parameters.push_back(Parameter(name.str(), type.str(), semantic, isInout)); 219 | } 220 | 221 | private: 222 | const clang::FunctionDecl* const funcDecl; 223 | const std::string sourceName; 224 | const std::string mangledName; 225 | const std::string returnType; 226 | std::vector parameters; 227 | }; 228 | 229 | 230 | class GPUFunction : public GPUFunctionBase { 231 | public: 232 | GPUFunction(const clang::FunctionDecl* funcDecl, const llvm::StringRef name, const llvm::StringRef returnType, 233 | bool hostCallable) 234 | : GPUFunctionBase(funcDecl, name, returnType) 235 | , hostCallable(hostCallable) 236 | {} 237 | 238 | void addParameter(llvm::StringRef name, llvm::StringRef type, bool isInout) { 239 | GPUFunctionBase::addParameter(name, type, ShaderParameterSemanticKind::None, isInout); 240 | } 241 | 242 | bool isHostCallable() const { return hostCallable; } 243 | 244 | private: 245 | const bool hostCallable; 246 | }; 247 | 248 | 249 | class EntryPoint : public GPUFunctionBase { 250 | public: 251 | enum class EntryPointKind { 252 | Compute, 253 | Unknown, 254 | }; 255 | 256 | void addParameter(llvm::StringRef name, llvm::StringRef type, ShaderParameterSemanticKind semantic) { 257 | GPUFunctionBase::addParameter(name, type, semantic); 258 | } 259 | 260 | EntryPointKind getKind() const { return kind; } 261 | 262 | protected: 263 | EntryPoint(const clang::CXXMethodDecl* const methodDecl, const llvm::StringRef name, 264 | const llvm::StringRef returnType, const EntryPointKind kind) 265 | : GPUFunctionBase(methodDecl, name, returnType) 266 | , kind(kind) 267 | { 268 | assert(kind != EntryPointKind::Unknown && "Creating an EntryPoint with Unknown kind"); 269 | } 270 | 271 | private: 272 | const EntryPointKind kind; 273 | }; 274 | 275 | class ComputeEntryPoint : public EntryPoint { 276 | public: 277 | ComputeEntryPoint(const clang::CXXMethodDecl* const methodDecl, 278 | const llvm::StringRef name, llvm::ArrayRef threadgroupSize) 279 | : EntryPoint(methodDecl, name, "void", EntryPoint::EntryPointKind::Compute) 280 | , threadgroupSize{threadgroupSize[0].str(), threadgroupSize[1].str(), threadgroupSize[2].str()} 281 | { 282 | assert(threadgroupSize.size() == 3 && "threadgroupSize must contain exactly 3 elements"); 283 | } 284 | 285 | static bool classof(const EntryPoint* entryPoint) { 286 | return entryPoint->getKind() == EntryPointKind::Compute; 287 | } 288 | 289 | const std::string threadgroupSize[3]; 290 | }; 291 | 292 | class OtherGPUDecl { 293 | public: 294 | OtherGPUDecl(const clang::Decl* decl, bool isHost, bool hasOutputOverride = false, const std::string outputOverride = "") 295 | : decl(decl) 296 | , isHostBool(isHost) 297 | , hasOutputOverrideBool(hasOutputOverride) 298 | , outputOverride(outputOverride) 299 | {} 300 | 301 | const clang::Decl* getDecl() const { return decl; } 302 | bool isHost() const { return isHostBool; } 303 | bool hasOutputOverride() const { return hasOutputOverrideBool; } 304 | llvm::StringRef getOutputOverride() const { return outputOverride; } 305 | 306 | private: 307 | const clang::Decl* decl; 308 | const bool isHostBool; 309 | const bool hasOutputOverrideBool; 310 | const std::string outputOverride; 311 | }; 312 | 313 | class PermutationShaderClassSelection { 314 | public: 315 | PermutationShaderClassSelection(llvm::StringRef fieldName, const ShaderClass* shaderClass, 316 | unsigned int typeIDRelative, const ImplClass* implClass) 317 | : fieldName(fieldName) 318 | , shaderClass(shaderClass) 319 | , typeIDRelative(typeIDRelative) 320 | , implClass(implClass) 321 | {} 322 | 323 | const std::string fieldName; 324 | const ShaderClass* const shaderClass; 325 | const unsigned int typeIDRelative; 326 | const ImplClass* const implClass; 327 | }; 328 | 329 | class ImplClass { 330 | // The code relies on stable pointers to ImplClass instances in various places, 331 | // so ImplClass objects can only be created as unique_ptrs. 332 | public: 333 | static std::unique_ptr create(const ShaderClass* parent, unsigned int id, std::vector& selections) { 334 | return std::unique_ptr(new ImplClass(parent, id, selections)); 335 | } 336 | 337 | ImplClass(const ImplClass&) = delete; 338 | 339 | llvm::StringRef getName() const { return name; } 340 | unsigned int getID() const { return id; } 341 | 342 | const std::vector& getSelections() const { return selections; } 343 | 344 | private: 345 | ImplClass(const ShaderClass* parent, unsigned int id, const std::vector& selections) 346 | : parent(parent) 347 | , name(generateImplClassName(parent, selections)) 348 | , id(id) 349 | , selections(selections) 350 | {} 351 | 352 | static std::string generateImplClassName(const ShaderClass* parent, const std::vector& selections); 353 | 354 | const ShaderClass* const parent; 355 | const std::string name; 356 | const unsigned int id; 357 | const std::vector selections; 358 | }; 359 | 360 | struct ShaderClass { 361 | // The code relies on stable pointers to ShaderClass instances in various places, 362 | // so ShaderClass objects can only be created as unique_ptrs. 363 | public: 364 | ShaderClass(const ShaderClass&) = delete; 365 | 366 | static std::unique_ptr create() { 367 | return std::unique_ptr(new ShaderClass); 368 | } 369 | 370 | void addGPUMethod(std::unique_ptr gpuFunc); 371 | 372 | const std::vector>& getGPUMethods() const { return gpuMethods; } 373 | 374 | void setBaseShaderClass(const ShaderClass* base) { 375 | if (base == nullptr) 376 | return; 377 | 378 | baseShaderClass = base; 379 | 380 | const ShaderClass* nextBase = base; 381 | while (nextBase) { 382 | typeIDMap.insert(std::make_pair(nextBase, nextBase->getNextTypeID())); 383 | nextBase = nextBase->baseShaderClass; 384 | } 385 | 386 | // TODO This way of handling base ShaderClasses (i.e., manually pulling in its elements) 387 | // runs into issues if a subclass shadows a base class declaration or if the subclass 388 | // (or its user) needs to call a base class version of a virtual function that has been 389 | // overridden in the derived class. 390 | 391 | for (const auto& e : base->uniformFields) { uniformFields.push_back(e); } 392 | for (const auto& e : base->permutationShaderClasses) { permutationShaderClasses.push_back(e); } 393 | for (const auto& e : base->permutationFields) { permutationFields.push_back(e); } 394 | for (const auto& e : base->otherGPUDecls) { otherGPUDecls.push_back(e); } 395 | for (const auto& e : base->gpuMethods) { gpuMethods.push_back(std::make_unique(*e)); } 396 | 397 | // TODO Handle virtual functions appropriately 398 | 399 | // TODO Maybe pull in from base: 400 | // - entryPoints 401 | // - hlslPrecode / hlslPostcode 402 | } 403 | 404 | llvm::StringRef getName() const { return name; } 405 | 406 | const clang::CXXRecordDecl* classDecl = nullptr; 407 | 408 | std::string name; 409 | const ShaderClass* baseShaderClass = nullptr; 410 | bool hasShouldCompilePermutation = false; 411 | 412 | std::vector uniformFields; 413 | std::vector permutationShaderClasses; 414 | std::vector permutationFields; 415 | std::vector otherGPUDecls; 416 | std::vector> entryPoints; 417 | 418 | std::string hlslPrecode; 419 | std::string hlslPostcode; 420 | 421 | bool isIntPermutationField(llvm::StringRef fieldName) const { 422 | for (const auto& p : permutationFields) { 423 | if (fieldName == p.name && p.fieldDecl->getType()->isIntegralType(p.fieldDecl->getASTContext())) 424 | return true; 425 | } 426 | 427 | return false; 428 | } 429 | 430 | bool isUniformField(const clang::FieldDecl* fieldDecl, UniformField::Kind kind = UniformField::Kind::Unknown) const { 431 | for (const auto& uniform : uniformFields) { 432 | if (fieldDecl == uniform.fieldDecl && (kind == uniform.getKind() || kind == UniformField::Kind::Unknown)) { 433 | return true; 434 | } 435 | } 436 | 437 | return false; 438 | } 439 | 440 | bool isPermutationField(const clang::Decl* decl) const { 441 | for (const auto& permField : permutationFields) { 442 | if (permField.fieldDecl == decl) 443 | return true; 444 | } 445 | 446 | return false; 447 | } 448 | 449 | bool isPermutationShaderClassField(const clang::Decl* decl) const { 450 | for (const auto& permShaderClass : permutationShaderClasses) { 451 | if (permShaderClass.getFieldDecl() == decl) { 452 | return true; 453 | } 454 | 455 | if (permShaderClass.getShaderClassType()->isPermutationShaderClassField(decl)) { 456 | return true; 457 | } 458 | } 459 | 460 | return false; 461 | } 462 | 463 | bool isOtherGPUDecl(const clang::Decl* decl) const { 464 | for (const auto& gpuDecl : otherGPUDecls) { 465 | if (gpuDecl.getDecl() == decl) 466 | return true; 467 | } 468 | 469 | return false; 470 | } 471 | 472 | unsigned int getTypeIDRelativeTo(const ShaderClass* base) const { 473 | const auto mapEntry = typeIDMap.find(base); 474 | assert(mapEntry != typeIDMap.end() && "Cannot get TypeID relative to a non-base class"); 475 | 476 | return mapEntry->second; 477 | } 478 | 479 | void generateImplClasses() { 480 | std::vector curSelections; 481 | generateImplClassesRecur(curSelections, 0); 482 | } 483 | 484 | const std::vector>& getImplClasses() const { return implClasses; } 485 | 486 | private: 487 | ShaderClass() { 488 | typeIDMap.insert(std::make_pair(this, getNextTypeID())); 489 | } 490 | 491 | unsigned int getNextTypeID() const { return nextTypeID++; } 492 | 493 | void generateImplClassesRecur(std::vector& curSelections, unsigned int fieldPos) { 494 | if (fieldPos >= permutationShaderClasses.size()) { 495 | implClasses.push_back(ImplClass::create(this, (unsigned int)implClasses.size(), curSelections)); 496 | return; 497 | } 498 | 499 | const PermutationShaderClass& field = permutationShaderClasses[fieldPos]; 500 | 501 | for (const auto* fieldShaderClass : field.getValdShaderClasses()) { 502 | for (const auto& fieldImplClass : fieldShaderClass->getImplClasses()) { 503 | curSelections.push_back(PermutationShaderClassSelection(field.getFieldName(), fieldShaderClass, 504 | fieldShaderClass->getTypeIDRelativeTo(field.getShaderClassType()), fieldImplClass.get())); 505 | generateImplClassesRecur(curSelections, fieldPos + 1); 506 | curSelections.pop_back(); 507 | } 508 | } 509 | } 510 | 511 | std::vector> gpuMethods; 512 | 513 | mutable unsigned int nextTypeID = 0; 514 | std::vector> implClasses; 515 | llvm::DenseMap typeIDMap; 516 | }; 517 | 518 | struct ShaderIR { 519 | std::vector otherGPUDecls; 520 | std::vector> gpuFunctions; 521 | 522 | inline 523 | void addShaderClass(std::unique_ptr shaderClass) { 524 | clangClassToShaderClass.insert(std::make_pair(shaderClass->classDecl, shaderClass.get())); 525 | shaderClass->generateImplClasses(); 526 | shaderClasses.push_back(std::move(shaderClass)); 527 | } 528 | 529 | inline 530 | const std::vector>& getShaderClasses() const { 531 | return shaderClasses; 532 | } 533 | 534 | inline 535 | const ShaderClass* getShaderClass(const clang::CXXRecordDecl* classDecl) const { 536 | if (classDecl == nullptr) return nullptr; 537 | 538 | const auto mapEntry = clangClassToShaderClass.find(classDecl); 539 | return (mapEntry == clangClassToShaderClass.end()) ? nullptr : mapEntry->second; 540 | } 541 | 542 | private: 543 | std::vector> shaderClasses; 544 | llvm::DenseMap clangClassToShaderClass; 545 | }; 546 | 547 | } 548 | -------------------------------------------------------------------------------- /source/thusc/ThuscBackendHLSL.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include "ThuscBackendHLSL.h" 30 | 31 | #include "ClangHelperFunctions.h" 32 | #include "TypeHelpers.h" 33 | 34 | DISABLE_WARNINGS_LLVM 35 | #include "clang/AST/ASTContext.h" 36 | #include "clang/Basic/FileManager.h" 37 | #include "clang/Basic/SourceManager.h" 38 | 39 | #include "llvm/Support/FileSystem.h" 40 | #include "llvm/Support/ToolOutputFile.h" 41 | ENABLE_WARNINGS_LLVM 42 | 43 | #include 44 | 45 | using namespace clang; 46 | using namespace llvm; 47 | using namespace thusc; 48 | 49 | // Helper functions 50 | 51 | const std::map semanticMap { 52 | {thusc::ShaderParameterSemanticKind::SV_DispatchThreadID, "SV_DispatchThreadID"}, 53 | {thusc::ShaderParameterSemanticKind::SV_GroupThreadID, "SV_GroupThreadID"}, 54 | {thusc::ShaderParameterSemanticKind::SV_GroupID, "SV_GroupID"}, 55 | {thusc::ShaderParameterSemanticKind::SV_GroupIndex, "SV_GroupIndex"}, 56 | }; 57 | 58 | StringRef getHLSLSemanticSpelling(ShaderParameterSemanticKind semantic) { 59 | auto spelling = semanticMap.find(semantic); 60 | 61 | assert(spelling != semanticMap.end() && "Unsupported parameter semantic kind"); 62 | 63 | return spelling->second; 64 | } 65 | 66 | 67 | // Helper AST Visitor 68 | 69 | class FindUsedGPUFunctionsVisitor : public RecursiveASTVisitor { 70 | public: 71 | FindUsedGPUFunctionsVisitor(const std::vector>& gpuFunctions) { 72 | for (const auto& gpuFunc : gpuFunctions) { 73 | funcUsedMap.insert(std::make_pair(gpuFunc->getFunctionDecl(), false)); 74 | } 75 | } 76 | 77 | bool isFunctionUsed(const GPUFunction* gpuFunc) const { 78 | const auto it = funcUsedMap.find(gpuFunc->getFunctionDecl()); 79 | if (it == funcUsedMap.end()) { 80 | return false; 81 | } 82 | else { 83 | return it->second; 84 | } 85 | }; 86 | 87 | bool VisitCallExpr(CallExpr* callExpr) { 88 | const FunctionDecl* calledFuncDecl = callExpr->getDirectCallee(); 89 | auto it = funcUsedMap.find(calledFuncDecl); 90 | 91 | // If the function is not in the map or has already been marked as used, skip it. 92 | if (it == funcUsedMap.end() || it->second == true) { 93 | return true; 94 | } 95 | // Otherwise, mark the function as used and recurse to find the GPU functions it calls. 96 | else { 97 | it->second = true; 98 | TraverseStmt(calledFuncDecl->getBody()); 99 | } 100 | 101 | return true; 102 | } 103 | 104 | private: 105 | llvm::DenseMap funcUsedMap; 106 | }; 107 | 108 | 109 | // Public methods 110 | 111 | bool ThuscBackendHLSL::Output(const FileID currentFileID) { 112 | // GPUFunctions 113 | // Nothing to do for GPUFunctions (they will be output per ShaderClass if needed) 114 | 115 | // ShaderClasses 116 | for (const auto& shaderClass : shaderIR.getShaderClasses()) { 117 | // Only output a ShaderClass if it is defined in the file we're currently processing 118 | if (sourceMgr.getFileID(shaderClass->classDecl->getLocation()) == currentFileID) { 119 | if (!OutputShaderClass(shaderClass.get())) { 120 | return false; 121 | } 122 | } 123 | } 124 | 125 | return true; 126 | } 127 | 128 | 129 | // Private methods 130 | 131 | bool ThuscBackendHLSL::OutputShaderClass(const ShaderClass* shaderClass) { 132 | // If the ShaderClass has no entry points, we do not need to output a file for it 133 | if (shaderClass->entryPoints.size() == 0) { 134 | return true; 135 | } 136 | 137 | const std::string shaderName = shaderClass->name; 138 | const std::string shaderOutputFilename = gpuOutputDir + "\\" + shaderName + ".usf"; 139 | 140 | translator = std::make_unique(clangFuncToThuscFunc, shaderClass, rewriter); 141 | 142 | // Precode - Code that will be inserted prior to the hardcoded input file 143 | std::string generatedPrecode; 144 | { 145 | raw_string_ostream precodeStream(generatedPrecode); 146 | 147 | // Other GPU Decls 148 | // TODO Should check if each decl is actually need by the ShaderClass, 149 | // rather than just always outputting everything 150 | for (const auto& gpuDecl : shaderIR.otherGPUDecls) { 151 | if (gpuDecl.hasOutputOverride()) 152 | precodeStream << gpuDecl.getOutputOverride() << "\n"; 153 | else 154 | precodeStream << rewriter.getRewrittenText(gpuDecl.getDecl()->getSourceRange()) << ";\n"; 155 | precodeStream << "\n"; 156 | } 157 | 158 | // Find all GPU functions called by the GPU methods of this ShaderClass 159 | FindUsedGPUFunctionsVisitor visitor(shaderIR.gpuFunctions); 160 | for (const auto& gpuMethod : shaderClass->getGPUMethods()) { 161 | visitor.TraverseStmt(gpuMethod->getFunctionDecl()->getBody()); 162 | } 163 | 164 | // GPU function 165 | // Only output a GPU function if it is used in a GPU method of this ShaderClass 166 | // or if the --outputAllGPUFuncs command line option was specified 167 | for (const auto& gpuFunc : shaderIR.gpuFunctions) { 168 | if (thusc::outputAllGPUFuncs || visitor.isFunctionUsed(gpuFunc.get())) { 169 | OutputGPUFunction(gpuFunc.get(), precodeStream); 170 | } 171 | } 172 | 173 | // Permutation ShaderClass parameters 174 | OutputPermutationShaderClassFields(shaderClass, precodeStream); 175 | 176 | // Permutation parameters 177 | OutputPermutationFields(shaderClass, precodeStream); 178 | 179 | // Uniform parameters 180 | OutputUniformFields(shaderClass, precodeStream); 181 | 182 | // ShaderClass Other GPU Decls 183 | for (const auto& gpuDecl : shaderClass->otherGPUDecls) { 184 | if (gpuDecl.hasOutputOverride()) 185 | precodeStream << gpuDecl.getOutputOverride() << "\n"; 186 | else 187 | precodeStream << rewriter.getRewrittenText(gpuDecl.getDecl()->getSourceRange()) << ";\n"; 188 | precodeStream << "\n"; 189 | } 190 | 191 | // Flush the stream to the string 192 | precodeStream.str(); 193 | } 194 | 195 | 196 | // Postcode - Code that will be inserted after the hardcoded input file 197 | std::string generatedPostcode; 198 | { 199 | raw_string_ostream postcodeStream(generatedPostcode); 200 | 201 | // GPU methods 202 | for (const auto& gpuMethod : shaderClass->getGPUMethods()) { 203 | OutputGPUFunction(gpuMethod.get(), postcodeStream); 204 | } 205 | 206 | // Entry point functions 207 | for (const auto& entryPoint : shaderClass->entryPoints) { 208 | if (const auto* computeEntryPoint = dyn_cast(entryPoint.get())) { 209 | OutputComputeShaderEntry(computeEntryPoint, postcodeStream); 210 | } 211 | else { 212 | diagError(diagEngine, entryPoint->getFunctionDecl()->getLocation(), "Entry point type unsupported in HLSL backend"); 213 | } 214 | } 215 | 216 | // Flush the stream to the string 217 | postcodeStream.str(); 218 | } 219 | 220 | std::error_code ec; 221 | ToolOutputFile shaderOutputFile(shaderOutputFilename, ec, llvm::sys::fs::OpenFlags::OF_None); 222 | if (ec) { 223 | llvm::errs() << "Error: Could not open GPU shader code output file '" << shaderOutputFilename << "': " << ec.message() << "\n"; 224 | return false; 225 | } 226 | 227 | const SourceManager& sourceMgr = shaderClass->classDecl->getASTContext().getSourceManager(); 228 | StringRef thuscShaderFilename = sourceMgr.getFileEntryForID(sourceMgr.getFileID(shaderClass->classDecl->getLocation()))->getName(); 229 | 230 | shaderOutputFile.os() << "// Generated from thusc shader '" << shaderName << "' in file " << thuscShaderFilename << "\n\n"; 231 | 232 | shaderOutputFile.os() << "#define HARDCODED_HLSL\n\n"; 233 | 234 | if(!shaderClass->hlslPrecode.empty()) 235 | shaderOutputFile.os() << "// Hardcoded HLSL Precode\n" << shaderClass->hlslPrecode << "\n"; 236 | 237 | shaderOutputFile.os() << "// Generated Precode\n" << generatedPrecode << "\n"; 238 | 239 | if(!shaderClass->hlslPostcode.empty()) 240 | shaderOutputFile.os() << "// Hardcoded HLSL Postcode\n" << shaderClass->hlslPostcode << "\n"; 241 | 242 | shaderOutputFile.os() << "// Generated Postcode\n" << generatedPostcode << "\n"; 243 | 244 | shaderOutputFile.keep(); 245 | 246 | return true; 247 | } 248 | 249 | // Permutation ShaderClass fields 250 | void ThuscBackendHLSL::OutputPermutationShaderClassFields(const ShaderClass* shaderClass, raw_ostream& stream) const { 251 | // For GPU code output, all of the relevant information we need for Permutation ShaderClass Fields is aggregated into 252 | // the ImplClass IR structure, so no need to use the ShaderClass:permutationShaderClasses field directly. 253 | 254 | for (auto ii = shaderClass->getImplClasses().begin(), ie = shaderClass->getImplClasses().end(); ii != ie; ++ii) { 255 | const auto* implClass = ii->get(); 256 | 257 | if (ii == shaderClass->getImplClasses().begin()) { 258 | stream << "#if defined("; 259 | } 260 | else { 261 | stream << "#elif defined("; 262 | } 263 | 264 | stream << implClass->getName() << ")\n"; 265 | 266 | for (const auto& s : implClass->getSelections()) { 267 | OutputSelection(s, stream); 268 | } 269 | } 270 | 271 | stream << "#else\n"; 272 | stream << " #error No ThuscImpl_ class defines were set\n"; 273 | stream << "#endif\n"; 274 | stream << "\n"; 275 | } 276 | 277 | void ThuscBackendHLSL::OutputSelection(const PermutationShaderClassSelection& selection, raw_ostream& stream, llvm::StringRef prevPrefix) const { 278 | const auto* shaderClass = selection.shaderClass; 279 | 280 | std::string prefix = (prevPrefix + selection.fieldName + "_").str(); 281 | 282 | stream << shaderClass->hlslPrecode << "\n"; 283 | 284 | // Permutation parameters 285 | // TODO Prefix for permutation parameters (maybe) 286 | // Currently, the only output for permutation parameters happens for Enum types, 287 | // which simply simply outputs a #define for each enum value. Rather than handling 288 | // that per permutation parameter, it might be better to just output each enum once. 289 | OutputPermutationFields(shaderClass, stream); 290 | 291 | // Uniform parameters 292 | OutputUniformFields(shaderClass, stream, prefix); 293 | 294 | // ShaderClass Other GPU Decls 295 | for (const auto& gpuDecl : shaderClass->otherGPUDecls) { 296 | // TODO Prefix for other GPU Decls 297 | if (gpuDecl.hasOutputOverride()) 298 | stream << gpuDecl.getOutputOverride() << "\n"; 299 | else 300 | stream << rewriter.getRewrittenText(gpuDecl.getDecl()->getSourceRange()) << ";\n"; 301 | stream << "\n"; 302 | } 303 | 304 | stream << shaderClass->hlslPostcode << "\n"; 305 | 306 | for (auto ii = shaderClass->getImplClasses().begin(), ie = shaderClass->getImplClasses().end(); ii != ie; ++ii) { 307 | const auto* implClass = ii->get(); 308 | 309 | for (const auto& s : implClass->getSelections()) { 310 | OutputSelection(s, stream, prefix); 311 | } 312 | } 313 | 314 | // GPU method 315 | for (const auto& gpuMethod : shaderClass->getGPUMethods()) { 316 | translator->pushNestedShaderClass(shaderClass, prefix); 317 | OutputGPUFunction(gpuMethod.get(), stream, prefix); 318 | translator->popNestedShaderClass(); 319 | } 320 | } 321 | 322 | // Permutation fields 323 | void ThuscBackendHLSL::OutputPermutationFields(const ShaderClass* shaderClass, raw_ostream& stream) const { 324 | for (const auto& perm : shaderClass->permutationFields) { 325 | // Enum 326 | if (perm.kind == PermutationField::Kind::Enum) { 327 | assert(perm.fieldDecl->getType()->isEnumeralType() && "Field is not of type enum"); 328 | 329 | const EnumDecl* enumType = cast(perm.fieldDecl->getType())->getDecl(); 330 | 331 | // For each value in the enum 332 | // Output `#define EnumTypeName_EnumValueName Value` 333 | for (const auto enumValue : enumType->enumerators()) { 334 | stream << "#define " << perm.type << "_" << enumValue->getName() << " " 335 | << enumValue->getInitVal() << "\n"; 336 | } 337 | stream << "\n"; 338 | } 339 | // Bool 340 | else if (perm.kind == PermutationField::Kind::Bool) { 341 | // Do nothing 342 | } 343 | // Int 344 | else if (perm.kind == PermutationField::Kind::Int) { 345 | // Do nothing 346 | } 347 | // SparseInt 348 | else if (perm.kind == PermutationField::Kind::SparseInt) { 349 | // Do nothing 350 | } 351 | else { 352 | diagError(diagEngine, perm.fieldDecl->getLocation(), "Permutation type unsupported in HLSL backend"); 353 | } 354 | } 355 | } 356 | 357 | // Uniform fields 358 | void ThuscBackendHLSL::OutputUniformFields(const ShaderClass* shaderClass, raw_ostream& stream, StringRef prefix) const { 359 | for (const auto& uniform : shaderClass->uniformFields) { 360 | OutputUniformField(&uniform, stream, prefix); 361 | } 362 | } 363 | 364 | void ThuscBackendHLSL::OutputUniformFieldSimple(const UniformField* uniform, raw_ostream& stream, const Twine& prefix) const { 365 | stream << uniform->type << " " << prefix << uniform->name << ";\n"; 366 | stream << "\n"; 367 | } 368 | 369 | void ThuscBackendHLSL::OutputFlattenedUniformStruct(const CXXRecordDecl* classDecl, raw_ostream& stream, const Twine& prefix) const { 370 | for (const auto& field : classDecl->fields()) { 371 | std::string gpuTypeName = getGpuTypeName(field->getType(), field->getASTContext()); 372 | 373 | if (isPrimitiveType(gpuTypeName)) { 374 | stream << gpuTypeName << " " << prefix << field->getName() << ";\n"; 375 | } 376 | else if (field->getType()->isStructureOrClassType()) { 377 | const CXXRecordDecl* nestedClassDecl = field->getType()->getAsCXXRecordDecl(); 378 | OutputFlattenedUniformStruct(nestedClassDecl, stream, prefix + field->getName() + "_"); 379 | } 380 | else { 381 | diagError(diagEngine, field->getLocation(), "Unsupported field type in struct used as uniform parameter"); 382 | } 383 | } 384 | } 385 | 386 | void ThuscBackendHLSL::OutputUniformFieldStruct(const UniformField* uniform, raw_ostream& stream, const Twine& prefix) const { 387 | assert(uniform->fieldDecl->getType()->isStructureOrClassType() && "Struct uniform field is not a struct or class"); 388 | 389 | const CXXRecordDecl* classDecl = uniform->fieldDecl->getType()->getAsCXXRecordDecl(); 390 | OutputFlattenedUniformStruct(classDecl, stream, prefix + uniform->name + "_"); 391 | stream << "\n"; 392 | } 393 | 394 | void ThuscBackendHLSL::OutputUniformField(const UniformField* uniform, raw_ostream& stream, const Twine& prefix) const { 395 | // TODO Rename map 396 | const UniformField::Kind kind = uniform->getKind(); 397 | 398 | if (kind == UniformField::Kind::Sampler || kind == UniformField::Kind::Texture || kind == UniformField::Kind::TextureUAV) { 399 | OutputUniformFieldSimple(uniform, stream, prefix); 400 | } 401 | else if (kind == UniformField::Kind::TextureSRV) { 402 | stream << getGpuTypeName(uniform->fieldDecl->getType(), uniform->fieldDecl->getASTContext()) << " " << prefix << uniform->name << ";\n"; 403 | stream << "\n"; 404 | } 405 | else if (kind == UniformField::Kind::Primitive) { 406 | stream << getGpuTypeName(uniform->fieldDecl->getType(), uniform->fieldDecl->getASTContext()) << " " << prefix << uniform->name << ";\n"; 407 | stream << "\n"; 408 | } 409 | else if (kind == UniformField::Kind::Array) { 410 | assert(isa(uniform->fieldDecl->getType()) && "Array uniform field kind specified, but fieldDecl type is not a ConstantArrayType"); 411 | const ConstantArrayType* arrType = cast(uniform->fieldDecl->getType().getTypePtr()); 412 | 413 | stream << uniform->type << " " << prefix << uniform->name << "[" << arrType->getSize() << "];\n"; 414 | stream << "\n"; 415 | } 416 | else if (kind == UniformField::Kind::GlobalUniformBuffer) { 417 | // Do nothing 418 | // In UE4, globally named shader parameter structs get spliced in to the shader automatically, 419 | // so no need to output a definition here. 420 | } 421 | else if (kind == UniformField::Kind::Struct) { 422 | OutputUniformFieldStruct(uniform, stream, prefix); 423 | } 424 | else { 425 | diagError(diagEngine, uniform->fieldDecl->getLocation(), "Uniform parameter type unsupported in HLSL backend"); 426 | } 427 | } 428 | 429 | void ThuscBackendHLSL::OutputFunctionDeclaration(const GPUFunctionBase* gpuFunc, raw_ostream& stream, bool shouldOutputSemantics, StringRef prefix) const { 430 | stream << gpuFunc->getReturnType() << " " << prefix << gpuFunc->getMangledName() << "("; 431 | 432 | for (auto ii = gpuFunc->getParameters().begin(), ie = gpuFunc->getParameters().end(); ii != ie; ++ii) { 433 | if (ii != gpuFunc->getParameters().begin()) { 434 | stream << ", "; 435 | } 436 | 437 | if (ii->isInout) { 438 | stream << "inout "; 439 | } 440 | 441 | stream << ii->type << " " << ii->name; 442 | 443 | if (shouldOutputSemantics) { 444 | stream << " : " << getHLSLSemanticSpelling(ii->semantic); 445 | } 446 | else { 447 | assert(ii->semantic == ShaderParameterSemanticKind::None && "GPU function parameter has unused semantic"); 448 | } 449 | } 450 | 451 | stream << ")\n"; 452 | } 453 | 454 | void ThuscBackendHLSL::OutputFunctionBody(const FunctionDecl* funcDecl, raw_ostream& stream) const { 455 | assert(funcDecl->hasBody() && "GPU function does not have a function body"); 456 | 457 | // Translate the function body and output it 458 | translator->TraverseStmt(funcDecl->getBody()); 459 | stream << rewriter.getRewrittenText(funcDecl->getBody()->getSourceRange()) << "\n\n"; 460 | 461 | // Undo the changes that the translator made 462 | // This lets the host backend and other ShaderClasses that output this function use the original function 463 | translator->undoRewrites(); 464 | } 465 | 466 | void ThuscBackendHLSL::OutputGPUFunction(const GPUFunction* gpuFunc, raw_ostream& stream, StringRef prefix) const { 467 | // TODO Rename map 468 | OutputFunctionDeclaration(gpuFunc, stream, false, prefix); 469 | OutputFunctionBody(gpuFunc->getFunctionDecl(), stream); 470 | } 471 | 472 | // Entry functions 473 | void ThuscBackendHLSL::OutputShaderEntryCommon(const EntryPoint* entryPoint, raw_ostream& stream) const { 474 | OutputFunctionDeclaration(entryPoint, stream, true); 475 | OutputFunctionBody(entryPoint->getFunctionDecl(), stream); 476 | } 477 | 478 | void ThuscBackendHLSL::OutputComputeShaderEntry(const ComputeEntryPoint* entryPoint, raw_ostream& stream) const { 479 | // Function annotation 480 | stream << "[numthreads(" << entryPoint->threadgroupSize[0] << ", " << 481 | entryPoint->threadgroupSize[1] << ", " << entryPoint->threadgroupSize[2] << ")]\n"; 482 | 483 | OutputShaderEntryCommon(entryPoint, stream); 484 | } 485 | 486 | -------------------------------------------------------------------------------- /source/thusc/ThuscBackendUE4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include "ThuscBackendUE4.h" 30 | 31 | #include "ClangHelperFunctions.h" 32 | #include "TypeHelpers.h" 33 | 34 | DISABLE_WARNINGS_LLVM 35 | #include "clang/AST/PrettyPrinter.h" 36 | ENABLE_WARNINGS_LLVM 37 | 38 | using namespace clang; 39 | using namespace llvm; 40 | using namespace thusc; 41 | 42 | 43 | // Public methods 44 | 45 | bool ThuscBackendUE4Host::Output(const FileID currentFileID) { 46 | // OtherGPUDecls 47 | for (const auto& gpuDecl : shaderIR.otherGPUDecls) { 48 | // Only output a GPUDecl if it is defined in the file we're currently processing 49 | if (sourceMgr.getFileID(gpuDecl.getDecl()->getLocation()) == currentFileID) { 50 | if (!gpuDecl.isHost()) { 51 | rewriter.RemoveText(gpuDecl.getDecl()->getSourceRange()); 52 | } 53 | } 54 | } 55 | 56 | // GPUFunctions 57 | for (const auto& gpuFunc : shaderIR.gpuFunctions) { 58 | // Only output a GPUFunction if it is defined in the file we're currently processing 59 | if (sourceMgr.getFileID(gpuFunc->getFunctionDecl()->getLocation()) == currentFileID) { 60 | if (!OutputGPUFunction(gpuFunc.get())) { 61 | return false; 62 | } 63 | } 64 | } 65 | 66 | // ShaderClasses 67 | for (const auto& shaderClass : shaderIR.getShaderClasses()) { 68 | // Only output a ShaderClass if it is defined in the file we're currently processing 69 | if (sourceMgr.getFileID(shaderClass->classDecl->getLocation()) == currentFileID) { 70 | if (!OutputShaderClass(shaderClass.get())) { 71 | return false; 72 | } 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | 80 | // Private methods 81 | 82 | // Uniform fields 83 | inline 84 | void OutputUniformFieldHelper(const StringRef name, const StringRef type, const StringRef macroName, raw_ostream& stream) { 85 | stream << macroName << "(" << type << ", " << name << ")\n"; 86 | } 87 | 88 | void ThuscBackendUE4Host::OutputUniformFieldStruct(const UniformField* uniform, raw_ostream& stream) const { 89 | assert(uniform->fieldDecl->getType()->isStructureOrClassType() && "Struct uniform field is not a struct or class"); 90 | 91 | const CXXRecordDecl* classDecl = uniform->fieldDecl->getType()->getAsCXXRecordDecl(); 92 | 93 | // Check for a declaration of the FTypeInfo struct with in the classDecl. 94 | bool foundFTypeInfo = false; 95 | for (const auto innerDecl : classDecl->decls()) { 96 | if (const NamedDecl* innerNamedDecl = dyn_cast(innerDecl)) { 97 | if (innerNamedDecl->getNameAsString() == "FTypeInfo") { 98 | foundFTypeInfo = true; 99 | break; 100 | } 101 | } 102 | } 103 | 104 | // If found, then this type was created using the UE4 BEGIN_SHADER_PARAMETER_STRUCT() macro, 105 | // so no additional processing is required. Else, we need to do some extra work. 106 | if (foundFTypeInfo) { 107 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER_STRUCT", stream); 108 | } 109 | else { 110 | // TODO We should eventually handle using struct types that were not declared with the UE4 macro 111 | diagError(diagEngine, uniform->fieldDecl->getLocation(), 112 | "When using a struct type uniform parameter, the struct type must be delcared using the UE4 BEGIN_SHADER_PARAMETER_STRUCT() macro"); 113 | } 114 | } 115 | 116 | void ThuscBackendUE4Host::OutputUniformField(const UniformField* uniform, raw_ostream& stream) const { 117 | const UniformField::Kind kind = uniform->getKind(); 118 | 119 | if (kind == UniformField::Kind::Sampler) { 120 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER_SAMPLER", stream); 121 | } 122 | else if (kind == UniformField::Kind::Texture) { 123 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER_RDG_TEXTURE", stream); 124 | } 125 | else if (kind == UniformField::Kind::TextureSRV) { 126 | OutputUniformFieldHelper(uniform->name, getGpuTypeName(uniform->fieldDecl->getType(), uniform->fieldDecl->getASTContext()), "SHADER_PARAMETER_RDG_TEXTURE_SRV", stream); 127 | } 128 | else if (kind == UniformField::Kind::TextureUAV) { 129 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER_RDG_TEXTURE_UAV", stream); 130 | } 131 | else if (kind == UniformField::Kind::Primitive) { 132 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER", stream); 133 | } 134 | else if (kind == UniformField::Kind::Array) { 135 | assert(isa(uniform->fieldDecl->getType()) && "Array uniform field kind specified, but fieldDecl type is not a ConstantArrayType"); 136 | const ConstantArrayType* arrType = cast(uniform->fieldDecl->getType().getTypePtr()); 137 | 138 | stream << "SHADER_PARAMETER_ARRAY(" << uniform->type << ", " << uniform->name << ", " << "[" << arrType->getSize() << "]" << ")\n"; 139 | } 140 | else if (kind == UniformField::Kind::GlobalUniformBuffer) { 141 | OutputUniformFieldHelper(uniform->name, uniform->type, "SHADER_PARAMETER_STRUCT_REF", stream); 142 | } 143 | else if (kind == UniformField::Kind::Struct) { 144 | OutputUniformFieldStruct(uniform, stream); 145 | } 146 | else { 147 | diagError(diagEngine, uniform->fieldDecl->getLocation(), "Uniform parameter type unsupported in UE4 backend"); 148 | } 149 | } 150 | 151 | void ThuscBackendUE4Host::OutputUniformFields(const ShaderClass* shaderClass, const ImplClass* implClass, raw_ostream& stream) const { 152 | // TODO Handle case where a ShaderClass (and its baseShaderClass if applicable) has no uniform fields 153 | 154 | // FParameters struct declaration 155 | stream << " BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )\n"; 156 | 157 | for (const auto& s : implClass->getSelections()) { 158 | stream << " " << "SHADER_PARAMETER_STRUCT(" << s.shaderClass->getName() << "::" << s.implClass->getName() << "::FParameters, " << s.fieldName << ")\n"; 159 | } 160 | 161 | for (const auto& uniform : shaderClass->uniformFields) { 162 | stream << " "; 163 | OutputUniformField(&uniform, stream); 164 | } 165 | stream << " END_SHADER_PARAMETER_STRUCT()\n"; 166 | stream << "\n"; 167 | 168 | // getPassParameters() function 169 | stream << " static void getPassParameters(const " << shaderClass->name << "& inst, FParameters* PassParameters) {\n"; 170 | 171 | for (const auto& s : implClass->getSelections()) { 172 | stream << " " << s.shaderClass->getName() << "::" << s.implClass->getName() << "::getPassParameters(*static_cast<" << s.shaderClass->getName() << "*>(inst." << s.fieldName << "), &PassParameters->" << s.fieldName << ");\n"; 173 | } 174 | 175 | for (const auto& uniform : shaderClass->uniformFields) { 176 | if (uniform.getKind() == UniformField::Kind::Array) { 177 | assert(isa(uniform.fieldDecl->getType()) && "Array uniform field kind specified, but fieldDecl type is not a ConstantArrayType"); 178 | const ConstantArrayType* arrType = cast(uniform.fieldDecl->getType().getTypePtr()); 179 | 180 | stream << " for (int i = 0; i < " << arrType->getSize() << "; ++i) {\n"; 181 | stream << " PassParameters->" << uniform.name << "[i] = inst." << uniform.name << "[i];\n"; 182 | stream << " }\n"; 183 | } 184 | else { 185 | stream << " PassParameters->" << uniform.name << " = inst." << uniform.name << ";\n"; 186 | } 187 | } 188 | stream << " }\n"; 189 | stream << "\n"; 190 | } 191 | 192 | // Permutation fields 193 | 194 | // Error-checking code (if applicable based on kind) 195 | void ThuscBackendUE4Host::OutputPermutationErrorChecking(const PermutationField& perm, raw_ostream& stream) const { 196 | if (perm.kind == PermutationField::Kind::Enum) { 197 | // Do nothing 198 | } 199 | else if (perm.kind == PermutationField::Kind::Bool) { 200 | // Do nothing 201 | } 202 | else if (perm.kind == PermutationField::Kind::Int) { 203 | stream << " "; 204 | stream << "checkf("; 205 | 206 | int value; 207 | // Using 10 as the Radix to indicate that the integers should be specified in decimal form 208 | if (StringRef(perm.valueOptions.front()).trim().getAsInteger(10, value)) { 209 | diagError(diagEngine, perm.fieldDecl->getLocation(), "Unable to convert string to int for permutation Int"); 210 | return; 211 | } 212 | 213 | for (int i = 0; i < value; ++i) { 214 | if (i != 0) 215 | stream << " || "; 216 | stream << "inst." << perm.name << " == " << i; 217 | } 218 | stream << ", TEXT(\"Invalid value %i assigned to permutation parameter " << perm.name << ".\"), " << "inst." << perm.name << ");\n"; 219 | } 220 | else if (perm.kind == PermutationField::Kind::SparseInt) { 221 | stream << " "; 222 | stream << "checkf("; 223 | for (auto ii = perm.valueOptions.begin(), ie = perm.valueOptions.end(); ii != ie; ++ii) { 224 | if (ii != perm.valueOptions.begin()) 225 | stream << " || "; 226 | stream << "inst." << perm.name << " == " << *ii; 227 | } 228 | stream << ", TEXT(\"Invalid value %i assigned to permutation parameter " << perm.name << ".\"), " << "inst." << perm.name << ");\n"; 229 | } 230 | else { 231 | diagError(diagEngine, perm.fieldDecl->getLocation(), "Permutation type unsupported in UE4 backend (for error checking)"); 232 | } 233 | } 234 | 235 | void ThuscBackendUE4Host::OutputPermutationFields(const ShaderClass* shaderClass, const ImplClass* implClass, raw_ostream& stream) const { 236 | // Permutation parameter declarations 237 | for (const auto& perm : shaderClass->permutationFields) { 238 | std::string macroName; 239 | 240 | if (perm.kind == PermutationField::Kind::Enum) 241 | macroName = "SHADER_PERMUTATION_ENUM_CLASS"; 242 | else if (perm.kind == PermutationField::Kind::Bool) 243 | macroName = "SHADER_PERMUTATION_BOOL"; 244 | else if (perm.kind == PermutationField::Kind::Int) 245 | macroName = "SHADER_PERMUTATION_INT"; 246 | else if (perm.kind == PermutationField::Kind::SparseInt) 247 | macroName = "SHADER_PERMUTATION_SPARSE_INT"; 248 | else 249 | diagError(diagEngine, perm.fieldDecl->getLocation(), "Permutation type unsupported in UE4 backend"); 250 | 251 | stream << " "; 252 | stream << "class " << permutationClassPrefix << perm.name << " : " << macroName << "(\"" << perm.name << "\""; 253 | 254 | for (const auto& option : perm.valueOptions) { 255 | stream << ", " << option; 256 | } 257 | stream << ");\n"; 258 | } 259 | 260 | // FPermutationDomain declaration 261 | stream << " using FPermutationDomain = TShaderPermutationDomain<"; 262 | 263 | for (auto ii = implClass->getSelections().begin(), ie = implClass->getSelections().end(); ii != ie; ++ii) { 264 | if (ii != implClass->getSelections().begin()) { 265 | stream << ", "; 266 | } 267 | stream << ii->shaderClass->getName() << "::" << ii->implClass->getName() << "::FPermutationDomain"; 268 | } 269 | 270 | for (auto ii = shaderClass->permutationFields.begin(), ie = shaderClass->permutationFields.end(); ii != ie; ++ii) { 271 | if (ii != shaderClass->permutationFields.begin() || implClass->getSelections().size() > 0) { 272 | stream << ", "; 273 | } 274 | stream << permutationClassPrefix << ii->name; 275 | } 276 | stream << ">;\n"; 277 | stream << "\n"; 278 | 279 | // getPermutationVector() function 280 | stream << " static FPermutationDomain getPermutationVector(const " << shaderClass->name << "& inst) {\n"; 281 | stream << " FPermutationDomain PermutationVector;\n"; 282 | stream << "\n"; 283 | 284 | for (const auto& s : implClass->getSelections()) { 285 | stream << " "; 286 | stream << "PermutationVector.Set<" << s.shaderClass->getName() << "::" << s.implClass->getName() << "::FPermutationDomain" << ">(" 287 | << s.shaderClass->getName() << "::" << s.implClass->getName() << "::getPermutationVector(*static_cast<" << s.shaderClass->getName() << "*>(inst." << s.fieldName << ")));\n"; 288 | } 289 | 290 | for (const auto& perm : shaderClass->permutationFields) { 291 | OutputPermutationErrorChecking(perm, stream); 292 | stream << " "; 293 | stream << "PermutationVector.Set<" << permutationClassPrefix << perm.name << ">(" << "inst." << perm.name << ");\n"; 294 | } 295 | stream << "\n"; 296 | 297 | stream << " return PermutationVector;\n"; 298 | stream << " }\n"; 299 | stream << "\n"; 300 | } 301 | 302 | 303 | // Entry functions 304 | 305 | // TODO actually do stuff with compute shader function 306 | void ThuscBackendUE4Host::OutputComputeShaderEntry(const ComputeEntryPoint* entryPoint, const ShaderClass* shaderClass, raw_ostream& stream) const { 307 | stream << "void AddComputePass(FRDGBuilder& GraphBuilder, FRDGEventName&& PassName, const FGlobalShaderMap* ShaderMap, FIntVector GroupCount)\n"; 308 | stream << "{\n"; 309 | stream << " unsigned int implClassID = " << thuscgetImplClassIDFunctionName << "();\n"; 310 | stream << "\n"; 311 | 312 | for (unsigned int i = 0; i < shaderClass->getImplClasses().size(); ++i) { 313 | assert(i == shaderClass->getImplClasses()[i]->getID() && "Invalid ImplClass ID"); 314 | const auto* implClass = shaderClass->getImplClasses()[i].get(); 315 | const auto implClassName = implClass->getName(); 316 | 317 | if (i == 0) { 318 | stream << " if "; 319 | } 320 | else { 321 | stream << " else if "; 322 | } 323 | stream << "(" << "implClassID == " << implClass->getID() << ")\n"; 324 | stream << " {\n"; 325 | 326 | // Permutation parameters 327 | stream << " // Permutation parameters\n"; 328 | stream << " " << implClassName << "::FPermutationDomain PermutationVector = " << implClassName << "::getPermutationVector(*this);\n"; 329 | stream << "\n"; 330 | 331 | // Uniform parameters 332 | stream << " // Uniform parameters\n"; 333 | stream << " " << implClassName << "::FParameters* PassParameters = GraphBuilder.AllocParameters<" << implClassName << "::FParameters>();\n"; 334 | stream << " " << implClassName << "::getPassParameters(*this, PassParameters);\n"; 335 | stream << "\n"; 336 | 337 | // Add pass 338 | stream << " // Add pass to render graph\n"; 339 | stream << " TShaderMapRef<" + implClassName + "> ShaderRef" << "(ShaderMap, PermutationVector);\n"; 340 | stream << " FComputeShaderUtils::AddPass(GraphBuilder, Forward(PassName), ShaderRef, PassParameters, GroupCount);\n"; 341 | stream << " }\n"; 342 | } 343 | 344 | stream << " else\n"; 345 | stream << " {\n"; 346 | stream << " " << "checkf(false, TEXT(\"Unsupported " << thuscgetImplClassIDFunctionName << "() for " << shaderClass->name << ". Something probably went wrong with thusc code generation.\"));\n"; 347 | stream << " }\n"; 348 | 349 | stream << "}\n"; 350 | } 351 | 352 | 353 | bool ThuscBackendUE4Host::OutputImplClass(const ShaderClass* shaderClass, const ImplClass* implClass, raw_ostream& stream) const { 354 | std::string baseClassName; 355 | if (shaderClass->classDecl->getNumBases() == 0 || shaderClass->baseShaderClass) { 356 | baseClassName = "FGlobalShader"; 357 | } 358 | else if (shaderClass->classDecl->getNumBases() == 1) { 359 | // TODO Might want to ensure that the base class is derived from FShader 360 | baseClassName = shaderClass->classDecl->bases_begin()->getType().getAsString(langOpts); 361 | } 362 | else { 363 | diagError(diagEngine, shaderClass->classDecl->getLocation(), "When using the UE4 backend, thusc shaders must have zero or one base class"); 364 | return false; 365 | } 366 | 367 | llvm::StringRef implClassName = implClass->getName(); 368 | 369 | // Begin UE4 shader C++ shader class 370 | stream << " class " << implClassName << " : public " << baseClassName << "\n"; 371 | stream << " {\n"; 372 | stream << " public:\n"; 373 | stream << " DECLARE_GLOBAL_SHADER(" << implClassName << ");\n"; 374 | stream << " SHADER_USE_PARAMETER_STRUCT(" << implClassName << ", " << baseClassName << ");\n"; 375 | stream << "\n"; 376 | 377 | // Uniform parameters 378 | OutputUniformFields(shaderClass, implClass, stream); 379 | 380 | // TODO Hardcode begin 381 | // (But this might be fine to leave as-is) 382 | stream << " static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)\n"; 383 | stream << " {\n"; 384 | stream << " FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);\n"; 385 | stream << " OutEnvironment.SetDefine(TEXT(\"" << implClassName << "\"), 1);\n"; 386 | stream << " }\n"; 387 | stream << "\n"; 388 | // TODO Hardcode end 389 | 390 | // If ShaderClass has ShouldCompilePermutation() method, output the Impl method that will forward to it 391 | if (shaderClass->hasShouldCompilePermutation) { 392 | stream << " static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)\n"; 393 | stream << " {\n"; 394 | stream << " FPermutationDomain PermutationVector(Parameters.PermutationId);\n"; 395 | stream << "\n"; 396 | stream << " " << shaderClass->name << " inst;\n"; 397 | stream << "\n"; 398 | for (const auto& s : implClass->getSelections()) { 399 | stream << " " << s.shaderClass->getName() << " " << s.fieldName << ";\n"; 400 | stream << " " << "inst." << s.fieldName << " = " << "&" << s.fieldName << ";\n"; 401 | } 402 | stream << "\n"; 403 | for (const auto& perm : shaderClass->permutationFields) { 404 | stream << " " << "inst." << perm.name << " = " << "PermutationVector.Get<" << permutationClassPrefix << perm.name << ">();\n"; 405 | } 406 | stream << "\n"; 407 | stream << " return " << shaderClass->name << "::ShouldCompilePermutation(inst, Parameters);\n"; 408 | stream << " }\n"; 409 | stream << "\n"; 410 | } 411 | 412 | // Permutation parameters 413 | OutputPermutationFields(shaderClass, implClass, stream); 414 | 415 | // End UE4 shader C++ shader class 416 | stream << " };\n"; 417 | stream << "\n"; 418 | 419 | return true; 420 | } 421 | 422 | bool ThuscBackendUE4Host::OutputShaderClass(const ShaderClass* shaderClass) { 423 | std::string stringForOStream; 424 | raw_string_ostream stream(stringForOStream); 425 | 426 | stream << "public:\n"; 427 | for (const auto& implClass : shaderClass->getImplClasses()) { 428 | if (!OutputImplClass(shaderClass, implClass.get(), stream)) 429 | return false; 430 | } 431 | 432 | // getTypeID functions 433 | stream << " virtual unsigned int " << thuscTypeIDPrefix << shaderClass->name << "() const { return " << shaderClass->getTypeIDRelativeTo(shaderClass) << "; }\n"; 434 | 435 | const auto* baseShaderClass = shaderClass->baseShaderClass; 436 | while (baseShaderClass) { 437 | stream << " virtual unsigned int " << thuscTypeIDPrefix << baseShaderClass->name << "() const override { return " << shaderClass->getTypeIDRelativeTo(baseShaderClass) << "; }\n"; 438 | baseShaderClass = baseShaderClass->baseShaderClass; 439 | } 440 | stream << "\n"; 441 | 442 | // getImplClassID function 443 | stream << " virtual unsigned int " << thuscgetImplClassIDFunctionName << "() const\n"; 444 | stream << " {\n"; 445 | if (shaderClass->getImplClasses().size() == 1) { 446 | assert(shaderClass->permutationShaderClasses.size() == 0 && "ShaderClass has ShaderClass permutation fields, but only one ImplClass was generated"); 447 | stream << " return 0;\n"; 448 | } 449 | else { 450 | for (const auto& field : shaderClass->permutationShaderClasses) { 451 | stream << " " << "unsigned int " << field.getFieldName() << "_shaderClassID = " 452 | << field.getFieldName() << "->" << thuscTypeIDPrefix << field.getShaderClassType()->name << "()" << ";\n"; 453 | stream << " " << "unsigned int " << field.getFieldName() << "_implClassID = " 454 | << field.getFieldName() << "->" << thuscgetImplClassIDFunctionName << "()" << ";\n"; 455 | } 456 | stream << "\n"; 457 | 458 | for (unsigned int i = 0; i < shaderClass->getImplClasses().size(); ++i) { 459 | if (i == 0) { 460 | stream << " " << "if ( "; 461 | } 462 | else { 463 | stream << " " << "else if ( "; 464 | } 465 | 466 | const auto* implClass = shaderClass->getImplClasses()[i].get(); 467 | for (auto s = implClass->getSelections().begin(), sEnd = implClass->getSelections().end(); s != sEnd; ++s) { 468 | if (s != implClass->getSelections().begin()) { 469 | stream << " && "; 470 | } 471 | stream << "(" << s->fieldName << "_shaderClassID == " << s->typeIDRelative << " && " << s->fieldName << "_implClassID == " << s->implClass->getID() << ")"; 472 | } 473 | stream << " ) {\n"; 474 | stream << " " << "return " << i << "; // " << implClass->getName() << "\n"; 475 | stream << " " << "}\n"; 476 | } 477 | stream << " " << "else {\n"; 478 | stream << " " << "checkf(false, TEXT(\"Thusc_getImplClassID() found an invalid configuration. Something probably went wrong with thusc code generation.\"));\n"; 479 | stream << " " << "return 0;\n"; 480 | stream << " " << "}\n"; 481 | } 482 | stream << " }\n"; 483 | stream << "\n"; 484 | 485 | // Other GPU Decls 486 | for (const auto& gpuDecl : shaderClass->otherGPUDecls) { 487 | if (!gpuDecl.isHost()) { 488 | rewriter.RemoveText(gpuDecl.getDecl()->getSourceRange()); 489 | } 490 | } 491 | 492 | // GPU functions 493 | for (const auto& gpuFunc : shaderClass->getGPUMethods()) { 494 | if (!OutputGPUFunction(gpuFunc.get())) { 495 | return false; 496 | } 497 | } 498 | 499 | // Shader entry functions 500 | std::string ueShaderType; 501 | for (const auto& entryPoint : shaderClass->entryPoints) { 502 | rewriter.RemoveText(entryPoint->getFunctionDecl()->getSourceRange()); 503 | 504 | if (const auto* computeEntryPoint = dyn_cast(entryPoint.get())) { 505 | ueShaderType = "SF_Compute"; 506 | OutputComputeShaderEntry(computeEntryPoint, shaderClass, stream); 507 | } 508 | else { 509 | diagError(diagEngine, entryPoint->getFunctionDecl()->getLocation(), "Entry point type unsupported in UE4 backend"); 510 | } 511 | } 512 | 513 | // Output the changes to the rewriter 514 | rewriter.InsertText(shaderClass->classDecl->getEndLoc().getLocWithOffset(-1), "\n\n" + stream.str(), true, true); 515 | 516 | if (shaderClass->entryPoints.size() == 0) { 517 | // Do nothing 518 | } 519 | else if (shaderClass->entryPoints.size() == 1) { 520 | const std::string shaderName = shaderClass->name; 521 | const std::string gpuOutputFilename = gpuOutputDir + "/" + shaderName + ".usf"; 522 | 523 | std::string finalLine = ""; 524 | for (const auto& implClass : shaderClass->getImplClasses()) { 525 | finalLine += ("IMPLEMENT_GLOBAL_SHADER(" + shaderName + "::" + implClass->getName() + ", " 526 | + "\"" + gpuOutputFilename + "\"" + ", \"" + shaderClass->entryPoints[0]->getMangledName() + "\", " + ueShaderType + ");\n").str(); 527 | } 528 | rewriter.InsertText(shaderClass->classDecl->getEndLoc().getLocWithOffset(2), "\n\n" + finalLine, true, true); 529 | } 530 | else { 531 | diagError(diagEngine, shaderClass->classDecl->getLocation(), "Shader classes must have zero or one entry point. Shader classes with two or more entry points are currently unimplemented."); 532 | return false; 533 | } 534 | 535 | return true; 536 | } 537 | 538 | bool ThuscBackendUE4Host::OutputGPUFunction(const GPUFunction* gpuFunc) { 539 | if (!gpuFunc->isHostCallable()) { 540 | rewriter.RemoveText(gpuFunc->getFunctionDecl()->getSourceRange()); 541 | } 542 | 543 | return true; 544 | } 545 | 546 | -------------------------------------------------------------------------------- /source/thusc/ThuscFrontendAction.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, The Regents of the University of California, 2 | // Davis campus. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions 6 | // are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither any name of The Regents of the University of California nor 13 | // the names of its contributors may be used to endorse or promote 14 | // products derived from this software without specific prior written 15 | // permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 18 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include "ThuscFrontendAction.h" 30 | 31 | #include "ClangHelperFunctions.h" 32 | #include "TypeHelpers.h" 33 | 34 | DISABLE_WARNINGS_LLVM 35 | #include "clang/AST/Attr.h" 36 | #include "clang/AST/Type.h" 37 | #include "clang/Basic/Diagnostic.h" 38 | #include "clang/Lex/Lexer.h" 39 | #include "clang/Rewrite/Core/Rewriter.h" 40 | 41 | #include "llvm/Support/raw_ostream.h" 42 | ENABLE_WARNINGS_LLVM 43 | 44 | using namespace clang; 45 | using namespace llvm; 46 | using namespace thusc; 47 | 48 | 49 | // Helper functions 50 | 51 | CharSourceRange removeTrailingWhitespace(const CharSourceRange range, const SourceManager& sourceMgr, const LangOptions& langOpts) { 52 | SourceLocation endLocation = range.getEnd(); 53 | if (range.isTokenRange()) { 54 | endLocation = Lexer::getLocForEndOfToken(endLocation, 0, sourceMgr, langOpts); 55 | } 56 | 57 | bool isInvalid = false; 58 | const char* endBuf = sourceMgr.getCharacterData(endLocation, &isInvalid); 59 | 60 | if (isInvalid) { 61 | llvm::errs() << "Warning: Invalid character data range. Trailing whitespace may not be removed.\n"; 62 | return range; 63 | } 64 | 65 | const char* endPos = endBuf; 66 | while (*endPos == ' ' || *endPos == '\t') { 67 | ++endPos; 68 | } 69 | 70 | int offset = (int)(endPos - endBuf); 71 | return CharSourceRange(SourceRange(range.getBegin(), endLocation.getLocWithOffset(offset)), false); 72 | } 73 | 74 | const AnnotateAttr* findAnnotateAttribute(const Decl* decl, const StringRef spelling, bool isFindPrefix = false) { 75 | for (const auto& attr : decl->attrs()) { 76 | if (attr->getKind() == attr::Kind::Annotate) { 77 | AnnotateAttr* annotateAttr = cast(attr); 78 | StringRef annotation = annotateAttr->getAnnotation(); 79 | 80 | if (isFindPrefix && annotation.startswith(spelling)) 81 | return annotateAttr; 82 | else if (annotation.equals(spelling)) 83 | return annotateAttr; 84 | } 85 | } 86 | 87 | return nullptr; 88 | } 89 | 90 | std::vector findAnnotateAttributeMultiple(const Decl* decl, const StringRef spelling, bool isFindPrefix = false) { 91 | std::vector ret; 92 | 93 | for (const auto& attr : decl->attrs()) { 94 | if (attr->getKind() == attr::Kind::Annotate) { 95 | AnnotateAttr* annotateAttr = cast(attr); 96 | StringRef annotation = annotateAttr->getAnnotation(); 97 | 98 | if (isFindPrefix && annotation.startswith(spelling)) 99 | ret.push_back(annotateAttr); 100 | else if (annotation.equals(spelling)) 101 | ret.push_back(annotateAttr); 102 | } 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | // Remove the attribute because the underlying compiler won't understand it. 109 | void removeCXX11AttrMacro(const Attr* attr, Rewriter& rewriter) { 110 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 111 | const LangOptions& langOpts = rewriter.getLangOpts(); 112 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 113 | 114 | if (!attr->isCXX11Attribute()) { 115 | diagError(diagEngine, attr->getLocation(), "Shader attributes must use the C++11 attribute syntax [[...]]"); 116 | return; 117 | } 118 | 119 | CharSourceRange macroCallRange = sourceMgr.getExpansionRange(attr->getRange()); 120 | SourceLocation beginLocation = macroCallRange.getBegin(); 121 | SourceLocation endLocation = macroCallRange.getEnd(); 122 | 123 | if (macroCallRange.isTokenRange()) { 124 | endLocation = Lexer::getLocForEndOfToken(endLocation, 0, sourceMgr, langOpts); 125 | } 126 | 127 | int beginOffset; 128 | int endOffset; 129 | 130 | // Find starting bracket position 131 | { 132 | bool isInvalid = false; 133 | const char* beginBuf = sourceMgr.getCharacterData(beginLocation, &isInvalid); 134 | const char* beginPos = beginBuf; 135 | 136 | if (isInvalid) { 137 | diagError(diagEngine, attr->getLocation(), "Unable to find starting brackets for attribute. Invalid character data range."); 138 | return; 139 | } 140 | 141 | --beginPos; 142 | 143 | // Trim whitespace 144 | while (*beginPos == ' ' || *beginPos == '\t') { 145 | --beginPos; 146 | } 147 | 148 | // Find "[[" 149 | if (*beginPos != '[') { 150 | diagError(diagEngine, attr->getLocation(), "Unable to find starting brackets for C++11 attribute after trimming whitespace."); 151 | return; 152 | } 153 | --beginPos; 154 | if (*beginPos != '[') { 155 | diagError(diagEngine, attr->getLocation(), "Unable to find starting brackets for C++11 attribute after trimming whitespace."); 156 | return; 157 | } 158 | 159 | beginOffset = (int)(beginPos - beginBuf); 160 | } 161 | 162 | 163 | // Find ending bracket position 164 | { 165 | bool isInvalid = false; 166 | const char* endBuf = sourceMgr.getCharacterData(endLocation, &isInvalid); 167 | const char* endPos = endBuf; 168 | 169 | if (isInvalid) { 170 | diagError(diagEngine, attr->getLocation(), "Unable to find ending brackets for attribute. Invalid character data range."); 171 | return; 172 | } 173 | 174 | // Trim whitespace 175 | while (*endPos == ' ' || *endPos == '\t') { 176 | ++endPos; 177 | } 178 | 179 | // Find "]]" 180 | if (*endPos != ']') { 181 | diagError(diagEngine, attr->getLocation(), "Unable to find ending brackets for C++11 attribute after trimming whitespace."); 182 | return; 183 | } 184 | ++endPos; 185 | if (*endPos != ']') { 186 | diagError(diagEngine, attr->getLocation(), "Unable to find ending brackets for C++11 attribute after trimming whitespace."); 187 | return; 188 | } 189 | ++endPos; 190 | 191 | // Trim whitespace 192 | while (*endPos == ' ' || *endPos == '\t') { 193 | ++endPos; 194 | } 195 | 196 | endOffset = (int)(endPos - endBuf); 197 | } 198 | 199 | CharSourceRange removalRange(SourceRange(beginLocation.getLocWithOffset(beginOffset), 200 | endLocation.getLocWithOffset(endOffset)), false); 201 | 202 | rewriter.RemoveText(removalRange); 203 | } 204 | 205 | template 206 | bool isTypeHelper(const StringRef typeName, const std::string (& typeNames)[N]) { 207 | for (const auto& name : typeNames) { 208 | if (name == typeName) 209 | return true; 210 | } 211 | 212 | return false; 213 | } 214 | 215 | bool isSamplerType(const StringRef typeName) { 216 | const std::string typeNames[] = { 217 | "SamplerState", 218 | }; 219 | 220 | return isTypeHelper(typeName, typeNames); 221 | } 222 | 223 | bool isTextureType(const StringRef typeName) { 224 | const std::string typeNames[] = { 225 | "Texture2D", 226 | }; 227 | 228 | return isTypeHelper(typeName, typeNames); 229 | } 230 | 231 | bool isTextureSRVType(const StringRef typeName) { 232 | const std::string typeNames[] = { 233 | "Texture2DSRV", 234 | }; 235 | 236 | return isTypeHelper(typeName, typeNames); 237 | } 238 | 239 | bool isTextureUAVType(const StringRef typeName) { 240 | const std::string typeNames[] = { 241 | "RWTexture2D", 242 | }; 243 | 244 | return isTypeHelper(typeName, typeNames); 245 | } 246 | 247 | std::string getTypeNameWithoutShaderClassPrefix(const QualType& type, const CXXRecordDecl* shaderClassDecl, const PrintingPolicy& printingPolicy) { 248 | std::string prefix = (shaderClassDecl->getName() + "::").str(); 249 | std::string typeName = type.getAsString(printingPolicy); 250 | 251 | StringRef typeRef = typeName; 252 | typeRef.consume_front(prefix); 253 | 254 | // TODO Figure out a better long-term solution rather than having special cases 255 | // Special cases 256 | if (typeName == ("RWTexture2D<" + prefix + "float4>")) 257 | return "RWTexture2D"; 258 | 259 | return typeRef.str(); 260 | } 261 | 262 | 263 | // ThuscASTVisitor Helper methods 264 | struct HLSLSemanticInfo { 265 | std::string thuscSpelling; 266 | ShaderParameterSemanticKind semanticKind; 267 | std::vector validTypes; 268 | }; 269 | 270 | const HLSLSemanticInfo kSupportedHLSLComputeShaderSemantics[] = { 271 | { THUSC_SV_DISPATCH_THREAD_ID_SPELLING, ShaderParameterSemanticKind::SV_DispatchThreadID, {"uint", "uint2", "uint3"} }, 272 | { THUSC_SV_GROUP_THREAD_ID_SPELLING, ShaderParameterSemanticKind::SV_GroupThreadID, {"uint", "uint2", "uint3"} }, 273 | { THUSC_SV_GROUP_ID_SPELLING, ShaderParameterSemanticKind::SV_GroupID, {"uint", "uint2", "uint3"} }, 274 | { THUSC_SV_GROUP_INDEX_SPELLING, ShaderParameterSemanticKind::SV_GroupIndex, {"uint"} }, 275 | }; 276 | 277 | ShaderParameterSemanticKind ThuscASTVisitor::getComputeEntryParameterSemantic(const ParmVarDecl* param, 278 | const CXXRecordDecl* shaderClassDecl) const { 279 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 280 | const LangOptions& langOpts = rewriter.getLangOpts(); 281 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 282 | 283 | std::vector attrs = findAnnotateAttributeMultiple(param, THUSC_HLSL_SEMANTIC_COMPUTESHADER_PREFIX, true); 284 | 285 | if (attrs.size() != 1) { 286 | diagError(diagEngine, param->getLocation(), "Computer shader entry function parameters must have exactly one HLSL semantic (e.g., [[SV_DispatchThreadID]])"); 287 | return ShaderParameterSemanticKind::Unknown; 288 | } 289 | 290 | StringRef annotation = attrs[0]->getAnnotation(); 291 | std::string typeName = getTypeNameWithoutShaderClassPrefix(param->getType(), shaderClassDecl, printingPolicy); 292 | 293 | for (const auto& semanticInfo : kSupportedHLSLComputeShaderSemantics) { 294 | if (annotation != semanticInfo.thuscSpelling) 295 | continue; 296 | 297 | for (const StringRef t : semanticInfo.validTypes) { 298 | if (typeName == t) { 299 | // Found valid semantic 300 | return semanticInfo.semanticKind; 301 | } 302 | } 303 | 304 | diagError(diagEngine, param->getTypeSpecStartLoc(), "Invalid type for specified compute shader semantic"); 305 | return ShaderParameterSemanticKind::Unknown; 306 | } 307 | 308 | diagError(diagEngine, attrs[0]->getLocation(), "Unsupported compute shader semantic"); 309 | return ShaderParameterSemanticKind::Unknown; 310 | } 311 | 312 | 313 | // ThuscASTVisitor functions 314 | 315 | bool ThuscASTVisitor::VisitCXXRecordDecl(CXXRecordDecl* classDecl) { 316 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 317 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 318 | 319 | // If this is just a GPU struct 320 | const AnnotateAttr* declAttr = findAnnotateAttribute(classDecl, THUSC_GPU_FN_SPELLING); 321 | if (declAttr != nullptr) { 322 | removeCXX11AttrMacro(declAttr, rewriter); 323 | 324 | if (classDecl->method_begin() != classDecl->method_begin()) { 325 | diagError(diagEngine, classDecl->getLocation(), "GPU structs cannot contain methods"); 326 | return false; 327 | } 328 | 329 | // TODO Add support for decls to also remain in host code 330 | shaderIR->otherGPUDecls.push_back(OtherGPUDecl(classDecl, false)); 331 | return true; 332 | } 333 | 334 | 335 | // If this class doesn't have the ThuscShader attribute, then return 336 | const Attr* classAttr = findAnnotateAttribute(classDecl, THUSC_SHADER_SPELLING); 337 | if (classAttr == nullptr) 338 | return true; 339 | 340 | std::unique_ptr shaderClass = ShaderClass::create(); 341 | 342 | shaderClass->classDecl = classDecl; 343 | shaderClass->name = classDecl->getName().str(); 344 | 345 | // Remove the macro call that generated this annotation, because the underlying compiler won't understand it. 346 | removeCXX11AttrMacro(classAttr, rewriter); 347 | 348 | // Look for a ShaderClass base class 349 | if (shaderClass->classDecl->getNumBases() == 1) { 350 | const CXXRecordDecl* baseDecl = shaderClass->classDecl->bases_begin()->getType()->getAsCXXRecordDecl(); 351 | shaderClass->setBaseShaderClass(shaderIR->getShaderClass(baseDecl)); 352 | } 353 | 354 | if (!HandleUniformFields(classDecl, shaderClass.get())) 355 | return false; 356 | 357 | if (!HandlePermutationFields(classDecl, shaderClass.get())) 358 | return false; 359 | 360 | if (!HandleHLSLPrecodePostcode(classDecl, shaderClass.get())) 361 | return false; 362 | 363 | if (!HandleOtherGPUDecls(classDecl, shaderClass.get())) 364 | return false; 365 | 366 | if (!HandleGPUMethods(classDecl, shaderClass.get())) 367 | return false; 368 | 369 | if (!HandleShaderEntryFunctions(classDecl, shaderClass.get())) 370 | return false; 371 | 372 | // Check for ShouldCompilePermutation() method 373 | for (const auto method : classDecl->methods()) { 374 | if (method->getNameAsString() == "ShouldCompilePermutation") { 375 | shaderClass->hasShouldCompilePermutation = true; 376 | break; 377 | } 378 | } 379 | 380 | // Find the call to the THUSC_SHADER() macro and replace it with other code 381 | for (const auto method : classDecl->methods()) { 382 | if (method->getNameAsString() == "ThuscShaderStubFunc") { 383 | auto macroCall = sourceMgr.getExpansionLoc(method->getLocation()); 384 | 385 | // Comment out the call to the THUSC_SHADER() macro, because the underlying compiler won't understand it 386 | rewriter.InsertText(macroCall, "//", true, true); 387 | 388 | break; 389 | } 390 | } 391 | 392 | shaderIR->addShaderClass(std::move(shaderClass)); 393 | 394 | return true; 395 | } 396 | 397 | bool ThuscASTVisitor::VisitFunctionDecl(clang::FunctionDecl* funcDecl) { 398 | // Class methods will be handled separately, so nothing to do here for them 399 | if (isa(funcDecl)) { 400 | return true; 401 | } 402 | 403 | const AnnotateAttr* funcAttr = findAnnotateAttribute(funcDecl, THUSC_GPU_FN_SPELLING); 404 | if (funcAttr == nullptr) 405 | return true; 406 | 407 | removeCXX11AttrMacro(funcAttr, rewriter); 408 | 409 | shaderIR->gpuFunctions.push_back(CreateGPUFunctionIR(funcDecl)); 410 | 411 | return true; 412 | } 413 | 414 | bool ThuscASTVisitor::VisitDecl(Decl* decl) { 415 | const AnnotateAttr* declAttr = findAnnotateAttribute(decl, THUSC_GPU_FN_SPELLING); 416 | if (declAttr == nullptr) 417 | return true; 418 | 419 | if (const VarDecl* varDecl = dyn_cast(decl)) { 420 | if (!varDecl->getType().isConstQualified()) { 421 | diagError(diagEngine, varDecl->getLocation(), "GPU global variable must be const"); 422 | return false; 423 | } 424 | 425 | if (varDecl->isStaticDataMember()) { 426 | // Handled within the ShaderClass 427 | return true; 428 | } 429 | 430 | // Special case for varaibles that are assigned using simple integer literals 431 | if ((varDecl->getType()->isIntegerType() || varDecl->getType()->isUnsignedIntegerType()) 432 | && isa(varDecl->getAnyInitializer()->IgnoreImpCasts())) { 433 | const auto* intLiteral = cast(varDecl->getAnyInitializer()->IgnoreImpCasts()); 434 | std::string output = "#define " + varDecl->getNameAsString() + " " + rewriter.getRewrittenText(intLiteral->getSourceRange()) + "\n"; 435 | // TODO Support host decls too 436 | shaderIR->otherGPUDecls.push_back(OtherGPUDecl(varDecl, false, true, output)); 437 | } 438 | else { 439 | // TODO Probably want to check the scope here too, because we might want to handle 440 | // const vars declared at ShaderClass scope separately 441 | 442 | // TODO Support host decls too 443 | shaderIR->otherGPUDecls.push_back(OtherGPUDecl(varDecl, false)); 444 | } 445 | } 446 | else { 447 | return true; 448 | } 449 | 450 | removeCXX11AttrMacro(declAttr, rewriter); 451 | 452 | return true; 453 | } 454 | 455 | bool ThuscASTVisitor::HandleUniformFields(CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 456 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 457 | const LangOptions& langOpts = rewriter.getLangOpts(); 458 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 459 | 460 | // Foreach field that is a uniform parameter 461 | for (const auto field : shaderClassDecl->fields()) { 462 | const AnnotateAttr* fieldAttr = findAnnotateAttribute(field, THUSC_UNIFORM_PREFIX, true); 463 | if (fieldAttr == nullptr) 464 | continue; 465 | 466 | // Remove the macro call that generated this annotation, because the underlying compiler won't understand it. 467 | removeCXX11AttrMacro(fieldAttr, rewriter); 468 | 469 | std::string typeString = getTypeNameWithoutShaderClassPrefix(field->getType(), shaderClassDecl, printingPolicy); 470 | 471 | UniformField::Kind kind = UniformField::Kind::Unknown; 472 | 473 | // Determine the uniform's type 474 | if (isSamplerType(typeString)) { 475 | kind = UniformField::Kind::Sampler; 476 | } 477 | else if (isTextureType(typeString)) { 478 | kind = UniformField::Kind::Texture; 479 | } 480 | else if (isTextureSRVType(typeString)) { 481 | kind = UniformField::Kind::TextureSRV; 482 | } 483 | else if (isTextureUAVType(typeString)) { 484 | kind = UniformField::Kind::TextureUAV; 485 | } 486 | else if (isPrimitiveType(getGpuTypeName(field->getType(), field->getASTContext()))) { 487 | kind = UniformField::Kind::Primitive; 488 | } 489 | else if (field->getType()->isConstantArrayType()) { 490 | kind = UniformField::Kind::Array; 491 | 492 | const ConstantArrayType* arrType = cast(field->getType().getTypePtr()); 493 | typeString = getTypeNameWithoutShaderClassPrefix(arrType->getElementType(), shaderClassDecl, printingPolicy); 494 | 495 | if (!isPrimitiveType(getGpuTypeName(arrType->getElementType(), field->getASTContext()))) { 496 | diagError(diagEngine, field->getLocation(), "Array uniform parameters must be arrays of primitive types"); 497 | return false; 498 | } 499 | } 500 | else if (StringRef(typeString).startswith("GlobalUniformBuffer")) { 501 | kind = UniformField::Kind::GlobalUniformBuffer; 502 | 503 | StringRef typeStringRef(typeString); 504 | 505 | if (!typeStringRef.consume_front("GlobalUniformBuffer<") || !typeStringRef.consume_back(">")) { 506 | diagError(diagEngine, field->getLocation(), "Cannot get template parameter type for GlobalUniformBuffer type. Expected type syntax to be 'GlobalUniformBuffer'."); 507 | return false; 508 | } 509 | 510 | typeString = typeStringRef.str(); 511 | } 512 | else if (field->getType()->isStructureOrClassType()) { 513 | kind = UniformField::Kind::Struct; 514 | } 515 | else if (field->getType()->isPointerType()) { 516 | diagError(diagEngine, field->getLocation(), "Pointer type uniform parameters are not allowed"); 517 | return false; 518 | } 519 | else { 520 | diagError(diagEngine, field->getLocation(), "Unsupported uniform parameter type"); 521 | return false; 522 | } 523 | 524 | UniformField uniformField(field, field->getName(), typeString, kind); 525 | shaderClass->uniformFields.push_back(std::move(uniformField)); 526 | } 527 | 528 | return true; 529 | } 530 | 531 | bool ThuscASTVisitor::HandlePermutationFields(CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 532 | // Foreach field that is a permutation parameter 533 | for (const auto field : shaderClassDecl->fields()) { 534 | const AnnotateAttr* fieldAttr = findAnnotateAttribute(field, THUSC_PERMUTATION_PREFIX, true); 535 | if (fieldAttr == nullptr) 536 | continue; 537 | 538 | // Remove the macro call that generated this annotation, because the underlying compiler won't understand it. 539 | removeCXX11AttrMacro(fieldAttr, rewriter); 540 | 541 | // Determine which permutation type it is, and create a ShaderIR member accordingly 542 | bool shouldContinue = true; 543 | StringRef annotation = fieldAttr->getAnnotation(); 544 | 545 | // Handle ShaderClass permutations separately 546 | if (annotation.consume_front(THUSC_PERMUTATION_SHADERCLASS_SPELLING)) { 547 | const ShaderClass* shaderClassType = shaderIR->getShaderClass(field->getType()->getPointeeCXXRecordDecl()); 548 | if (!shaderClassType) { 549 | diagError(diagEngine, field->getLocation(), "The type of a permutation ShaderClass field must be a pointer to a ShaderClass"); 550 | continue; 551 | } 552 | 553 | shaderClass->permutationShaderClasses.push_back(PermutationShaderClass(field, field->getName(), shaderClassType, shaderIR)); 554 | 555 | continue; 556 | } 557 | 558 | // Handle other types of permutations 559 | QualType typeWithoutQualifiers(QualType(field->getType().getTypePtr(), 0)); 560 | std::string typeString = typeWithoutQualifiers.getAsString(printingPolicy); 561 | 562 | PermutationField::Kind kind = PermutationField::Kind::Unknown; 563 | SmallVector valueOptions; 564 | 565 | // Enum 566 | if (annotation.equals(THUSC_PERMUTATION_ENUM_SPELLING)) { 567 | if (!field->getType()->isEnumeralType()) { 568 | diagError(diagEngine, field->getTypeSpecStartLoc(), "Permutation enum specified, but field type is not an enum"); 569 | } 570 | else { 571 | kind = PermutationField::Kind::Enum; 572 | valueOptions.push_back(typeString); 573 | } 574 | } 575 | // Bool 576 | else if (annotation.consume_front(THUSC_PERMUTATION_BOOL_SPELLING)) { 577 | if (!field->getType()->isBooleanType()) { 578 | diagError(diagEngine, field->getTypeSpecStartLoc(), "Permutation bool specified, but field type is not a bool"); 579 | } 580 | else { 581 | kind = PermutationField::Kind::Bool; 582 | } 583 | } 584 | // Int (range from 0 to N) 585 | else if (annotation.consume_front(THUSC_PERMUTATION_INT_SPELLING)) { 586 | if (!field->getType()->isIntegralType(field->getASTContext())) { 587 | diagError(diagEngine, field->getTypeSpecStartLoc(), "Permutation Int specified, but field type is not an int"); 588 | } 589 | else if (annotation.equals("")) { 590 | diagError(diagEngine, fieldAttr->getLocation(), "Permutation Int declaration requires one decimal integer value"); 591 | } 592 | else { 593 | kind = PermutationField::Kind::Int; 594 | 595 | int value; 596 | // Using 10 as the Radix to indicate that the integers should be specified in decimal form 597 | if (annotation.trim().getAsInteger(10, value)) { 598 | diagError(diagEngine, fieldAttr->getLocation(), "Permutation Int declaration requires one decimal integer value"); 599 | return false; 600 | } 601 | 602 | valueOptions.push_back(annotation.str()); 603 | } 604 | } 605 | // Sparse Int 606 | else if (annotation.consume_front(THUSC_PERMUTATION_SPARSE_INT_SPELLING)) { 607 | if (!field->getType()->isIntegralType(field->getASTContext())) { 608 | diagError(diagEngine, field->getTypeSpecStartLoc(), "Permutation SparseInt specified, but field type is not an int"); 609 | } 610 | else if (annotation.equals("")) { 611 | diagError(diagEngine, fieldAttr->getLocation(), "Permutation SparseInt declaration requires one or more decimal integer values (separated by commas)"); 612 | } 613 | else { 614 | kind = PermutationField::Kind::SparseInt; 615 | 616 | SmallVector sparseIntStrings; 617 | annotation.split(sparseIntStrings, ','); 618 | 619 | for (auto v : sparseIntStrings) { 620 | int value; 621 | // Using 10 as the Radix to indicate that the integers should be specified in decimal form 622 | if (v.trim().getAsInteger(10, value)) { 623 | diagError(diagEngine, fieldAttr->getLocation(), "Permutation SparseInt declaration requires one or more decimal integer values (separated by commas)"); 624 | break; 625 | } 626 | 627 | valueOptions.push_back(v.str()); 628 | } 629 | 630 | assert(sparseIntStrings.size() == valueOptions.size() && "When parsing Permutation SparseInt, number of parsed integers does not equal number of string elements"); 631 | } 632 | } 633 | // TODO Add other permutation types 634 | else { 635 | diagError(diagEngine, fieldAttr->getLocation(), "Unrecognized permutation type"); 636 | } 637 | 638 | PermutationField permField(field, field->getName(), typeString, std::move(valueOptions), kind); 639 | shaderClass->permutationFields.push_back(std::move(permField)); 640 | 641 | if (!shouldContinue) 642 | return false; 643 | } 644 | 645 | return true; 646 | } 647 | 648 | bool ThuscASTVisitor::HandleShaderEntryFunctions(const CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 649 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 650 | const LangOptions& langOpts = rewriter.getLangOpts(); 651 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 652 | 653 | for (const auto method : shaderClassDecl->methods()) { 654 | const AnnotateAttr* methodAttr = findAnnotateAttribute(method, THUSC_SHADER_ENTRY_PREFIX, true); 655 | if (methodAttr == nullptr) 656 | continue; 657 | 658 | if (method->getAttrs().size() != 1) { 659 | diagError(diagEngine, method->getLocation(), "Shader entry functions must have only one attribute"); 660 | } 661 | 662 | removeCXX11AttrMacro(methodAttr, rewriter); 663 | 664 | if (!method->getReturnType()->isVoidType()) { 665 | diagError(diagEngine, method->getReturnTypeSourceRange().getBegin(), "Shader entry functions must return void"); 666 | } 667 | 668 | if (!method->isConst()) { 669 | diagError(diagEngine, method->getLocation(), "Shader entry functions must be 'const'"); 670 | } 671 | 672 | if (!method->hasBody()) { 673 | diagError(diagEngine, method->getLocation(), "Shader entry functions must have a function body"); 674 | } 675 | 676 | // Determine which shader entry type it is, and create a ShaderIR member accordingly 677 | bool shouldContinue = true; 678 | StringRef annotation = methodAttr->getAnnotation(); 679 | 680 | std::unique_ptr entryPoint; 681 | 682 | // Compute shader entry 683 | if (annotation.consume_front(THUSC_ENTRY_COMPUTE_SPELLING)) { 684 | // Get threadgroup size info 685 | SmallVector threadgroupSize; 686 | annotation.split(threadgroupSize, ','); 687 | 688 | assert(threadgroupSize.size() == 3 && "When parsing compute shader entry, number of groupsize elements does not equal 3"); 689 | 690 | // Verify that threadgroupSize parameters are either integer literals or permutation parameters 691 | for (const auto s : threadgroupSize) { 692 | // Integer literal 693 | int value; 694 | if (!s.trim().getAsInteger(10, value)) { 695 | // Do nothing 696 | } 697 | // Permutation parameter 698 | else if (shaderClass->isIntPermutationField(s)) { 699 | // Do nothing 700 | } 701 | else { 702 | // TODO Commenting this out for now, since we can also use [[gpu]] const integers that are initialized with simple integer literals, 703 | // but we don't have a good way to map from the annotation string to the such a declaration right now. 704 | //diagError(diagEngine, methodAttr->getLocation(), "Threadgroup size values for compute shader entry functions must be either integer literals or integer permtuation parameters"); 705 | } 706 | } 707 | 708 | std::unique_ptr computeEntry = std::make_unique(method, method->getName(), threadgroupSize); 709 | 710 | // Add entry point parameters, including HLSL semantics info 711 | for (const auto param : method->parameters()) { 712 | ShaderParameterSemanticKind semantic = getComputeEntryParameterSemantic(param, shaderClassDecl); 713 | 714 | if (semantic == ShaderParameterSemanticKind::Unknown) { 715 | // Error already reported 716 | return false; 717 | } 718 | computeEntry->addParameter(param->getName(), getTypeNameWithoutShaderClassPrefix(param->getType(), shaderClassDecl, printingPolicy), semantic); 719 | } 720 | 721 | entryPoint = std::move(computeEntry); 722 | } 723 | // TODO Add other types of shader entry functions 724 | else { 725 | diagError(diagEngine, methodAttr->getLocation(), "Unrecognized shader entry type"); 726 | } 727 | 728 | shaderClass->entryPoints.push_back(std::move(entryPoint)); 729 | 730 | if (!shouldContinue) 731 | return false; 732 | } 733 | 734 | return true; 735 | } 736 | 737 | bool ThuscASTVisitor::HandleOtherGPUDecls(const CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 738 | for (const auto* decl : shaderClassDecl->decls()) { 739 | const AnnotateAttr* declAttr = findAnnotateAttribute(decl, THUSC_GPU_FN_SPELLING); 740 | if (declAttr == nullptr) 741 | continue; 742 | 743 | removeCXX11AttrMacro(declAttr, rewriter); 744 | 745 | if (const FieldDecl* fieldDecl = dyn_cast(decl)) { 746 | if (fieldDecl->getType().isConstQualified()) { 747 | std::string output = "static " + rewriter.getRewrittenText(fieldDecl->getSourceRange()) + ";"; 748 | // TODO add support for host side too 749 | shaderClass->otherGPUDecls.push_back(OtherGPUDecl(fieldDecl, false, true, output)); 750 | } 751 | else { 752 | // TODO add support for host side too 753 | shaderClass->otherGPUDecls.push_back(OtherGPUDecl(fieldDecl, false)); 754 | } 755 | } 756 | else if (const VarDecl* varDecl = dyn_cast(decl)) { 757 | if (!varDecl->isStaticDataMember()) { 758 | diagError(diagEngine, decl->getLocation(), "Unsupported [[gpu]] decl in Shader Class (must be static)"); 759 | } 760 | else { 761 | // TODO add support for host side too 762 | shaderClass->otherGPUDecls.push_back(OtherGPUDecl(varDecl, false)); 763 | } 764 | } 765 | else if (isa(decl)) { 766 | // Do nothing (handled in HandleGPUMethods() function) 767 | } 768 | // TODO add other cases 769 | else { 770 | diagError(diagEngine, decl->getLocation(), "Unsupported [[gpu]] decl in Shader Class"); 771 | } 772 | } 773 | 774 | return true; 775 | } 776 | 777 | bool ThuscASTVisitor::HandleGPUMethods(const CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 778 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 779 | const LangOptions& langOpts = rewriter.getLangOpts(); 780 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 781 | 782 | for (const auto method : shaderClassDecl->methods()) { 783 | const AnnotateAttr* methodAttr = findAnnotateAttribute(method, THUSC_GPU_FN_SPELLING); 784 | if (methodAttr == nullptr) 785 | continue; 786 | 787 | removeCXX11AttrMacro(methodAttr, rewriter); 788 | 789 | if (!method->isConst()) { 790 | diagError(diagEngine, method->getLocation(), "ShaderClass GPU methods must be 'const'"); 791 | } 792 | 793 | shaderClass->addGPUMethod(CreateGPUFunctionIR(method)); 794 | } 795 | 796 | return true; 797 | } 798 | 799 | bool ThuscASTVisitor::HandleHLSLPrecodePostcode(const CXXRecordDecl* shaderClassDecl, thusc::ShaderClass* shaderClass) { 800 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 801 | const LangOptions& langOpts = rewriter.getLangOpts(); 802 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 803 | 804 | for (const auto* method : shaderClassDecl->methods()) { 805 | const AnnotateAttr* methodAttr = findAnnotateAttribute(method, THUSC_HLSL_PRECODE_SPELLING); 806 | 807 | bool isPrecode = false; 808 | if (methodAttr != nullptr) { 809 | isPrecode = true; 810 | } 811 | else { 812 | methodAttr = findAnnotateAttribute(method, THUSC_HLSL_POSTCODE_SPELLING); 813 | } 814 | 815 | if (methodAttr == nullptr) { 816 | continue; 817 | } 818 | 819 | if (method->getAttrs().size() != 1) { 820 | diagError(diagEngine, method->getLocation(), "PrecodeHLSL and PostcodeHLSL functions must have only one attribute"); 821 | } 822 | 823 | removeCXX11AttrMacro(methodAttr, rewriter); 824 | 825 | if (!method->isConst()) { 826 | diagError(diagEngine, method->getLocation(), "PrecodeHLSL and PostcodeHLSL class functions must be 'const'"); 827 | } 828 | 829 | if (!method->hasBody()) { 830 | diagError(diagEngine, method->getLocation(), "PrecodeHLSL and PostcodeHLSL functions must have a function body"); 831 | } 832 | 833 | std::string code = rewriter.getRewrittenText( 834 | SourceRange(method->getBody()->getBeginLoc().getLocWithOffset(1), method->getBodyRBrace().getLocWithOffset(-1))); 835 | 836 | if (isPrecode) { 837 | shaderClass->hlslPrecode.append(code); 838 | } 839 | else { 840 | shaderClass->hlslPostcode.append(code); 841 | } 842 | 843 | rewriter.RemoveText(method->getSourceRange()); 844 | } 845 | 846 | return true; 847 | } 848 | 849 | std::unique_ptr ThuscASTVisitor::CreateGPUFunctionIR(const FunctionDecl* funcDecl) { 850 | const SourceManager& sourceMgr = rewriter.getSourceMgr(); 851 | const LangOptions& langOpts = rewriter.getLangOpts(); 852 | DiagnosticsEngine& diagEngine = sourceMgr.getDiagnostics(); 853 | 854 | // TODO commenting this out, because c++ override counts as an attribute. Figure out a better solution. 855 | //if (funcDecl->getAttrs().size() != 1) { 856 | // diagError(diagEngine, funcDecl->getLocation(), "GPU functions must have only one attribute"); 857 | //} 858 | 859 | if (!funcDecl->hasBody()) { 860 | diagError(diagEngine, funcDecl->getLocation(), "GPU functions must have a function body"); 861 | } 862 | 863 | // TODO Add support for host-callable GPU functions 864 | std::unique_ptr gpuFunction = std::make_unique(funcDecl, funcDecl->getName(), 865 | funcDecl->getReturnType().getAsString(printingPolicy), false); 866 | 867 | for (const auto param : funcDecl->parameters()) { 868 | bool isInout = (findAnnotateAttribute(param, THUSC_HLSL_INOUT_SPELLING)) ? true : false; 869 | 870 | gpuFunction->addParameter(param->getName(), param->getType().getAsString(printingPolicy), isInout); 871 | } 872 | 873 | return std::move(gpuFunction); 874 | } 875 | 876 | --------------------------------------------------------------------------------