diff --git a/Build/JenkinsConfiguration.txt b/Build/JenkinsConfiguration.txt index 2ca65cd..aefab79 100644 --- a/Build/JenkinsConfiguration.txt +++ b/Build/JenkinsConfiguration.txt @@ -2,6 +2,7 @@ // "P4ROOT" is passed in to this build script from Jenkins // "SOLUTION" is passed in to this build script from Jenkins // "ARCH" is passed in to this build script from Jenkins (must be "x64" or "x86") +// "ARCHIVE_NAME" is passed in to this build script from Jenkins Define "RGAPATH" "[P4ROOT]\RGA" Define "BUILDPATH" "[RGAPATH]\BuildOutput" @@ -34,10 +35,11 @@ Image XCopy "[OUTPUT_PATH]\Release_Static\bin\[ARCH]\amdspv.exe" "[ZIPDIRRGA]\bin\[ARCH]\amdspv.exe*" XCopy "[OUTPUT_PATH]\Release_Static\bin\[ARCH]\spvgen.dll" "[ZIPDIRRGA]\bin\[ARCH]\spvgen.dll*" XCopy "[OUTPUT_PATH]\Release_Static\bin\[ARCH]\shae.exe" "[ZIPDIRRGA]\bin\[ARCH]\shae.exe*" + XCopy "[OUTPUT_PATH]\Release_Static\bin\[ARCH]\RGADX11.exe" "[ZIPDIRRGA]\bin\[ARCH]\RGADX11.exe*" XCopy "C:\Program Files (x86)\Windows Kits\10\bin\[ARCH]\d3dcompiler_47.dll" "[ZIPDIRRGA]\bin\[ARCH]\d3dcompiler_47.dll*" // Copy the VC++ Redistributable package binaries XCopy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\[ARCH]\Microsoft.VC140.CRT\concrt140.dll" "[ZIPDIRRGA]\bin\concrt140.dll*" XCopy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\[ARCH]\Microsoft.VC140.CRT\msvcp140.dll" "[ZIPDIRRGA]\bin\msvcp140.dll*" XCopy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\[ARCH]\Microsoft.VC140.CRT\vccorlib140.dll" "[ZIPDIRRGA]\bin\vccorlib140.dll*" XCopy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\[ARCH]\Microsoft.VC140.CRT\vcruntime140.dll" "[ZIPDIRRGA]\bin\vcruntime140.dll*" - Zip "[BUILDPATH]\RGA.[DATE].[BUILD].zip" "[ZIPDIRRGA]\bin" + Zip "[BUILDPATH]\[ARCHIVE_NAME].[BUILD]_[DATE].zip" "[ZIPDIRRGA]\bin" \ No newline at end of file diff --git a/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj b/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj index de77425..6fad5dd 100644 --- a/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj +++ b/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj @@ -375,6 +375,7 @@ + diff --git a/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj.filters b/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj.filters index 2e981f9..b0c4913 100644 --- a/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj.filters +++ b/Build/VS2015/RadeonGPUAnalyzerCLI.vcxproj.filters @@ -54,6 +54,9 @@ Source Files + + Source Files + diff --git a/Build/VS2015/rga_copy_x64.bat b/Build/VS2015/rga_copy_x64.bat index 5339a94..7c2a25e 100644 --- a/Build/VS2015/rga_copy_x64.bat +++ b/Build/VS2015/rga_copy_x64.bat @@ -8,6 +8,7 @@ if not exist %OUTPUT_DIR% mkdir %OUTPUT_DIR% rem Copy core files: XCopy /r /d /y "..\..\Core\OpenGL\VirtualContext\Release\win64\VirtualContext.exe" "%OUTPUT_DIR%\x64\" XCopy /r /d /y "..\..\Core\ShaderAnalysis\Windows\x86\shae.exe" "%OUTPUT_DIR%\x64\" +XCopy /r /d /y "..\..\Core\DX\DX10\bin\RGADX11.exe" "%OUTPUT_DIR%\x64\" IF "%2"=="-internal" ( ECHO Copying internal vulkan backend diff --git a/Build/VS2015/rga_copy_x86.bat b/Build/VS2015/rga_copy_x86.bat index 9e2af48..a588d75 100644 --- a/Build/VS2015/rga_copy_x86.bat +++ b/Build/VS2015/rga_copy_x86.bat @@ -8,6 +8,7 @@ if not exist %OUTPUT_DIR% mkdir %OUTPUT_DIR% rem Copy core files: XCopy /r /d /y "..\..\Core\OpenGL\VirtualContext\Release\win32\VirtualContext.exe" "%OUTPUT_DIR%\x86\" XCopy /r /d /y "..\..\Core\ShaderAnalysis\Windows\x86\shae.exe" "%OUTPUT_DIR%\x86\" +XCopy /r /d /y "..\..\Core\DX\DX10\bin\RGADX11.exe" "%OUTPUT_DIR%\x64\" IF "%2"=="-internal" ( ECHO Copying internal vulkan backend diff --git a/CMakeLists.txt b/CMakeLists.txt index a3191e6..00314c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,12 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") endif() +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CONFIGURATION_TYPES "Debug") +else() + set(CMAKE_CONFIGURATION_TYPES "Release") +endif() + # Continue to the following subdirectories add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/../Common/Src/AMDTBaseTools CMAKE_CURRENT_BINARY_DIR}/../Common/Src/AMDTBaseTools) add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/../Common/Src/AMDTOSWrappers CMAKE_CURRENT_BINARY_DIR}/../Common/Src/AMDTOSWrappers) diff --git a/Core/DX/DX10/bin/RGADX11.exe b/Core/DX/DX10/bin/RGADX11.exe new file mode 100755 index 0000000..6457a09 Binary files /dev/null and b/Core/DX/DX10/bin/RGADX11.exe differ diff --git a/Core/OpenGL/VirtualContext/Release/lnx64/VirtualContext b/Core/OpenGL/VirtualContext/Release/lnx64/VirtualContext old mode 100644 new mode 100755 index c24801e..b9a416a Binary files a/Core/OpenGL/VirtualContext/Release/lnx64/VirtualContext and b/Core/OpenGL/VirtualContext/Release/lnx64/VirtualContext differ diff --git a/Core/OpenGL/VirtualContext/Release/win32/VirtualContext.exe b/Core/OpenGL/VirtualContext/Release/win32/VirtualContext.exe index 08a64da..99d3089 100644 Binary files a/Core/OpenGL/VirtualContext/Release/win32/VirtualContext.exe and b/Core/OpenGL/VirtualContext/Release/win32/VirtualContext.exe differ diff --git a/Core/OpenGL/VirtualContext/Release/win64/VirtualContext.exe b/Core/OpenGL/VirtualContext/Release/win64/VirtualContext.exe index ddf3c2c..b5b43a4 100644 Binary files a/Core/OpenGL/VirtualContext/Release/win64/VirtualContext.exe and b/Core/OpenGL/VirtualContext/Release/win64/VirtualContext.exe differ diff --git a/Core/Vulkan/rev_1_0_0/Release/lnx32/amdspv b/Core/Vulkan/rev_1_0_0/Release/lnx32/amdspv index 0b4f972..afee671 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/lnx32/amdspv and b/Core/Vulkan/rev_1_0_0/Release/lnx32/amdspv differ diff --git a/Core/Vulkan/rev_1_0_0/Release/lnx32/spvgen.so b/Core/Vulkan/rev_1_0_0/Release/lnx32/spvgen.so index 6809a8a..3b07542 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/lnx32/spvgen.so and b/Core/Vulkan/rev_1_0_0/Release/lnx32/spvgen.so differ diff --git a/Core/Vulkan/rev_1_0_0/Release/lnx64/amdspv b/Core/Vulkan/rev_1_0_0/Release/lnx64/amdspv index f3d117c..11be331 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/lnx64/amdspv and b/Core/Vulkan/rev_1_0_0/Release/lnx64/amdspv differ diff --git a/Core/Vulkan/rev_1_0_0/Release/lnx64/spvgen.so b/Core/Vulkan/rev_1_0_0/Release/lnx64/spvgen.so index a05c30f..f26bbff 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/lnx64/spvgen.so and b/Core/Vulkan/rev_1_0_0/Release/lnx64/spvgen.so differ diff --git a/Core/Vulkan/rev_1_0_0/Release/win32/amdspv.exe b/Core/Vulkan/rev_1_0_0/Release/win32/amdspv.exe index fcb62f6..217891c 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/win32/amdspv.exe and b/Core/Vulkan/rev_1_0_0/Release/win32/amdspv.exe differ diff --git a/Core/Vulkan/rev_1_0_0/Release/win32/spvgen.dll b/Core/Vulkan/rev_1_0_0/Release/win32/spvgen.dll index 56dad10..c9299ec 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/win32/spvgen.dll and b/Core/Vulkan/rev_1_0_0/Release/win32/spvgen.dll differ diff --git a/Core/Vulkan/rev_1_0_0/Release/win64/amdspv.exe b/Core/Vulkan/rev_1_0_0/Release/win64/amdspv.exe index 7d58974..b43293b 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/win64/amdspv.exe and b/Core/Vulkan/rev_1_0_0/Release/win64/amdspv.exe differ diff --git a/Core/Vulkan/rev_1_0_0/Release/win64/spvgen.dll b/Core/Vulkan/rev_1_0_0/Release/win64/spvgen.dll index f9068fc..7056fb5 100644 Binary files a/Core/Vulkan/rev_1_0_0/Release/win64/spvgen.dll and b/Core/Vulkan/rev_1_0_0/Release/win64/spvgen.dll differ diff --git a/Installer/RGA-Installer.aip b/Installer/RGA-Installer.aip index b3ae5c4..0fa280c 100644 --- a/Installer/RGA-Installer.aip +++ b/Installer/RGA-Installer.aip @@ -9,10 +9,10 @@ - + - + @@ -31,6 +31,7 @@ + @@ -43,14 +44,15 @@ - + + - + diff --git a/README.md b/README.md index 2b79520..b1352e2 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ The Radeon Software Crimson Edition or AMD Catalyst release must be installed to ## Build Instructions ## + Before starting, run the UpdateCommon.py script, which fetches the solution's dependencies. + ### Building on Windows ### There are two ways to build RGA on Windows: @@ -48,8 +50,9 @@ There are two ways to build RGA on Windows: cmake.exe -G "Visual Studio 14 2015 Win64" –DCMAKE_BUILD_TYPE=Release (or: Debug) - Before starting, run the UpdateCommon.py script, which fetches the solution's dependencies. - + If you are intending to analyze DirectX shaders using RGA, copy the x64 version of Microsoft's D3D compiler to a subdirectory + named "x64" under the RGA executable's directory (for example, D3DCompiler_47.dll). + Note that after switching CMake build configurations, you might need to delete the old files that were previously generated by CMake. Therefore, it is recommended to work with separate output directories, one for each build configuration. diff --git a/RadeonGPUAnalyzerBackend/CMakeLists.txt b/RadeonGPUAnalyzerBackend/CMakeLists.txt index 6fbf228..a375685 100644 --- a/RadeonGPUAnalyzerBackend/CMakeLists.txt +++ b/RadeonGPUAnalyzerBackend/CMakeLists.txt @@ -3,7 +3,7 @@ project (RadeonGPUAnalyzerBackend) # The version number set (RadeonGPUAnalyzerBackend_VERSION_MAJOR 1) -set (RadeonGPUAnalyzerBackend_VERSION_MINOR 0) +set (RadeonGPUAnalyzerBackend_VERSION_MINOR 2) # Project-specific preprocessor directives add_definitions(-DRGA_BACKEND_EXPORTS) diff --git a/RadeonGPUAnalyzerBackend/include/beBackend.h b/RadeonGPUAnalyzerBackend/include/beBackend.h index 8225a56..530ebbd 100644 --- a/RadeonGPUAnalyzerBackend/include/beBackend.h +++ b/RadeonGPUAnalyzerBackend/include/beBackend.h @@ -47,7 +47,7 @@ class RGA_BACKEND_DECLDIR Backend : public TSingleton // // Public member functions // - beStatus Initialize(BuiltProgramKind ProgramKind, LoggingCallBackFuncP callback, const string& sDllModule = ""); + beStatus Initialize(BuiltProgramKind ProgramKind, LoggingCallBackFuncP callback); /// dtor /// If a Log stream is available, unreleased built programs may be diagnosed. diff --git a/RadeonGPUAnalyzerBackend/include/beInclude.h b/RadeonGPUAnalyzerBackend/include/beInclude.h index e0faa63..bf1a119 100644 --- a/RadeonGPUAnalyzerBackend/include/beInclude.h +++ b/RadeonGPUAnalyzerBackend/include/beInclude.h @@ -111,9 +111,13 @@ enum beStatus beStatus_GLUnknownHardwareFamily, beStatus_VulkanAmdspvLaunchFailure, beStatus_VulkanAmdspvCompilationFailure, + beStatus_VulkanNoInputFile, + beStatus_FailedOutputVerification, + beStatus_VulkanMixedInputFiles, beStatus_shaeCannotLocateAnalyzer, beStatus_shaeIsaFileNotFound, beStatus_shaeFailedToLaunch, + beStatus_dxDriverLaunchFailure, beStatus_General_FAILED, }; diff --git a/RadeonGPUAnalyzerBackend/include/beProgramBuilder.h b/RadeonGPUAnalyzerBackend/include/beProgramBuilder.h index 0d1be27..c690a6e 100644 --- a/RadeonGPUAnalyzerBackend/include/beProgramBuilder.h +++ b/RadeonGPUAnalyzerBackend/include/beProgramBuilder.h @@ -82,10 +82,6 @@ class RGA_BACKEND_DECLDIR beProgramBuilder } protected: - /// Ctor - //virtual beProgramBuilder() = 0; - - virtual beKA::beStatus Initialize(const std::string& sDllModule = "") = 0; /// Stream for diagnostic output. set externally. LoggingCallBackFuncP m_LogCallback; diff --git a/RadeonGPUAnalyzerBackend/include/beProgramBuilderDX.h b/RadeonGPUAnalyzerBackend/include/beProgramBuilderDX.h index 2f62722..8c0cf97 100644 --- a/RadeonGPUAnalyzerBackend/include/beProgramBuilderDX.h +++ b/RadeonGPUAnalyzerBackend/include/beProgramBuilderDX.h @@ -18,7 +18,7 @@ using namespace beKA; class CElf; class CElfSection; -class RGA_BACKEND_DECLDIR beProgramBuilderDX : public beProgramBuilder +class beProgramBuilderDX : public beProgramBuilder { public: @@ -131,7 +131,14 @@ class RGA_BACKEND_DECLDIR beProgramBuilderDX : public beProgramBuilder void ReleaseProgram(); beKA::beStatus GetDeviceTable(std::vector& table) override; bool CompileOK(std::string& device); + public: + /// Ctor + beProgramBuilderDX(); + + /// Initialize the Builder + beKA::beStatus Initialize(const std::string& dxxModuleName, const std::string& compilerModuleName = ""); + /// compile the specified source file /// \param[in] sourceLanguage specify the source language- can be HLSL or DXAsm /// \param[in] programSource the string of the source code @@ -188,10 +195,12 @@ class RGA_BACKEND_DECLDIR beProgramBuilderDX : public beProgramBuilder /// Sets the set of public device names. void SetPublicDeviceNames(const std::set& publicDeviceNames); -protected: - /// Ctor - beProgramBuilderDX(); - beKA::beStatus Initialize(const std::string& sDllModule = ""); + /// Retrieves the list of names of AMD display adapters installed on the system. + static bool GetSupportedDisplayAdapterNames(std::vector& adapterNames); + + /// Gets the full path to AMD DXX library for GPU adapter specified by "adapterID". + /// Returns the adapter name in "adapterName" and DXX lib path in "dxxModulePath". + static bool GetDXXModulePathForAdapter(int adapterID, std::string& adapterName, std::string& dxxModulePath); private: // members /// Interface with atidxx{32,64}.dll @@ -245,9 +254,6 @@ class RGA_BACKEND_DECLDIR beProgramBuilderDX : public beProgramBuilder void ClearFormerBuildOutputs(); void SetDeviceElf(const std::string& deviceName, const AmdDxGsaCompileShaderOutput& shaderOutput); bool GetDeviceElfBinPair(const std::string& deviceName, CelfBinaryPair& elfBinPair) const; - - // Friends. - friend class Backend; }; #endif diff --git a/RadeonGPUAnalyzerBackend/include/beProgramBuilderOpenGL.h b/RadeonGPUAnalyzerBackend/include/beProgramBuilderOpenGL.h index 9229ee1..c5c4c31 100644 --- a/RadeonGPUAnalyzerBackend/include/beProgramBuilderOpenGL.h +++ b/RadeonGPUAnalyzerBackend/include/beProgramBuilderOpenGL.h @@ -16,8 +16,8 @@ struct OpenGLOptions : public beKA::CompileOptions { - OpenGLOptions() : m_chipFamily(0), m_chipRevision(0), m_isAmdIsaBinariesRequired(true), - m_isAmdIsaDisassemblyRequired(true), m_isScStatsRequired(true) + OpenGLOptions() : m_chipFamily(0), m_chipRevision(0), m_isAmdIsaBinariesRequired(false), + m_isAmdIsaDisassemblyRequired(false), m_isScStatsRequired(false), m_isCfgRequired(false), m_isLiveRegisterAnalysisRequired(false) { CompileOptions::m_SourceLanguage = beKA::SourceLanguage_GLSL; } @@ -87,7 +87,7 @@ class RGA_BACKEND_DECLDIR beProgramBuilderOpenGL : virtual bool CompileOK(std::string& device) override; - virtual beKA::beStatus Initialize(const std::string& sDllModule = "") override; + beKA::beStatus Initialize(const std::string& sDllModule = ""); beKA::beStatus Compile(const OpenGLOptions& vulkanOptions, bool& cancelSignal, gtString& compilerOutput); diff --git a/RadeonGPUAnalyzerBackend/include/beProgramBuilderVulkan.h b/RadeonGPUAnalyzerBackend/include/beProgramBuilderVulkan.h index ed8561d..f08f70b 100644 --- a/RadeonGPUAnalyzerBackend/include/beProgramBuilderVulkan.h +++ b/RadeonGPUAnalyzerBackend/include/beProgramBuilderVulkan.h @@ -27,6 +27,9 @@ struct VulkanOptions : public beKA::CompileOptions // The target devices. std::string m_targetDeviceName; + // The generic input file name. + std::string m_stagelessInputFile; + // The input shader file names. beProgramPipeline m_pipelineShaders; @@ -101,7 +104,7 @@ class RGA_BACKEND_DECLDIR beProgramBuilderVulkan : public virtual bool CompileOK(std::string& device) override; - virtual beKA::beStatus Initialize(const std::string& sDllModule = "") override; + beKA::beStatus Initialize(const std::string& sDllModule = ""); beKA::beStatus Compile(const VulkanOptions& vulkanOptions, bool& cancelSignal, gtString& buildLog); diff --git a/RadeonGPUAnalyzerBackend/include/beUtils.h b/RadeonGPUAnalyzerBackend/include/beUtils.h index 182bdb2..20f3d33 100644 --- a/RadeonGPUAnalyzerBackend/include/beUtils.h +++ b/RadeonGPUAnalyzerBackend/include/beUtils.h @@ -39,6 +39,10 @@ class beUtils // Deletes a physical file from the file system. static void DeleteFile(const gtString& filePath); + // If "required" is true, checks if the "fileName" file exists and is not empty. + // If "requierd" is false, returns "true" unconditionally. + static bool isFilePresent(const std::string& fileName, bool required); + private: // No instances for this class, as this is a static utility class. beUtils(); diff --git a/RadeonGPUAnalyzerBackend/src/beBackend.cpp b/RadeonGPUAnalyzerBackend/src/beBackend.cpp index c662e04..40851fe 100644 --- a/RadeonGPUAnalyzerBackend/src/beBackend.cpp +++ b/RadeonGPUAnalyzerBackend/src/beBackend.cpp @@ -30,7 +30,6 @@ // Infra. #include #include -#include #include #include @@ -45,11 +44,8 @@ Backend::Backend() : m_supportedPublicDevices() m_driverVersionInfo.clear(); } -beKA::beStatus Backend::Initialize(BuiltProgramKind ProgramKind, LoggingCallBackFuncP callback, const string& sDllModule) +beKA::beStatus Backend::Initialize(BuiltProgramKind ProgramKind, LoggingCallBackFuncP callback) { -#ifndef _WIN32 - GT_UNREFERENCED_PARAMETER(sDllModule); -#endif beKA::beStatus retVal = beStatus_General_FAILED; m_LogCallback = callback; @@ -71,7 +67,6 @@ beKA::beStatus Backend::Initialize(BuiltProgramKind ProgramKind, LoggingCallBack } #ifdef _WIN32 - if (ProgramKind == BuiltProgramKind_DX) { // Initialize the DX backend. @@ -92,42 +87,7 @@ beKA::beStatus Backend::Initialize(BuiltProgramKind ProgramKind, LoggingCallBack m_beDX->AddDxSearchDir(dir); } } - - if (m_beDX != NULL) - { - // Set the name of the module to be loaded. - std::string moduleToLoad; - - if (sDllModule.empty()) - { - // This solves the VS extension issue where devenv.exe looked for the D3D compiler - // at its own directory, instead of looking for it at CodeXL's binaries directory. - osFilePath defaultCompilerFilePath; - - // Get CodeXL's binaries directory. Both the 32 and 64-bit versions of d3dcompiler are bundled with CodeXL. - // We use the 32-bit version by default - bool isOk = osGetCurrentApplicationDllsPath(defaultCompilerFilePath, OS_I386_ARCHITECTURE); - - if (isOk) - { - // Create the full path to the default D3D compiler. - defaultCompilerFilePath.setFileName(SA_BE_STR_HLSL_optionsDefaultCompilerFileName); - defaultCompilerFilePath.setFileExtension(SA_BE_STR_HLSL_optionsDefaultCompilerFileExtension); - moduleToLoad = defaultCompilerFilePath.asString().asASCIICharArray(); - } - } - else - { - // Use the given name. - moduleToLoad = sDllModule; - } - - // Initialize the DX backend. - m_beDX->SetLog(callback); - retVal = m_beDX->Initialize(moduleToLoad); - } } - #endif return retVal; diff --git a/RadeonGPUAnalyzerBackend/src/beProgramBuilderDX.cpp b/RadeonGPUAnalyzerBackend/src/beProgramBuilderDX.cpp index 452cb0d..5665a68 100644 --- a/RadeonGPUAnalyzerBackend/src/beProgramBuilderDX.cpp +++ b/RadeonGPUAnalyzerBackend/src/beProgramBuilderDX.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ #include #include #include +#include +#include using namespace std; using namespace D3D10ShaderObject; @@ -41,6 +44,12 @@ using namespace xlt; const char* DEVICE_NAME_TONGA = "Tonga"; const char* DEVICE_NAME_ICELAND = "Iceland"; +const char* AMD_ADAPTER_TOKEN = "AMD"; +const char* RGA_DX11_DRIVER_EXECUTABLE_PATH = "x64\\RGADX11.exe"; +const char* RGA_DX11_DRIVER_GET_ADAPTERS_ARG = "--list-adapters"; +const char* RGA_DX11_DRIVER_GET_ADAPTER_INFO = "--get-adapter-info"; +const char* RGA_DX11_DRIVER_ERROR_TOKEN = "Error"; + // This function returns true if the given device is // affected by the HW issue which forces an allocation of // a fixed number of SGPRs. @@ -155,14 +164,43 @@ beProgramBuilderDX::~beProgramBuilderDX(void) m_TheAMDDXXModule.UnloadModule(); } -beKA::beStatus beProgramBuilderDX::Initialize(const string& msD3DCompilerModuleToLoad/* = ""*/) +beKA::beStatus beProgramBuilderDX::Initialize(const std::string& dxxModuleName, const std::string& compilerModuleName) { beStatus beRet = beStatus_SUCCESS; + std::string compilerDllName = compilerModuleName; + + if (compilerDllName.empty()) + { + // This solves the VS extension issue where devenv.exe looked for the D3D compiler + // at its own directory, instead of looking for it at CodeXL's binaries directory. + osFilePath defaultCompilerFilePath; + + // Get CodeXL's binaries directory. Both the 32 and 64-bit versions of d3dcompiler are bundled with CodeXL. + // We use the 32-bit version by default + bool isOk = osGetCurrentApplicationDllsPath(defaultCompilerFilePath, OS_I386_ARCHITECTURE); + + if (isOk) + { + // Create the full path to the default D3D compiler. + defaultCompilerFilePath.setFileName(SA_BE_STR_HLSL_optionsDefaultCompilerFileName); + defaultCompilerFilePath.setFileExtension(SA_BE_STR_HLSL_optionsDefaultCompilerFileExtension); + compilerDllName = defaultCompilerFilePath.asString().asASCIICharArray(); + } + } // Clear the outputs of former builds (if there are any). ClearFormerBuildOutputs(); - // load AMD module + // load AMD DXX module + if (!dxxModuleName.empty()) + { + if (m_TheAMDDXXModule.IsLoaded()) + { + m_TheAMDDXXModule.UnloadModule(); + } + m_TheAMDDXXModule.LoadModule(dxxModuleName); + } + if (!m_TheAMDDXXModule.IsLoaded()) { // Notice: This message receives an extra "\n", since later in the call chain, one is removed. We do want to remove them for @@ -173,14 +211,14 @@ beKA::beStatus beProgramBuilderDX::Initialize(const string& msD3DCompilerModuleT beRet = beStatus_AMDDXX_MODULE_NOT_LOADED; } - // load ms module + // load D3D compiler module if (beRet == beStatus_SUCCESS) { bool isDllLoad = false; int errorCode = 0; // If the user did not specify a default D3D compiler, use the one in the sub-folder. - std::string fixedMsD3DModuleName = msD3DCompilerModuleToLoad; + std::string fixedMsD3DModuleName = compilerDllName; if (fixedMsD3DModuleName.empty()) { #if _WIN64 @@ -235,8 +273,8 @@ beKA::beStatus beProgramBuilderDX::Initialize(const string& msD3DCompilerModuleT if (!isDllLoad) { // Take the relevant module's name. - const char* pModuleName = (msD3DCompilerModuleToLoad.length() > 0) ? - msD3DCompilerModuleToLoad.c_str() : D3DCompileModule::s_DefaultModuleName; + const char* pModuleName = (compilerDllName.length() > 0) ? + compilerDllName.c_str() : D3DCompileModule::s_DefaultModuleName; // This flag will be true if the given D3D module's bitness is 64-bit, while // this process' bitness is 32-bit. @@ -1121,6 +1159,25 @@ bool beProgramBuilderDX::GetDeviceElfBinPair(const string& deviceName, CelfBinar return result; } +static beKA::beStatus InvokeDX11Driver(const std::string & args, std::string& output) +{ + beKA::beStatus status = beStatus_dxDriverLaunchFailure; + std::stringstream cmdLine; + bool cancelSignal = false; + gtString gtOutput; + cmdLine << RGA_DX11_DRIVER_EXECUTABLE_PATH << " " << args; + + bool result = osExecAndGrabOutput(cmdLine.str().c_str(), cancelSignal, gtOutput); + + if (result) + { + status = beStatus_SUCCESS; + output = gtOutput.asASCIICharArray(); + } + + return status; +} + CElf* beProgramBuilderDX::GetDeviceElf(const string& deviceName) const { CElf* pRet = nullptr; @@ -1151,4 +1208,63 @@ void beProgramBuilderDX::SetPublicDeviceNames(const std::set& publi { m_publicDeviceNames = publicDeviceNames; } + +bool beProgramBuilderDX::GetSupportedDisplayAdapterNames(std::vector& adapterNames) +{ + bool result = false; + std::string driverOut; + + beKA::beStatus status = InvokeDX11Driver(RGA_DX11_DRIVER_GET_ADAPTERS_ARG, driverOut); + + result = (status == beKA::beStatus_SUCCESS && !driverOut.empty()); + + if (result) + { + if (driverOut.find(RGA_DX11_DRIVER_ERROR_TOKEN) == std::string::npos) + { + std::stringstream outStream; + std::string adapterName; + outStream << driverOut; + while (std::getline(outStream, adapterName, '\n')) + { + adapterNames.push_back(adapterName); + } + } + else + { + result = false; + } + } + + return result; +} + +bool beProgramBuilderDX::GetDXXModulePathForAdapter(int adapterID, std::string& adapterName, std::string& dxxModulePath) +{ + bool result = false; + std::string driverOut; + std::stringstream args; + args << RGA_DX11_DRIVER_GET_ADAPTER_INFO << " " << adapterID; + + beKA::beStatus status = InvokeDX11Driver(args.str().c_str(), driverOut); + + result = (status == beKA::beStatus_SUCCESS && !driverOut.empty()); + + if (result) + { + if (driverOut.find(RGA_DX11_DRIVER_ERROR_TOKEN) == std::string::npos) + { + std::stringstream outStream; + outStream << driverOut; + + result = (std::getline(outStream, adapterName, '\n') && std::getline(outStream, dxxModulePath)); + } + else + { + result = false; + } + } + + return result; +} #endif diff --git a/RadeonGPUAnalyzerBackend/src/beProgramBuilderOpenGL.cpp b/RadeonGPUAnalyzerBackend/src/beProgramBuilderOpenGL.cpp index a31a295..8ad0020 100644 --- a/RadeonGPUAnalyzerBackend/src/beProgramBuilderOpenGL.cpp +++ b/RadeonGPUAnalyzerBackend/src/beProgramBuilderOpenGL.cpp @@ -132,10 +132,50 @@ beKA::beStatus beProgramBuilderOpenGL::Initialize(const std::string& sDllModule return beKA::beStatus_SUCCESS; } +// Checks if the required output files are generated by the amdspv. +// Only verifies the files requested in the "options.m_pipelineShaders" name list. +static bool VerifyVirtualContextOutput(const OpenGLOptions& options) +{ + bool ret = true; + if (options.m_isAmdIsaDisassemblyRequired) + { + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isAmdIsaBinariesRequired) + { + ret &= beUtils::isFilePresent(options.m_programBinaryFile.asASCIICharArray(), true); + } + if (ret && options.m_isCfgRequired) + { + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_controlFlowGraphOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isScStatsRequired) + { + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + + return ret; +} + beKA::beStatus beProgramBuilderOpenGL::Compile(const OpenGLOptions& glOptions, bool& cancelSignal, gtString& vcOutput) { GT_UNREFERENCED_PARAMETER(cancelSignal); - beKA::beStatus ret = beKA::beStatus_General_FAILED; + beKA::beStatus ret = beKA::beStatus_SUCCESS; // Clear the output buffer if needed. if (!vcOutput.isEmpty()) @@ -191,7 +231,7 @@ beKA::beStatus beProgramBuilderOpenGL::Compile(const OpenGLOptions& glOptions, b cmd << "\""; // Build the GL program. - bool isCompilerOutputRelevant = true; + bool isCompilerOutputRelevant = false; bool isLaunchSuccess = osExecAndGrabOutput(cmd.str().c_str(), cancelSignal, vcOutput); if (isLaunchSuccess) @@ -200,14 +240,14 @@ beKA::beStatus beProgramBuilderOpenGL::Compile(const OpenGLOptions& glOptions, b gtString vcOutputInLowerCase = vcOutput; vcOutputInLowerCase.toLowerCase(); - if (vcOutputInLowerCase.find(VC_ERROR_TOKEN) == -1) + if (vcOutputInLowerCase.find(VC_ERROR_TOKEN) != -1) { - ret = beKA::beStatus_SUCCESS; - isCompilerOutputRelevant = false; + ret = beKA::beStatus_GLOpenGLBuildError; + isCompilerOutputRelevant = true; } - else + else if (!VerifyVirtualContextOutput(glOptions)) { - ret = beKA::beStatus_GLOpenGLBuildError; + ret = beKA::beStatus_FailedOutputVerification; } } else diff --git a/RadeonGPUAnalyzerBackend/src/beProgramBuilderVulkan.cpp b/RadeonGPUAnalyzerBackend/src/beProgramBuilderVulkan.cpp index d6b3973..7731cd5 100644 --- a/RadeonGPUAnalyzerBackend/src/beProgramBuilderVulkan.cpp +++ b/RadeonGPUAnalyzerBackend/src/beProgramBuilderVulkan.cpp @@ -16,6 +16,28 @@ #include #include +// ***************************************** +// *** INTERNALLY LINKED SYMBOLS - START *** +// ***************************************** + +static const std::string STR_VERT_SPV_OUTPUT_FILE_NAME = "vert.spv"; +static const std::string STR_TESC_SPV_OUTPUT_FILE_NAME = "tesc.spv"; +static const std::string STR_TESE_SPV_OUTPUT_FILE_NAME = "tese.spv"; +static const std::string STR_GEOM_SPV_OUTPUT_FILE_NAME = "geom.spv"; +static const std::string STR_FRAG_SPV_OUTPUT_FILE_NAME = "frag.spv"; +static const std::string STR_COMP_SPV_OUTPUT_FILE_NAME = "comp.spv"; + +static const std::string STR_VERT_PALIL_OUTPUT_FILE_NAME = "vert.palIl"; +static const std::string STR_TESC_PALIL_OUTPUT_FILE_NAME = "tesc.palIl"; +static const std::string STR_TESE_PALIL_OUTPUT_FILE_NAME = "tese.palIl"; +static const std::string STR_GEOM_PALIL_OUTPUT_FILE_NAME = "geom.palIl"; +static const std::string STR_FRAG_PALIL_OUTPUT_FILE_NAME = "frag.palIl"; +static const std::string STR_COMP_PALIL_OUTPUT_FILE_NAME = "comp.palIl"; + +// *************************************** +// *** INTERNALLY LINKED SYMBOLS - END *** +// *************************************** + // Internally-linked utilities. static bool GetAmdspvPath(std::string& amdspvPath) { @@ -91,6 +113,223 @@ static std::string GetInputPrefix(const VulkanOptions& vulkanOptions, const std: return ret; } +static beKA::beStatus AddInputFileNames(const VulkanOptions& options, std::stringstream& cmd) +{ + beKA::beStatus status = beKA::beStatus_SUCCESS; + + // Indicates that a stage-less input file name was provided. + bool isNonStageInput = false; + + // Indicates that some of stage-specific file names was provided (--frag, --vert, etc.). + bool isStageInput = false; + + if (options.m_SourceLanguage == beKA::SourceLanguage_SPIRV_Vulkan || options.m_SourceLanguage == beKA::SourceLanguage_SPIRVTXT_Vulkan) + { + if (!options.m_stagelessInputFile.empty()) + { + cmd << GetInputPrefix(options, "") << options.m_stagelessInputFile << "\" "; + isNonStageInput = true; + } + } + + // You cannot mix compute and non-compute shaders in Vulkan, + // so this has to be mutually exclusive. + if (options.m_pipelineShaders.m_computeShader.isEmpty()) + { + // Vertex shader. + if (!options.m_pipelineShaders.m_vertexShader.isEmpty()) + { + cmd << GetInputPrefix(options, "in.vert.glsl=\"") << options.m_pipelineShaders.m_vertexShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + + // Tessellation control shader. + if (!options.m_pipelineShaders.m_tessControlShader.isEmpty()) + { + cmd << GetInputPrefix(options, "in.tesc.glsl=\"") << options.m_pipelineShaders.m_tessControlShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + + // Tessellation evaluation shader. + if (!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()) + { + cmd << GetInputPrefix(options, "in.tese.glsl=\"") << options.m_pipelineShaders.m_tessEvaluationShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + + // Geometry shader. + if (!options.m_pipelineShaders.m_geometryShader.isEmpty()) + { + cmd << GetInputPrefix(options, "in.geom.glsl=\"") << options.m_pipelineShaders.m_geometryShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + + // Fragment shader. + if (!options.m_pipelineShaders.m_fragmentShader.isEmpty()) + { + cmd << GetInputPrefix(options, "in.frag.glsl=\"") << options.m_pipelineShaders.m_fragmentShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + } + else + { + // Compute shader. + cmd << GetInputPrefix(options, "in.comp.glsl=\"") << options.m_pipelineShaders.m_computeShader.asASCIICharArray() << "\" "; + isStageInput = true; + } + + if (!isNonStageInput && !isStageInput) + { + status = beKA::beStatus_VulkanNoInputFile; + } + else if (isNonStageInput && isStageInput) + { + status = beKA::beStatus_VulkanMixedInputFiles; + } + + return status; +} + +static void AddOutputFileNames(const VulkanOptions& options, std::stringstream& cmd) +{ + bool isSpv = (options.m_SourceLanguage == beKA::SourceLanguage_SPIRV_Vulkan || + options.m_SourceLanguage == beKA::SourceLanguage_SPIRVTXT_Vulkan); + + auto AddOutputFile = [&](bool flag, const std::string& option, const std::string& fileName) + { + if (flag || isSpv) + { + cmd << option << "\"" << fileName << "\"" << " "; + } + }; + + // SPIR-V binaries generation. + if (options.m_isSpirvBinariesRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.spv=", STR_COMP_SPV_OUTPUT_FILE_NAME); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.spv=", STR_VERT_SPV_OUTPUT_FILE_NAME); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.spv=", STR_TESC_SPV_OUTPUT_FILE_NAME); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.spv=", STR_TESE_SPV_OUTPUT_FILE_NAME); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.spv=", STR_GEOM_SPV_OUTPUT_FILE_NAME); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.spv=", STR_FRAG_SPV_OUTPUT_FILE_NAME); + } + } + + // AMD IL Binaries generation (for now we only support PAL IL). + if (options.m_isAmdPalIlBinariesRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.palIl=", STR_COMP_PALIL_OUTPUT_FILE_NAME); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.palIl=", STR_VERT_PALIL_OUTPUT_FILE_NAME); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.palIl=", STR_TESC_PALIL_OUTPUT_FILE_NAME); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.palIl=", STR_TESE_PALIL_OUTPUT_FILE_NAME); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.palIl=", STR_GEOM_PALIL_OUTPUT_FILE_NAME); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.palIl=", STR_FRAG_PALIL_OUTPUT_FILE_NAME); + } + } + + // AMD IL disassembly generation (for now we only support PAL IL). + if (options.m_isAmdPalIlDisassemblyRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_computeShader.asASCIICharArray()); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_vertexShader.asASCIICharArray()); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray()); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray()); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_geometryShader.asASCIICharArray()); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.palIlText=", options.m_pailIlDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray()); + } + } + + // AMD ISA binary generation. + if (options.m_isAmdIsaBinariesRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.isa=", options.m_isaBinaryFiles.m_computeShader.asASCIICharArray()); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.isa=", options.m_isaBinaryFiles.m_vertexShader.asASCIICharArray()); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.isa=", options.m_isaBinaryFiles.m_tessControlShader.asASCIICharArray()); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.isa=", options.m_isaBinaryFiles.m_tessEvaluationShader.asASCIICharArray()); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.isa=", options.m_isaBinaryFiles.m_geometryShader.asASCIICharArray()); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.isa=", options.m_isaBinaryFiles.m_fragmentShader.asASCIICharArray()); + } + } + + // AMD ISA disassembly generation. + if (options.m_isAmdIsaDisassemblyRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.isaText=", options.m_isaDisassemblyOutputFiles.m_computeShader.asASCIICharArray()); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.isaText=", options.m_isaDisassemblyOutputFiles.m_vertexShader.asASCIICharArray()); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.isaText=", options.m_isaDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray()); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.isaText=", options.m_isaDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray()); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.isaText=", options.m_isaDisassemblyOutputFiles.m_geometryShader.asASCIICharArray()); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.isaText=", options.m_isaDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray()); + } + } + + // Shader compiler statistics disassembly generation. + if (options.m_isScStatsRequired) + { + // Compute. + AddOutputFile(!options.m_pipelineShaders.m_computeShader.isEmpty(), "out.comp.isaInfo=", options.m_scStatisticsOutputFiles.m_computeShader.asASCIICharArray()); + + if (options.m_pipelineShaders.m_computeShader.isEmpty() || isSpv) + { + // Vertex. + AddOutputFile(!options.m_pipelineShaders.m_vertexShader.isEmpty(), "out.vert.isaInfo=", options.m_scStatisticsOutputFiles.m_vertexShader.asASCIICharArray()); + // Tessellation control. + AddOutputFile(!options.m_pipelineShaders.m_tessControlShader.isEmpty(), "out.tesc.isaInfo=", options.m_scStatisticsOutputFiles.m_tessControlShader.asASCIICharArray()); + // Tessellation evaluation. + AddOutputFile(!options.m_pipelineShaders.m_tessEvaluationShader.isEmpty(), "out.tese.isaInfo=", options.m_scStatisticsOutputFiles.m_tessEvaluationShader.asASCIICharArray()); + // Geometry. + AddOutputFile(!options.m_pipelineShaders.m_geometryShader.isEmpty(), "out.geom.isaInfo=", options.m_scStatisticsOutputFiles.m_geometryShader.asASCIICharArray()); + // Fragment. + AddOutputFile(!options.m_pipelineShaders.m_fragmentShader.isEmpty(), "out.frag.isaInfo=", options.m_scStatisticsOutputFiles.m_fragmentShader.asASCIICharArray()); + } + } +} + beProgramBuilderVulkan::beProgramBuilderVulkan() { } @@ -191,6 +430,68 @@ beKA::beStatus beProgramBuilderVulkan::Initialize(const std::string& sDllModule return beKA::beStatus_SUCCESS; } +// Checks if the required output files are generated by the amdspv. +// Only verifies the files requested in the "options.m_pipelineShaders" name list. +static bool VerifyAmdspvOutput(const VulkanOptions& options) +{ + bool ret = true; + if (options.m_isAmdIsaDisassemblyRequired) + { + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaDisassemblyOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isAmdIsaBinariesRequired) + { + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_isaBinaryFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isSpirvBinariesRequired) + { + ret &= beUtils::isFilePresent(STR_COMP_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_FRAG_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_GEOM_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_TESC_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_TESE_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_VERT_SPV_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isAmdPalIlBinariesRequired) + { + ret &= beUtils::isFilePresent(STR_COMP_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_FRAG_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_GEOM_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_TESC_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_TESE_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(STR_VERT_PALIL_OUTPUT_FILE_NAME, !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isAmdPalIlDisassemblyRequired) + { + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_pailIlDisassemblyOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + if (ret && options.m_isScStatsRequired) + { + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_computeShader.asASCIICharArray(), !options.m_pipelineShaders.m_computeShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_fragmentShader.asASCIICharArray(), !options.m_pipelineShaders.m_fragmentShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_geometryShader.asASCIICharArray(), !options.m_pipelineShaders.m_geometryShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_tessControlShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessControlShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_tessEvaluationShader.asASCIICharArray(), !options.m_pipelineShaders.m_tessEvaluationShader.isEmpty()); + ret &= beUtils::isFilePresent(options.m_scStatisticsOutputFiles.m_vertexShader.asASCIICharArray(), !options.m_pipelineShaders.m_vertexShader.isEmpty()); + } + + return ret; +} beKA::beStatus beProgramBuilderVulkan::Compile(const VulkanOptions& vulkanOptions, bool& cancelSignal, gtString& buildLog) { @@ -216,376 +517,76 @@ beKA::beStatus beProgramBuilderVulkan::Compile(const VulkanOptions& vulkanOption { // Build the command for invoking amdspv. std::stringstream cmd; - cmd << ambdbilPath << " -set spirvDasmLegacyFormat=1 -Dall -l -gfxip " << deviceGfxIp << " -set "; - - // Flags for each pipeline stage that specify if its shader is present. - // We will need them throughout this process. - bool isVertShaderPresent = false; - bool isTescShaderPresent = false; - bool isTeseShaderPresent = false; - bool isGeomShaderPresent = false; - bool isFragShaderPresent = false; - bool isCompShaderPresent = false; - - // You cannot mix compute and non-compute shaders in Vulkan, - // so this has to be mutually exclusive. - if (vulkanOptions.m_pipelineShaders.m_computeShader.isEmpty()) - { - // Vertex shader. - if (!vulkanOptions.m_pipelineShaders.m_vertexShader.isEmpty()) - { - isVertShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.vert.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_vertexShader.asASCIICharArray() << "\" "; - } - - // Tessellation control shader. - if (!vulkanOptions.m_pipelineShaders.m_tessControlShader.isEmpty()) - { - isTescShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.tesc.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_tessControlShader.asASCIICharArray() << "\" "; - } - - // Tessellation evaluation shader. - if (!vulkanOptions.m_pipelineShaders.m_tessEvaluationShader.isEmpty()) - { - isTeseShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.tese.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_tessEvaluationShader.asASCIICharArray() << "\" "; - } - - // Geometry shader. - if (!vulkanOptions.m_pipelineShaders.m_geometryShader.isEmpty()) - { - isGeomShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.geom.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_geometryShader.asASCIICharArray() << "\" "; - } - - // Fragment shader. - if (!vulkanOptions.m_pipelineShaders.m_fragmentShader.isEmpty()) - { - isFragShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.frag.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_fragmentShader.asASCIICharArray() << "\" "; - } - } - else - { - // Compute shader. - isCompShaderPresent = true; - cmd << GetInputPrefix(vulkanOptions, "in.comp.glsl=\"") << - vulkanOptions.m_pipelineShaders.m_computeShader.asASCIICharArray() << "\" "; - } + cmd << ambdbilPath << " -Dall -l -gfxip " << deviceGfxIp << " -set "; - // SPIR-V binaries generation. - if (vulkanOptions.m_isSpirvBinariesRequired) + if ((ret = AddInputFileNames(vulkanOptions, cmd)) == beKA::beStatus_SUCCESS) { - // Compute. - if (!isCompShaderPresent) - { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.spv=vert.spv "; - } + AddOutputFileNames(vulkanOptions, cmd); - // Tessellation control. - if (isTescShaderPresent) - { - cmd << "out.tesc.spv=tesc.spv "; - } + // Redirect build log to a temporary file. + std::string tmpFileAmdspv; + const gtString AMPSPV_TMP_OUTPUT_FILE = L"amdspvTempFile.txt"; + osFilePath tmpFilePath(osFilePath::OS_TEMP_DIRECTORY); + tmpFilePath.setFileName(AMPSPV_TMP_OUTPUT_FILE); + cmd << "out.glslLog=\"" << tmpFilePath.asString().asASCIICharArray() << "\" "; - // Tessellation evaluation. - if (isTeseShaderPresent) - { - cmd << "out.tese.spv=tese.spv "; - } + // No default output (only generate the output files that we explicitly specified). + cmd << "defaultOutput=0"; - // Geometry. - if (isGeomShaderPresent) - { - cmd << "out.geom.spv=geom.spv "; - } + // Launch amdspv. + gtString amdspvOutput; + bool isLaunchSuccess = osExecAndGrabOutput(cmd.str().c_str(), cancelSignal, amdspvOutput); - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.spv=frag.spv "; - } - } - else - { - // Compute. - cmd << "out.comp.spv=comp.spv "; - } - } - - // AMD IL Binaries generation (for now we only support PAL IL). - if (vulkanOptions.m_isAmdPalIlBinariesRequired) - { - // Compute. - if (!isCompShaderPresent) + if (isLaunchSuccess) { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.palIl=vert.palIl "; - } - - // Tessellation control. - if (isTescShaderPresent) - { - cmd << "out.tesc.palIl=tesc.palIl "; - } - - // Tessellation evaluation. - if (isTeseShaderPresent) - { - cmd << "out.tese.palIl=tese.palIl "; - } + // This is how amdspv signals success. + const gtString AMDSPV_SUCCESS_TOKEN = L"SUCCESS!"; - // Geometry. - if (isGeomShaderPresent) + // Check if the output files were generated and amdspv returned "success". + if (amdspvOutput.find(AMDSPV_SUCCESS_TOKEN) == std::string::npos) { - cmd << "out.geom.palIl=geom.palIl "; - } - - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.palIl=frag.palIl "; - } - } - else - { - // Compute. - cmd << "out.comp.palIl=comp.palIl "; - } - } - - // AMD IL disassembly generation (for now we only support PAL IL). - if (vulkanOptions.m_isAmdPalIlDisassemblyRequired) - { - // Compute. - if (!isCompShaderPresent) - { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_vertexShader.asASCIICharArray() << "\" "; - } - - // Tessellation control. - if (isTescShaderPresent) - { - cmd << "out.tesc.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray() << "\" "; - } - - // Tessellation evaluation. - if (isTeseShaderPresent) - { - cmd << "out.tese.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray() << "\" "; - } - - // Geometry. - if (isGeomShaderPresent) - { - cmd << "out.geom.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_geometryShader.asASCIICharArray() << "\" "; - } - - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray() << "\" "; - } - } - else - { - // Compute. - cmd << "out.comp.palIlText=\"" << vulkanOptions.m_pailIlDisassemblyOutputFiles.m_computeShader.asASCIICharArray() << "\" "; - } - } - - // AMD ISA binary generation. - if (vulkanOptions.m_isAmdIsaBinariesRequired) - { - // Compute. - if (!isCompShaderPresent) - { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_vertexShader.asASCIICharArray() << "\" "; - } - - // Tessellation control. - if (isTescShaderPresent) - { - cmd << "out.tesc.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_tessControlShader.asASCIICharArray() << "\" "; - } - - // Tessellation evaluation. - if (isTeseShaderPresent) - { - cmd << "out.tese.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_tessEvaluationShader.asASCIICharArray() << "\" "; - } - - // Geometry. - if (isGeomShaderPresent) - { - cmd << "out.geom.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_geometryShader.asASCIICharArray() << "\" "; - } + ret = beKA::beStatus_VulkanAmdspvCompilationFailure; - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_fragmentShader.asASCIICharArray() << "\" "; - } - } - else - { - // Compute. - cmd << "out.comp.isa=\"" << vulkanOptions.m_isaBinaryFiles.m_computeShader.asASCIICharArray() << "\" "; - } - } - - // AMD ISA disassembly generation. - if (vulkanOptions.m_isAmdIsaDisassemblyRequired) - { - // Compute. - if (!isCompShaderPresent) - { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_vertexShader.asASCIICharArray() << "\" "; - } - - // Tessellation control. - if (isTescShaderPresent) - { - cmd << "out.tesc.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_tessControlShader.asASCIICharArray() << "\" "; - } - - // Tessellation evaluation. - if (isTeseShaderPresent) - { - cmd << "out.tese.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_tessEvaluationShader.asASCIICharArray() << "\" "; - } - - // Geometry. - if (isGeomShaderPresent) - { - cmd << "out.geom.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_geometryShader.asASCIICharArray() << "\" "; - } - - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_fragmentShader.asASCIICharArray() << "\" "; - } - } - else - { - // Compute. - cmd << "out.comp.isaText=\"" << vulkanOptions.m_isaDisassemblyOutputFiles.m_computeShader.asASCIICharArray() << "\" "; - } - } - - // Shader compiler statistics disassembly generation. - if (vulkanOptions.m_isScStatsRequired) - { - // Compute. - if (!isCompShaderPresent) - { - // Vertex. - if (isVertShaderPresent) - { - cmd << "out.vert.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_vertexShader.asASCIICharArray() << "\" "; + // Read the build log. + if (tmpFilePath.exists()) + { + // Read the build log. + gtString compilerOutput; + std::ifstream file(tmpFilePath.asString().asASCIICharArray()); + std::string tmpCmdOutput((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + buildLog << tmpCmdOutput.c_str(); + + // Delete the temporary file. + osFile fileToDelete(tmpFilePath); + fileToDelete.deleteFile(); + } + + // Let's end the build log with the error that was provided by the backend. + if (!amdspvOutput.isEmpty()) + { + buildLog << "Error: " << amdspvOutput << L"\n"; + } } - - // Tessellation control. - if (isTescShaderPresent) + else if (!VerifyAmdspvOutput(vulkanOptions)) { - cmd << "out.tesc.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_tessControlShader.asASCIICharArray() << "\" "; + ret = beKA::beStatus_FailedOutputVerification; } - - // Tessellation evaluation. - if (isTeseShaderPresent) + else { - cmd << "out.tese.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_tessEvaluationShader.asASCIICharArray() << "\" "; - } + ret = beKA::beStatus_SUCCESS; - // Geometry. - if (isGeomShaderPresent) - { - cmd << "out.geom.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_geometryShader.asASCIICharArray() << "\" "; + // Delete the ISA binaries if they are not required. + if (!vulkanOptions.m_isAmdIsaBinariesRequired) + { + beUtils::DeleteOutputFiles(vulkanOptions.m_isaBinaryFiles); + } } - - // Fragment. - if (isFragShaderPresent) - { - cmd << "out.frag.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_fragmentShader.asASCIICharArray() << "\" "; - } - } - else - { - // Compute. - cmd << "out.comp.isaInfo=\"" << vulkanOptions.m_scStatisticsOutputFiles.m_computeShader.asASCIICharArray() << "\" "; - } - } - - // Redirect build log to a temporary file. - std::string tmpFileAmdspv; - const gtString AMPSPV_TMP_OUTPUT_FILE = L"amdspvTempFile.txt"; - osFilePath tmpFilePath(osFilePath::OS_TEMP_DIRECTORY); - tmpFilePath.setFileName(AMPSPV_TMP_OUTPUT_FILE); - cmd << "out.glslLog=\"" << tmpFilePath.asString().asASCIICharArray() << "\" "; - - // No default output (only generate the output files that we explicitly specified). - cmd << "defaultOutput=0"; - - // Launch amdspv. - gtString amdspvOutput; - bool isLaunchSuccess = osExecAndGrabOutput(cmd.str().c_str(), cancelSignal, amdspvOutput); - - if (isLaunchSuccess) - { - // This is how amdspv signals success. - const gtString AMDSPV_SUCCESS_TOKEN = L"SUCCESS!"; - - if (amdspvOutput.find(AMDSPV_SUCCESS_TOKEN) != -1) - { - ret = beKA::beStatus_SUCCESS; - - // Delete the ISA binaries. - beUtils::DeleteOutputFiles(vulkanOptions.m_isaBinaryFiles); } else { - ret = beKA::beStatus_VulkanAmdspvCompilationFailure; - - // Read the build log. - if (tmpFilePath.exists()) - { - // Read the build log. - gtString compilerOutput; - std::ifstream file(tmpFilePath.asString().asASCIICharArray()); - std::string tmpCmdOutput((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - buildLog << tmpCmdOutput.c_str(); - - // Delete the temporary file. - osFile fileToDelete(tmpFilePath); - fileToDelete.deleteFile(); - } + ret = beKA::beStatus_VulkanAmdspvLaunchFailure; } } - else - { - ret = beKA::beStatus_VulkanAmdspvLaunchFailure; - } } else { diff --git a/RadeonGPUAnalyzerBackend/src/beUtils.cpp b/RadeonGPUAnalyzerBackend/src/beUtils.cpp index 5a7149b..5e386ee 100644 --- a/RadeonGPUAnalyzerBackend/src/beUtils.cpp +++ b/RadeonGPUAnalyzerBackend/src/beUtils.cpp @@ -5,6 +5,7 @@ // C++. #include #include +#include // Infra. #include @@ -208,10 +209,20 @@ void beUtils::DeleteFile(const gtString& filePath) } } -beUtils::beUtils() +bool beUtils::isFilePresent(const std::string& fileName, bool required) { + bool ret = true; + if (required && !fileName.empty()) + { + std::ifstream file(fileName); + ret = (file.good() && file.peek() != std::ifstream::traits_type::eof()); + } + return ret; } +beUtils::beUtils() +{ +} beUtils::~beUtils() { diff --git a/RadeonGPUAnalyzerCLI/CMakeLists.txt b/RadeonGPUAnalyzerCLI/CMakeLists.txt index b2fa87c..70e000a 100644 --- a/RadeonGPUAnalyzerCLI/CMakeLists.txt +++ b/RadeonGPUAnalyzerCLI/CMakeLists.txt @@ -3,7 +3,7 @@ project (RadeonGPUAnalyzerCLI) # The version number set (RadeonGPUAnalyzerCLI_VERSION_MAJOR 1) -set (RadeonGPUAnalyzerCLI_VERSION_MINOR 1) +set (RadeonGPUAnalyzerCLI_VERSION_MINOR 2) # Pass the build number if (NOT "$ENV{BUILD_NUMBER}" STREQUAL "") diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommander.cpp b/RadeonGPUAnalyzerCLI/src/kcCLICommander.cpp new file mode 100755 index 0000000..b141bb0 --- /dev/null +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommander.cpp @@ -0,0 +1,93 @@ +//================================================================= +// Copyright 2017 Advanced Micro Devices, Inc. All rights reserved. +//================================================================= + +// Local. +#include +#include + + +std::string GetInpulLanguageString(beKA::SourceLanguage lang) +{ + switch (lang) + { + case beKA::SourceLanguage_OpenCL: + return "OpenCL"; + case beKA::SourceLanguage_GLSL_OpenGL: + return "OpenGL"; + case beKA::SourceLanguage_GLSL_Vulkan: + case beKA::SourceLanguage_SPIRV_Vulkan: + return "Vulkan"; + case beKA::SourceLanguage_HLSL: + return "DX"; + default: + return ""; + } +} + +void kcCLICommander::ListAdapters(Config & config, LoggingCallBackFunc_t callback) +{ + GT_UNREFERENCED_PARAMETER(config); + std::stringstream msg; + msg << STR_ERR_COMMAND_NOT_SUPPORTED << std::endl; + callback(msg.str()); +} + +bool kcCLICommander::InitRequestedAsicList(const Config& config, const std::set& supportedDevices, std::set& targets) +{ + if (!config.m_ASICs.empty()) + { + // Take the devices which the user selected. + std::set matchedArchs; + for (const std::string& asicName : config.m_ASICs) + { + std::string matchedArchName; + std::string msg; + bool foundSingleTarget = kcUtils::FindGPUArchName(asicName, matchedArchName, msg); + LogCallBack(msg); + + if (foundSingleTarget) + { + // Check if the matched architecture is in the list of known ASICs. + // The architecture returned by FindGPUArchName() is an extended name, for example: "gfx804 (Graphics IP v8)", + // while the items in the list of known ASICs are "short" names: "gfx804". + bool isSupported = false; + for (const std::string& asic : supportedDevices) + { + if (matchedArchName.find(asic) != std::string::npos) + { + targets.insert(asic); + isSupported = true; + break; + } + } + if (!isSupported) + { + std::stringstream log; + log << STR_ERR_ERROR << GetInpulLanguageString(config.m_SourceLanguage) + << STR_ERR_TARGET_IS_NOT_SUPPORTED << matchedArchName << std::endl; + LogCallBack(log.str()); + } + } + } + } + else + { + // Take all known devices and do not print anything. + for (const std::string& device : supportedDevices) + { + std::string archName = ""; + std::string tmpMsg; + + bool found = kcUtils::FindGPUArchName(device, archName, tmpMsg); + if (found) + { + targets.insert(device); + } + } + } + + bool ret = (targets.size() != 0); + + return ret; +} diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommander.h b/RadeonGPUAnalyzerCLI/src/kcCLICommander.h index 836c3d5..58390dd 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommander.h +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommander.h @@ -22,6 +22,9 @@ class kcCLICommander /// List the asics as got from device virtual void ListAsics(Config& config, LoggingCallBackFunc_t callback) = 0; + /// List the adapters installed on the system. + virtual void ListAdapters(Config& config, LoggingCallBackFunc_t callback); + /// list the driver version virtual void Version(Config& config, LoggingCallBackFunc_t callback) = 0; @@ -30,6 +33,10 @@ class kcCLICommander protected: // functions + + /// Initialize the list of GPU targets. + bool InitRequestedAsicList(const Config& config, const std::set& supportedDevices, std::set& targets); + LoggingCallBackFunc_t m_LogCallback; /// Logging callback type. diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.cpp b/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.cpp index 1fe01db..cb08f49 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.cpp @@ -28,6 +28,18 @@ // Backend. #include +// ***************************************** +// *** INTERNALLY LINKED SYMBOLS - START *** +// ***************************************** + +static const std::string STR_GFX804_TARGET_NAME = "gfx804"; + +static const std::set UnsupportedTargets = { STR_GFX804_TARGET_NAME }; + +// *************************************** +// *** INTERNALLY LINKED SYMBOLS - END *** +// *************************************** + kcCLICommanderCL::kcCLICommanderCL() : m_isAllKernels(false) { @@ -112,7 +124,8 @@ bool kcCLICommanderCL::Init(const Config& config, LoggingCallBackFunc_t callback if (ret) { // Initialize the devices list. - beRet = be->theOpenCLBuilder()->GetDevices(m_devices); + std::set devices; + beRet = be->theOpenCLBuilder()->GetDevices(devices); ret = (beRet == beKA::beStatus_SUCCESS); // Only external (non-placeholder) and based on CXL version devices should be used. @@ -125,7 +138,7 @@ bool kcCLICommanderCL::Init(const Config& config, LoggingCallBackFunc_t callback { for (vector::const_iterator it = m_table.begin(); it != m_table.end(); ++it) { - if ((m_devices.find(it->m_szCALName) != m_devices.end())) + if ((devices.find(it->m_szCALName) != devices.end()) && UnsupportedTargets.count(it->m_szCALName) == 0) { m_externalDevices.insert(it->m_szCALName); } @@ -204,100 +217,6 @@ void kcCLICommanderCL::Version(Config& config, LoggingCallBackFunc_t callback) LogCallBack(s_Log.str()); } -bool kcCLICommanderCL::InitRequestedAsicList(const Config& config) -{ - bool bRet = (m_externalDevices.empty() ? false : true); - - if (!config.m_ASICs.empty()) - { - // Take the devices which the user selected. - m_asics = std::set(config.m_ASICs.begin(), config.m_ASICs.end()); - } - else - { - // Take all public devices. - m_asics = m_externalDevices; - } - - if (!m_asics.empty() && !m_externalDevices.empty()) - { - // Start by mapping DeviceID and Marketing Name to CAL name. - // Do this in 2 steps: find the changes, make the changes. - // Do this because STL iterators don't handle changes while iterating. - set toBeAdded; - set toBeErased; - - for (set::const_iterator asicIter = m_asics.begin(); asicIter != m_asics.end(); ++asicIter) - { - if (be->GetDeviceInfo(*asicIter, NULL) == beStatus_SUCCESS) - { - // this is a CAL name. - continue; - } - - // Try to find a DeviceID - // First check for a valid hex number. - if (asicIter->find_first_not_of("0123456789abcdefABCDEF") == string::npos) - { - istringstream idStringStream(*asicIter); - size_t id; - idStringStream >> hex >> id; - - GDT_GfxCardInfo info; - - // TODO: should look up by device id/ rev id pair - if (be->GetDeviceInfo(id, info) == beStatus_SUCCESS) - { - toBeErased.insert(*asicIter); - toBeAdded.insert(info.m_szCALName); - continue; - } - } - - // There are duplicate marketing names with different ASICs. - // I think the right thing to do is add all of the possible ASICs. - vector cardInfo; - - if (be->GetDeviceInfoMarketingName(*asicIter, cardInfo) == beStatus_SUCCESS) - { - toBeErased.insert(*asicIter); - - for (vector::const_iterator infoIter = cardInfo.begin(); infoIter != cardInfo.end(); ++infoIter) - { - toBeAdded.insert(infoIter->m_szCALName); - } - - continue; - } - } - - // Now that we have finished walking "asics", we can erase and add things. - for (set::const_iterator asicIter = toBeErased.begin(); asicIter != toBeErased.end(); ++asicIter) - { - m_asics.erase(*asicIter); - } - - for (set::const_iterator asicIter = toBeAdded.begin(); asicIter != toBeAdded.end(); ++asicIter) - { - m_asics.insert(*asicIter); - } - - for (set::const_iterator asicIter = m_asics.begin(); asicIter != m_asics.end(); ++asicIter) - { - if (m_externalDevices.find(*asicIter) == m_externalDevices.end()) - { - std::stringstream s_Log; - s_Log << "Error: '" << *asicIter << "' is not a known device ASIC." << endl; - s_Log << " Use --list-asics option to find known device ASICs." << endl; - LogCallBack(s_Log.str()); - bRet = false; - } - } - } - - return bRet; -} - void kcCLICommanderCL::ListAsics(Config& config, LoggingCallBackFunc_t callback) { if (!Init(config, callback)) @@ -321,7 +240,7 @@ void kcCLICommanderCL::RunCompileCommands(const Config& config, LoggingCallBackF { if (Init(config, callback)) { - if (InitRequestedAsicList(config)) + if (InitRequestedAsicList(config, m_externalDevices, m_asics)) { if (Compile(config)) { @@ -352,16 +271,18 @@ void kcCLICommanderCL::Analysis(const Config& config) return; } - if (config.m_Function.size() == 0) + if (m_requiredKernels.size() == 0) { std::stringstream s_Log; - s_Log << "Error: Must specify kernel name to get Analysis text" << endl; + s_Log << STR_ERR_NO_KERNELS_FOR_ANALYSIS << endl; LogCallBack(s_Log.str()); } if ((config.m_SuppressSection.size() > 0) && (config.m_BinaryOutputFile.size() == 0)) { - LogCallBack("Warning: --suppress option is valid only with output binary\n"); + std::stringstream s_Log; + s_Log << STR_WRN_CL_SUPPRESS_WIHTOUT_BINARY << std::endl; + LogCallBack(s_Log.str()); } // Get the separator for CSV list items. @@ -540,7 +461,16 @@ void kcCLICommanderCL::GetILText(const Config& config) { // Inform the user. std::stringstream msg; - msg << STR_ERR_CANNOT_DISASSEMBLE_AMD_IL << " for " << deviceName << "(kernel: " << kernelName << ")." << std::endl; + msg << STR_ERR_CANNOT_DISASSEMBLE_AMD_IL << " for " << deviceName; + + // If we have the kernel name - specify it in the error message. + if (!kernelName.empty()) + { + msg << " (kernel: " << kernelName << ")"; + } + + // Print the message. + msg << "." << std::endl; m_LogCallback(msg.str().c_str()); } @@ -620,7 +550,16 @@ void kcCLICommanderCL::GetISAText(const Config& config) { // Inform the user. std::stringstream msg; - msg << STR_ERR_CANNOT_DISASSEMBLE_ISA << " for " << deviceName << "(kernel: " << kernelName << ")." << std::endl; + msg << STR_ERR_CANNOT_DISASSEMBLE_ISA << " for " << deviceName; + + // If we have the kernel name - specify it in the error message. + if (!kernelName.empty()) + { + msg << " (kernel: " << kernelName << ")"; + } + + // Print the message. + msg << "." << std::endl; m_LogCallback(msg.str().c_str()); } @@ -775,7 +714,8 @@ void kcCLICommanderCL::InitRequiredKernels(const Config& config, const std::set< const std::string& deviceName = *firstDevice; std::string requestedKernel = config.m_Function; std::transform(requestedKernel.begin(), requestedKernel.end(), requestedKernel.begin(), ::tolower); - m_isAllKernels = (requestedKernel.compare("all") == 0); + // Process all kernels by default or if all kernels are explicitly requested. + m_isAllKernels = (requestedKernel.compare("all") == 0 || requestedKernel.empty()); beProgramBuilderOpenCL* pClBuilder = be->theOpenCLBuilder(); if (pClBuilder != NULL) diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.h b/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.h index 2628855..7f76059 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.h +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderCL.h @@ -36,7 +36,6 @@ class kcCLICommanderCL: kcCLICommander private: // functions bool Init(const Config& config, LoggingCallBackFunc_t callback); - bool InitRequestedAsicList(const Config& config); bool Compile(const Config& config); /// output for all commands that requires compilation @@ -54,10 +53,9 @@ class kcCLICommanderCL: kcCLICommander void InitRequiredKernels(const Config& config, const std::set& requiredDevices, std::vector& requiredKernels); private: //members - std::set m_devices; - std::set m_externalDevices; + std::set m_externalDevices; std::vector m_table; - std::set m_asics; + std::set m_asics; // Holds the name of the kernels to be built. std::vector m_requiredKernels; diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.cpp b/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.cpp index c0017b8..0fa1220 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.cpp @@ -22,6 +22,10 @@ #include #include +// Static constants. +const int DX_MAX_SUPPORTED_SHADER_MODEL_MAJOR = 5; +const int DX_MAX_SUPPORTED_SHADER_MODEL_MINOR = 0; + kcCLICommanderDX::kcCLICommanderDX(void) { m_pBackEndHandler = nullptr; @@ -46,6 +50,27 @@ void kcCLICommanderDX::ListAsics(Config& config, LoggingCallBackFuncP callback) } } +void kcCLICommanderDX::ListAdapters(Config & config, LoggingCallBackFunc_t callback) +{ + std::vector adapterNames; + stringstream msg; + + if (beProgramBuilderDX::GetSupportedDisplayAdapterNames(adapterNames)) + { + msg << STR_FOUND_ADAPTERS << std::endl << std::endl; + for (size_t i = 0; i < adapterNames.size(); i++) + { + msg << " " << i << "\t" << adapterNames[i] << std::endl; + } + } + else + { + msg << STR_ERR_LIST_ADAPTERS_FAILED << std::endl; + } + + msg << std::endl; + callback(msg.str()); +} void kcCLICommanderDX::Version(Config& config, LoggingCallBackFuncP callback) { @@ -61,7 +86,7 @@ void kcCLICommanderDX::Version(Config& config, LoggingCallBackFuncP callback) } -void kcCLICommanderDX::InitRequestedAsicList(const Config& config) +void kcCLICommanderDX::InitRequestedAsicListDX(const Config& config) { stringstream s_Log; @@ -71,35 +96,23 @@ void kcCLICommanderDX::InitRequestedAsicList(const Config& config) m_dxDefaultAsicsList.clear(); std::vector dxDeviceTable; std::set supportedDevices; + std::set matchedTargets; + if (beUtils::GetAllGraphicsCards(dxDeviceTable, supportedDevices)) { - for (const std::string& gfxDevice : config.m_ASICs) + if (InitRequestedAsicList(config, supportedDevices, matchedTargets)) { - bool isDeviceSupported = false; - if (supportedDevices.find(gfxDevice) != supportedDevices.end()) + for (const std::string& target : matchedTargets) { - // Both CAL and Marketing names are accepted (case-insensitive). for (const GDT_GfxCardInfo& dxDevice : dxDeviceTable) { - if ((boost::iequals(dxDevice.m_szCALName, gfxDevice)) || - (boost::iequals(dxDevice.m_szMarketingName, gfxDevice))) + if (boost::iequals(dxDevice.m_szCALName, target)) { - isDeviceSupported = true; m_dxDefaultAsicsList.push_back(dxDevice); - - // We are done. break; } } } - - if (!isDeviceSupported) - { - // Notify the user. - std::stringstream msg; - msg << KA_CLI_STR_ERR_D3D_COMPILATION_NOT_SUPPORTED_FOR_DEVICE << gfxDevice << ".\n"; - LogCallBack(msg.str().c_str()); - } } } } @@ -350,7 +363,7 @@ void kcCLICommanderDX::RunCompileCommands(const Config& config, LoggingCallBackF } // see if the user asked for specific asics - InitRequestedAsicList(config); + InitRequestedAsicListDX(config); // for logging purposes we will iterate through the requested ASICs, if input by user std::vector::const_iterator asicIter; @@ -529,67 +542,117 @@ bool kcCLICommanderDX::WriteAnalysisDataForDX(const Config& config, const std::v return true; } +bool ParseProfileString(const std::string& profile, std::pair& version) +{ + bool result = false; + // profile string format: XX_N_N + if (!profile.empty()) + { + size_t minor, major = profile.find('_'); + if (major != std::string::npos) + { + if ((minor = profile.find('_', ++major)) != std::string::npos) + { + try + { + version = { std::stoi(profile.substr(major, minor)), std::stoi(profile.substr(++minor)) }; + result = true; + } + catch (...) {} + } + } + } + return result; +} /// Output the ISA representative of the compilation bool kcCLICommanderDX::Compile(const Config& config, const GDT_GfxCardInfo& gfxCardInfo, string sDevicenametoLog) { - bool bRet = false; - std::stringstream s_Log; + bool ret = true; + std::stringstream s_Log; + std::pair version; + beProgramBuilderDX::DXOptions dxOptions; - /// basically, dx supports offline compilation of dx10/dx11. I will not fail this but will give a warning - if ((config.m_Profile.find("_3_") != string::npos) || (config.m_Profile.find("_2_") != string::npos) || (config.m_Profile.find("_1_") != string::npos) - || (config.m_Profile.find("level_9_") != string::npos)) + if (config.m_SourceLanguage != SourceLanguage_AMDIL && + !ParseProfileString(config.m_Profile, version)) { - s_Log << STR_WRN_DX_MIN_SUPPORTED_VERSION << std::endl; - - // Notify the user. - LogCallBack(s_Log.str()); - - // Clear the stream. - s_Log.str(""); + s_Log << STR_ERR_PARSE_DX_SHADER_MODEL_FAILED << std::endl; + ret = false; } - - //// prepare the options - beProgramBuilderDX::DXOptions dxOptions; - dxOptions.m_Entrypoint = config.m_Function; - dxOptions.m_Target = config.m_Profile; - dxOptions.m_DXFlags.flagsAsInt = config.m_DXFlags; - dxOptions.m_bDumpMSIntermediate = (config.m_DumpMSIntermediate.size() > 0 ? true : false); - dxOptions.m_isShaderIntrinsicsEnabled = config.m_EnableShaderIntrinsics; - dxOptions.m_UAVSlot = config.m_UAVSlot; - - // Process config.m_Defines - // The interface for DX is different here. - // It is set up for the GUI. - // The CLI does some work here to translate. - for (vector::const_iterator it = config.m_Defines.begin(); - it != config.m_Defines.end(); - ++it) + // Check the provided shader profile version. + if (ret) { - size_t equal_pos = it->find('='); + if (version.first > DX_MAX_SUPPORTED_SHADER_MODEL_MAJOR || + (version.first == DX_MAX_SUPPORTED_SHADER_MODEL_MAJOR && version.second > DX_MAX_SUPPORTED_SHADER_MODEL_MINOR)) + { + s_Log << STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_1 << config.m_Profile << ". " + << STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_2 << DX_MAX_SUPPORTED_SHADER_MODEL_MAJOR << "." << DX_MAX_SUPPORTED_SHADER_MODEL_MINOR << " " + << STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_3 << std::endl; + ret = false; + } - if (equal_pos == string::npos) + if ((config.m_Profile.find("_3_") != string::npos) || (config.m_Profile.find("_2_") != string::npos) || (config.m_Profile.find("_1_") != string::npos) + || (config.m_Profile.find("level_9_") != string::npos)) { - dxOptions.m_defines.push_back(make_pair(*it, string(""))); + s_Log << STR_WRN_DX_MIN_SUPPORTED_VERSION << std::endl; } - else + } + + if (ret) + { + //// prepare the options + dxOptions.m_Entrypoint = config.m_Function; + dxOptions.m_Target = config.m_Profile; + dxOptions.m_DXFlags.flagsAsInt = config.m_DXFlags; + dxOptions.m_bDumpMSIntermediate = (config.m_DumpMSIntermediate.size() > 0 ? true : false); + dxOptions.m_isShaderIntrinsicsEnabled = config.m_EnableShaderIntrinsics; + dxOptions.m_UAVSlot = config.m_UAVSlot; + + // Process config.m_Defines + // The interface for DX is different here. + // It is set up for the GUI. + // The CLI does some work here to translate. + for (vector::const_iterator it = config.m_Defines.begin(); + it != config.m_Defines.end(); + ++it) { - dxOptions.m_defines.push_back(make_pair(it->substr(0, equal_pos), - it->substr(equal_pos + 1, string::npos))); + size_t equal_pos = it->find('='); + + if (equal_pos == string::npos) + { + dxOptions.m_defines.push_back(make_pair(*it, string(""))); + } + else + { + dxOptions.m_defines.push_back(make_pair(it->substr(0, equal_pos), + it->substr(equal_pos + 1, string::npos))); + } } } // read the source string sSource; - bRet = KAUtils::ReadProgramSource(config.m_InputFile, sSource); - - if (!bRet) + + if (ret) { - s_Log << "Error: Unable to read: \'" << config.m_InputFile << "\'." << endl; + if (!config.m_InputFile.empty()) + { + ret = KAUtils::ReadProgramSource(config.m_InputFile, sSource); + if (!ret) + { + s_Log << STR_ERR_CANNOT_READ_FILE << config.m_InputFile << std::endl; + } + } + else + { + s_Log << STR_ERR_NO_INPUT_FILE << std::endl; + ret = false; + } } - else + + if (ret) { // dx interface like the chip revision and family beStatus beRet = m_pBackEndHandler->GetDeviceChipFamilyRevision(gfxCardInfo, dxOptions.m_ChipFamily, dxOptions.m_ChipRevision); @@ -598,7 +661,7 @@ bool kcCLICommanderDX::Compile(const Config& config, const GDT_GfxCardInfo& gfxC { // the use must have got the asics spelled wrong- let him know and continue s_Log << "Error: Couldn't find device named: " << sDevicenametoLog << ". Run \'-s HLSL -l to view available devices." << endl; - bRet = false; + ret = false; } else { @@ -619,24 +682,24 @@ bool kcCLICommanderDX::Compile(const Config& config, const GDT_GfxCardInfo& gfxC if (beRet == beStatus_Create_Bolob_FromInput_Failed) { s_Log << "Error reading DX ASM file. "; - bRet = false; + ret = false; } if (beRet != beStatus_SUCCESS) { s_Log << KA_CLI_STR_COMPILING << sDevicenametoLog << KA_CLI_STR_STATUS_FAILURE << std::endl; - bRet = false; + ret = false; } else { s_Log << KA_CLI_STR_COMPILING << sDevicenametoLog << KA_CLI_STR_STATUS_SUCCESS << std::endl; - bRet = true; + ret = true; } } } LogCallBack(s_Log.str()); - return bRet; + return ret; } bool kcCLICommanderDX::Init(const Config& config, LoggingCallBackFuncP callback) @@ -648,15 +711,41 @@ bool kcCLICommanderDX::Init(const Config& config, LoggingCallBackFuncP callback) // initialize backend m_pBackEndHandler = Backend::Instance(); - if (!m_pBackEndHandler->Initialize(BuiltProgramKind_DX, callback, config.m_DXLocation)) + if (!m_pBackEndHandler->Initialize(BuiltProgramKind_DX, callback)) { bCont = false; } + beProgramBuilderDX& dxBuilder = *(m_pBackEndHandler->theOpenDXBuilder()); + + if (bCont) + { + // Initialize the DX Builder + dxBuilder.SetLog(callback); + + // If a particular GPU adapter is requested, choose corresponding driver. + std::string adapterName = "", dxxModulePath; + if (config.m_DXAdapter != -1) + { + bCont = beProgramBuilderDX::GetDXXModulePathForAdapter(config.m_DXAdapter, adapterName, dxxModulePath); + } + + if (bCont) + { + bCont = (dxBuilder.Initialize(dxxModulePath, config.m_DXLocation) == beKA::beStatus_SUCCESS); + } + else + { + std::stringstream errMsg; + errMsg << STR_ERR_SET_ADAPTER_FAILED << std::endl; + callback(errMsg.str()); + } + } + if (bCont)// init ASICs list { std::vector dxDeviceTable; - beStatus bRet = m_pBackEndHandler->theOpenDXBuilder()->GetDeviceTable(dxDeviceTable); + beStatus bRet = dxBuilder.GetDeviceTable(dxDeviceTable); if (bRet == beStatus_SUCCESS) { diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.h b/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.h index af119ed..01b550a 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.h +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderDX.h @@ -19,6 +19,9 @@ class kcCLICommanderDX : public kcCLICommander /// List the asics as got from device void ListAsics(Config& config, LoggingCallBackFunc_t callback); + /// List the adapters installed on the system. + void ListAdapters(Config& config, LoggingCallBackFunc_t callback) override; + /// list the driver version void Version(Config& config, LoggingCallBackFunc_t callback); @@ -27,7 +30,7 @@ class kcCLICommanderDX : public kcCLICommander private: //functions bool Init(const Config& config, LoggingCallBackFunc_t callback); bool ListGraphicsAdapters(const Config& config, LoggingCallBackFunc_t callback); - void InitRequestedAsicList(const Config& config); + void InitRequestedAsicListDX(const Config& config); bool Compile(const Config& config, const GDT_GfxCardInfo& gfxCardInfo, string sDevicenametoLog); bool WriteAnalysisDataForDX(const Config& config, const std::vector& AnalysisDataVec, const std::vector& DeviceAnalysisDataVec, const std::string& sAnalysisFile, std::stringstream& log); diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderOpenGL.cpp b/RadeonGPUAnalyzerCLI/src/kcCLICommanderOpenGL.cpp index 9794f45..3ce0144 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderOpenGL.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderOpenGL.cpp @@ -131,48 +131,54 @@ void kcCLICommanderOpenGL::Version(Config& config, LoggingCallBackFunc_t callbac } // Helper function to remove unnecessary file paths. -static void GenerateRenderingPipelineOutputPaths(const Config& config, const std::string& baseOutputFileName, const std::string& device, beProgramPipeline& pipelineToAdjust) +static bool GenerateRenderingPipelineOutputPaths(const Config& config, const std::string& baseOutputFileName, + const std::string& device, beProgramPipeline& pipelineToAdjust) { // Generate the output file paths. - kcUtils::AdjustRenderingPipelineOutputFileNames(baseOutputFileName, device, pipelineToAdjust); + bool ret = kcUtils::AdjustRenderingPipelineOutputFileNames(baseOutputFileName, device, pipelineToAdjust); - // Clear irrelevant paths. - bool isVertexShaderPresent = (!config.m_VertexShader.empty()); - bool isTessControlShaderPresent = (!config.m_TessControlShader.empty()); - bool isTessEvaluationShaderPresent = (!config.m_TessEvaluationShader.empty()); - bool isGeometryexShaderPresent = (!config.m_GeometryShader.empty()); - bool isFragmentShaderPresent = (!config.m_FragmentShader.empty()); - bool isComputeShaderPresent = (!config.m_ComputeShader.empty()); - - if (!isVertexShaderPresent) + if (ret) { - pipelineToAdjust.m_vertexShader.makeEmpty(); - } + // Clear irrelevant paths. + bool isVertexShaderPresent = (!config.m_VertexShader.empty()); + bool isTessControlShaderPresent = (!config.m_TessControlShader.empty()); + bool isTessEvaluationShaderPresent = (!config.m_TessEvaluationShader.empty()); + bool isGeometryexShaderPresent = (!config.m_GeometryShader.empty()); + bool isFragmentShaderPresent = (!config.m_FragmentShader.empty()); + bool isComputeShaderPresent = (!config.m_ComputeShader.empty()); + + if (!isVertexShaderPresent) + { + pipelineToAdjust.m_vertexShader.makeEmpty(); + } - if (!isTessControlShaderPresent) - { - pipelineToAdjust.m_tessControlShader.makeEmpty(); - } + if (!isTessControlShaderPresent) + { + pipelineToAdjust.m_tessControlShader.makeEmpty(); + } - if (!isTessEvaluationShaderPresent) - { - pipelineToAdjust.m_tessEvaluationShader.makeEmpty(); - } + if (!isTessEvaluationShaderPresent) + { + pipelineToAdjust.m_tessEvaluationShader.makeEmpty(); + } - if (!isGeometryexShaderPresent) - { - pipelineToAdjust.m_geometryShader.makeEmpty(); - } + if (!isGeometryexShaderPresent) + { + pipelineToAdjust.m_geometryShader.makeEmpty(); + } - if (!isFragmentShaderPresent) - { - pipelineToAdjust.m_fragmentShader.makeEmpty(); - } + if (!isFragmentShaderPresent) + { + pipelineToAdjust.m_fragmentShader.makeEmpty(); + } - if (!isComputeShaderPresent) - { - pipelineToAdjust.m_computeShader.makeEmpty(); + if (!isComputeShaderPresent) + { + pipelineToAdjust.m_computeShader.makeEmpty(); + } } + + return ret; } void kcCLICommanderOpenGL::RunCompileCommands(const Config& config, LoggingCallBackFunc_t callback) @@ -182,6 +188,7 @@ void kcCLICommanderOpenGL::RunCompileCommands(const Config& config, LoggingCallB // Input validation. bool shouldAbort = false; + bool status = true; bool isVertexShaderPresent = (!config.m_VertexShader.empty()); bool isTessControlShaderPresent = (!config.m_TessControlShader.empty()); bool isTessEvaluationShaderPresent = (!config.m_TessEvaluationShader.empty()); @@ -278,76 +285,67 @@ void kcCLICommanderOpenGL::RunCompileCommands(const Config& config, LoggingCallB if (!shouldAbort) { // Set the log callback for the backend. + m_LogCallback = callback; m_pOglBuilder->SetLog(callback); - // If the user did not specify any device, we should use all supported devices. - std::vector targetDevices; - bool shouldUseAlldevices = config.m_ASICs.empty(); + std::set targetDevices; - if (!shouldAbort) + if (m_supportedDevicesCache.empty()) { - if (!shouldUseAlldevices) + // We need to populate the list of supported devices. + bool isDeviceListExtracted = m_pOglBuilder->GetSupportedDevices(m_supportedDevicesCache); + + if (!isDeviceListExtracted) { - // If the user specified a device, go with the user's choice. - targetDevices = config.m_ASICs; + std::stringstream errMsg; + errMsg << STR_ERR_CANNOT_EXTRACT_SUPPORTED_DEVICE_LIST << std::endl; + shouldAbort = true; } - else - { - if (m_supportedDevicesCache.empty()) - { - // We need to populate the list of supported devices. - bool isDeviceListExtracted = m_pOglBuilder->GetSupportedDevices(m_supportedDevicesCache); - - if (!isDeviceListExtracted) - { - std::stringstream errMsg; - errMsg << STR_ERR_CANNOT_EXTRACT_SUPPORTED_DEVICE_LIST << std::endl; - shouldAbort = true; - } - } + } - std::copy(m_supportedDevicesCache.begin(), m_supportedDevicesCache.end(), std::back_inserter(targetDevices)); - } + if (!shouldAbort) + { + InitRequestedAsicList(config, m_supportedDevicesCache, targetDevices); for (const std::string& device : targetDevices) { if (!shouldAbort) { - // Generate the output message. - logMsg << KA_CLI_STR_COMPILING << device; + // Generate the output message. + logMsg << KA_CLI_STR_COMPILING << device; - // Set the target device info for the backend. - bool isDeviceGlInfoExtracted = m_pOglBuilder->GetDeviceGLInfo(device, glOptions.m_chipFamily, glOptions.m_chipRevision); + // Set the target device info for the backend. + bool isDeviceGlInfoExtracted = m_pOglBuilder->GetDeviceGLInfo(device, glOptions.m_chipFamily, glOptions.m_chipRevision); - if (!isDeviceGlInfoExtracted) - { - logMsg << STR_ERR_CANNOT_GET_DEVICE_INFO << device << std::endl; - continue; - } + if (!isDeviceGlInfoExtracted) + { + logMsg << STR_ERR_CANNOT_GET_DEVICE_INFO << device << std::endl; + continue; + } // Adjust the output file names to the device and shader type. if (isIsaRequired) { glOptions.m_isAmdIsaDisassemblyRequired = true; - GenerateRenderingPipelineOutputPaths(config, config.m_ISAFile, device, glOptions.m_isaDisassemblyOutputFiles); + status &= GenerateRenderingPipelineOutputPaths(config, config.m_ISAFile, device, glOptions.m_isaDisassemblyOutputFiles); } if (isLiveRegAnalysisRequired) { glOptions.m_isLiveRegisterAnalysisRequired = true; - GenerateRenderingPipelineOutputPaths(config, config.m_LiveRegisterAnalysisFile, device, glOptions.m_liveRegisterAnalysisOutputFiles); + status &= GenerateRenderingPipelineOutputPaths(config, config.m_LiveRegisterAnalysisFile, device, glOptions.m_liveRegisterAnalysisOutputFiles); } if (isCfgRequired) { glOptions.m_isLiveRegisterAnalysisRequired = true; - GenerateRenderingPipelineOutputPaths(config, config.m_ControlFlowGraphFile, device, glOptions.m_controlFlowGraphOutputFiles); + status &= GenerateRenderingPipelineOutputPaths(config, config.m_ControlFlowGraphFile, device, glOptions.m_controlFlowGraphOutputFiles); } if (isStatisticsRequired) { glOptions.m_isScStatsRequired = true; - GenerateRenderingPipelineOutputPaths(config, config.m_AnalysisFile, device, glOptions.m_scStatisticsOutputFiles); + status &= GenerateRenderingPipelineOutputPaths(config, config.m_AnalysisFile, device, glOptions.m_scStatisticsOutputFiles); } if (isIsaBinary) @@ -362,6 +360,13 @@ void kcCLICommanderOpenGL::RunCompileCommands(const Config& config, LoggingCallB GT_ASSERT_EX(glOptions.m_programBinaryFile != L"", L"Cannot create a temp file."); } + if (!status) + { + logMsg << STR_ERR_FAILED_ADJUST_FILE_NAMES << std::endl; + shouldAbort = true; + break; + } + // A handle for canceling the build. Currently not in use. bool shouldCancel = false; @@ -486,6 +491,10 @@ void kcCLICommanderOpenGL::RunCompileCommands(const Config& config, LoggingCallB { logMsg << STR_ERR_CANNOT_INVOKE_COMPILER << std::endl; } + else if (buildStatus == beKA::beStatus_FailedOutputVerification) + { + logMsg << STR_ERR_FAILED_OUTPUT_FILE_VERIFICATION << std::endl; + } } // Notify the user about build errors if any. diff --git a/RadeonGPUAnalyzerCLI/src/kcCLICommanderVulkan.cpp b/RadeonGPUAnalyzerCLI/src/kcCLICommanderVulkan.cpp index dbb4b6f..a1564db 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCLICommanderVulkan.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcCLICommanderVulkan.cpp @@ -95,6 +95,9 @@ void kcCLICommanderVulkan::Version(Config& config, LoggingCallBackFunc_t callbac void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallBackFunc_t callback) { + m_LogCallback = callback; + bool status = true; + // Output stream. std::stringstream logMsg; @@ -141,6 +144,12 @@ void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallB // Options to be passed to the backend. VulkanOptions vulkanOptions(config.m_SourceLanguage); + if (!shouldAbort && !config.m_InputFile.empty()) + { + shouldAbort = !kcUtils::ValidateShaderFileName("", config.m_InputFile, logMsg); + vulkanOptions.m_stagelessInputFile = config.m_InputFile; + } + // Validate the input shaders. if (!shouldAbort && isVertexShaderPresent) { @@ -215,10 +224,9 @@ void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallB m_pVulkanBuilder->SetLog(callback); // If the user did not specify any device, we should use all supported devices. - std::vector targetDecives; - bool shouldUseAlldevices = config.m_ASICs.empty(); + std::set targetDevices; - if (shouldUseAlldevices && m_supportedDevicesCache.empty()) + if (m_supportedDevicesCache.empty()) { // We need to populate the list of supported devices. bool isDeviceListExtracted = GetSupportedDevices(); @@ -233,18 +241,9 @@ void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallB if (!shouldAbort) { - if (!shouldUseAlldevices) - { - // If the user specified a device, go with the user's choice. - targetDecives = config.m_ASICs; - } - else - { - // Otherwise, use the cached list of all supported devices. - std::copy(m_supportedDevicesCache.begin(), m_supportedDevicesCache.end(), std::back_inserter(targetDecives)); - } + InitRequestedAsicList(config, m_supportedDevicesCache, targetDevices); - for (const std::string& device : targetDecives) + for (const std::string& device : targetDevices) { // Generate the output message. logMsg << KA_CLI_STR_COMPILING << device; @@ -257,34 +256,41 @@ void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallB { // We must generate the ISA binaries (we will delete them in the end of the process). vulkanOptions.m_isAmdIsaBinariesRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_BinaryOutputFile, device, vulkanOptions.m_isaBinaryFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_BinaryOutputFile, device, vulkanOptions.m_isaBinaryFiles); vulkanOptions.m_isAmdIsaDisassemblyRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ISAFile, device, vulkanOptions.m_isaDisassemblyOutputFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ISAFile, device, vulkanOptions.m_isaDisassemblyOutputFiles); } if (isLiveRegAnalysisRequired) { vulkanOptions.m_isLiveRegisterAnalysisRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_LiveRegisterAnalysisFile, device, vulkanOptions.m_liveRegisterAnalysisOutputFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_LiveRegisterAnalysisFile, device, vulkanOptions.m_liveRegisterAnalysisOutputFiles); } if (isCfgRequired) { vulkanOptions.m_isControlFlowGraphRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ControlFlowGraphFile, device, vulkanOptions.m_controlFlowGraphOutputFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ControlFlowGraphFile, device, vulkanOptions.m_controlFlowGraphOutputFiles); } if (isIlRequired) { vulkanOptions.m_isAmdPalIlDisassemblyRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ILFile, device, vulkanOptions.m_pailIlDisassemblyOutputFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_ILFile, device, vulkanOptions.m_pailIlDisassemblyOutputFiles); } if (isStatisticsRequired) { vulkanOptions.m_isScStatsRequired = true; - kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_AnalysisFile, device, vulkanOptions.m_scStatisticsOutputFiles); + status &= kcUtils::AdjustRenderingPipelineOutputFileNames(config.m_AnalysisFile, device, vulkanOptions.m_scStatisticsOutputFiles); + } + + if (!status) + { + logMsg << STR_ERR_FAILED_ADJUST_FILE_NAMES << std::endl; + shouldAbort = true; + break; } // A handle for canceling the build. Currently not in use. @@ -403,15 +409,28 @@ void kcCLICommanderVulkan::RunCompileCommands(const Config& config, LoggingCallB } } } - else if (compilationStatus == beStatus_VulkanAmdspvCompilationFailure) - { - logMsg << KA_CLI_STR_STATUS_FAILURE << std::endl; - logMsg << buildErrorLog.asASCIICharArray(); - } - else if (compilationStatus == beStatus_VulkanAmdspvLaunchFailure) + else { logMsg << KA_CLI_STR_STATUS_FAILURE << std::endl; - logMsg << STR_ERR_CANNOT_INVOKE_COMPILER << std::endl; + + switch (compilationStatus) + { + case beStatus_VulkanAmdspvCompilationFailure: + logMsg << buildErrorLog.asASCIICharArray(); + break; + case beStatus_VulkanAmdspvLaunchFailure: + logMsg << STR_ERR_CANNOT_INVOKE_COMPILER << std::endl; + break; + case beStatus_VulkanNoInputFile: + logMsg << STR_ERR_NO_INPUT_FILE << std::endl; + break; + case beStatus_VulkanMixedInputFiles: + logMsg << STR_ERR_MIXED_INPUT_FILES << std::endl; + break; + case beStatus_FailedOutputVerification: + logMsg << STR_ERR_FAILED_OUTPUT_FILE_VERIFICATION << std::endl; + break; + } } // Print the message for the current device. diff --git a/RadeonGPUAnalyzerCLI/src/kcCliStringConstants.h b/RadeonGPUAnalyzerCLI/src/kcCliStringConstants.h index 5a3d6f9..798ff54 100644 --- a/RadeonGPUAnalyzerCLI/src/kcCliStringConstants.h +++ b/RadeonGPUAnalyzerCLI/src/kcCliStringConstants.h @@ -1,7 +1,7 @@ #ifndef __kcCliStringConstantsh_h #define __kcCliStringConstantsh_h - const char* const STR_RGA_VERSION = "1.1"; + const char* const STR_RGA_VERSION = "1.2"; #ifdef RGA_BUILD_NUMBER #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) @@ -14,11 +14,17 @@ const char* const STR_ERR_DRIVER_VERSION_EXTRACTION_FAILURE = "could not extract the driver version."; const char* const STR_ERR_INITIALIZATION_FAILURE = "Failed to initialize."; const char* const STR_ERR_OPENGL_VERSION_EXTRACTION_FAILURE = "Unable to extract the supported OpenGL version.\n"; + const char* const STR_TARGET_DETECTED = "Target GPU detected:"; + const char* const STR_FOUND_TARGETS = "Detected following target GPUs:"; + const char* const STR_FOUND_ADAPTERS = "Found the following supported display adapters installed on this system:"; + const char* const STR_DX_ADAPTERS_HELP_COMMON_TEXT = "This is only relevant if you have multiple display adapters installed on your system, and you would like RGA to use the driver which is associated with a non-primary display adapter.By default RGA will use the driver that is associated with the primary display adapter."; // Errors. + const char* const STR_ERR_ERROR = "Error: "; const char* const STR_ERR_INVALID_GLSL_SHADER_TYPE = "Error: The Specified profile is invalid. Possible options: Vertex, Fragment, Compute, Geometry, TessControl, TessEval."; const char* const STR_ERR_UNSUPPORTED_GL_RT_VERSION = "Unsupported OpenGL runtime version. Please install Catalyst driver version 15.20 or above."; const char* const STR_ERR_NO_VALID_CMD_DETECTED = "Error: No valid command. Please run -h for available commands."; + const char* const STR_ERR_COMMAND_NOT_SUPPORTED = "Error: the command is not supported for this mode."; const char* const STR_ERR_CANNOT_EXTRACT_SUPPORTED_DEVICE_LIST = "Error: Unable to extract the list of supported devices."; const char* const STR_ERR_MEMORY_ALLOC_FAILURE = "Error: memory allocation failure."; const char* const STR_ERR_RENDER_COMPUTE_MIX = "Error: cannot mix compute and non-compute shaders."; @@ -27,6 +33,7 @@ const char* const STR_ERR_CANNOT_FIND_OUTPUT_DIR = "Error: output directory does not exist: "; const char* const STR_ERR_CANNOT_INVOKE_COMPILER = "Error: unable to invoke the compiler."; const char* const STR_ERR_CANNOT_GET_DEVICE_INFO = "Error: cannot get device info for: "; + const char* const STR_ERR_CANNOT_READ_FILE = "Error: unable to read: "; const char* const STR_ERR_CANNOT_OPEN_FILE_FOR_WRITE_A = "Error: Unable to open "; const char* const STR_ERR_CANNOT_OPEN_FILE_FOR_WRITE_B = " for write."; const char* const STR_ERR_CANNOT_LOCATE_LIVE_REG_ANALYZER = "Error: cannot locate the live register analyzer."; @@ -42,6 +49,20 @@ const char* const STR_ERR_GLSL_MODE_DEPRECATED = "Error: GLSL mode is no longer supported. Please use OpenGL mode to build OpenGL programs."; const char* const STR_ERR_CANNOT_EXTRACT_OPENGL_VERSION = "Error: unable to extract the OpenGL version."; const char* const STR_ERR_INVALID_INPUT_TYPE = "Error: invalid input type."; + const char* const STR_ERR_TARGET_IS_NOT_SUPPORTED = " offline compilation for the detected target GPU is not supported: "; + const char* const STR_ERR_COULD_NOT_DETECT_TARGET = "Error: could not detect target GPU -> "; + const char* const STR_ERR_AMBIGUOUS_TARGET = "Error: ambiguous target GPU name -> "; + const char* const STR_ERR_NO_KERNELS_FOR_ANALYSIS = "Error: No kernels provided for analysis."; + const char* const STR_ERR_LIST_ADAPTERS_FAILED = "Error: failed to get the list of supported display adapters installed on this system."; + const char* const STR_ERR_SET_ADAPTER_FAILED = "Error: failed to set display adapter with provided ID."; + const char* const STR_ERR_PARSE_DX_SHADER_MODEL_FAILED = "Error: incorrect DX shader model provided."; + const char* const STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_1 = "Error: unsupported Shader Model detected: "; + const char* const STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_2 = "RGA supports Shader Model "; + const char* const STR_ERR_UNSUPPORTED_DX_SHADER_MODEL_3 = "and below."; + const char* const STR_ERR_FAILED_ADJUST_FILE_NAMES = "Error: failed to construct some of the output file names."; + const char* const STR_ERR_NO_INPUT_FILE = "Error: no input file provided."; + const char* const STR_ERR_MIXED_INPUT_FILES = "Error: cannot mix stage-specific input files (--vert, --tesc, --tese, --geom, --frag, --comp) with a stage-less SPIR-V input file."; + const char* const STR_ERR_FAILED_OUTPUT_FILE_VERIFICATION = "Error: failed to generate one or more output files."; // Warnings. #define STR_WRN_DX_MIN_SUPPORTED_VERSION "Warning: AMD DirectX driver supports DX10 and above." @@ -112,6 +133,9 @@ #define KC_STR_DEFAULT_DXASM_SUFFIX "dxasm" #define KC_STR_DEFAULT_STATISTICS_SUFFIX "csv" +// Default file names. +#define KC_STR_DEFAULT_LIVEREG_OUTPUT_FILE_NAME "livereg" + // Front-End strings // Family names: #define KA_STR_familyNameSICards " (HD7000 / HD8000 series)" diff --git a/RadeonGPUAnalyzerCLI/src/kcConfig.cpp b/RadeonGPUAnalyzerCLI/src/kcConfig.cpp index 4975397..1f4161c 100644 --- a/RadeonGPUAnalyzerCLI/src/kcConfig.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcConfig.cpp @@ -7,6 +7,7 @@ #endif #include +#include using namespace std; @@ -17,7 +18,7 @@ std::string Config::sourceKindDXAsmT = "DXAsmTxt"; std::string Config::sourceKindOpenCL = "CL"; std::string Config::sourceKindGLSL = "GLSL"; std::string Config::sourceKindOpenGL = "OPENGL"; -std::string Config::sourceKindVulkan = "VULKAN"; +std::string Config::sourceKindGLSLVulkan = "VULKAN"; std::string Config::sourceKindSpirvBin = "VULKAN-SPV"; std::string Config::sourceKindSpirvTxt = "VULKAN-SPV-TXT"; @@ -28,7 +29,7 @@ Config::Config() : m_AnalysisFile(), m_ILFile(), m_ISAFile(), - m_LiveRegisterAnalysisFile(), + m_LiveRegisterAnalysisFile(KC_STR_DEFAULT_LIVEREG_OUTPUT_FILE_NAME), m_BinaryOutputFile(), m_Function(), m_CSVSeparator(), @@ -44,10 +45,10 @@ Config::Config() : m_Profile(), m_DXFlags(0), m_DXLocation(), + m_DXAdapter(-1), m_FXC(), m_DumpMSIntermediate(), m_EnableShaderIntrinsics(false), - m_ListGraphicsAdapters(false), m_UAVSlot(-1) { } @@ -71,7 +72,6 @@ Config::dump(ostream& out) const out << "m_FXC: " << m_FXC << endl; out << "m_DumpMSIntermediate: " << m_DumpMSIntermediate << endl; out << "m_EnableShaderIntrinsics: " << m_EnableShaderIntrinsics << endl; - out << "m_ListGraphicsAdapters: " << m_ListGraphicsAdapters << endl; out << "m_UAVSlot: " << m_UAVSlot << endl; out << "m_ASICs: "; diff --git a/RadeonGPUAnalyzerCLI/src/kcConfig.h b/RadeonGPUAnalyzerCLI/src/kcConfig.h index a8ebcbc..c3fcc8e 100644 --- a/RadeonGPUAnalyzerCLI/src/kcConfig.h +++ b/RadeonGPUAnalyzerCLI/src/kcConfig.h @@ -23,7 +23,7 @@ class Config static std::string sourceKindOpenCL; static std::string sourceKindGLSL; static std::string sourceKindOpenGL; - static std::string sourceKindVulkan; + static std::string sourceKindGLSLVulkan; static std::string sourceKindSpirvBin; static std::string sourceKindSpirvTxt; @@ -34,6 +34,7 @@ class Config ccListKernels, ccHelp, ccListAsics, + ccListAdapters, ccVersion, }; @@ -61,16 +62,16 @@ class Config bool m_isRetainUserBinaryPath; ///< If true then CLI will not add the asic name to the generated binary output file // DX/GL - std::string m_SourceKind; ///< Kind of source HLSL or GLSL (maybe more later like ASM kinds). - std::string m_Profile; ///< Profile used with GSA compilations. Target in DX - unsigned int m_DXFlags; ///< Flags to pass to D3DCompile. - std::string m_DXLocation; ///< D3DCompiler dll location - std::string m_FXC; ///< FXC path and arguments - std::string m_DumpMSIntermediate; /// the location where to save the ms blob as text + std::string m_SourceKind; ///< Kind of source HLSL or GLSL (maybe more later like ASM kinds). + std::string m_Profile; ///< Profile used with GSA compilations. Target in DX + int m_DXAdapter; ///< ID of GPU adapter to use for DX. + unsigned int m_DXFlags; ///< Flags to pass to D3DCompile. + std::string m_DXLocation; ///< D3DCompiler dll location + std::string m_FXC; ///< FXC path and arguments + std::string m_DumpMSIntermediate; /// the location where to save the ms blob as text bool m_EnableShaderIntrinsics; /// true to enable DX shader intrinsics. - bool m_AMDILInput; /// true when the input language is AMDIL rather than HLSL. - bool m_ListGraphicsAdapters; /// true to list all of the DX graphics adapters that are installed on the system. - int m_UAVSlot; /// User-defined UAV slot for shader intrinsics. + bool m_AMDILInput; /// true when the input language is AMDIL rather than HLSL. + int m_UAVSlot; /// User-defined UAV slot for shader intrinsics. // Vulkan. diff --git a/RadeonGPUAnalyzerCLI/src/kcMain.cpp b/RadeonGPUAnalyzerCLI/src/kcMain.cpp index 80a35e1..85d7e69 100644 --- a/RadeonGPUAnalyzerCLI/src/kcMain.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcMain.cpp @@ -136,6 +136,10 @@ int main(int argc, char* argv[]) PrintAsicList(); break; + case Config::ccListAdapters: + pMyCommander->ListAdapters(config, loggingCallback); + break; + case Config::ccVersion: pMyCommander->Version(config, loggingCallback); break; diff --git a/RadeonGPUAnalyzerCLI/src/kcParseCmdLine.cpp b/RadeonGPUAnalyzerCLI/src/kcParseCmdLine.cpp index 9099069..62eff98 100644 --- a/RadeonGPUAnalyzerCLI/src/kcParseCmdLine.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcParseCmdLine.cpp @@ -4,6 +4,7 @@ #include #include +#include #ifdef _WIN32 #pragma warning (push, 3) // disable warning level 4 for boost #endif @@ -61,18 +62,22 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) ; // DX Options + std::string adaptersDesc = "List all of the supported display adapters that are installed on the system.\n" + std::string(STR_DX_ADAPTERS_HELP_COMMON_TEXT); + std::string setAdaptDesc = "Specify the id of the display adapter whose driver you would like RGA to use.\n" + std::string(STR_DX_ADAPTERS_HELP_COMMON_TEXT); + po::options_description dxOpt("DirectX Shader Analyzer options"); dxOpt.add_options() - ("function,f", po::value(&config.m_Function), "D3D shader to compile, DX ASM shader.") + ("function,f", po::value(&config.m_Function), "D3D shader to compile, DX ASM shader.") ("profile,p", po::value(&config.m_Profile), "Profile to use for compilation. REQUIRED.\nFor example: vs_5_0, ps_5_0, etc.") - ("DXFlags", po::value(&config.m_DXFlags), "Flags to pass to D3DCompile.") - ("DXLocation", po::value(&config.m_DXLocation), "Location to the D3DCompiler Dll required for compilation. If none is specified, the default D3D compiler that is bundled with the Analyzer will be used.") - ("FXC", po::value(&config.m_FXC), "FXC Command Line. Use full path and specify all arguments in \"\". For example:\n" - " rga.exe -f VsMain1 -s DXAsm -p vs_5_0 \\vsBlob.obj --isa \\vsTest.isa --FXC \"\\fxc.exe /E VsMain1 /T vs_5_0 /Fo \\vsBlob.obj \\vsTest.fx\"\n " - " In order to use it, DXAsm must be specified. /Fo switch must be used and output file must be the same as the input file for rga.") + ("DXFlags", po::value(&config.m_DXFlags), "Flags to pass to D3DCompile.") + ("DXLocation", po::value(&config.m_DXLocation), "Location to the D3DCompiler Dll required for compilation. If none is specified, the default D3D compiler that is bundled with the Analyzer will be used.") + ("FXC", po::value(&config.m_FXC), "FXC Command Line. Use full path and specify all arguments in \"\". For example:\n" + " rga.exe -f VsMain1 -s DXAsm -p vs_5_0 \\vsBlob.obj --isa \\vsTest.isa --FXC \"\\fxc.exe /E VsMain1 /T vs_5_0 /Fo \\vsBlob.obj \\vsTest.fx\"\n " + " In order to use it, DXAsm must be specified. /Fo switch must be used and output file must be the same as the input file for rga.") ("DumpMSIntermediate", po::value(&config.m_DumpMSIntermediate), "Location to save the MS Blob as text. ") ("intrinsics", "Enable AMD D3D11 Shader Intrinsics extension.") - ("adapters", "List all of the graphics adapters that are installed on the system.") + ("adapters", adaptersDesc.c_str()) + ("set-adapter", po::value(&config.m_DXAdapter), setAdaptDesc.c_str()) ("UAVSlot", po::value(&config.m_UAVSlot), "This value should be in the range of [0,63].\nThe driver will use the slot to track which UAV is being used to specify the intrinsic. The UAV slot that is selected cannot be used for any other purposes.\nThis option is only relevant when AMD D3D11 Shader Intrinsics is enabled (specify --intrinsics).") ; @@ -90,13 +95,11 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) ("debugil", po::value(&config.m_DebugILFile), "Path to output Debug IL file(s).") ("metadata", po::value(&config.m_MetadataFile), "Path to output Metadata file(s).\n" "Requires --" KERNEL_OPTION ".") - ("kernel,k", po::value(&config.m_Function), "Kernel to analyze or make IL or ISA.\n") + ("kernel,k", po::value(&config.m_Function), "Kernel to be compiled and analyzed. If not specified, all kernels will be targeted.\n") ("binary,b", po::value(&config.m_BinaryOutputFile), "Path to binary output file(s).") ("retain-user-filename", "Retain the output path and name for the generated binary file as specified without adding the target asic name.") ("suppress", po::value >(&config.m_SuppressSection), "Section to omit from binary output. Repeatable. Available options: .source, .amdil, .debugil, .debug_info, .debug_abbrev, .debug_line, .debug_pubnames, .debug_pubtypes, .debug_loc, .debug_str, .llvmir, .text\nNote: Debug sections are valid only with \"-g\" compile option") - ("OpenCLoption", po::value< vector >(&config.m_OpenCLOptions), "OpenCL compiler options. Repeatable.\n" - "\tTo display a list of options use a command like:\n" - "\trga k.cl --OpenCLoption=-h --asic Cypress"); + ("OpenCLoption", po::value< vector >(&config.m_OpenCLOptions), "OpenCL compiler options. Repeatable."); // Vulkan-specific. po::options_description pipelinedOpt(""); @@ -148,7 +151,7 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) if (vm.count("adapters")) { - config.m_ListGraphicsAdapters = true; + config.m_RequestedCommand = Config::ccListAdapters; } if (vm.count("retain-user-filename")) @@ -179,7 +182,8 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) { config.m_RequestedCommand = Config::ccCompile; } - else if (config.m_LiveRegisterAnalysisFile.size() > 0) + else if (config.m_LiveRegisterAnalysisFile.size() > 0 && + config.m_LiveRegisterAnalysisFile.compare(KC_STR_DEFAULT_LIVEREG_OUTPUT_FILE_NAME) != 0) { config.m_RequestedCommand = Config::ccCompile; } @@ -234,7 +238,7 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) { config.m_SourceLanguage = SourceLanguage_GLSL_OpenGL; } - else if ((boost::iequals(config.m_SourceKind, Config::sourceKindVulkan))) + else if ((boost::iequals(config.m_SourceKind, Config::sourceKindGLSLVulkan))) { config.m_SourceLanguage = SourceLanguage_GLSL_Vulkan; } @@ -367,16 +371,57 @@ bool ParseCmdLine(int argc, char* argv[], Config& config) else if ((config.m_RequestedCommand == Config::ccHelp) && (config.m_SourceLanguage == SourceLanguage_GLSL_Vulkan || config.m_SourceLanguage == SourceLanguage_SPIRV_Vulkan || config.m_SourceLanguage == SourceLanguage_SPIRVTXT_Vulkan)) { + // Get the mode name, and the relevant file extensions. We will use it when constructing the example string. + const char* FILE_EXT_SPV = "spv"; + const char* FILE_EXT_SPV_TXT = "txt"; + std::string rgaModeName; + std::string vertExt; + std::string fragExt; + switch (config.m_SourceLanguage) + { + case SourceLanguage_GLSL_Vulkan: + rgaModeName = Config::sourceKindGLSLVulkan; + vertExt = "vert"; + fragExt = "frag"; + break; + case SourceLanguage_SPIRV_Vulkan: + rgaModeName = Config::sourceKindSpirvBin; + vertExt = FILE_EXT_SPV; + fragExt = FILE_EXT_SPV; + break; + case SourceLanguage_SPIRVTXT_Vulkan: + rgaModeName = Config::sourceKindSpirvTxt; + vertExt = FILE_EXT_SPV_TXT; + fragExt = FILE_EXT_SPV_TXT; + break; + } + + // Convert the mode name string to lower case for presentation. + std::transform(rgaModeName.begin(), rgaModeName.end(), rgaModeName.begin(), ::tolower); + cout << "*** Vulkan Instructions & Options ***" << endl; cout << "=================================" << endl; - cout << "Usage: " << programName << " [options]" << endl; + cout << "Usage: " << programName << " [options]"; + if (config.m_SourceLanguage == SourceLanguage_SPIRV_Vulkan || config.m_SourceLanguage == SourceLanguage_SPIRVTXT_Vulkan) + { + cout << " [optional: spv_input_file]"; + } + cout << endl << endl; + if (config.m_SourceLanguage == SourceLanguage_SPIRV_Vulkan || config.m_SourceLanguage == SourceLanguage_SPIRVTXT_Vulkan) + { + cout << "Notes:" << endl; + cout << " * The input file(s) must be specified in one of two ways:" << endl << + " 1) A single SPIR-V input file provided as \"spv_input_file\", or\n" << + " 2) One or more pipeline stage specific shader files specified by the pipeline stage options (--vert, --tesc, etc.)." << endl << endl; + } cout << genericOpt << endl; cout << pipelinedOpt << endl; cout << "Examples:" << endl; - cout << " Extract ISA, AMD IL and statistics for a Vulkan program that is comprised of a vertex shader and a fragment shader for all devices: " << programName << " -s vulkan --isa c:\\output\\vulkan_isa.isa --il c:\\output\\vulkan_il.amdil -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader.vert --frag c:\\source\\myFragmentShader.frag" << endl; - cout << " Extract ISA, AMD IL and statistics for a Vulkan program that is comprised of a vertex shader and a fragment shader for Iceland and Fiji: " << programName << " -s vulkan -c Iceland -c Fiji --isa c:\\output\\vulkan_isa.isa --il c:\\output\\vulkan_il.amdil -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader.vert --frag c:\\source\\myFragmentShader.frag" << endl; - cout << " Extract ISA and binaries for a Vulkan program that is comprised of a vertex shader and a fragment shader for all devices: " << programName << " -s vulkan --isa c:\\output\\vulkan_isa.isa -b c:\\output\\vulkan_bin.bin -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader.vert --frag c:\\source\\myFragmentShader.frag" << endl; - cout << " Extract ISA and perform live register analysis for a Vulkan program for all devices: " << programName << " -s vulkan --isa c:\\output\\vulkan_isa.isa --livereg c:\\output\\ --vert c:\\source\\myVertexShader.vert --frag c:\\source\\myFragmentShader.frag" << endl; + cout << " Extract ISA, AMD IL and statistics for a Vulkan program that is comprised of a vertex shader and a fragment shader for all devices: " << programName << " -s " << rgaModeName << " --isa c:\\output\\vulkan_isa.isa --il c:\\output\\vulkan_il.amdil -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader." << vertExt << " --frag c:\\source\\myFragmentShader." << fragExt << endl; + cout << " Extract ISA, AMD IL and statistics for a Vulkan program that is comprised of a vertex shader and a fragment shader for Iceland and Fiji: " << programName << " -s " << rgaModeName << " -c Iceland -c Fiji --isa c:\\output\\vulkan_isa.isa --il c:\\output\\vulkan_il.amdil -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader." << vertExt << " --frag c:\\source\\myFragmentShader." << fragExt << endl; + cout << " Extract ISA and binaries for a Vulkan program that is comprised of a vertex shader and a fragment shader for all devices: " << programName << " -s " << rgaModeName << " --isa c:\\output\\vulkan_isa.isa -b c:\\output\\vulkan_bin.bin -a c:\\output\\vulkan_stats.stats --vert c:\\source\\myVertexShader." << vertExt << " --frag c:\\source\\myFragmentShader." << fragExt << endl; + cout << " Extract ISA and perform live register analysis for a Vulkan program for all devices: " << programName << " -s " << rgaModeName << " --isa c:\\output\\vulkan_isa.isa --livereg c:\\output\\ --vert c:\\source\\myVertexShader." << vertExt << " --frag c:\\source\\myFragmentShader." << fragExt << endl; + cout << " Extract ISA for a single SPIR-V file, without specifying the pipeline stages: " << programName << " -s " << rgaModeName << " --isa c:\\output\\program.isa c:\\source\\program.spv" << endl; } else if ((config.m_RequestedCommand == Config::ccHelp) && (config.m_SourceLanguage == SourceLanguage_GLSL_OpenGL)) { diff --git a/RadeonGPUAnalyzerCLI/src/kcUtils.cpp b/RadeonGPUAnalyzerCLI/src/kcUtils.cpp index 043ad85..58d0d5c 100644 --- a/RadeonGPUAnalyzerCLI/src/kcUtils.cpp +++ b/RadeonGPUAnalyzerCLI/src/kcUtils.cpp @@ -20,6 +20,8 @@ using namespace beKA; +static bool GetRGATempDir(osDirectory & dir); + bool kcUtils::ValidateShaderFileName(const char* shaderType, const std::string& shaderFileName, std::stringstream& logMsg) { bool isShaderNameValid = true; @@ -57,7 +59,8 @@ bool kcUtils::ValidateShaderOutputDir(const std::string& outputFileName, std::st } -void kcUtils::AdjustRenderingPipelineOutputFileNames(const std::string& baseOutputFileName, const std::string& device, beProgramPipeline& pipelineFiles) +bool kcUtils::AdjustRenderingPipelineOutputFileNames(const std::string& baseOutputFileName, + const std::string& device, beProgramPipeline& pipelineFiles) { // Clear the existing pipeline. pipelineFiles.ClearAll(); @@ -67,47 +70,69 @@ void kcUtils::AdjustRenderingPipelineOutputFileNames(const std::string& baseOutp outputFileAsGtStr << baseOutputFileName.c_str(); osFilePath outputFilePath(outputFileAsGtStr); - // Directory. - osDirectory outputDir; - outputFilePath.getFileDirectory(outputDir); - - // File name. - gtString originalFileName; - outputFilePath.getFileName(originalFileName); + if (!baseOutputFileName.empty() && outputFilePath.isDirectory()) + { + osDirectory outputDir(outputFileAsGtStr); + outputFilePath.setFileDirectory(outputDir); + outputFilePath.clearFileName(); + outputFilePath.clearFileExtension(); + } - // File extension. - gtString originalFileExtension; - outputFilePath.getFileExtension(originalFileExtension); + osDirectory outputDir; + bool status = true; - // Make the adjustments. - gtString fixedFileName; - fixedFileName << outputDir.directoryPath().asString(true) << device.c_str() << "_"; - pipelineFiles.m_vertexShader << fixedFileName << KA_CLI_STR_VERTEX_ABBREVIATION; - pipelineFiles.m_tessControlShader << fixedFileName << KA_CLI_STR_TESS_CTRL_ABBREVIATION; - pipelineFiles.m_tessEvaluationShader << fixedFileName << KA_CLI_STR_TESS_EVAL_ABBREVIATION; - pipelineFiles.m_geometryShader << fixedFileName << KA_CLI_STR_GEOMETRY_ABBREVIATION; - pipelineFiles.m_fragmentShader << fixedFileName << KA_CLI_STR_FRAGMENT_ABBREVIATION; - pipelineFiles.m_computeShader << fixedFileName << KA_CLI_STR_COMPUTE_ABBREVIATION; - - if (!originalFileName.isEmpty()) + // Use system temp folder if no path is provided by a user. + if (baseOutputFileName.empty()) { - pipelineFiles.m_vertexShader << "_" << originalFileName.asASCIICharArray(); - pipelineFiles.m_tessControlShader << "_" << originalFileName.asASCIICharArray(); - pipelineFiles.m_tessEvaluationShader << "_" << originalFileName.asASCIICharArray(); - pipelineFiles.m_geometryShader << "_" << originalFileName.asASCIICharArray(); - pipelineFiles.m_fragmentShader << "_" << originalFileName.asASCIICharArray(); - pipelineFiles.m_computeShader << "_" << originalFileName.asASCIICharArray(); + status = GetRGATempDir(outputDir); + } + else + { + outputFilePath.getFileDirectory(outputDir); } - if (!originalFileExtension.isEmpty()) + if (status) { - pipelineFiles.m_vertexShader << "." << originalFileExtension.asASCIICharArray(); - pipelineFiles.m_tessControlShader << "." << originalFileExtension.asASCIICharArray(); - pipelineFiles.m_tessEvaluationShader << "." << originalFileExtension.asASCIICharArray(); - pipelineFiles.m_geometryShader << "." << originalFileExtension.asASCIICharArray(); - pipelineFiles.m_fragmentShader << "." << originalFileExtension.asASCIICharArray(); - pipelineFiles.m_computeShader << "." << originalFileExtension.asASCIICharArray(); + // File name. + gtString originalFileName; + outputFilePath.getFileName(originalFileName); + + // File extension. + gtString originalFileExtension; + outputFilePath.getFileExtension(originalFileExtension); + + // Make the adjustments. + gtString fixedFileName; + fixedFileName << outputDir.directoryPath().asString(true) << device.c_str() << "_"; + pipelineFiles.m_vertexShader << fixedFileName << KA_CLI_STR_VERTEX_ABBREVIATION; + pipelineFiles.m_tessControlShader << fixedFileName << KA_CLI_STR_TESS_CTRL_ABBREVIATION; + pipelineFiles.m_tessEvaluationShader << fixedFileName << KA_CLI_STR_TESS_EVAL_ABBREVIATION; + pipelineFiles.m_geometryShader << fixedFileName << KA_CLI_STR_GEOMETRY_ABBREVIATION; + pipelineFiles.m_fragmentShader << fixedFileName << KA_CLI_STR_FRAGMENT_ABBREVIATION; + pipelineFiles.m_computeShader << fixedFileName << KA_CLI_STR_COMPUTE_ABBREVIATION; + + if (!originalFileName.isEmpty()) + { + pipelineFiles.m_vertexShader << "_" << originalFileName.asASCIICharArray(); + pipelineFiles.m_tessControlShader << "_" << originalFileName.asASCIICharArray(); + pipelineFiles.m_tessEvaluationShader << "_" << originalFileName.asASCIICharArray(); + pipelineFiles.m_geometryShader << "_" << originalFileName.asASCIICharArray(); + pipelineFiles.m_fragmentShader << "_" << originalFileName.asASCIICharArray(); + pipelineFiles.m_computeShader << "_" << originalFileName.asASCIICharArray(); + } + + if (!originalFileExtension.isEmpty()) + { + pipelineFiles.m_vertexShader << "." << originalFileExtension.asASCIICharArray(); + pipelineFiles.m_tessControlShader << "." << originalFileExtension.asASCIICharArray(); + pipelineFiles.m_tessEvaluationShader << "." << originalFileExtension.asASCIICharArray(); + pipelineFiles.m_geometryShader << "." << originalFileExtension.asASCIICharArray(); + pipelineFiles.m_fragmentShader << "." << originalFileExtension.asASCIICharArray(); + pipelineFiles.m_computeShader << "." << originalFileExtension.asASCIICharArray(); + } } + + return status; } std::string kcUtils::DeviceStatisticsToCsvString(const Config& config, const std::string& device, const beKA::AnalysisData& statistics) @@ -449,36 +474,200 @@ void kcUtils::ConstructOutputFileName(const std::string& baseOutputFileName, con gtString kcUtils::ConstructTempFileName(const gtString& prefix, const gtString & ext) { const unsigned int MAX_ATTEMPTS = 1024; - osFilePath tempFilePath(osFilePath::OS_TEMP_DIRECTORY); - gtString tempFileBaseName = prefix; - tempFileBaseName.appendUnsignedIntNumber(osGetCurrentProcessId()); - gtString tempFileName = tempFileBaseName; - tempFileName.append(ext); - tempFilePath.setFileName(tempFileName); - - uint32_t suffixNum = 0; - while (tempFilePath.exists() && suffixNum < MAX_ATTEMPTS) + osDirectory rgaTempDir; + gtString ret = L""; + + if (GetRGATempDir(rgaTempDir)) { - tempFileName = tempFileBaseName; - tempFileName.appendUnsignedIntNumber(suffixNum++); + if (!rgaTempDir.exists()) + { + rgaTempDir.create(); + } + + osFilePath tempFilePath; + tempFilePath.setFileDirectory(rgaTempDir); + + gtString tempFileBaseName = prefix; + tempFileBaseName.appendUnsignedIntNumber(osGetCurrentProcessId()); + gtString tempFileName = tempFileBaseName; tempFileName.append(ext); tempFilePath.setFileName(tempFileName); + + uint32_t suffixNum = 0; + while (tempFilePath.exists() && suffixNum < MAX_ATTEMPTS) + { + tempFileName = tempFileBaseName; + tempFileName.appendUnsignedIntNumber(suffixNum++); + tempFileName.append(ext); + tempFilePath.setFileName(tempFileName); + } + + ret = suffixNum < MAX_ATTEMPTS ? tempFilePath.asString() : L""; } - return suffixNum < MAX_ATTEMPTS ? tempFilePath.asString() : L""; + return ret; } -bool kcUtils::GetMarketingNameToCodenameMapping(std::map>& cardsMap) +bool kcUtils::GetMarketingNameToCodenameMapping(DeviceNameMap& cardsMap) { bool ret = beUtils::GetMarketingNameToCodenameMapping(cardsMap); return ret; } -kcUtils::kcUtils() +// Converts "srcName" string to lowercase and removes all spaces and '-'. +// Stores the result in "dstName". +static void ReduceDeviceName(std::string& name) { + std::transform(name.begin(), name.end(), name.begin(), [](const char& c) {return std::tolower(c);}); + name.erase(std::remove_if(name.begin(), name.end(), [](const char& c) {return (std::isspace(c) || c == '-');}), name.end()); } +// Helper function that interprets the matched devices found by the "kcUtils::FindGPUArchName()". +// Returns the message in "outMessage" string. +static bool ResolveMatchedDevices(const kcUtils::DeviceNameMap& matchedDevices, const std::string& device, std::string& outMessage) +{ + bool status = false; + std::stringstream msg; + + // Routine printing the architecture name and all its device names. + auto PrintArchAndDevices = [&](const kcUtils::DeviceNameMap::value_type& arch) + { + msg << arch.first << std::endl; + for (const std::string& marketingName : arch.second) + { + msg << "\t" << marketingName << std::endl; + } + }; -kcUtils::~kcUtils() + if (matchedDevices.size() == 0) + { + // No matching architectures found. Failure. + msg << STR_ERR_COULD_NOT_DETECT_TARGET << device << std::endl; + } + else if (matchedDevices.size() == 1) + { + // Found exactly one GPU architectire. Success. + msg << STR_TARGET_DETECTED << std::endl << std::endl; + PrintArchAndDevices(*(matchedDevices.begin())); + status = true; + } + else + { + // Found multiple GPU architectures. Failure. + msg << STR_ERR_AMBIGUOUS_TARGET << device << std::endl << std::endl; + for (auto& arch : matchedDevices) + { + PrintArchAndDevices(arch); + } + } + msg << std::endl; + outMessage = msg.str(); + + return status; +} + +bool kcUtils::FindGPUArchName(const std::string & deviceName, std::string & archName, std::string& msg) +{ + bool status = false; + const char* FILTER_INDICATOR_1 = ":"; + const char* FILTER_INDICATOR_2 = "Not Used"; + + // Mappings "architecture <--> marketing names" + DeviceNameMap matchedDevices, cardsMapping; + + // Transform the device name to lower case and remove spaces. + std::string reducedName = deviceName; + ReduceDeviceName(reducedName); + + bool rc = kcUtils::GetMarketingNameToCodenameMapping(cardsMapping); + + // Walk over all known architectures and devices. + if (rc && !cardsMapping.empty()) + { + for (const auto& pair : cardsMapping) + { + const std::string& archName = pair.first; + std::string reducedArchName = archName; + ReduceDeviceName(reducedArchName); + + // If we found a match with an arch name -- add it to the list of matched archs and continue. + // Otherwise, look at the marketing device names. + if (reducedArchName.find(reducedName) != std::string::npos) + { + // Put the found arch with an all its devices into the "matchedArchs" map. + // Filter out the devices with code names and unused names. + std::set& deviceList = matchedDevices[archName]; + for (const std::string& device : pair.second) + { + if (device.find(FILTER_INDICATOR_1) == std::string::npos && device.find(FILTER_INDICATOR_2) == std::string::npos) + { + deviceList.emplace(device); + } + } + } + else + { + bool addedArch = false; + + for (const std::string& marketingName : pair.second) + { + // We do not want to display names that contain these strings. + if (marketingName.find(FILTER_INDICATOR_1) == std::string::npos && marketingName.find(FILTER_INDICATOR_2) == std::string::npos) + { + std::string reducedMarketingName = marketingName; + ReduceDeviceName(reducedMarketingName); + + if (reducedMarketingName.find(reducedName) != std::string::npos) + { + // Found a match with the marketing name -- add it to the device list corresponding to the "archName" architecture. + if (!addedArch) + { + matchedDevices[archName] = std::set(); + addedArch = true; + } + matchedDevices[archName].emplace(marketingName); + } + } + } + } + } + } + + // Interpret the matched names. + if (ResolveMatchedDevices(matchedDevices, deviceName, msg)) + { + archName = (*(matchedDevices.begin())).first; + status = true; + } + + return status; +} + +// Returns a subdirectory of the OS temp directory where RGA keeps all temporary files. +// Creates the subdirectory if it does not exists. +bool GetRGATempDir(osDirectory & dir) { + const gtString AMD_RGA_TEMP_DIR_1 = L"GPUOpen"; + const gtString AMD_RGA_TEMP_DIR_2 = L"rga"; + bool ret = true; + + osFilePath tempDirPath(osFilePath::OS_TEMP_DIRECTORY); + tempDirPath.appendSubDirectory(AMD_RGA_TEMP_DIR_1); + tempDirPath.getFileDirectory(dir); + if (!dir.exists()) + { + ret = dir.create(); + } + + if (ret) + { + tempDirPath.appendSubDirectory(AMD_RGA_TEMP_DIR_2); + tempDirPath.getFileDirectory(dir); + if (!dir.exists()) + { + ret = dir.create(); + } + } + + return ret; } diff --git a/RadeonGPUAnalyzerCLI/src/kcUtils.h b/RadeonGPUAnalyzerCLI/src/kcUtils.h index 174e0de..d0f867a 100644 --- a/RadeonGPUAnalyzerCLI/src/kcUtils.h +++ b/RadeonGPUAnalyzerCLI/src/kcUtils.h @@ -25,6 +25,9 @@ class kcUtils { public: + // Map "architecture name <--> list of marketing device names". + typedef std::map> DeviceNameMap; + // Helper function to validate a shader's file name and generate the appropriate output message. // shaderType: the stage of the shader (vertex, tessellation control, tessellation evaluation, geometry, fragment or compute). // shaderFileName: the file name to be validated. @@ -40,7 +43,8 @@ class kcUtils // Helper function to construct the output file name in Analyzer CLI's output format, which combines // the base output file name, the target device name and the rendering pipeline stage. - static void AdjustRenderingPipelineOutputFileNames(const std::string& baseOutputFileName, + // Returns true if succeeded and false in case of error. + static bool AdjustRenderingPipelineOutputFileNames(const std::string& baseOutputFileName, const std::string& device, beProgramPipeline& pipelineFiles); // Creates the statistics file according to the user's configuration. @@ -111,13 +115,18 @@ class kcUtils static gtString ConstructTempFileName(const gtString& prefix, const gtString& ext); // Get all available graphics cards public names, grouped by the internal code name. - static bool GetMarketingNameToCodenameMapping(std::map>& cardsMap); + static bool GetMarketingNameToCodenameMapping(DeviceNameMap& cardsMap); + + // Tries to find a GPU architecture name that corresponds to the user-provided device name. + // Returns "true" if corresponding arch is found, "false" otherwise. + // Returns matched architecture in "archName" (if succeeded) and text message in "msg" (if failed). + static bool FindGPUArchName(const std::string& deviceName, std::string& archName, std::string& msg); private: // This is a static class (no instances). kcUtils(const kcUtils& other); - kcUtils(); - ~kcUtils(); + kcUtils() = default; + ~kcUtils() = default; }; #endif // kcUtils_h__ diff --git a/UpdateCommon-PreRelease.py b/UpdateCommon-PreRelease.py new file mode 100644 index 0000000..e6dd086 --- /dev/null +++ b/UpdateCommon-PreRelease.py @@ -0,0 +1,47 @@ +#! /usr/bin/python +# +# Script to get the "pre-release" version of Common repos. + +import os +import subprocess +import shutil +import errno +import stat + +def RemoveErrorHandler(func, path, exc): + if func in (os.rmdir, os.remove) and exc[1].errno == errno.EACCES: + os.chmod(path, stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO) + func(path) + else: + raise + +# to allow the script to be run from anywhere - not just the cwd - store the absolute path to the script file +scriptRoot = os.path.dirname(os.path.realpath(__file__)) + +gitRoot = "ssh://git.amd.com:29418/DevTools/ec/" + +# Define a set of dependencies that exist as separate git projects. +gitMapping = { +# Remote repo name Local path Branch + "common-src-DeviceInfo" : ["../Common/Src/DeviceInfo", "amd-master"] +} + +# for each dependency - remove it if it exists and clone from git repo. +for key in gitMapping: + # convert path to OS specific format + # add script directory to path + localPath = gitMapping[key][0] + gitBranch = gitMapping[key][1] + tmppath = os.path.join(scriptRoot, localPath) + # clean up path, collapsing and ../ and converting / to \ for Windows + path = os.path.normpath(tmppath) + + if os.path.isdir(path): + # directory exists - remove it + print("\n " + path + " exists, removing...") + shutil.rmtree(path, ignore_errors=False, onerror=RemoveErrorHandler) + + # clone + print("\n" + path + ": Using 'git clone' to get latest") + p = subprocess.Popen(["git", "clone", "-b", gitBranch, gitRoot+key, path]) + p.wait();