├── src ├── resource.h ├── ShaderToggler.sln ├── stdafx.h ├── ShaderToggler.vcxproj.filters ├── ShaderToggler.rc ├── crc32_hash.hpp ├── KeyData.h ├── ToggleGroup.h ├── ShaderManager.h ├── KeyData.cpp ├── ToggleGroup.cpp ├── ShaderToggler.vcxproj ├── ShaderManager.cpp ├── Include │ ├── reshade.hpp │ ├── reshade_api_format.hpp │ ├── reshade_api_resource.hpp │ └── reshade_api_pipeline.hpp ├── CDataFile.h ├── CDataFile.cpp └── Main.cpp ├── LICENSE ├── README.md └── .gitignore /src/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by ShaderToggler.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Frans Bouma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ShaderToggler.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32112.339 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderToggler", "ShaderToggler.vcxproj", "{0DE84E87-DC72-40A6-A1FD-D01E49659B21}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Debug|x64.ActiveCfg = Debug|x64 17 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Debug|x64.Build.0 = Debug|x64 18 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Debug|x86.ActiveCfg = Debug|Win32 19 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Debug|x86.Build.0 = Debug|Win32 20 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Release|x64.ActiveCfg = Release|x64 21 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Release|x64.Build.0 = Release|x64 22 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Release|x86.ActiveCfg = Release|Win32 23 | {0DE84E87-DC72-40A6-A1FD-D01E49659B21}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {79AA9AD7-D1FB-4210-AEBC-77CCAB5B88CE} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Part of Injectable Generic Camera System 3 | // Copyright(c) 2017, Frans Bouma 4 | // All rights reserved. 5 | // https://github.com/FransBouma/InjectableGenericCameraSystem 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met : 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and / or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 28 | // stdafx.h : include file for standard system include files, 29 | // or project specific include files that are used frequently, but 30 | // are changed infrequently 31 | // 32 | 33 | #pragma once 34 | 35 | #include 36 | 37 | // Windows Header Files: 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | // TODO: reference additional headers your program requires here 47 | -------------------------------------------------------------------------------- /src/ShaderToggler.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | 58 | 59 | Resource Files 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/ShaderToggler.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | #endif // English (United States) resources 48 | ///////////////////////////////////////////////////////////////////////////// 49 | 50 | 51 | ///////////////////////////////////////////////////////////////////////////// 52 | // English (United Kingdom) resources 53 | 54 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 55 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 56 | #pragma code_page(1252) 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | VS_VERSION_INFO VERSIONINFO 64 | FILEVERSION 1,2,1,0 65 | PRODUCTVERSION 1,2,1,0 66 | FILEFLAGSMASK 0x3fL 67 | #ifdef _DEBUG 68 | FILEFLAGS 0x1L 69 | #else 70 | FILEFLAGS 0x0L 71 | #endif 72 | FILEOS 0x40004L 73 | FILETYPE 0x2L 74 | FILESUBTYPE 0x0L 75 | BEGIN 76 | BLOCK "StringFileInfo" 77 | BEGIN 78 | BLOCK "040004b0" 79 | BEGIN 80 | VALUE "CompanyName", "Frans 'Otis_Inf' Bouma" 81 | VALUE "FileDescription", "ShaderToggler Addon for ReShade 5+" 82 | VALUE "FileVersion", "1.2.1.0" 83 | VALUE "InternalName", "ShaderTo.dll" 84 | VALUE "LegalCopyright", "Copyright (C) 2023 Frans Bouma" 85 | VALUE "OriginalFilename", "ShaderTo.dll" 86 | VALUE "ProductName", "Shader Toggler addon for ReShade 5+" 87 | VALUE "ProductVersion", "1.2.1.0" 88 | END 89 | END 90 | BLOCK "VarFileInfo" 91 | BEGIN 92 | VALUE "Translation", 0x400, 1200 93 | END 94 | END 95 | 96 | #endif // English (United Kingdom) resources 97 | ///////////////////////////////////////////////////////////////////////////// 98 | 99 | 100 | 101 | #ifndef APSTUDIO_INVOKED 102 | ///////////////////////////////////////////////////////////////////////////// 103 | // 104 | // Generated from the TEXTINCLUDE 3 resource. 105 | // 106 | 107 | 108 | ///////////////////////////////////////////////////////////////////////////// 109 | #endif // not APSTUDIO_INVOKED 110 | 111 | -------------------------------------------------------------------------------- /src/crc32_hash.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 1986 Gary S. Brown. 3 | * You may use this program, or code or tables extracted from it, as desired without restriction. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | inline uint32_t compute_crc32(const uint8_t *data, size_t size) 11 | { 12 | static constexpr uint32_t crc32_table[256] = { // CRC polynomial 0xEDB88320 13 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 14 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 15 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 16 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 17 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 18 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 19 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 20 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 21 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 22 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 23 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 24 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 25 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 26 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 27 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 28 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 29 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 30 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 31 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 32 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 33 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 34 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 35 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 36 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 37 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 38 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 39 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 40 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 41 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 42 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 43 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 44 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 45 | }; 46 | 47 | uint32_t crc = 0xFFFFFFFF; 48 | for (; size != 0; --size, ++data) 49 | crc = (crc >> 8) ^ crc32_table[(crc ^ (*data)) & 0xFF]; 50 | return ~crc; 51 | } 52 | -------------------------------------------------------------------------------- /src/KeyData.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | #pragma once 33 | 34 | #include 35 | 36 | #include "stdafx.h" 37 | 38 | namespace ShaderToggler 39 | { 40 | /// 41 | /// Class which is used to contain keybinding data 42 | /// 43 | class KeyData 44 | { 45 | public: 46 | KeyData(); 47 | 48 | /// 49 | /// Ini file variant which has ctrl/shift/alt requirements baked in. 50 | /// 51 | /// 52 | void setKeyFromIniFile(uint32_t newKeyValue); 53 | /// 54 | /// Sets the passed in vk keycode as the key to use for 55 | /// 56 | /// 57 | /// 58 | /// 59 | /// 60 | void setKey(uint8_t newKeyValue, bool shiftRequired=false, bool altRequired=false, bool ctrlRequired=false); 61 | uint32_t getKeyForIniFile() const; 62 | void clear(); 63 | /// 64 | /// Used for when the instance of this class is used to collect temporary keybinding data for editing 65 | /// 66 | /// 67 | void collectKeysPressed(const reshade::api::effect_runtime* runtime); 68 | /// 69 | /// Returns true if the keyboard shortcut defined by this instance is currently pressed down 70 | /// 71 | /// 72 | /// 73 | bool isKeyPressed(const reshade::api::effect_runtime* runtime); 74 | 75 | /// 76 | /// Returns a usable description for the keyboard shortcut, or 'Press a key' if undefined/empty 77 | /// 78 | /// 79 | std::string getKeyAsString() { return _keyAsString;} 80 | uint8_t getKeyCode() { return _keyCode;} 81 | bool isValid() { return _keyCode > 0; } 82 | 83 | private: 84 | static std::string vkCodeToString(uint8_t vkCode); 85 | 86 | void setKeyAsString(); 87 | 88 | uint8_t _keyCode; 89 | bool _shiftRequired; 90 | bool _altRequired; 91 | bool _ctrlRequired; 92 | std::string _keyAsString; 93 | 94 | }; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShaderToggler 2 | Reshade 5.1+ addin to toggle game shaders on/off in groups based on a key press. It allows you to configure these groups from within the addin as well. 3 | This addon is meant to toggle a game's shaders on/off, not reshade effects. 4 | 5 | It's mainly for 64bit reshade. There's a 32bit version in the releases, but it's the original old version, newer versions are 64 bit only. 6 | 7 | ## How to use 8 | Place the `ShaderToggler.addon64` in the same folder as where the game exe is located. This is in most cases the same folder as where the Reshade 5.1+ dll 9 | is located. Only for games which use Vulkan, the Reshade dll is likely elsewhere. For Unreal Engine powered games there might be two 10 | game exe's: one in the game's installation folder, and one in a folder deeper into that folder, e.g. 11 | `GameName\Binaries\Win64\GameName-Win64-Shipping.exe`; the shader toggler addon has to be in that second folder, in our example: 12 | `GameName\Binaries\Win64`. Reshade has to be placed in that folder as well. 13 | 14 | Be sure to use the Reshade version which supports Addons (so the unsigned version). When you start your game, the `Addons` tab in 15 | the Reshade gui should show the Shader Toggler information and controls. 16 | 17 | To obtain the unsigned Reshade version, go to and click on *Download*, then on the grey button to download the ReShade version with Add-on support. 18 | 19 | To create a toggle for a set of shaders, open the reshade gui and go to the addon's tab -> Shader Toggler area. Then click 20 | the `New` button to create a new *Toggle group*. By default the new toggle group has as name `Default` and as toggle key 21 | `Caps Lock`. To change these, click the `Edit` button of the Toggle Group. You can then change the name of the toggle group 22 | and also the keyboard shortcut. The keyboard shortcut nor the name have to be unique. The keyboard shortcut can be 23 | any key on your keyboard, optionally in combination of using `Alt`, `Control`, and `Shift`. 24 | 25 | A toggle group needs shaders assigned to it, that's done with marking shaders below. 26 | 27 | ## Marking Shaders 28 | To successfully be able to mark a set of shaders to toggle, be sure the elements you want to toggle, so the elements 29 | using the shaders, are visible, e.g. a menu or a hud or a certain effect. After you've made sure the elements to toggle are 30 | visible, click the `Change Shaders` button of the toggle group. This will start the 'Shader hunting' phase for the particular 31 | toggle group. 32 | 33 | You should see the shader overlay in the top left corner with the information you need. By default it waits a certain amount 34 | of frames (which you can configure in the reshade overlay) to see which shaders are currently active. This is to avoid having 35 | to walk through potentially thousands of shaders which aren't currently used. 36 | 37 | After the frames have been collected, it has enough information to allow you to browse the shaders. 38 | 39 | To walk the available pixel shaders, use the `Numpad 1` and `Numpad 2` keys. If an element disappears, the shader that's 40 | currently 'active' is rendering these elements, so if you want to hide these elements, press `Numpad 3`. The shader is then 41 | marked, and part of the toggle group. Press `Numpad 3` again to remove it from the group. 42 | 43 | To walk the available vertex shaders, instead use the `Numpad 4` and `Numpad 5` keys. To mark a vertex shader to be part of the toggle group, press `Numpad 6`. 44 | To walk the available compute shaders, instead use the `Numpad 7` and `Numpad 8` keys. To mark a compute shader to be part of the toggle group, press `Numpad 9`. 45 | 46 | To walk all shaders you already marked in the current group, you can hold down `Ctrl` and press the Numpad keys for the shader type (`Numpad 1` and `Numpad 2` for pixel shaders, `Numpad 4` and `Numpad 5` for vertex shaders and `Numpad 7` and `Numpad 8` for compute shaders) to quickly move back/forth through the shaders in a group, e.g. when you made a mistake and you want to unmark a shader. 47 | 48 | To test your current group, press the toggle key you assigned to the group. When you're done, click the 'Done' button in the 49 | reshade overlay for the particular toggle group. 50 | 51 | To re-use this information the next time you run the game, click the Save toggle group button. This will write an ini file 52 | (`ShaderToggler.ini`) with the information to create the set of shaders to toggle next time you start the game. This file is 53 | located in the same folder as `ShaderToggler.addon64`. 54 | -------------------------------------------------------------------------------- /src/ToggleGroup.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | #pragma once 33 | 34 | #include 35 | #include 36 | 37 | #include "CDataFile.h" 38 | #include "KeyData.h" 39 | 40 | namespace ShaderToggler 41 | { 42 | class ToggleGroup 43 | { 44 | public: 45 | ToggleGroup(std::string name, int Id); 46 | 47 | static int getNewGroupId(); 48 | 49 | void setToggleKey(uint8_t newKeyValue, bool shiftRequired, bool altRequired, bool ctrlRequired); 50 | void setToggleKey(KeyData newData); 51 | void setName(std::string newName); 52 | /// 53 | /// Writes the shader hashes, name and toggle key to the ini file specified, using a Group + groupCounter section. 54 | /// 55 | /// 56 | /// 57 | void saveState(CDataFile& iniFile, int groupCounter) const; 58 | /// 59 | /// Loads the shader hashes, name and toggle key from the ini file specified, using a Group + groupCounter section. 60 | /// 61 | /// 62 | /// if -1, the ini file is in the pre-1.0 format 63 | void loadState(CDataFile& iniFile, int groupCounter); 64 | void storeCollectedHashes(const std::unordered_set pixelShaderHashes, const std::unordered_set vertexShaderHashes, const std::unordered_set computeShaderHashes); 65 | bool isBlockedPixelShader(uint32_t shaderHash); 66 | bool isBlockedVertexShader(uint32_t shaderHash); 67 | bool isBlockedComputeShader(uint32_t shaderHash); 68 | void clearHashes(); 69 | 70 | void toggleActive() { _isActive = !_isActive;} 71 | void setIsActiveAtStartup(bool newValue) { _isActiveAtStartup = newValue; } 72 | void setEditing(bool isEditing) { _isEditing = isEditing;} 73 | 74 | std::string getToggleKeyAsString() { return _keyData.getKeyAsString();} 75 | uint8_t getToggleKey() { return _keyData.getKeyCode();} 76 | std::string getName() { return _name;} 77 | bool isActiveAtStartup() { return _isActiveAtStartup; } 78 | bool isActive() { return _isActive;} 79 | bool isEditing() { return _isEditing;} 80 | bool isEmpty() const { return _vertexShaderHashes.size() <= 0 && _pixelShaderHashes.size() <= 0 && _computeShaderHashes.size() <= 0; } 81 | int getId() const { return _id; } 82 | std::unordered_set getPixelShaderHashes() const { return _pixelShaderHashes;} 83 | std::unordered_set getVertexShaderHashes() const { return _vertexShaderHashes;} 84 | std::unordered_set getComputeShaderHashes() const { return _computeShaderHashes; } 85 | bool isToggleKeyPressed(const reshade::api::effect_runtime* runtime) { return _keyData.isKeyPressed(runtime);} 86 | 87 | bool operator==(const ToggleGroup& rhs) 88 | { 89 | return getId() == rhs.getId(); 90 | } 91 | 92 | private: 93 | int _id; 94 | std::string _name; 95 | KeyData _keyData; 96 | std::unordered_set _vertexShaderHashes; 97 | std::unordered_set _pixelShaderHashes; 98 | std::unordered_set _computeShaderHashes; 99 | bool _isActive; // true means the group is actively toggled (so the hashes have to be hidden). 100 | bool _isEditing; // true means the group is actively edited (name, key) 101 | bool _isActiveAtStartup; // true means the group is active when the host game is started and the toggler has loaded the groups. 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | [Aa]dditional[Hh]elper[Ff]iles/ 10 | 11 | Deployment/ 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | *.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | -------------------------------------------------------------------------------- /src/ShaderManager.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "CDataFile.h" 42 | #include "ToggleGroup.h" 43 | 44 | 45 | namespace ShaderToggler 46 | { 47 | /// 48 | /// Class which manages a set of shaders for a given type (pixel, vertex...) 49 | /// 50 | class ShaderManager 51 | { 52 | public: 53 | ShaderManager(); 54 | 55 | void addHashHandlePair(uint32_t shaderHash, uint64_t pipelineHandle); 56 | void removeHandle(uint64_t handle); 57 | /// 58 | /// Switches on the hunting mode for the shader manager. It will copy the passed in hashes to the set of marked hashes. Hunting mode is the mode 59 | /// where the user can step through collected active shaders to mark them for assignment to the current edited group. 60 | /// 61 | /// 62 | void startHuntingMode(const std::unordered_set currentMarkedHashes); 63 | void stopHuntingMode(); 64 | /// 65 | /// Moves to the next shader. If control is pressed as well, it'll step to the next marked shader (if any). If there aren't any shaders in that 66 | /// situation, it'll stay on the current shader. 67 | /// 68 | /// If control is pressed as well, it'll step to the next marked shader (if any). If there aren't any shaders in that 69 | /// situation, it'll stay on the current shader. 70 | void huntNextShader(bool ctrlPressed); 71 | /// 72 | /// Moves to the previous shader. If control is pressed as well, it'll step to the previous marked shader (if any). If there aren't any shaders in that 73 | /// situation, it'll stay on the current shader. 74 | /// 75 | /// If control is pressed as well, it'll step to the previous marked shader (if any). If there aren't any shaders in that 76 | /// situation, it'll stay on the current shader. 77 | void huntPreviousShader(bool ctrlPressed); 78 | /// 79 | /// Returns true if the shader hash passed in is the currently hunted shader or it's part of the marked shader hashes 80 | /// 81 | /// 82 | /// 83 | bool isBlockedShader(uint32_t shaderHash); 84 | /// 85 | /// Returns the shader hash for the passed in pipeline handle, if found. 0 otherwise. 86 | /// 87 | /// 88 | /// 89 | uint32_t getShaderHash(uint64_t handle); 90 | void addActivePipelineHandle(uint64_t handle); 91 | void toggleMarkOnHuntedShader(); 92 | 93 | uint32_t getPipelineCount() {return _handleToShaderHash.size();} 94 | uint32_t getShaderCount() { return _shaderHashes.size();} 95 | uint32_t getAmountShaderHashesCollected() { return _collectedActiveShaderHashes.size(); } 96 | bool isInHuntingMode() { return _isInHuntingMode;} 97 | uint32_t getActiveHuntedShaderHash() { return _activeHuntedShaderHash;} 98 | int getActiveHuntedShaderIndex() { return _activeHuntedShaderIndex; } 99 | void toggleHideMarkedShaders() { _hideMarkedShaders=!_hideMarkedShaders;} 100 | 101 | bool isHuntedShaderMarked() 102 | { 103 | std::shared_lock lock(_markedShaderHashMutex); 104 | return _markedShaderHashes.count(_activeHuntedShaderHash)==1; 105 | } 106 | 107 | std::unordered_set getMarkedShaderHashes() 108 | { 109 | std::shared_lock lock(_markedShaderHashMutex); 110 | return _markedShaderHashes; 111 | } 112 | 113 | uint32_t getMarkedShaderCount() 114 | { 115 | std::shared_lock lock(_markedShaderHashMutex); 116 | return _markedShaderHashes.size(); 117 | } 118 | 119 | bool isKnownHandle(uint64_t pipelineHandle) 120 | { 121 | std::shared_lock lock(_hashHandlesMutex); 122 | return _handleToShaderHash.count(pipelineHandle)==1; 123 | } 124 | 125 | private: 126 | void setActiveHuntedShaderHandle(); 127 | 128 | std::unordered_set _shaderHashes; // all shader hashes added through init pipeline 129 | std::map _handleToShaderHash; // pipeline handle per shader hash. Handle is removed when a pipeline is destroyed. 130 | std::unordered_set _collectedActiveShaderHashes; // shader hashes bound to pipeline handles which were collected during the collection phase after hunting was enabled, which are the pipeline handles active during the last X frames 131 | std::unordered_set _markedShaderHashes; // the hashes for shaders which are currently marked. 132 | 133 | bool _isInHuntingMode = false; 134 | int _activeHuntedShaderIndex = -1; 135 | uint32_t _activeHuntedShaderHash; 136 | std::shared_mutex _collectedActiveHandlesMutex; 137 | std::shared_mutex _hashHandlesMutex; 138 | std::shared_mutex _markedShaderHashMutex; 139 | bool _hideMarkedShaders = false; 140 | }; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/KeyData.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | #include "KeyData.h" 33 | 34 | namespace ShaderToggler 35 | { 36 | KeyData::KeyData(): _keyCode(0), _shiftRequired(false), _altRequired(false), _ctrlRequired(false) 37 | { 38 | } 39 | 40 | 41 | void KeyData::setKeyFromIniFile(uint32_t newKeyValue) 42 | { 43 | if(newKeyValue==0) 44 | { 45 | return; 46 | } 47 | _keyCode = ((newKeyValue >> 24) & 0xFF);; 48 | _altRequired = ((newKeyValue >> 16) & 0xFF) == 0x01; 49 | _ctrlRequired = ((newKeyValue >> 8) & 0xFF) == 0x01; 50 | _shiftRequired = (newKeyValue & 0xFF) == 0x01; 51 | setKeyAsString(); 52 | } 53 | 54 | void KeyData::setKey(uint8_t newKeyValue, bool shiftRequired, bool altRequired, bool ctrlRequired) 55 | { 56 | if(newKeyValue==0) 57 | { 58 | return; 59 | } 60 | _keyCode = newKeyValue; 61 | _ctrlRequired = ctrlRequired; 62 | _shiftRequired = shiftRequired; 63 | _altRequired = altRequired; 64 | setKeyAsString(); 65 | } 66 | 67 | 68 | uint32_t KeyData::getKeyForIniFile() const 69 | { 70 | return (_keyCode & 0xFF) << 24 | ((_altRequired ? 1 : 0) << 16) | ((_ctrlRequired ? 1 : 0) << 8) | ((_shiftRequired ? 1 : 0)); 71 | } 72 | 73 | 74 | void KeyData::clear() 75 | { 76 | _altRequired = false; 77 | _ctrlRequired = false; 78 | _shiftRequired = false; 79 | _keyCode = 0; 80 | setKeyAsString(); 81 | } 82 | 83 | 84 | void KeyData::collectKeysPressed(const reshade::api::effect_runtime* runtime) 85 | { 86 | // keys below 7 aren't interesting. 87 | for(int i=7;i<256;i++) 88 | { 89 | switch(i) 90 | { 91 | case VK_MENU: 92 | case VK_CONTROL: 93 | case VK_SHIFT: 94 | break; 95 | default: 96 | if(runtime->is_key_down(i)) 97 | { 98 | _keyCode = i; 99 | _altRequired = runtime->is_key_down(VK_MENU); 100 | _ctrlRequired = runtime->is_key_down(VK_CONTROL); 101 | _shiftRequired = runtime->is_key_down(VK_SHIFT); 102 | } 103 | } 104 | } 105 | setKeyAsString(); 106 | } 107 | 108 | 109 | bool KeyData::isKeyPressed(const reshade::api::effect_runtime* runtime) 110 | { 111 | bool toReturn = runtime->is_key_pressed(_keyCode); 112 | const bool altPressed = runtime->is_key_down(VK_MENU);; 113 | const bool shiftPressed = runtime->is_key_down(VK_SHIFT); 114 | const bool ctrlPressed = runtime->is_key_down(VK_CONTROL); 115 | 116 | toReturn &= ((_altRequired && altPressed) || (!_altRequired && !altPressed)); 117 | toReturn &= ((_shiftRequired && shiftPressed) || (!_shiftRequired && !shiftPressed)); 118 | toReturn &= ((_ctrlRequired && ctrlPressed) || (!_ctrlRequired && !ctrlPressed)); 119 | return toReturn; 120 | } 121 | 122 | 123 | std::string KeyData::vkCodeToString(uint8_t vkCode) 124 | { 125 | // from ReShade 126 | static const char *keyboard_keys[256] = { 127 | "", "Left Mouse", "Right Mouse", "Cancel", "Middle Mouse", "X1 Mouse", "X2 Mouse", "", "Backspace", "Tab", "", "", "Clear", "Enter", "", "", 128 | "Shift", "Control", "Alt", "Pause", "Caps Lock", "", "", "", "", "", "", "Escape", "", "", "", "", 129 | "Space", "Page Up", "Page Down", "End", "Home", "Left Arrow", "Up Arrow", "Right Arrow", "Down Arrow", "Select", "", "", "Print Screen", "Insert", "Delete", "Help", 130 | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "", "", "", "", "", "", 131 | "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", 132 | "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Left Windows", "Right Windows", "Apps", "", "Sleep", 133 | "Numpad 0", "Numpad 1", "Numpad 2", "Numpad 3", "Numpad 4", "Numpad 5", "Numpad 6", "Numpad 7", "Numpad 8", "Numpad 9", "Numpad *", "Numpad +", "", "Numpad -", "Numpad Decimal", "Numpad /", 134 | "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", 135 | "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "", "", "", "", "", "", "", "", 136 | "Num Lock", "Scroll Lock", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 137 | "Left Shift", "Right Shift", "Left Control", "Right Control", "Left Menu", "Right Menu", "Browser Back", "Browser Forward", "Browser Refresh", "Browser Stop", "Browser Search", "Browser Favorites", "Browser Home", "Volume Mute", "Volume Down", "Volume Up", 138 | "Next Track", "Previous Track", "Media Stop", "Media Play/Pause", "Mail", "Media Select", "Launch App 1", "Launch App 2", "", "", "OEM ;", "OEM +", "OEM ,", "OEM -", "OEM .", "OEM /", 139 | "OEM ~", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 140 | "", "", "", "", "", "", "", "", "", "", "", "OEM [", "OEM \\", "OEM ]", "OEM '", "OEM 8", 141 | "", "", "OEM <", "", "", "", "", "", "", "", "", "", "", "", "", "", 142 | "", "", "", "", "", "", "Attn", "CrSel", "ExSel", "Erase EOF", "Play", "Zoom", "", "PA1", "OEM Clear", "" 143 | }; 144 | 145 | return keyboard_keys[vkCode]; 146 | } 147 | 148 | 149 | void KeyData::setKeyAsString() 150 | { 151 | if (!_altRequired && !_ctrlRequired && !_shiftRequired && (_keyCode <= 0)) 152 | { 153 | // empty 154 | _keyAsString = "Press a key"; 155 | return; 156 | } 157 | _keyAsString.clear(); 158 | if (_altRequired) 159 | { 160 | _keyAsString.append("Alt + "); 161 | } 162 | if (_ctrlRequired) 163 | { 164 | _keyAsString.append("Ctrl + "); 165 | } 166 | if (_shiftRequired) 167 | { 168 | _keyAsString.append("Shift + "); 169 | } 170 | if (_keyCode > 0) 171 | { 172 | _keyAsString.append(vkCodeToString(_keyCode)); 173 | } 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/ToggleGroup.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | 33 | #include "stdafx.h" 34 | #include "ToggleGroup.h" 35 | #include "KeyData.h" 36 | 37 | namespace ShaderToggler 38 | { 39 | ToggleGroup::ToggleGroup(std::string name, int id): _id(id), _isActive(false), _isEditing(false), _isActiveAtStartup(false) 40 | { 41 | _name = name.size() > 0 ? name : "Default"; 42 | } 43 | 44 | 45 | int ToggleGroup::getNewGroupId() 46 | { 47 | static atomic_int s_groupId = 0; 48 | 49 | ++s_groupId; 50 | return s_groupId; 51 | } 52 | 53 | 54 | void ToggleGroup::setToggleKey(uint8_t newKeyValue, bool shiftRequired, bool altRequired, bool ctrlRequired) 55 | { 56 | _keyData.setKey(newKeyValue, shiftRequired, altRequired, ctrlRequired); 57 | } 58 | 59 | 60 | void ToggleGroup::setToggleKey(KeyData newData) 61 | { 62 | if(newData.isValid()) 63 | { 64 | _keyData = newData; 65 | } 66 | } 67 | 68 | 69 | void ToggleGroup::storeCollectedHashes(const std::unordered_set pixelShaderHashes, const std::unordered_set vertexShaderHashes, const std::unordered_set computeShaderHashes) 70 | { 71 | clearHashes(); 72 | 73 | for(const auto hash : vertexShaderHashes) 74 | { 75 | _vertexShaderHashes.emplace(hash); 76 | } 77 | for(const auto hash : pixelShaderHashes) 78 | { 79 | _pixelShaderHashes.emplace(hash); 80 | } 81 | for(const auto hash : computeShaderHashes) 82 | { 83 | _computeShaderHashes.emplace(hash); 84 | } 85 | } 86 | 87 | 88 | bool ToggleGroup::isBlockedPixelShader(uint32_t shaderHash) 89 | { 90 | return _isActive && (_pixelShaderHashes.count(shaderHash)==1); 91 | } 92 | 93 | 94 | bool ToggleGroup::isBlockedVertexShader(uint32_t shaderHash) 95 | { 96 | return _isActive && (_vertexShaderHashes.count(shaderHash) == 1); 97 | } 98 | 99 | 100 | bool ToggleGroup::isBlockedComputeShader(uint32_t shaderHash) 101 | { 102 | return _isActive && (_computeShaderHashes.count(shaderHash) == 1); 103 | } 104 | 105 | 106 | void ToggleGroup::clearHashes() 107 | { 108 | _pixelShaderHashes.clear(); 109 | _vertexShaderHashes.clear(); 110 | _computeShaderHashes.clear(); 111 | } 112 | 113 | 114 | void ToggleGroup::setName(std::string newName) 115 | { 116 | if(newName.size()<=0) 117 | { 118 | return; 119 | } 120 | _name = newName; 121 | } 122 | 123 | 124 | void ToggleGroup::saveState(CDataFile& iniFile, int groupCounter) const 125 | { 126 | const std::string sectionRoot = "Group" + std::to_string(groupCounter); 127 | const std::string vertexHashesCategory = sectionRoot + "_VertexShaders"; 128 | const std::string pixelHashesCategory = sectionRoot + "_PixelShaders"; 129 | const std::string computeHashesCategory = sectionRoot + "_ComputeShaders"; 130 | 131 | int counter = 0; 132 | for(const auto hash: _vertexShaderHashes) 133 | { 134 | iniFile.SetUInt("ShaderHash" + std::to_string(counter), hash, "", vertexHashesCategory); 135 | counter++; 136 | } 137 | iniFile.SetUInt("AmountHashes", counter, "", vertexHashesCategory); 138 | 139 | counter=0; 140 | for(const auto hash: _pixelShaderHashes) 141 | { 142 | iniFile.SetUInt("ShaderHash" + std::to_string(counter), hash, "", pixelHashesCategory); 143 | counter++; 144 | } 145 | iniFile.SetUInt("AmountHashes", counter, "", pixelHashesCategory); 146 | 147 | counter = 0; 148 | for(const auto hash : _computeShaderHashes) 149 | { 150 | iniFile.SetUInt("ShaderHash" + std::to_string(counter), hash, "", computeHashesCategory); 151 | counter++; 152 | } 153 | iniFile.SetUInt("AmountHashes", counter, "", computeHashesCategory); 154 | 155 | iniFile.SetValue("Name", _name, "", sectionRoot); 156 | iniFile.SetUInt("ToggleKey", _keyData.getKeyForIniFile(), "", sectionRoot); 157 | iniFile.SetBool("IsActiveAtStartup", _isActiveAtStartup, "", sectionRoot); 158 | } 159 | 160 | 161 | void ToggleGroup::loadState(CDataFile& iniFile, int groupCounter) 162 | { 163 | if(groupCounter<0) 164 | { 165 | int amount = iniFile.GetInt("AmountHashes", "PixelShaders"); 166 | for(int i = 0;i< amount;i++) 167 | { 168 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), "PixelShaders"); 169 | if(hash!=UINT_MAX) 170 | { 171 | _pixelShaderHashes.emplace(hash); 172 | } 173 | } 174 | amount = iniFile.GetInt("AmountHashes", "VertexShaders"); 175 | for(int i = 0;i< amount;i++) 176 | { 177 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), "VertexShaders"); 178 | if(hash!=UINT_MAX) 179 | { 180 | _vertexShaderHashes.emplace(hash); 181 | } 182 | } 183 | amount = iniFile.GetInt("AmountHashes", "ComputeShaders"); 184 | for(int i = 0; i < amount; i++) 185 | { 186 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), "ComputeShaders"); 187 | if(hash != UINT_MAX) 188 | { 189 | _computeShaderHashes.emplace(hash); 190 | } 191 | } 192 | 193 | // done 194 | return; 195 | } 196 | 197 | const std::string sectionRoot = "Group" + std::to_string(groupCounter); 198 | const std::string vertexHashesCategory = sectionRoot + "_VertexShaders"; 199 | const std::string pixelHashesCategory = sectionRoot + "_PixelShaders"; 200 | const std::string computeHashesCategory = sectionRoot + "_ComputeShaders"; 201 | 202 | int amountShaders = iniFile.GetInt("AmountHashes", vertexHashesCategory); 203 | for(int i = 0;i< amountShaders;i++) 204 | { 205 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), vertexHashesCategory); 206 | if(hash!=UINT_MAX) 207 | { 208 | _vertexShaderHashes.emplace(hash); 209 | } 210 | } 211 | 212 | amountShaders = iniFile.GetInt("AmountHashes", pixelHashesCategory); 213 | for(int i = 0;i< amountShaders;i++) 214 | { 215 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), pixelHashesCategory); 216 | if(hash!=UINT_MAX) 217 | { 218 | _pixelShaderHashes.emplace(hash); 219 | } 220 | } 221 | 222 | amountShaders = iniFile.GetInt("AmountHashes", computeHashesCategory); 223 | for(int i = 0; i < amountShaders; i++) 224 | { 225 | uint32_t hash = iniFile.GetUInt("ShaderHash" + std::to_string(i), computeHashesCategory); 226 | if(hash != UINT_MAX) 227 | { 228 | _computeShaderHashes.emplace(hash); 229 | } 230 | } 231 | 232 | _name = iniFile.GetValue("Name", sectionRoot); 233 | if(_name.size()<=0) 234 | { 235 | _name = "Default"; 236 | } 237 | const uint32_t toggleKeyValue = iniFile.GetUInt("ToggleKey", sectionRoot); 238 | if(toggleKeyValue == UINT_MAX) 239 | { 240 | _keyData.setKey(VK_CAPITAL, false, false, false); 241 | } 242 | else 243 | { 244 | _keyData.setKeyFromIniFile(toggleKeyValue); 245 | } 246 | _isActiveAtStartup = iniFile.GetBool("IsActiveAtStartup", sectionRoot); 247 | _isActive = _isActiveAtStartup; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/ShaderToggler.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {0de84e87-dc72-40a6-a1fd-d01e49659b21} 25 | ShaderToggler 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | .addon 79 | 80 | 81 | true 82 | .addon64 83 | 84 | 85 | false 86 | .addon64 87 | 88 | 89 | 90 | Level3 91 | true 92 | WIN32;_DEBUG;SHADERTOGGLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 93 | true 94 | Use 95 | pch.h 96 | 97 | 98 | Windows 99 | true 100 | false 101 | 102 | 103 | 104 | 105 | Level3 106 | true 107 | true 108 | true 109 | WIN32;NDEBUG;SHADERTOGGLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 110 | true 111 | NotUsing 112 | $(SolutionDir)Include;%(AdditionalIncludeDirectories) 113 | stdcpp20 114 | 115 | 116 | Windows 117 | true 118 | true 119 | true 120 | false 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | _DEBUG;WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions) 128 | true 129 | NotUsing 130 | pch.h 131 | $(SolutionDir)Include;%(AdditionalIncludeDirectories) 132 | stdcpp20 133 | 134 | 135 | Windows 136 | true 137 | false 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Level3 147 | true 148 | true 149 | true 150 | NDEBUG;WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions) 151 | true 152 | NotUsing 153 | $(SolutionDir)Include;%(AdditionalIncludeDirectories) 154 | stdcpp20 155 | 156 | 157 | Windows 158 | true 159 | true 160 | true 161 | false 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/ShaderManager.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | 33 | #include "ShaderManager.h" 34 | 35 | using namespace reshade::api; 36 | 37 | namespace ShaderToggler 38 | { 39 | ShaderManager::ShaderManager(): _activeHuntedShaderHash(0) 40 | { 41 | } 42 | 43 | 44 | void ShaderManager::addHashHandlePair(uint32_t shaderHash, uint64_t pipelineHandle) 45 | { 46 | if(pipelineHandle>0 && shaderHash > 0) 47 | { 48 | std::unique_lock lock(_hashHandlesMutex); 49 | _handleToShaderHash[pipelineHandle] = shaderHash; 50 | _shaderHashes.emplace(shaderHash); 51 | } 52 | } 53 | 54 | 55 | void ShaderManager::removeHandle(uint64_t handle) 56 | { 57 | std::unique_lock ulock(_hashHandlesMutex); 58 | if(_handleToShaderHash.count(handle)==1) 59 | { 60 | const auto it = _handleToShaderHash.find(handle); 61 | const auto shaderHash = it->second; 62 | _handleToShaderHash.erase(handle); 63 | _collectedActiveShaderHashes.erase(shaderHash); 64 | _shaderHashes.erase(shaderHash); 65 | } 66 | } 67 | 68 | 69 | void ShaderManager::startHuntingMode(const std::unordered_set currentMarkedHashes) 70 | { 71 | // copy the currently marked hashes (from the active group) to the set of marked hashes. 72 | { 73 | std::unique_lock lock(_markedShaderHashMutex); 74 | _markedShaderHashes.clear(); 75 | for(const auto hash : currentMarkedHashes) 76 | { 77 | _markedShaderHashes.emplace(hash); 78 | } 79 | } 80 | 81 | // switch on hunting mode 82 | _isInHuntingMode = true; 83 | _activeHuntedShaderIndex = -1; 84 | _activeHuntedShaderHash = 0; 85 | { 86 | std::unique_lock lock(_collectedActiveHandlesMutex); 87 | _collectedActiveShaderHashes.clear(); // clear it so we start with a clean slate 88 | } 89 | } 90 | 91 | 92 | void ShaderManager::stopHuntingMode() 93 | { 94 | _isInHuntingMode = false; 95 | _activeHuntedShaderIndex = -1; 96 | _activeHuntedShaderHash = 0; 97 | { 98 | std::unique_lock lock(_markedShaderHashMutex); 99 | _markedShaderHashes.clear(); 100 | } 101 | } 102 | 103 | 104 | void ShaderManager::setActiveHuntedShaderHandle() 105 | { 106 | if(_activeHuntedShaderIndex<0 || _collectedActiveShaderHashes.size()<=0 || _activeHuntedShaderIndex >= _collectedActiveShaderHashes.size()) 107 | { 108 | _activeHuntedShaderHash = 0; 109 | return; 110 | } 111 | 112 | // no lock needed, collecting phase is over 113 | auto it = _collectedActiveShaderHashes.begin(); 114 | std::advance(it, _activeHuntedShaderIndex); 115 | _activeHuntedShaderHash = *it; 116 | } 117 | 118 | 119 | void ShaderManager::huntNextShader(bool ctrlPressed) 120 | { 121 | if(!_isInHuntingMode) 122 | { 123 | return; 124 | } 125 | if(_collectedActiveShaderHashes.size()<=0) 126 | { 127 | return; 128 | } 129 | if(ctrlPressed) 130 | { 131 | if(_markedShaderHashes.size()==0 || (_markedShaderHashes.size() == 1 && _markedShaderHashes.count(_activeHuntedShaderHash)==1)) 132 | { 133 | // optimization: if the current active shader is part of marked shader hashes and there is 134 | // just 1 marked, then we can also stop. We then don't need to do anything so we can return 135 | // also if there are no marked shaders, we won't find a next, so return now too. 136 | return; 137 | } 138 | 139 | // we have marked shaders, find the next one in collected active shader hashes that's part of this set. 140 | auto it = _collectedActiveShaderHashes.begin(); 141 | int index = _activeHuntedShaderIndex + 1; 142 | std::advance(it, index); 143 | bool foundHash = false; 144 | uint32_t hash = 0; 145 | while(index!=_activeHuntedShaderIndex) 146 | { 147 | if(it == _collectedActiveShaderHashes.end()) 148 | { 149 | it = _collectedActiveShaderHashes.begin(); 150 | index = 0; 151 | } 152 | hash = *it; 153 | if(_markedShaderHashes.count(hash) == 1) 154 | { 155 | // found one 156 | foundHash = true; 157 | break; 158 | } 159 | ++it; 160 | index++; 161 | } 162 | if(foundHash) 163 | { 164 | _activeHuntedShaderIndex = index; 165 | _activeHuntedShaderHash = hash; 166 | } 167 | // always done 168 | return; 169 | } 170 | if(_activeHuntedShaderIndex < _collectedActiveShaderHashes.size() - 1) 171 | { 172 | _activeHuntedShaderIndex++; 173 | } 174 | else 175 | { 176 | _activeHuntedShaderIndex = 0; 177 | } 178 | setActiveHuntedShaderHandle(); 179 | } 180 | 181 | 182 | void ShaderManager::huntPreviousShader(bool ctrlPressed) 183 | { 184 | if(!_isInHuntingMode) 185 | { 186 | return; 187 | } 188 | if(_collectedActiveShaderHashes.size()<=0) 189 | { 190 | return; 191 | } 192 | if(ctrlPressed) 193 | { 194 | if(_markedShaderHashes.size() == 0 || (_markedShaderHashes.size() == 1 && _markedShaderHashes.count(_activeHuntedShaderHash) == 1)) 195 | { 196 | // optimization: if the current active shader is part of marked shader hashes and there is 197 | // just 1 marked, then we can also stop. We then don't need to do anything so we can return 198 | // also if there are no marked shaders, we won't find a next, so return now too. 199 | return; 200 | } 201 | // we have marked shaders, find the next one in collected active shader hashes that's part of this set. 202 | auto it = _collectedActiveShaderHashes.begin(); 203 | int index = _activeHuntedShaderIndex - 1; 204 | std::advance(it, index); 205 | bool foundHash = false; 206 | uint32_t hash = 0; 207 | while(index != _activeHuntedShaderIndex) 208 | { 209 | if(it == _collectedActiveShaderHashes.begin()) 210 | { 211 | it = _collectedActiveShaderHashes.end(); 212 | --it; 213 | index = _collectedActiveShaderHashes.size() - 1; 214 | } 215 | hash = *it; 216 | if(_markedShaderHashes.count(hash) == 1) 217 | { 218 | // found one 219 | foundHash = true; 220 | break; 221 | } 222 | --it; 223 | index--; 224 | } 225 | if(foundHash) 226 | { 227 | _activeHuntedShaderIndex = index; 228 | _activeHuntedShaderHash = hash; 229 | } 230 | // always done 231 | return; 232 | 233 | } 234 | if(_activeHuntedShaderIndex <= 0) 235 | { 236 | _activeHuntedShaderIndex = _collectedActiveShaderHashes.size() - 1; 237 | } 238 | else 239 | { 240 | --_activeHuntedShaderIndex; 241 | } 242 | setActiveHuntedShaderHandle(); 243 | } 244 | 245 | 246 | bool ShaderManager::isBlockedShader(uint32_t shaderHash) 247 | { 248 | bool toReturn = false; 249 | if(_isInHuntingMode) 250 | { 251 | // get the shader hash bound to this pipeline handle 252 | toReturn |= shaderHash<=0 ? false : _activeHuntedShaderHash == shaderHash; 253 | } 254 | if(_hideMarkedShaders) 255 | { 256 | // check if the shader hash is part of the toggle group 257 | toReturn |= _markedShaderHashes.count(shaderHash)==1; 258 | } 259 | 260 | return toReturn; 261 | } 262 | 263 | 264 | void ShaderManager::addActivePipelineHandle(uint64_t handle) 265 | { 266 | // get the shader hash bound to this pipeline handle 267 | const auto shaderHash = getShaderHash(handle); 268 | if(shaderHash>0) 269 | { 270 | std::unique_lock lock(_collectedActiveHandlesMutex); 271 | _collectedActiveShaderHashes.emplace(shaderHash); 272 | } 273 | } 274 | 275 | 276 | void ShaderManager::toggleMarkOnHuntedShader() 277 | { 278 | if(_activeHuntedShaderHash<=0) 279 | { 280 | return; 281 | } 282 | std::unique_lock lock(_markedShaderHashMutex); 283 | if(_markedShaderHashes.count(_activeHuntedShaderHash)==1) 284 | { 285 | // remove it 286 | _markedShaderHashes.erase(_activeHuntedShaderHash); 287 | } 288 | else 289 | { 290 | // add it 291 | _markedShaderHashes.emplace(_activeHuntedShaderHash); 292 | } 293 | } 294 | 295 | 296 | uint32_t ShaderManager::getShaderHash(uint64_t handle) 297 | { 298 | if(_handleToShaderHash.count(handle)!=1) 299 | { 300 | return 0; 301 | } 302 | return _handleToShaderHash.at(handle); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/Include/reshade.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Patrick Mours 3 | * License: https://github.com/crosire/reshade#license 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "reshade_events.hpp" 9 | #include "reshade_overlay.hpp" 10 | #include 11 | #include 12 | 13 | #define RESHADE_API_VERSION 2 14 | 15 | // Use the kernel32 variant of module enumeration functions so it can be safely called from 'DllMain' 16 | extern "C" BOOL WINAPI K32EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded); 17 | 18 | namespace reshade 19 | { 20 | namespace internal 21 | { 22 | /// 23 | /// Gets the handle to the ReShade module. 24 | /// 25 | inline HMODULE &get_reshade_module_handle() 26 | { 27 | static HMODULE handle = nullptr; 28 | if (handle == nullptr) 29 | { 30 | HMODULE modules[1024]; DWORD num = 0; 31 | if (K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &num)) 32 | { 33 | if (num > sizeof(modules)) 34 | num = sizeof(modules); 35 | 36 | for (DWORD i = 0; i < num / sizeof(HMODULE); ++i) 37 | { 38 | if (GetProcAddress(modules[i], "ReShadeRegisterAddon") && 39 | GetProcAddress(modules[i], "ReShadeUnregisterAddon")) 40 | { 41 | handle = modules[i]; 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | return handle; 48 | } 49 | 50 | /// 51 | /// Gets the handle to the current add-on module. 52 | /// 53 | inline HMODULE &get_current_module_handle() 54 | { 55 | static HMODULE handle = nullptr; 56 | return handle; 57 | } 58 | } 59 | 60 | /// 61 | /// Writes a message to ReShade's log. 62 | /// 63 | /// Severity level (1 = error, 2 = warning, 3 = info, 4 = debug). 64 | /// A null-terminated message string. 65 | inline void log_message(int level, const char *message) 66 | { 67 | static const auto func = reinterpret_cast( 68 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeLogMessage")); 69 | func(internal::get_current_module_handle(), level, message); 70 | } 71 | 72 | /// 73 | /// Gets a value from one of ReShade's config files. 74 | /// 75 | /// Optional effect runtime to use the config file from, or to use the global config file. 76 | /// Name of the config section. 77 | /// Name of the config value. 78 | /// Pointer to a string buffer that is filled with the config value. 79 | /// Pointer to an integer that contains the size of the string buffer and upon completion is set to the actual length of the string. 80 | /// if the specified config value exists, otherwise. 81 | inline bool config_get_value(api::effect_runtime *runtime, const char *section, const char *key, char *value, size_t *length) 82 | { 83 | static const auto func = reinterpret_cast( 84 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeGetConfigValue")); 85 | return func(internal::get_current_module_handle(), runtime, section, key, value, length); 86 | } 87 | template 88 | inline bool config_get_value(api::effect_runtime *runtime, const char *section, const char *key, T &value) 89 | { 90 | char value_string[32] = ""; size_t value_length = sizeof(value_string) - 1; 91 | if (!config_get_value(runtime, section, key, value_string, &value_length)) 92 | return false; 93 | return std::from_chars(value_string, value_string + value_length, value).ec == std::errc {}; 94 | } 95 | template <> 96 | inline bool config_get_value(api::effect_runtime *runtime, const char *section, const char *key, bool &value) 97 | { 98 | int value_int = 0; 99 | if (!config_get_value(runtime, section, key, value_int)) 100 | return false; 101 | value = value_int != 0; 102 | return true; 103 | } 104 | 105 | /// 106 | /// Sets and saves a value in one of ReShade's config files. 107 | /// 108 | /// Optional effect runtime to use the config file from, or to use the global config file. 109 | /// Name of the config section. 110 | /// Name of the config value. 111 | /// Config value to set. 112 | inline void config_set_value(api::effect_runtime *runtime, const char *section, const char *key, const char *value) 113 | { 114 | static const auto func = reinterpret_cast( 115 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeSetConfigValue")); 116 | func(internal::get_current_module_handle(), runtime, section, key, value); 117 | } 118 | template 119 | inline void config_set_value(api::effect_runtime *runtime, const char *section, const char *key, const T &value) 120 | { 121 | char value_string[32] = ""; 122 | std::to_chars(value_string, value_string + sizeof(value_string) - 1, value); 123 | config_set_value(runtime, section, key, static_cast(value_string)); 124 | } 125 | template <> 126 | inline void config_set_value(api::effect_runtime *runtime, const char *section, const char *key, const bool &value) 127 | { 128 | config_set_value(runtime, section, key, value ? 1 : 0); 129 | } 130 | 131 | /// 132 | /// Registers this module as an add-on with ReShade. 133 | /// Call this in 'DllMain' during process attach, before any of the other API functions! 134 | /// 135 | /// Handle of the current add-on module. 136 | inline bool register_addon(HMODULE module) 137 | { 138 | internal::get_current_module_handle() = module; 139 | 140 | const HMODULE reshade_module = internal::get_reshade_module_handle(); 141 | if (reshade_module == nullptr) 142 | return false; 143 | 144 | // Check that the ReShade module supports the used API 145 | const auto func = reinterpret_cast( 146 | GetProcAddress(reshade_module, "ReShadeRegisterAddon")); 147 | if (!func(module, RESHADE_API_VERSION)) 148 | return false; 149 | 150 | #if defined(IMGUI_VERSION_NUM) 151 | // Check that the ReShade module was built with Dear ImGui support and supports the used version 152 | const auto imgui_func = reinterpret_cast( 153 | GetProcAddress(reshade_module, "ReShadeGetImGuiFunctionTable")); 154 | if (imgui_func == nullptr || !(imgui_function_table_instance() = imgui_func(IMGUI_VERSION_NUM))) 155 | return false; 156 | #endif 157 | 158 | return true; 159 | } 160 | /// 161 | /// Unregisters this module. Call this in 'DllMain' during process detach, after any of the other API functions. 162 | /// 163 | /// Handle of the current add-on module. 164 | inline void unregister_addon(HMODULE module) 165 | { 166 | const HMODULE reshade_module = internal::get_reshade_module_handle(); 167 | if (reshade_module == nullptr) 168 | return; 169 | 170 | const auto func = reinterpret_cast( 171 | GetProcAddress(reshade_module, "ReShadeUnregisterAddon")); 172 | func(module); 173 | } 174 | 175 | /// 176 | /// Registers a callback for the specified event (via template) with ReShade. 177 | /// The callback function is then called whenever the application performs a task associated with this event (see also the enumeration). 178 | /// 179 | /// Pointer to the callback function. 180 | template 181 | inline void register_event(typename reshade::addon_event_traits::decl callback) 182 | { 183 | static const auto func = reinterpret_cast( 184 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeRegisterEvent")); 185 | func(ev, static_cast(callback)); 186 | } 187 | /// 188 | /// Unregisters a callback for the specified event (via template) that was previously registered via . 189 | /// 190 | /// Pointer to the callback function. 191 | template 192 | inline void unregister_event(typename reshade::addon_event_traits::decl callback) 193 | { 194 | static const auto func = reinterpret_cast( 195 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeUnregisterEvent")); 196 | func(ev, static_cast(callback)); 197 | } 198 | 199 | /// 200 | /// Registers an overlay with ReShade. 201 | /// The callback function is then called when the overlay is visible and allows adding Dear ImGui widgets for user interaction. 202 | /// 203 | /// Null-terminated title string, or to register a settings overlay for this add-on. 204 | /// Pointer to the callback function. 205 | inline void register_overlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime)) 206 | { 207 | static const auto func = reinterpret_cast( 208 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeRegisterOverlay")); 209 | func(title, callback); 210 | } 211 | /// 212 | /// Unregisters an overlay that was previously registered via . 213 | /// 214 | /// Null-terminated title string. 215 | /// Pointer to the callback function. 216 | inline void unregister_overlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime)) 217 | { 218 | static const auto func = reinterpret_cast( 219 | GetProcAddress(internal::get_reshade_module_handle(), "ReShadeUnregisterOverlay")); 220 | func(title, callback); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/CDataFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // CDataFile Class Implementation 3 | // 4 | // The purpose of this class is to provide a simple, full featured means to 5 | // store persistent data to a text file. It uses a simple key/value paradigm 6 | // to achieve this. The class can read/write to standard Windows .ini files, 7 | // and yet does not rely on any windows specific calls. It should work as 8 | // well in a linux environment (with some minor adjustments) as it does in 9 | // a Windows one. 10 | // 11 | // Written July, 2002 by Gary McNickle 12 | // If you use this class in your application, credit would be appreciated. 13 | // 14 | #pragma once 15 | 16 | #include "stdafx.h" 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | 23 | // Globally defined structures, defines, & types 24 | ////////////////////////////////////////////////////////////////////////////////// 25 | 26 | // AUTOCREATE_SECTIONS 27 | // When set, this define will cause SetValue() to create a new section, if 28 | // the requested section does not allready exist. 29 | #define AUTOCREATE_SECTIONS (1L<<1) 30 | 31 | // AUOTCREATE_KEYS 32 | // When set, this define causes SetValue() to create a new key, if the 33 | // requested key does not allready exist. 34 | #define AUTOCREATE_KEYS (1L<<2) 35 | 36 | // MAX_BUFFER_LEN 37 | // Used simply as a max size of some internal buffers. Determines the maximum 38 | // length of a line that will be read from or written to the file or the 39 | // report output. 40 | #define MAX_BUFFER_LEN 512 41 | 42 | 43 | // eDebugLevel 44 | // Used by our Report function to classify levels of reporting and severity 45 | // of report. 46 | enum e_DebugLevel 47 | { 48 | // detailed programmatic informational messages used as an aid in 49 | // troubleshooting problems by programmers 50 | E_DEBUG = 0, 51 | // brief informative messages to use as an aid in troubleshooting 52 | // problems by production support and programmers 53 | E_INFO, 54 | // messages intended to notify help desk, production support and 55 | // programmers of possible issues with respect to the running application 56 | E_WARN, 57 | // messages that detail a programmatic error, these are typically 58 | // messages intended for help desk, production support, programmers and 59 | // occasionally users 60 | E_ERROR, 61 | // severe messages that are programmatic violations that will usually 62 | // result in application failure. These messages are intended for help 63 | // desk, production support, programmers and possibly users 64 | E_FATAL, 65 | // notice that all processing should be stopped immediately after the 66 | // log is written. 67 | E_CRITICAL 68 | }; 69 | 70 | 71 | typedef std::string t_Str; 72 | 73 | // CommentIndicators 74 | // This constant contains the characters that we check for to determine if a 75 | // line is a comment or not. Note that the first character in this constant is 76 | // the one used when writing comments to disk (if the comment does not allready 77 | // contain an indicator) 78 | const t_Str CommentIndicators = t_Str(";#"); 79 | 80 | // EqualIndicators 81 | // This constant contains the characters that we check against to determine if 82 | // a line contains an assignment ( key = value ) 83 | // Note that changing these from their defaults ("=:") WILL affect the 84 | // ability of CDataFile to read/write to .ini files. Also, note that the 85 | // first character in this constant is the one that is used when writing the 86 | // values to the file. (EqualIndicators[0]) 87 | const t_Str EqualIndicators = t_Str("=:"); 88 | 89 | // WhiteSpace 90 | // This constant contains the characters that the Trim() function removes from 91 | // the head and tail of strings. 92 | const t_Str WhiteSpace = t_Str(" \t\n\r"); 93 | 94 | // st_key 95 | // This structure stores the definition of a key. A key is a named identifier 96 | // that is associated with a value. It may or may not have a comment. All comments 97 | // must PRECEDE the key on the line in the config file. 98 | typedef struct st_key 99 | { 100 | t_Str szKey; 101 | t_Str szValue; 102 | t_Str szComment; 103 | 104 | st_key() 105 | { 106 | szKey = t_Str(""); 107 | szValue = t_Str(""); 108 | szComment = t_Str(""); 109 | } 110 | 111 | } t_Key; 112 | 113 | typedef std::vector KeyList; 114 | typedef KeyList::iterator KeyItor; 115 | 116 | // st_section 117 | // This structure stores the definition of a section. A section contains any number 118 | // of keys (see st_keys), and may or may not have a comment. Like keys, all 119 | // comments must precede the section. 120 | typedef struct st_section 121 | { 122 | t_Str szName; 123 | t_Str szComment; 124 | KeyList Keys; 125 | 126 | st_section() 127 | { 128 | szName = t_Str(""); 129 | szComment = t_Str(""); 130 | Keys.clear(); 131 | } 132 | 133 | } t_Section; 134 | 135 | typedef std::vector SectionList; 136 | typedef SectionList::iterator SectionItor; 137 | 138 | 139 | 140 | /// General Purpose Utility Functions /////////////////////////////////////////// 141 | ///////////////////////////////////////////////////////////////////////////////// 142 | void Report(e_DebugLevel DebugLevel, const char *fmt, ...); 143 | t_Str GetNextWord(t_Str& CommandLine); 144 | int CompareNoCase(t_Str str1, t_Str str2); 145 | void Trim(t_Str& szStr); 146 | int WriteLn(fstream& stream, const char* fmt, ...); 147 | 148 | 149 | /// Class Definitions /////////////////////////////////////////////////////////// 150 | ///////////////////////////////////////////////////////////////////////////////// 151 | 152 | 153 | // CDataFile 154 | class CDataFile 155 | { 156 | // Methods 157 | public: 158 | // Constructors & Destructors 159 | ///////////////////////////////////////////////////////////////// 160 | CDataFile(); 161 | CDataFile(t_Str szFileName); 162 | virtual ~CDataFile(); 163 | 164 | // File handling methods 165 | ///////////////////////////////////////////////////////////////// 166 | bool Load(t_Str szFileName); 167 | bool Save(); 168 | 169 | // Data handling methods 170 | ///////////////////////////////////////////////////////////////// 171 | 172 | // GetValue: Our default access method. Returns the raw t_Str value 173 | // Note that this returns keys specific to the given section only. 174 | t_Str GetValue(t_Str szKey, t_Str szSection = t_Str("")); 175 | // GetString: Returns the value as a t_Str 176 | t_Str GetString(t_Str szKey, t_Str szSection = t_Str("")); 177 | // GetFloat: Return the value as a float 178 | float GetFloat(t_Str szKey, t_Str szSection = t_Str("")); 179 | // GetInt: Return the value as an int 180 | int GetInt(t_Str szKey, t_Str szSection = t_Str("")); 181 | // GetUInt: Return the value as an int 182 | uint32_t GetUInt(t_Str szKey, t_Str szSection = t_Str("")); 183 | // GetBool: Return the value as a bool 184 | bool GetBool(t_Str szKey, t_Str szSection = t_Str("")); 185 | 186 | // SetValue: Sets the value of a given key. Will create the 187 | // key if it is not found and AUTOCREATE_KEYS is active. 188 | bool SetValue(t_Str szKey, t_Str szValue, 189 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 190 | 191 | // SetFloat: Sets the value of a given key. Will create the 192 | // key if it is not found and AUTOCREATE_KEYS is active. 193 | bool SetFloat(t_Str szKey, float fValue, 194 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 195 | 196 | // SetInt: Sets the value of a given key. Will create the 197 | // key if it is not found and AUTOCREATE_KEYS is active. 198 | bool SetInt(t_Str szKey, int nValue, 199 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 200 | 201 | // SetUInt: Sets the value of a given key. Will create the 202 | // key if it is not found and AUTOCREATE_KEYS is active. 203 | bool SetUInt(t_Str szKey, uint32_t nValue, 204 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 205 | 206 | // SetBool: Sets the value of a given key. Will create the 207 | // key if it is not found and AUTOCREATE_KEYS is active. 208 | bool SetBool(t_Str szKey, bool bValue, 209 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 210 | 211 | // Sets the comment for a given key. 212 | bool SetKeyComment(t_Str szKey, t_Str szComment, t_Str szSection = t_Str("")); 213 | 214 | // Sets the comment for a given section 215 | bool SetSectionComment(t_Str szSection, t_Str szComment); 216 | 217 | // DeleteKey: Deletes a given key from a specific section 218 | bool DeleteKey(t_Str szKey, t_Str szFromSection = t_Str("")); 219 | 220 | // DeleteSection: Deletes a given section. 221 | bool DeleteSection(t_Str szSection); 222 | 223 | // Key/Section handling methods 224 | ///////////////////////////////////////////////////////////////// 225 | 226 | // CreateKey: Creates a new key in the requested section. The 227 | // Section will be created if it does not exist and the 228 | // AUTOCREATE_SECTIONS bit is set. 229 | bool CreateKey(t_Str szKey, t_Str szValue, 230 | t_Str szComment = t_Str(""), t_Str szSection = t_Str("")); 231 | // CreateSection: Creates the new section if it does not allready 232 | // exist. Section is created with no keys. 233 | bool CreateSection(t_Str szSection, t_Str szComment = t_Str("")); 234 | // CreateSection: Creates the new section if it does not allready 235 | // exist, and copies the keys passed into it into the new section. 236 | bool CreateSection(t_Str szSection, t_Str szComment, KeyList Keys); 237 | 238 | // Utility Methods 239 | ///////////////////////////////////////////////////////////////// 240 | // SectionCount: Returns the number of valid sections in the database. 241 | int SectionCount(); 242 | // KeyCount: Returns the total number of keys, across all sections. 243 | int KeyCount(); 244 | // Clear: Initializes the member variables to their default states 245 | void Clear(); 246 | // SetFileName: For use when creating the object by hand 247 | // initializes the file name so that it can be later saved. 248 | void SetFileName(t_Str szFileName); 249 | // CommentStr 250 | // Parses a string into a proper comment token/comment. 251 | t_Str CommentStr(t_Str szComment); 252 | 253 | 254 | protected: 255 | // Note: I've tried to insulate the end user from the internal 256 | // data structures as much as possible. This is by design. Doing 257 | // so has caused some performance issues (multiple calls to a 258 | // GetSection() function that would otherwise not be necessary,etc). 259 | // But, I believe that doing so will provide a safer, more stable 260 | // environment. You'll notice that nothing returns a reference, 261 | // to modify the data values, you have to call member functions. 262 | // think carefully before changing this. 263 | 264 | // GetKey: Returns the requested key (if found) from the requested 265 | // Section. Returns NULL otherwise. 266 | t_Key* GetKey(t_Str szKey, t_Str szSection); 267 | // GetSection: Returns the requested section (if found), NULL otherwise. 268 | t_Section* GetSection(t_Str szSection); 269 | 270 | 271 | // Data 272 | public: 273 | long m_Flags; // Our settings flags. 274 | 275 | protected: 276 | SectionList m_Sections; // Our list of sections 277 | t_Str m_szFileName; // The filename to write to 278 | bool m_bDirty; // Tracks whether or not data has changed. 279 | }; 280 | 281 | 282 | -------------------------------------------------------------------------------- /src/Include/reshade_api_format.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Patrick Mours 3 | * License: https://github.com/crosire/reshade#license 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace reshade::api 12 | { 13 | /// 14 | /// The available data and texture formats. 15 | /// This is mostly compatible with 'DXGI_FORMAT'. 16 | /// 17 | enum class format : uint32_t 18 | { 19 | unknown = 0, 20 | 21 | // Color formats 22 | 23 | r1_unorm = 66, 24 | l8_unorm = 0x3030384C, 25 | a8_unorm = 65, 26 | r8_typeless = 60, 27 | r8_uint = 62, 28 | r8_sint = 64, 29 | r8_unorm = 61, 30 | r8_snorm = 63, 31 | l8a8_unorm = 0x3038414C, 32 | r8g8_typeless = 48, 33 | r8g8_uint = 50, 34 | r8g8_sint = 52, 35 | r8g8_unorm = 49, 36 | r8g8_snorm = 51, 37 | r8g8b8a8_typeless = 27, 38 | r8g8b8a8_uint = 30, 39 | r8g8b8a8_sint = 32, 40 | r8g8b8a8_unorm = 28, 41 | r8g8b8a8_unorm_srgb = 29, 42 | r8g8b8a8_snorm = 31, 43 | r8g8b8x8_typeless = 0x424757B8, 44 | r8g8b8x8_unorm = 0x424757B9, 45 | r8g8b8x8_unorm_srgb = 0x424757BA, 46 | b8g8r8a8_typeless = 90, 47 | b8g8r8a8_unorm = 87, 48 | b8g8r8a8_unorm_srgb = 91, 49 | b8g8r8x8_typeless = 92, 50 | b8g8r8x8_unorm = 88, 51 | b8g8r8x8_unorm_srgb = 93, 52 | r10g10b10a2_typeless = 23, 53 | r10g10b10a2_uint = 25, 54 | r10g10b10a2_unorm = 24, 55 | r10g10b10a2_xr_bias = 89, 56 | b10g10r10a2_typeless = 0x42475330, 57 | b10g10r10a2_uint = 0x42475332, 58 | b10g10r10a2_unorm = 0x42475331, 59 | l16_unorm = 0x3036314C, 60 | r16_typeless = 53, 61 | r16_uint = 57, 62 | r16_sint = 59, 63 | r16_float = 54, 64 | r16_unorm = 56, 65 | r16_snorm = 58, 66 | l16a16_unorm = 0x3631414C, 67 | r16g16_typeless = 33, 68 | r16g16_uint = 36, 69 | r16g16_sint = 38, 70 | r16g16_float = 34, 71 | r16g16_unorm = 35, 72 | r16g16_snorm = 37, 73 | r16g16b16a16_typeless = 9, 74 | r16g16b16a16_uint = 12, 75 | r16g16b16a16_sint = 14, 76 | r16g16b16a16_float = 10, 77 | r16g16b16a16_unorm = 11, 78 | r16g16b16a16_snorm = 13, 79 | r32_typeless = 39, 80 | r32_uint = 42, 81 | r32_sint = 43, 82 | r32_float = 41, 83 | r32g32_typeless = 15, 84 | r32g32_uint = 17, 85 | r32g32_sint = 18, 86 | r32g32_float = 16, 87 | r32g32b32_typeless = 5, 88 | r32g32b32_uint = 7, 89 | r32g32b32_sint = 8, 90 | r32g32b32_float = 6, 91 | r32g32b32a32_typeless = 1, 92 | r32g32b32a32_uint = 3, 93 | r32g32b32a32_sint = 4, 94 | r32g32b32a32_float = 2, 95 | r9g9b9e5 = 67, 96 | r11g11b10_float = 26, 97 | b5g6r5_unorm = 85, 98 | b5g5r5a1_unorm = 86, 99 | b5g5r5x1_unorm = 0x424757B5, 100 | b4g4r4a4_unorm = 115, 101 | 102 | // Depth-stencil formats 103 | 104 | s8_uint = 0x30303853, 105 | d16_unorm = 55, 106 | d16_unorm_s8_uint = 0x38363144, 107 | d24_unorm_x8_uint = 0x38343244, 108 | d24_unorm_s8_uint = 45, 109 | d32_float = 40, 110 | d32_float_s8_uint = 20, 111 | 112 | r32_g8_typeless = 19, 113 | r32_float_x8_uint = 21, 114 | x32_float_g8_uint = 22, 115 | r24_g8_typeless = 44, 116 | r24_unorm_x8_uint = 46, 117 | x24_unorm_g8_uint = 47, 118 | 119 | // Compressed data formats 120 | 121 | bc1_typeless = 70, 122 | bc1_unorm = 71, 123 | bc1_unorm_srgb = 72, 124 | bc2_typeless = 73, 125 | bc2_unorm = 74, 126 | bc2_unorm_srgb = 75, 127 | bc3_typeless = 76, 128 | bc3_unorm = 77, 129 | bc3_unorm_srgb = 78, 130 | bc4_typeless = 79, 131 | bc4_unorm = 80, 132 | bc4_snorm = 81, 133 | bc5_typeless = 82, 134 | bc5_unorm = 83, 135 | bc5_snorm = 84, 136 | bc6h_typeless = 94, 137 | bc6h_ufloat = 95, 138 | bc6h_sfloat = 96, 139 | bc7_typeless = 97, 140 | bc7_unorm = 98, 141 | bc7_unorm_srgb = 99, 142 | 143 | // Video formats 144 | 145 | r8g8_b8g8_unorm = 68, 146 | g8r8_g8b8_unorm = 69, 147 | 148 | // Special purpose formats 149 | 150 | intz = 0x5A544E49, 151 | }; 152 | 153 | /// 154 | /// Converts the specified format to its equivalent typeless variant. 155 | /// 156 | /// The format to convert. 157 | inline format format_to_typeless(format value) 158 | { 159 | switch (value) 160 | { 161 | case format::l8_unorm: 162 | case format::r8_typeless: 163 | case format::r8_uint: 164 | case format::r8_sint: 165 | case format::r8_unorm: 166 | case format::r8_snorm: 167 | return format::r8_typeless; 168 | case format::l8a8_unorm: 169 | case format::r8g8_typeless: 170 | case format::r8g8_uint: 171 | case format::r8g8_sint: 172 | case format::r8g8_unorm: 173 | case format::r8g8_snorm: 174 | return format::r8g8_typeless; 175 | case format::r8g8b8a8_typeless: 176 | case format::r8g8b8a8_uint: 177 | case format::r8g8b8a8_sint: 178 | case format::r8g8b8a8_unorm: 179 | case format::r8g8b8a8_unorm_srgb: 180 | case format::r8g8b8a8_snorm: 181 | return format::r8g8b8a8_typeless; 182 | case format::b8g8r8a8_typeless: 183 | case format::b8g8r8a8_unorm: 184 | case format::b8g8r8a8_unorm_srgb: 185 | return format::b8g8r8a8_typeless; 186 | case format::b8g8r8x8_typeless: 187 | case format::b8g8r8x8_unorm: 188 | case format::b8g8r8x8_unorm_srgb: 189 | return format::b8g8r8x8_typeless; 190 | case format::r10g10b10a2_typeless: 191 | case format::r10g10b10a2_uint: 192 | case format::r10g10b10a2_unorm: 193 | case format::r10g10b10a2_xr_bias: 194 | return format::r10g10b10a2_typeless; 195 | case format::b10g10r10a2_typeless: 196 | case format::b10g10r10a2_uint: 197 | case format::b10g10r10a2_unorm: 198 | return format::b10g10r10a2_typeless; 199 | case format::l16_unorm: 200 | case format::d16_unorm: 201 | case format::r16_typeless: 202 | case format::r16_uint: 203 | case format::r16_sint: 204 | case format::r16_float: 205 | case format::r16_unorm: 206 | case format::r16_snorm: 207 | return format::r16_typeless; 208 | case format::l16a16_unorm: 209 | case format::r16g16_typeless: 210 | case format::r16g16_uint: 211 | case format::r16g16_sint: 212 | case format::r16g16_float: 213 | case format::r16g16_unorm: 214 | case format::r16g16_snorm: 215 | return format::r16g16_typeless; 216 | case format::r16g16b16a16_typeless: 217 | case format::r16g16b16a16_uint: 218 | case format::r16g16b16a16_sint: 219 | case format::r16g16b16a16_float: 220 | case format::r16g16b16a16_unorm: 221 | case format::r16g16b16a16_snorm: 222 | return format::r16g16b16a16_typeless; 223 | case format::d32_float: 224 | case format::r32_typeless: 225 | case format::r32_uint: 226 | case format::r32_sint: 227 | case format::r32_float: 228 | return format::r32_typeless; 229 | case format::r32g32_typeless: 230 | case format::r32g32_uint: 231 | case format::r32g32_sint: 232 | case format::r32g32_float: 233 | return format::r32g32_typeless; 234 | case format::r32g32b32_typeless: 235 | case format::r32g32b32_uint: 236 | case format::r32g32b32_sint: 237 | case format::r32g32b32_float: 238 | return format::r32g32b32_typeless; 239 | case format::r32g32b32a32_typeless: 240 | case format::r32g32b32a32_uint: 241 | case format::r32g32b32a32_sint: 242 | case format::r32g32b32a32_float: 243 | return format::r32g32b32a32_typeless; 244 | case format::d32_float_s8_uint: 245 | case format::r32_g8_typeless: 246 | case format::r32_float_x8_uint: 247 | case format::x32_float_g8_uint: 248 | return format::r32_g8_typeless; 249 | case format::d24_unorm_s8_uint: // Do not also convert 'd24_unorm_x8_uint' here, to keep it distinguishable from 'd24_unorm_s8_uint' 250 | case format::r24_g8_typeless: 251 | case format::r24_unorm_x8_uint: 252 | case format::x24_unorm_g8_uint: 253 | return format::r24_g8_typeless; 254 | case format::bc1_typeless: 255 | case format::bc1_unorm: 256 | case format::bc1_unorm_srgb: 257 | return format::bc1_typeless; 258 | case format::bc2_typeless: 259 | case format::bc2_unorm: 260 | case format::bc2_unorm_srgb: 261 | return format::bc2_typeless; 262 | case format::bc3_typeless: 263 | case format::bc3_unorm: 264 | case format::bc3_unorm_srgb: 265 | return format::bc2_typeless; 266 | case format::bc4_typeless: 267 | case format::bc4_unorm: 268 | case format::bc4_snorm: 269 | return format::bc4_typeless; 270 | case format::bc5_typeless: 271 | case format::bc5_unorm: 272 | case format::bc5_snorm: 273 | return format::bc5_typeless; 274 | case format::bc6h_typeless: 275 | case format::bc6h_ufloat: 276 | case format::bc6h_sfloat: 277 | return format::bc6h_typeless; 278 | case format::bc7_typeless: 279 | case format::bc7_unorm: 280 | case format::bc7_unorm_srgb: 281 | return format::bc7_typeless; 282 | default: 283 | return value; 284 | } 285 | } 286 | 287 | /// 288 | /// Converts the specified format to its equivalent typed variant ("unorm" or "float"). 289 | /// 290 | /// The format to convert. 291 | /// Set to 1 to get sRGB variant, 0 to get linear variant and -1 to preserve existing one. 292 | inline format format_to_default_typed(format value, int srgb_variant = -1) 293 | { 294 | switch (value) 295 | { 296 | case format::r8_typeless: 297 | return format::r8_unorm; 298 | case format::r8g8_typeless: 299 | return format::r8g8_unorm; 300 | case format::r8g8b8a8_typeless: 301 | case format::r8g8b8a8_unorm: 302 | return srgb_variant == 1 ? format::r8g8b8a8_unorm_srgb : format::r8g8b8a8_unorm; 303 | case format::r8g8b8a8_unorm_srgb: 304 | return srgb_variant != 0 ? format::r8g8b8a8_unorm_srgb : format::r8g8b8a8_unorm; 305 | case format::r8g8b8x8_typeless: 306 | case format::r8g8b8x8_unorm: 307 | return srgb_variant == 1 ? format::r8g8b8x8_unorm_srgb : format::r8g8b8x8_unorm; 308 | case format::r8g8b8x8_unorm_srgb: 309 | return srgb_variant != 0 ? format::r8g8b8x8_unorm_srgb : format::r8g8b8x8_unorm; 310 | case format::b8g8r8a8_typeless: 311 | case format::b8g8r8a8_unorm: 312 | return srgb_variant == 1 ? format::b8g8r8a8_unorm_srgb : format::b8g8r8a8_unorm; 313 | case format::b8g8r8a8_unorm_srgb: 314 | return srgb_variant != 0 ? format::b8g8r8a8_unorm_srgb : format::b8g8r8a8_unorm; 315 | case format::b8g8r8x8_typeless: 316 | case format::b8g8r8x8_unorm: 317 | return srgb_variant == 1 ? format::b8g8r8x8_unorm_srgb : format::b8g8r8x8_unorm; 318 | case format::b8g8r8x8_unorm_srgb: 319 | return srgb_variant != 0 ? format::b8g8r8x8_unorm_srgb : format::b8g8r8x8_unorm; 320 | case format::r10g10b10a2_typeless: 321 | return format::r10g10b10a2_unorm; 322 | case format::b10g10r10a2_typeless: 323 | return format::b10g10r10a2_unorm; 324 | case format::d16_unorm: 325 | case format::r16_typeless: 326 | return format::r16_unorm; 327 | case format::r16g16_typeless: 328 | return format::r16g16_unorm; 329 | case format::r16g16b16a16_typeless: 330 | return format::r16g16b16a16_unorm; 331 | case format::d32_float: 332 | case format::r32_typeless: 333 | return format::r32_float; 334 | case format::r32g32_typeless: 335 | return format::r32g32_float; 336 | case format::r32g32b32_typeless: 337 | return format::r32g32b32_float; 338 | case format::r32g32b32a32_typeless: 339 | return format::r32g32b32a32_float; 340 | case format::d32_float_s8_uint: 341 | case format::r32_g8_typeless: 342 | return format::r32_float_x8_uint; 343 | case format::d24_unorm_s8_uint: // Do not also convert 'd24_unorm_x8_uint' here, to keep it distinguishable from 'd24_unorm_s8_uint' 344 | case format::r24_g8_typeless: 345 | return format::r24_unorm_x8_uint; 346 | case format::bc1_typeless: 347 | case format::bc1_unorm: 348 | return srgb_variant == 1 ? format::bc1_unorm_srgb : format::bc1_unorm; 349 | case format::bc1_unorm_srgb: 350 | return srgb_variant != 0 ? format::bc1_unorm_srgb : format::bc1_unorm; 351 | case format::bc2_typeless: 352 | case format::bc2_unorm: 353 | return srgb_variant == 1 ? format::bc2_unorm_srgb : format::bc2_unorm; 354 | case format::bc2_unorm_srgb: 355 | return srgb_variant != 0 ? format::bc2_unorm_srgb : format::bc2_unorm; 356 | case format::bc3_typeless: 357 | case format::bc3_unorm: 358 | return srgb_variant == 1 ? format::bc3_unorm_srgb : format::bc3_unorm; 359 | case format::bc3_unorm_srgb: 360 | return srgb_variant != 0 ? format::bc3_unorm_srgb : format::bc3_unorm; 361 | case format::bc4_typeless: 362 | return format::bc4_unorm; 363 | case format::bc5_typeless: 364 | return format::bc5_unorm; 365 | case format::bc6h_typeless: 366 | return format::bc6h_ufloat; 367 | case format::bc7_typeless: 368 | case format::bc7_unorm: 369 | return srgb_variant == 1 ? format::bc7_unorm_srgb : format::bc7_unorm; 370 | case format::bc7_unorm_srgb: 371 | return srgb_variant != 0 ? format::bc7_unorm_srgb : format::bc7_unorm; 372 | default: 373 | return value; 374 | } 375 | } 376 | 377 | /// 378 | /// Converts the specified format to its equivalent depth-stencil variant. 379 | /// 380 | /// The format to convert. 381 | inline format format_to_depth_stencil_typed(format value) 382 | { 383 | switch (value) 384 | { 385 | case format::r16_typeless: 386 | case format::r16_unorm: 387 | return format::d16_unorm; 388 | case format::r32_typeless: 389 | case format::r32_float: 390 | return format::d32_float; 391 | case format::r32_g8_typeless: 392 | case format::r32_float_x8_uint: 393 | case format::x32_float_g8_uint: 394 | return format::d32_float_s8_uint; 395 | case format::r24_g8_typeless: 396 | case format::r24_unorm_x8_uint: 397 | case format::x24_unorm_g8_uint: 398 | return format::d24_unorm_s8_uint; 399 | default: 400 | return value; 401 | } 402 | } 403 | 404 | /// 405 | /// Gets the number of bytes a texture row of the specified format occupies. 406 | /// 407 | inline const uint32_t format_row_pitch(format value, uint32_t width) 408 | { 409 | if (value == format::unknown) 410 | return 0; 411 | 412 | if (value <= format::r32g32b32a32_sint) 413 | return 16 * width; 414 | if (value <= format::r32g32b32_sint) 415 | return 12 * width; 416 | if (value <= format::x32_float_g8_uint) 417 | return 8 * width; 418 | if (value <= format::x24_unorm_g8_uint || value == format::l16a16_unorm) 419 | return 4 * width; 420 | if (value <= format::r16_sint || value == format::b5g6r5_unorm || value == format::b5g5r5a1_unorm || value == format::b5g5r5x1_unorm || value == format::l8a8_unorm || value == format::l16_unorm) 421 | return 2 * width; 422 | if (value <= format::a8_unorm || value == format::l8_unorm) 423 | return 1 * width; 424 | if (value <= format::g8r8_g8b8_unorm || (value >= format::b8g8r8a8_unorm && value <= format::b8g8r8x8_unorm_srgb) || (value >= format::r8g8b8x8_typeless && value <= format::r8g8b8x8_unorm_srgb)) 425 | return 4 * width; 426 | 427 | // Block compressed formats are bytes per block, rather than per pixel 428 | if ((value >= format::bc1_typeless && value <= format::bc1_unorm_srgb) || (value >= format::bc4_typeless && value <= format::bc4_snorm)) 429 | return 8 * ((width + 3) / 4); 430 | if ((value >= format::bc2_typeless && value <= format::bc2_unorm_srgb) || (value >= format::bc3_typeless && value <= format::bc3_unorm_srgb) || (value >= format::bc5_typeless && value <= format::bc7_unorm_srgb)) 431 | return 16 * ((width + 3) / 4); 432 | 433 | return 0; 434 | } 435 | /// 436 | /// Gets the number of bytes a texture slice of the specified format occupies. 437 | /// 438 | inline const uint32_t format_slice_pitch(format value, uint32_t row_pitch, uint32_t height) 439 | { 440 | if ((value >= format::bc1_typeless && value <= format::bc5_snorm) || (value >= format::bc6h_typeless && value <= format::bc7_unorm_srgb)) 441 | return row_pitch * ((height + 3) / 4); 442 | 443 | return row_pitch * height; 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /src/Include/reshade_api_resource.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Patrick Mours 3 | * License: https://github.com/crosire/reshade#license 4 | */ 5 | 6 | #pragma once 7 | 8 | #define RESHADE_DEFINE_HANDLE(name) \ 9 | typedef struct { uint64_t handle; } name; \ 10 | constexpr bool operator< (name lhs, name rhs) { return lhs.handle < rhs.handle; } \ 11 | constexpr bool operator!=(name lhs, name rhs) { return lhs.handle != rhs.handle; } \ 12 | constexpr bool operator!=(name lhs, uint64_t rhs) { return lhs.handle != rhs; } \ 13 | constexpr bool operator==(name lhs, name rhs) { return lhs.handle == rhs.handle; } \ 14 | constexpr bool operator==(name lhs, uint64_t rhs) { return lhs.handle == rhs; } 15 | 16 | #define RESHADE_DEFINE_ENUM_FLAG_OPERATORS(type) \ 17 | constexpr type operator~(type a) { return static_cast(~static_cast(a)); } \ 18 | inline type &operator&=(type &a, type b) { return reinterpret_cast(reinterpret_cast(a) &= static_cast(b)); } \ 19 | constexpr type operator&(type a, type b) { return static_cast(static_cast(a) & static_cast(b)); } \ 20 | inline type &operator|=(type &a, type b) { return reinterpret_cast(reinterpret_cast(a) |= static_cast(b)); } \ 21 | constexpr type operator|(type a, type b) { return static_cast(static_cast(a) | static_cast(b)); } \ 22 | inline type &operator^=(type &a, type b) { return reinterpret_cast(reinterpret_cast(a) ^= static_cast(b)); } \ 23 | constexpr type operator^(type a, type b) { return static_cast(static_cast(a) ^ static_cast(b)); } \ 24 | constexpr bool operator==(type lhs, uint32_t rhs) { return static_cast(lhs) == rhs; } \ 25 | constexpr bool operator!=(type lhs, uint32_t rhs) { return static_cast(lhs) != rhs; } 26 | 27 | #include "reshade_api_format.hpp" 28 | 29 | namespace reshade::api 30 | { 31 | /// 32 | /// The available comparison types. 33 | /// 34 | enum class compare_op : uint32_t 35 | { 36 | never = 0, 37 | less = 1, 38 | equal = 2, 39 | less_equal = 3, 40 | greater = 4, 41 | not_equal = 5, 42 | greater_equal = 6, 43 | always = 7 44 | }; 45 | 46 | /// 47 | /// The available filtering modes used for texture sampling operations. 48 | /// 49 | enum class filter_mode : uint32_t 50 | { 51 | min_mag_mip_point = 0, 52 | min_mag_point_mip_linear = 0x1, 53 | min_point_mag_linear_mip_point = 0x4, 54 | min_point_mag_mip_linear = 0x5, 55 | min_linear_mag_mip_point = 0x10, 56 | min_linear_mag_point_mip_linear = 0x11, 57 | min_mag_linear_mip_point = 0x14, 58 | min_mag_mip_linear = 0x15, 59 | anisotropic = 0x55, 60 | compare_min_mag_mip_point = 0x80, 61 | compare_min_mag_point_mip_linear = 0x81, 62 | compare_min_point_mag_linear_mip_point = 0x84, 63 | compare_min_point_mag_mip_linear = 0x85, 64 | compare_min_linear_mag_mip_point = 0x90, 65 | compare_min_linear_mag_point_mip_linear = 0x91, 66 | compare_min_mag_linear_mip_point = 0x94, 67 | compare_min_mag_mip_linear = 0x95, 68 | compare_anisotropic = 0xd5 69 | }; 70 | 71 | /// 72 | /// Specifies behavior of sampling with texture coordinates outside a texture resource. 73 | /// 74 | enum class texture_address_mode : uint32_t 75 | { 76 | wrap = 1, 77 | mirror = 2, 78 | clamp = 3, 79 | border = 4, 80 | mirror_once = 5 81 | }; 82 | 83 | /// 84 | /// Describes a sampler state. 85 | /// 86 | struct sampler_desc 87 | { 88 | /// 89 | /// Filtering mode to use when sampling a texture. 90 | /// 91 | filter_mode filter = filter_mode::min_mag_mip_linear; 92 | /// 93 | /// Method to use for resolving U texture coordinates outside 0 to 1 range. 94 | /// 95 | texture_address_mode address_u = texture_address_mode::clamp; 96 | /// 97 | /// Method to use for resolving V texture coordinates outside 0 to 1 range. 98 | /// 99 | texture_address_mode address_v = texture_address_mode::clamp; 100 | /// 101 | /// Method to use for resolving W texture coordinates outside 0 to 1 range. 102 | /// 103 | texture_address_mode address_w = texture_address_mode::clamp; 104 | /// 105 | /// Offset applied to the calculated mipmap level when sampling a texture. 106 | /// 107 | float mip_lod_bias = 0.0f; 108 | /// 109 | /// Clamping value to use when filtering mode is . 110 | /// 111 | float max_anisotropy = 1.0f; 112 | /// 113 | /// Comparison function to use to compare sampled data against existing sampled data. 114 | /// 115 | compare_op compare_op = compare_op::never; 116 | /// 117 | /// RGBA value to return for texture coordinates outside 0 to 1 range when addressing mode is . 118 | /// 119 | float border_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; 120 | /// 121 | /// Lower end of the mipmap range to clamp access to. 122 | /// 123 | float min_lod = -FLT_MAX; 124 | /// 125 | /// Upper end of the mipmap range to clamp access to. 126 | /// 127 | float max_lod = +FLT_MAX; 128 | }; 129 | 130 | /// 131 | /// An opaque handle to a sampler state object. 132 | /// Depending on the render API this can be a pointer to a 'ID3D10SamplerState', 'ID3D11SamplerState' or a 'D3D12_CPU_DESCRIPTOR_HANDLE' (to a sampler descriptor) or 'VkSampler' handle. 133 | /// 134 | RESHADE_DEFINE_HANDLE(sampler); 135 | 136 | /// 137 | /// The available memory mapping access types. 138 | /// 139 | enum class map_access 140 | { 141 | read_only = 1, 142 | write_only, 143 | read_write, 144 | write_discard 145 | }; 146 | 147 | /// 148 | /// The available memory heap types, which give a hint as to where to place the memory allocation for a resource. 149 | /// 150 | enum class memory_heap : uint32_t 151 | { 152 | unknown, // Usually indicates a resource that is reserved, but not yet bound to any memory. 153 | gpu_only, 154 | // Upload heap 155 | cpu_to_gpu, 156 | // Readback heap 157 | gpu_to_cpu, 158 | cpu_only, 159 | custom 160 | }; 161 | 162 | /// 163 | /// The available resource types. The type of a resource is specified during creation and is immutable. 164 | /// Various operations may have special requirements on the type of resources they operate on (e.g. copies can only happen between resources of the same type, ...). 165 | /// 166 | enum class resource_type : uint32_t 167 | { 168 | unknown, 169 | buffer, 170 | texture_1d, 171 | texture_2d, 172 | texture_3d, 173 | surface // Special type for resources that are implicitly both resource and render target view. 174 | }; 175 | 176 | /// 177 | /// A list of flags that describe additional parameters of a resource. 178 | /// 179 | enum class resource_flags : uint32_t 180 | { 181 | none = 0, 182 | dynamic = (1 << 3), 183 | cube_compatible = (1 << 2), 184 | generate_mipmaps = (1 << 0), 185 | shared = (1 << 1), 186 | shared_nt_handle = (1 << 11), 187 | structured = (1 << 6), 188 | sparse_binding = (1 << 18) 189 | }; 190 | RESHADE_DEFINE_ENUM_FLAG_OPERATORS(resource_flags); 191 | 192 | /// 193 | /// A list of flags that specify how a resource is to be used. 194 | /// This needs to be specified during creation and is also used to transition between different resource states within a command list. 195 | /// 196 | enum class resource_usage : uint32_t 197 | { 198 | undefined = 0, 199 | 200 | index_buffer = 0x2, 201 | vertex_buffer = 0x1, 202 | constant_buffer = 0x8000, 203 | stream_output = 0x100, 204 | indirect_argument = 0x200, 205 | 206 | depth_stencil = 0x30, 207 | depth_stencil_read = 0x20, 208 | depth_stencil_write = 0x10, 209 | render_target = 0x4, 210 | shader_resource = 0xC0, 211 | shader_resource_pixel = 0x80, 212 | shader_resource_non_pixel = 0x40, 213 | unordered_access = 0x8, 214 | 215 | copy_dest = 0x400, 216 | copy_source = 0x800, 217 | resolve_dest = 0x1000, 218 | resolve_source = 0x2000, 219 | 220 | // The following are special resource states and may only be used in barriers: 221 | 222 | general = 0x80000000, 223 | present = 0x80000000 | render_target | copy_source, 224 | cpu_access = vertex_buffer | index_buffer | shader_resource | indirect_argument | copy_source 225 | }; 226 | RESHADE_DEFINE_ENUM_FLAG_OPERATORS(resource_usage); 227 | 228 | /// 229 | /// Describes a resource, such as a buffer or texture. 230 | /// 231 | struct resource_desc 232 | { 233 | constexpr resource_desc() : texture() {} 234 | constexpr resource_desc(uint64_t size, memory_heap heap, resource_usage usage) : 235 | type(resource_type::buffer), buffer({ size }), heap(heap), usage(usage) {} 236 | constexpr resource_desc(uint32_t width, uint32_t height, uint16_t layers, uint16_t levels, format format, uint16_t samples, memory_heap heap, resource_usage usage, resource_flags flags = resource_flags::none) : 237 | type(resource_type::texture_2d), texture({ width, height, layers, levels, format, samples }), heap(heap), usage(usage), flags(flags) {} 238 | constexpr resource_desc(resource_type type, uint32_t width, uint32_t height, uint16_t depth_or_layers, uint16_t levels, format format, uint16_t samples, memory_heap heap, resource_usage usage, resource_flags flags = resource_flags::none) : 239 | type(type), texture({ width, height, depth_or_layers, levels, format, samples }), heap(heap), usage(usage), flags(flags) {} 240 | 241 | /// 242 | /// Type of the resource. 243 | /// 244 | resource_type type = resource_type::unknown; 245 | 246 | union 247 | { 248 | /// 249 | /// Used when resource type is a buffer. 250 | /// 251 | struct 252 | { 253 | /// 254 | /// Size of the buffer (in bytes). 255 | /// 256 | uint64_t size = 0; 257 | /// 258 | /// Structure stride for structured buffers (in bytes), otherwise zero. 259 | /// 260 | uint32_t stride = 0; 261 | } buffer; 262 | 263 | /// 264 | /// Used when resource type is a texture or surface. 265 | /// 266 | struct 267 | { 268 | /// 269 | /// Width of the texture (in texels). 270 | /// 271 | uint32_t width = 1; 272 | /// 273 | /// If this is a 2D or 3D texture, height of the texture (in texels), otherwise 1. 274 | /// 275 | uint32_t height = 1; 276 | /// 277 | /// If this is a 3D texture, depth of the texture (in texels), otherwise number of array layers. 278 | /// 279 | uint16_t depth_or_layers = 1; 280 | /// 281 | /// Maximum number of mipmap levels in the texture, including the base level, so at least 1. 282 | /// Can also be zero in case the exact number of mipmap levels is unknown. 283 | /// 284 | uint16_t levels = 1; 285 | /// 286 | /// Data format of each texel in the texture. 287 | /// 288 | format format = format::unknown; 289 | /// 290 | /// The number of samples per texel. Set to a value higher than 1 for multisampling. 291 | /// 292 | uint16_t samples = 1; 293 | } texture; 294 | }; 295 | 296 | /// 297 | /// Memory heap the resource allocation is placed in. 298 | /// 299 | memory_heap heap = memory_heap::unknown; 300 | /// 301 | /// Flags that specify how this resource may be used. 302 | /// 303 | resource_usage usage = resource_usage::undefined; 304 | /// 305 | /// Flags that describe additional parameters. 306 | /// 307 | resource_flags flags = resource_flags::none; 308 | }; 309 | 310 | /// 311 | /// An opaque handle to a resource object (buffer, texture, ...). 312 | /// Resources created by the application are only guaranteed to be valid during event callbacks. 313 | /// Depending on the render API this can be a pointer to a 'IDirect3DResource9', 'ID3D10Resource', 'ID3D11Resource' or 'ID3D12Resource' object or a 'VkImage' handle. 314 | /// 315 | RESHADE_DEFINE_HANDLE(resource); 316 | 317 | /// 318 | /// The available resource view types. These identify how a resource view interprets the data of its resource. 319 | /// 320 | enum class resource_view_type : uint32_t 321 | { 322 | unknown, 323 | buffer, 324 | texture_1d, 325 | texture_1d_array, 326 | texture_2d, 327 | texture_2d_array, 328 | texture_2d_multisample, 329 | texture_2d_multisample_array, 330 | texture_3d, 331 | texture_cube, 332 | texture_cube_array 333 | }; 334 | 335 | /// 336 | /// Describes a resource view, which specifies how to interpret the data of a resource. 337 | /// 338 | struct resource_view_desc 339 | { 340 | constexpr resource_view_desc() : texture() {} 341 | constexpr resource_view_desc(format format, uint64_t offset, uint64_t size) : 342 | type(resource_view_type::buffer), format(format), buffer({ offset, size }) {} 343 | constexpr resource_view_desc(format format, uint32_t first_level, uint32_t levels, uint32_t first_layer, uint32_t layers) : 344 | type(resource_view_type::texture_2d), format(format), texture({ first_level, levels, first_layer, layers }) {} 345 | constexpr resource_view_desc(resource_view_type type, format format, uint32_t first_level, uint32_t levels, uint32_t first_layer, uint32_t layers) : 346 | type(type), format(format), texture({ first_level, levels, first_layer, layers }) {} 347 | constexpr explicit resource_view_desc(format format) : type(resource_view_type::texture_2d), format(format), texture({ 0, 1, 0, 1 }) {} 348 | 349 | /// 350 | /// Resource type the view should interpret the resource data to. 351 | /// 352 | resource_view_type type = resource_view_type::unknown; 353 | /// 354 | /// Format the view should reinterpret the resource data to (can be different than the format of the resource as long as they are compatible). 355 | /// 356 | format format = format::unknown; 357 | 358 | union 359 | { 360 | /// 361 | /// Used when view type is a buffer. 362 | /// 363 | struct 364 | { 365 | /// 366 | /// Offset from the start of the buffer resource (in bytes). 367 | /// 368 | uint64_t offset = 0; 369 | /// 370 | /// Number of elements this view covers in the buffer resource (in bytes). 371 | /// Set to -1 (UINT64_MAX) to indicate that the entire buffer resource should be used. 372 | /// 373 | uint64_t size = UINT64_MAX; 374 | } buffer; 375 | 376 | /// 377 | /// Used when view type is a texture. 378 | /// 379 | struct 380 | { 381 | /// 382 | /// Index of the most detailed mipmap level to use. This number has to be between zero and the maximum number of mipmap levels in the texture minus 1. 383 | /// 384 | uint32_t first_level = 0; 385 | /// 386 | /// Maximum number of mipmap levels for the view of the texture. 387 | /// Set to -1 (UINT32_MAX) to indicate that all mipmap levels down to the least detailed should be used. 388 | /// 389 | uint32_t level_count = UINT32_MAX; 390 | /// 391 | /// Index of the first array layer of the texture array to use. This value is ignored if the texture is not layered. 392 | /// 393 | uint32_t first_layer = 0; 394 | /// 395 | /// Maximum number of array layers for the view of the texture array. This value is ignored if the texture is not layered. 396 | /// Set to -1 (UINT32_MAX) to indicate that all array layers should be used. 397 | /// 398 | uint32_t layer_count = UINT32_MAX; 399 | } texture; 400 | }; 401 | }; 402 | 403 | /// 404 | /// An opaque handle to a resource view object (depth-stencil, render target, shader resource view, ...). 405 | /// Resource views created by the application are only guaranteed to be valid during event callbacks. 406 | /// Depending on the render API this can be a pointer to a 'IDirect3DResource9', 'ID3D10View' or 'ID3D11View' object, or a 'D3D12_CPU_DESCRIPTOR_HANDLE' (to a view descriptor) or 'VkImageView' handle. 407 | /// 408 | RESHADE_DEFINE_HANDLE(resource_view); 409 | 410 | /// 411 | /// Describes a region inside a subresource. 412 | /// 413 | struct subresource_box 414 | { 415 | int32_t left = 0; 416 | int32_t top = 0; 417 | int32_t front = 0; 418 | int32_t right = 0; 419 | int32_t bottom = 0; 420 | int32_t back = 0; 421 | 422 | constexpr uint32_t width() const { return right - left; } 423 | constexpr uint32_t height() const { return bottom - top; } 424 | constexpr uint32_t depth() const { return back - front; } 425 | }; 426 | 427 | /// 428 | /// Describes the data of a subresource. 429 | /// 430 | struct subresource_data 431 | { 432 | /// 433 | /// Pointer to the data. 434 | /// 435 | void *data = nullptr; 436 | /// 437 | /// Row pitch of the data (added to the data pointer to move between texture rows, unused for buffers and 1D textures). 438 | /// 439 | /// 440 | uint32_t row_pitch = 0; 441 | /// 442 | /// Depth pitch of the data (added to the data pointer to move between texture depth/array slices, unused for buffers and 1D/2D textures). 443 | /// 444 | /// 445 | uint32_t slice_pitch = 0; 446 | }; 447 | 448 | /// 449 | /// Specifies how the contents of a render target or depth-stencil view are treated at the start of a render pass. 450 | /// 451 | enum class render_pass_load_op : uint32_t 452 | { 453 | load, 454 | clear, 455 | discard, 456 | dont_care 457 | }; 458 | 459 | /// 460 | /// Specifies how the contents of a render target or depth-stencil view are treated at the end of a render pass. 461 | /// 462 | enum class render_pass_store_op : uint32_t 463 | { 464 | store, 465 | discard, 466 | dont_care 467 | }; 468 | 469 | /// 470 | /// Describes a depth-stencil view and how it is treated at the start and end of a render pass. 471 | /// 472 | struct render_pass_depth_stencil_desc 473 | { 474 | /// 475 | /// Depth-stencil resource view. 476 | /// 477 | resource_view view = { 0 }; 478 | /// 479 | /// Specifies how the depth contents of the depth-stencil view are treated at the start of the render pass. 480 | /// 481 | render_pass_load_op depth_load_op = render_pass_load_op::load; 482 | /// 483 | /// Specifies how the depth contents of the depth-stencil view are treated at the end of the render pass. 484 | /// 485 | render_pass_store_op depth_store_op = render_pass_store_op::store; 486 | /// 487 | /// Specifies how the stencil contents of the depth-stencil view are treated at the start of the render pass. 488 | /// 489 | render_pass_load_op stencil_load_op = render_pass_load_op::load; 490 | /// 491 | /// Specifies how the stencil contents of the depth-stencil view are treated at the end of the render pass. 492 | /// 493 | render_pass_store_op stencil_store_op = render_pass_store_op::store; 494 | /// 495 | /// Value the depth contents of the depth-stencil resource is cleared to when is . 496 | /// 497 | float clear_depth = 0.0f; 498 | /// 499 | /// Value the stencil contents of the depth-stencil resource is cleared to when is . 500 | /// 501 | uint8_t clear_stencil = 0; 502 | }; 503 | 504 | /// 505 | /// Describes a render target view and how it is treated at the start and end of a render pass. 506 | /// 507 | struct render_pass_render_target_desc 508 | { 509 | /// 510 | /// Render target resource view. 511 | /// 512 | resource_view view = { 0 }; 513 | /// 514 | /// Specifies how the contents of the render target view are treated at the start of the render pass. 515 | /// 516 | render_pass_load_op load_op = render_pass_load_op::load; 517 | /// 518 | /// Specifies how the contents of the render target view are treated at the end of the render pass. 519 | /// 520 | render_pass_store_op store_op = render_pass_store_op::store; 521 | /// 522 | /// Value the render target resource is cleared to when is . 523 | /// 524 | float clear_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 525 | }; 526 | } 527 | -------------------------------------------------------------------------------- /src/CDataFile.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CDataFile Class Implementation 3 | // 4 | // The purpose of this class is to provide a simple, full featured means to 5 | // store persistent data to a text file. It uses a simple key/value paradigm 6 | // to achieve this. The class can read/write to standard Windows .ini files, 7 | // and yet does not rely on any windows specific calls. It should work as 8 | // well in a linux environment (with some minor adjustments) as it does in 9 | // a Windows one. 10 | // 11 | // Written July, 2002 by Gary McNickle 12 | // If you use this class in your application, credit would be appreciated. 13 | // 14 | 15 | // 16 | // CDataFile 17 | // The purpose of this class is to provide the means to easily store key/value 18 | // pairs in a config file, seperated by independant sections. Sections may not 19 | // have duplicate keys, although two or more sections can have the same key. 20 | // Simple support for comments is included. Each key, and each section may have 21 | // it's own multiline comment. 22 | // 23 | // An example might look like this; 24 | // 25 | // [UserSettings] 26 | // Name=Joe User 27 | // Date of Birth=12/25/01 28 | // 29 | // ; 30 | // ; Settings unique to this server 31 | // ; 32 | // [ServerSettings] 33 | // Port=1200 34 | // IP_Address=127.0.0.1 35 | // MachineName=ADMIN 36 | // 37 | #include "stdafx.h" 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef WIN32 47 | #include 48 | #endif 49 | 50 | #include "CDataFile.h" 51 | 52 | // Compatibility Defines //////////////////////////////////////////////////////// 53 | ///////////////////////////////////////////////////////////////////////////////// 54 | #ifdef WIN32 55 | #define snprintf _snprintf 56 | #define vsnprintf _vsnprintf 57 | #endif 58 | 59 | 60 | // CDataFile 61 | // Our default contstructor. If it can load the file, it will do so and populate 62 | // the section list with the values from the file. 63 | CDataFile::CDataFile(t_Str szFileName) 64 | { 65 | m_bDirty = false; 66 | m_szFileName = szFileName; 67 | m_Flags = (AUTOCREATE_SECTIONS | AUTOCREATE_KEYS); 68 | m_Sections.push_back( *(new t_Section) ); 69 | 70 | Load(m_szFileName); 71 | } 72 | 73 | CDataFile::CDataFile() 74 | { 75 | Clear(); 76 | m_Flags = (AUTOCREATE_SECTIONS | AUTOCREATE_KEYS); 77 | m_Sections.push_back( *(new t_Section) ); 78 | } 79 | 80 | // ~CDataFile 81 | // Saves the file if any values have changed since the last save. 82 | CDataFile::~CDataFile() 83 | { 84 | if ( m_bDirty ) 85 | Save(); 86 | } 87 | 88 | // Clear 89 | // Resets the member variables to their defaults 90 | void CDataFile::Clear() 91 | { 92 | m_bDirty = false; 93 | m_szFileName = t_Str(""); 94 | m_Sections.clear(); 95 | } 96 | 97 | // SetFileName 98 | // Set's the m_szFileName member variable. For use when creating the CDataFile 99 | // object by hand (-vs- loading it from a file 100 | void CDataFile::SetFileName(t_Str szFileName) 101 | { 102 | if (m_szFileName.size() != 0 && CompareNoCase(szFileName, m_szFileName) != 0) 103 | { 104 | m_bDirty = true; 105 | 106 | Report(E_WARN, "[CDataFile::SetFileName] The filename has changed from <%s> to <%s>.", 107 | m_szFileName.c_str(), szFileName.c_str()); 108 | } 109 | 110 | m_szFileName = szFileName; 111 | } 112 | 113 | // Load 114 | // Attempts to load in the text file. If successful it will populate the 115 | // Section list with the key/value pairs found in the file. Note that comments 116 | // are saved so that they can be rewritten to the file later. 117 | bool CDataFile::Load(t_Str szFileName) 118 | { 119 | // We dont want to create a new file here. If it doesn't exist, just 120 | // return false and report the failure. 121 | fstream File(szFileName.c_str(), ios::in); 122 | 123 | if ( File.is_open() ) 124 | { 125 | bool bDone = false; 126 | bool bAutoKey = (m_Flags & AUTOCREATE_KEYS) == AUTOCREATE_KEYS; 127 | bool bAutoSec = (m_Flags & AUTOCREATE_SECTIONS) == AUTOCREATE_SECTIONS; 128 | 129 | t_Str szLine; 130 | t_Str szComment; 131 | char buffer[MAX_BUFFER_LEN]; 132 | t_Section* pSection = GetSection(""); 133 | 134 | // These need to be set, we'll restore the original values later. 135 | m_Flags |= AUTOCREATE_KEYS; 136 | m_Flags |= AUTOCREATE_SECTIONS; 137 | 138 | while ( !bDone ) 139 | { 140 | memset(buffer, 0, MAX_BUFFER_LEN); 141 | File.getline(buffer, MAX_BUFFER_LEN); 142 | 143 | szLine = buffer; 144 | Trim(szLine); 145 | 146 | bDone = ( File.eof() || File.bad() || File.fail() ); 147 | 148 | if ( szLine.find_first_of(CommentIndicators) == 0 ) 149 | { 150 | szComment += "\n"; 151 | szComment += szLine; 152 | } 153 | else 154 | if ( szLine.find_first_of('[') == 0 ) // new section 155 | { 156 | szLine.erase( 0, 1 ); 157 | szLine.erase( szLine.find_last_of(']'), 1 ); 158 | 159 | CreateSection(szLine, szComment); 160 | pSection = GetSection(szLine); 161 | szComment = t_Str(""); 162 | } 163 | else 164 | if ( szLine.size() > 0 ) // we have a key, add this key/value pair 165 | { 166 | t_Str szKey = GetNextWord(szLine); 167 | t_Str szValue = szLine; 168 | 169 | if ( szKey.size() > 0 && szValue.size() > 0 ) 170 | { 171 | SetValue(szKey, szValue, szComment, pSection->szName); 172 | szComment = t_Str(""); 173 | } 174 | } 175 | } 176 | 177 | // Restore the original flag values. 178 | if ( !bAutoKey ) 179 | m_Flags &= ~AUTOCREATE_KEYS; 180 | 181 | if ( !bAutoSec ) 182 | m_Flags &= ~AUTOCREATE_SECTIONS; 183 | } 184 | else 185 | { 186 | Report(E_INFO, "[CDataFile::Load] Unable to open file. Does it exist?"); 187 | return false; 188 | } 189 | 190 | File.close(); 191 | 192 | return true; 193 | } 194 | 195 | 196 | // Save 197 | // Attempts to save the Section list and keys to the file. Note that if Load 198 | // was never called (the CDataFile object was created manually), then you 199 | // must set the m_szFileName variable before calling save. 200 | bool CDataFile::Save() 201 | { 202 | if ( KeyCount() == 0 && SectionCount() == 0 ) 203 | { 204 | // no point in saving 205 | Report(E_INFO, "[CDataFile::Save] Nothing to save."); 206 | return false; 207 | } 208 | 209 | if ( m_szFileName.size() == 0 ) 210 | { 211 | Report(E_ERROR, "[CDataFile::Save] No filename has been set."); 212 | return false; 213 | } 214 | 215 | fstream File(m_szFileName.c_str(), ios::out|ios::trunc); 216 | 217 | if ( File.is_open() ) 218 | { 219 | SectionItor s_pos; 220 | KeyItor k_pos; 221 | t_Section Section; 222 | t_Key Key; 223 | 224 | for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); s_pos++) 225 | { 226 | Section = (*s_pos); 227 | bool bWroteComment = false; 228 | 229 | if ( Section.szComment.size() > 0 ) 230 | { 231 | bWroteComment = true; 232 | WriteLn(File, "\n%s", CommentStr(Section.szComment).c_str()); 233 | } 234 | 235 | if ( Section.szName.size() > 0 ) 236 | { 237 | WriteLn(File, "%s[%s]", 238 | bWroteComment ? "" : "\n", 239 | Section.szName.c_str()); 240 | } 241 | 242 | for (k_pos = Section.Keys.begin(); k_pos != Section.Keys.end(); k_pos++) 243 | { 244 | Key = (*k_pos); 245 | 246 | if ( Key.szKey.size() > 0 && Key.szValue.size() > 0 ) 247 | { 248 | WriteLn(File, "%s%s%s%s%c%s", 249 | Key.szComment.size() > 0 ? "\n" : "", 250 | CommentStr(Key.szComment).c_str(), 251 | Key.szComment.size() > 0 ? "\n" : "", 252 | Key.szKey.c_str(), 253 | EqualIndicators[0], 254 | Key.szValue.c_str()); 255 | } 256 | } 257 | } 258 | 259 | } 260 | else 261 | { 262 | Report(E_ERROR, "[CDataFile::Save] Unable to save file."); 263 | return false; 264 | } 265 | 266 | m_bDirty = false; 267 | 268 | File.flush(); 269 | File.close(); 270 | 271 | return true; 272 | } 273 | 274 | // SetKeyComment 275 | // Set the comment of a given key. Returns true if the key is not found. 276 | bool CDataFile::SetKeyComment(t_Str szKey, t_Str szComment, t_Str szSection) 277 | { 278 | KeyItor k_pos; 279 | t_Section* pSection; 280 | 281 | if ( (pSection = GetSection(szSection)) == NULL ) 282 | return false; 283 | 284 | for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); k_pos++) 285 | { 286 | if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 287 | { 288 | (*k_pos).szComment = szComment; 289 | m_bDirty = true; 290 | return true; 291 | } 292 | } 293 | 294 | return false; 295 | 296 | } 297 | 298 | // SetSectionComment 299 | // Set the comment for a given section. Returns false if the section 300 | // was not found. 301 | bool CDataFile::SetSectionComment(t_Str szSection, t_Str szComment) 302 | { 303 | SectionItor s_pos; 304 | 305 | for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); s_pos++) 306 | { 307 | if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 308 | { 309 | (*s_pos).szComment = szComment; 310 | m_bDirty = true; 311 | return true; 312 | } 313 | } 314 | 315 | return false; 316 | } 317 | 318 | 319 | // SetValue 320 | // Given a key, a value and a section, this function will attempt to locate the 321 | // Key within the given section, and if it finds it, change the keys value to 322 | // the new value. If it does not locate the key, it will create a new key with 323 | // the proper value and place it in the section requested. 324 | bool CDataFile::SetValue(t_Str szKey, t_Str szValue, t_Str szComment, t_Str szSection) 325 | { 326 | t_Key* pKey = GetKey(szKey, szSection); 327 | t_Section* pSection = GetSection(szSection); 328 | 329 | if (pSection == NULL) 330 | { 331 | if ( !(m_Flags & AUTOCREATE_SECTIONS) || !CreateSection(szSection,"")) 332 | return false; 333 | 334 | pSection = GetSection(szSection); 335 | } 336 | 337 | // Sanity check... 338 | if ( pSection == NULL ) 339 | return false; 340 | 341 | // if the key does not exist in that section, and the value passed 342 | // is not t_Str("") then add the new key. 343 | if ( pKey == NULL && szValue.size() > 0 && (m_Flags & AUTOCREATE_KEYS)) 344 | { 345 | pKey = new t_Key; 346 | 347 | pKey->szKey = szKey; 348 | pKey->szValue = szValue; 349 | pKey->szComment = szComment; 350 | 351 | m_bDirty = true; 352 | 353 | pSection->Keys.push_back(*pKey); 354 | 355 | return true; 356 | } 357 | 358 | if ( pKey != NULL ) 359 | { 360 | pKey->szValue = szValue; 361 | pKey->szComment = szComment; 362 | 363 | m_bDirty = true; 364 | 365 | return true; 366 | } 367 | 368 | return false; 369 | } 370 | 371 | // SetFloat 372 | // Passes the given float to SetValue as a string 373 | bool CDataFile::SetFloat(t_Str szKey, float fValue, t_Str szComment, t_Str szSection) 374 | { 375 | char szStr[64]; 376 | 377 | _snprintf_s(szStr, 64, "%f", fValue); 378 | 379 | return SetValue(szKey, szStr, szComment, szSection); 380 | } 381 | 382 | // SetInt 383 | // Passes the given int to SetValue as a string 384 | bool CDataFile::SetInt(t_Str szKey, int nValue, t_Str szComment, t_Str szSection) 385 | { 386 | char szStr[64]; 387 | 388 | _snprintf_s(szStr, 64, "%d", nValue); 389 | 390 | return SetValue(szKey, szStr, szComment, szSection); 391 | 392 | } 393 | 394 | 395 | // SetUInt 396 | // Passes the given int to SetValue as a string 397 | bool CDataFile::SetUInt(t_Str szKey, uint32_t nValue, t_Str szComment, t_Str szSection) 398 | { 399 | char szStr[64]; 400 | 401 | _snprintf_s(szStr, 64, "%u", nValue); 402 | 403 | return SetValue(szKey, szStr, szComment, szSection); 404 | 405 | } 406 | 407 | 408 | // SetBool 409 | // Passes the given bool to SetValue as a string 410 | bool CDataFile::SetBool(t_Str szKey, bool bValue, t_Str szComment, t_Str szSection) 411 | { 412 | t_Str szValue = bValue ? "True" : "False"; 413 | 414 | return SetValue(szKey, szValue, szComment, szSection); 415 | } 416 | 417 | // GetValue 418 | // Returns the key value as a t_Str object. A return value of 419 | // t_Str("") indicates that the key could not be found. 420 | t_Str CDataFile::GetValue(t_Str szKey, t_Str szSection) 421 | { 422 | t_Key* pKey = GetKey(szKey, szSection); 423 | 424 | return (pKey == NULL) ? t_Str("") : pKey->szValue; 425 | } 426 | 427 | // GetString 428 | // Returns the key value as a t_Str object. A return value of 429 | // t_Str("") indicates that the key could not be found. 430 | t_Str CDataFile::GetString(t_Str szKey, t_Str szSection) 431 | { 432 | return GetValue(szKey, szSection); 433 | } 434 | 435 | // GetFloat 436 | // Returns the key value as a float type. Returns FLT_MIN if the key is 437 | // not found. 438 | float CDataFile::GetFloat(t_Str szKey, t_Str szSection) 439 | { 440 | t_Str szValue = GetValue(szKey, szSection); 441 | 442 | if ( szValue.size() == 0 ) 443 | return FLT_MIN; 444 | 445 | return (float)atof( szValue.c_str() ); 446 | } 447 | 448 | // GetInt 449 | // Returns the key value as an integer type. Returns INT_MIN if the key is 450 | // not found. 451 | int CDataFile::GetInt(t_Str szKey, t_Str szSection) 452 | { 453 | t_Str szValue = GetValue(szKey, szSection); 454 | 455 | if ( szValue.size() == 0 ) 456 | return INT_MIN; 457 | 458 | return atoi( szValue.c_str() ); 459 | } 460 | 461 | // GetUInt 462 | // Returns the key value as an integer type. Returns UINT_MAX if the key is 463 | // not found. 464 | uint32_t CDataFile::GetUInt(t_Str szKey, t_Str szSection) 465 | { 466 | t_Str szValue = GetValue(szKey, szSection); 467 | 468 | if ( szValue.size() == 0 ) 469 | return UINT_MAX; 470 | 471 | return static_cast(atoll( szValue.c_str() )); 472 | } 473 | 474 | // GetBool 475 | // Returns the key value as a bool type. Returns false if the key is 476 | // not found. 477 | bool CDataFile::GetBool(t_Str szKey, t_Str szSection) 478 | { 479 | bool bValue = false; 480 | t_Str szValue = GetValue(szKey, szSection); 481 | 482 | if ( szValue.find("1") == 0 483 | || CompareNoCase(szValue, "true") == 0 484 | || CompareNoCase(szValue, "yes") == 0 ) 485 | { 486 | bValue = true; 487 | } 488 | 489 | return bValue; 490 | } 491 | 492 | // DeleteSection 493 | // Delete a specific section. Returns false if the section cannot be 494 | // found or true when sucessfully deleted. 495 | bool CDataFile::DeleteSection(t_Str szSection) 496 | { 497 | SectionItor s_pos; 498 | 499 | for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); s_pos++) 500 | { 501 | if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 502 | { 503 | m_Sections.erase(s_pos); 504 | return true; 505 | } 506 | } 507 | 508 | return false; 509 | } 510 | 511 | // DeleteKey 512 | // Delete a specific key in a specific section. Returns false if the key 513 | // cannot be found or true when sucessfully deleted. 514 | bool CDataFile::DeleteKey(t_Str szKey, t_Str szFromSection) 515 | { 516 | KeyItor k_pos; 517 | t_Section* pSection; 518 | 519 | if ( (pSection = GetSection(szFromSection)) == NULL ) 520 | return false; 521 | 522 | for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); k_pos++) 523 | { 524 | if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 525 | { 526 | pSection->Keys.erase(k_pos); 527 | return true; 528 | } 529 | } 530 | 531 | return false; 532 | } 533 | 534 | // CreateKey 535 | // Given a key, a value and a section, this function will attempt to locate the 536 | // Key within the given section, and if it finds it, change the keys value to 537 | // the new value. If it does not locate the key, it will create a new key with 538 | // the proper value and place it in the section requested. 539 | bool CDataFile::CreateKey(t_Str szKey, t_Str szValue, t_Str szComment, t_Str szSection) 540 | { 541 | bool bAutoKey = (m_Flags & AUTOCREATE_KEYS) == AUTOCREATE_KEYS; 542 | bool bReturn = false; 543 | 544 | m_Flags |= AUTOCREATE_KEYS; 545 | 546 | bReturn = SetValue(szKey, szValue, szComment, szSection); 547 | 548 | if ( !bAutoKey ) 549 | m_Flags &= ~AUTOCREATE_KEYS; 550 | 551 | return bReturn; 552 | } 553 | 554 | 555 | // CreateSection 556 | // Given a section name, this function first checks to see if the given section 557 | // allready exists in the list or not, if not, it creates the new section and 558 | // assigns it the comment given in szComment. The function returns true if 559 | // sucessfully created, or false otherwise. 560 | bool CDataFile::CreateSection(t_Str szSection, t_Str szComment) 561 | { 562 | t_Section* pSection = GetSection(szSection); 563 | 564 | if ( pSection ) 565 | { 566 | Report(E_INFO, "[CDataFile::CreateSection] Section <%s> allready exists. Aborting.", szSection.c_str()); 567 | return false; 568 | } 569 | 570 | pSection = new t_Section; 571 | 572 | pSection->szName = szSection; 573 | pSection->szComment = szComment; 574 | m_Sections.push_back(*pSection); 575 | m_bDirty = true; 576 | 577 | return true; 578 | } 579 | 580 | // CreateSection 581 | // Given a section name, this function first checks to see if the given section 582 | // allready exists in the list or not, if not, it creates the new section and 583 | // assigns it the comment given in szComment. The function returns true if 584 | // sucessfully created, or false otherwise. This version accpets a KeyList 585 | // and sets up the newly created Section with the keys in the list. 586 | bool CDataFile::CreateSection(t_Str szSection, t_Str szComment, KeyList Keys) 587 | { 588 | if ( !CreateSection(szSection, szComment) ) 589 | return false; 590 | 591 | t_Section* pSection = GetSection(szSection); 592 | 593 | if ( !pSection ) 594 | return false; 595 | 596 | KeyItor k_pos; 597 | 598 | pSection->szName = szSection; 599 | for (k_pos = Keys.begin(); k_pos != Keys.end(); k_pos++) 600 | { 601 | t_Key* pKey = new t_Key; 602 | pKey->szComment = (*k_pos).szComment; 603 | pKey->szKey = (*k_pos).szKey; 604 | pKey->szValue = (*k_pos).szValue; 605 | 606 | pSection->Keys.push_back(*pKey); 607 | } 608 | 609 | m_Sections.push_back(*pSection); 610 | m_bDirty = true; 611 | 612 | return true; 613 | } 614 | 615 | // SectionCount 616 | // Simply returns the number of sections in the list. 617 | int CDataFile::SectionCount() 618 | { 619 | return static_cast(m_Sections.size()); 620 | } 621 | 622 | // KeyCount 623 | // Returns the total number of keys contained within all the sections. 624 | int CDataFile::KeyCount() 625 | { 626 | int nCounter = 0; 627 | SectionItor s_pos; 628 | 629 | for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); s_pos++) 630 | nCounter += static_cast((*s_pos).Keys.size()); 631 | 632 | return nCounter; 633 | } 634 | 635 | 636 | // Protected Member Functions /////////////////////////////////////////////////// 637 | ///////////////////////////////////////////////////////////////////////////////// 638 | 639 | // GetKey 640 | // Given a key and section name, looks up the key and if found, returns a 641 | // pointer to that key, otherwise returns NULL. 642 | t_Key* CDataFile::GetKey(t_Str szKey, t_Str szSection) 643 | { 644 | KeyItor k_pos; 645 | t_Section* pSection; 646 | 647 | // Since our default section has a name value of t_Str("") this should 648 | // always return a valid section, wether or not it has any keys in it is 649 | // another matter. 650 | if ( (pSection = GetSection(szSection)) == NULL ) 651 | return NULL; 652 | 653 | for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); k_pos++) 654 | { 655 | if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 656 | return (t_Key*)&(*k_pos); 657 | } 658 | 659 | return NULL; 660 | } 661 | 662 | // GetSection 663 | // Given a section name, locates that section in the list and returns a pointer 664 | // to it. If the section was not found, returns NULL 665 | t_Section* CDataFile::GetSection(t_Str szSection) 666 | { 667 | SectionItor s_pos; 668 | 669 | for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); s_pos++) 670 | { 671 | if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 672 | return (t_Section*)&(*s_pos); 673 | } 674 | 675 | return NULL; 676 | } 677 | 678 | 679 | t_Str CDataFile::CommentStr(t_Str szComment) 680 | { 681 | t_Str szNewStr = t_Str(""); 682 | 683 | Trim(szComment); 684 | 685 | if ( szComment.size() == 0 ) 686 | return szComment; 687 | 688 | if ( szComment.find_first_of(CommentIndicators) != 0 ) 689 | { 690 | szNewStr = CommentIndicators[0]; 691 | szNewStr += " "; 692 | } 693 | 694 | szNewStr += szComment; 695 | 696 | return szNewStr; 697 | } 698 | 699 | 700 | 701 | // Utility Functions //////////////////////////////////////////////////////////// 702 | ///////////////////////////////////////////////////////////////////////////////// 703 | 704 | // GetNextWord 705 | // Given a key +delimiter+ value string, pulls the key name from the string, 706 | // deletes the delimiter and alters the original string to contain the 707 | // remainder. Returns the key 708 | t_Str GetNextWord(t_Str& CommandLine) 709 | { 710 | int nPos = static_cast(CommandLine.find_first_of(EqualIndicators)); 711 | t_Str sWord = t_Str(""); 712 | 713 | if ( nPos > -1 ) 714 | { 715 | sWord = CommandLine.substr(0, nPos); 716 | CommandLine.erase(0, nPos+1); 717 | } 718 | else 719 | { 720 | sWord = CommandLine; 721 | CommandLine = t_Str(""); 722 | } 723 | 724 | Trim(sWord); 725 | return sWord; 726 | } 727 | 728 | 729 | // CompareNoCase 730 | // it's amazing what features std::string lacks. This function simply 731 | // does a lowercase compare against the two strings, returning 0 if they 732 | // match. 733 | int CompareNoCase(t_Str str1, t_Str str2) 734 | { 735 | #ifdef WIN32 736 | return _stricmp(str1.c_str(), str2.c_str()); 737 | #else 738 | return strcasecmp(str1.c_str(), str2.c_str()); 739 | #endif 740 | } 741 | 742 | // Trim 743 | // Trims whitespace from both sides of a string. 744 | void Trim(t_Str& szStr) 745 | { 746 | t_Str szTrimChars = WhiteSpace; 747 | 748 | szTrimChars += EqualIndicators; 749 | int nPos, rPos; 750 | 751 | // trim left 752 | nPos = static_cast(szStr.find_first_not_of(szTrimChars)); 753 | 754 | if ( nPos > 0 ) 755 | szStr.erase(0, nPos); 756 | 757 | // trim right and return 758 | nPos = static_cast(szStr.find_last_not_of(szTrimChars)); 759 | rPos = static_cast(szStr.find_last_of(szTrimChars)); 760 | 761 | if ( rPos > nPos && rPos > -1) 762 | szStr.erase(rPos, szStr.size()-rPos); 763 | } 764 | 765 | // WriteLn 766 | // Writes the formatted output to the file stream, returning the number of 767 | // bytes written. 768 | int WriteLn(std::fstream& stream, const char* fmt, ...) 769 | { 770 | char buf[MAX_BUFFER_LEN]; 771 | int nLength; 772 | t_Str szMsg; 773 | 774 | memset(buf, 0, MAX_BUFFER_LEN); 775 | va_list args; 776 | 777 | va_start (args, fmt); 778 | nLength = _vsnprintf_s(buf, MAX_BUFFER_LEN, fmt, args); 779 | va_end (args); 780 | 781 | 782 | if ( buf[nLength] != '\n' && buf[nLength] != '\r' ) 783 | buf[nLength++] = '\n'; 784 | 785 | 786 | stream.write(buf, nLength); 787 | 788 | return nLength; 789 | } 790 | 791 | // Report 792 | // A simple reporting function. Outputs the report messages to stdout 793 | // This is a dumb'd down version of a simmilar function of mine, so if 794 | // it looks like it should do more than it does, that's why... 795 | void Report(e_DebugLevel DebugLevel, const char *fmt, ...) 796 | { 797 | char buf[MAX_BUFFER_LEN]; 798 | int nLength; 799 | t_Str szMsg; 800 | 801 | va_list args; 802 | 803 | memset(buf, 0, MAX_BUFFER_LEN); 804 | 805 | va_start (args, fmt); 806 | nLength = _vsnprintf_s(buf, MAX_BUFFER_LEN, fmt, args); 807 | va_end (args); 808 | 809 | 810 | if ( buf[nLength] != '\n' && buf[nLength] != '\r' ) 811 | buf[nLength++] = '\n'; 812 | 813 | 814 | switch ( DebugLevel ) 815 | { 816 | case E_DEBUG: 817 | szMsg = " "; 818 | break; 819 | case E_INFO: 820 | szMsg = " "; 821 | break; 822 | case E_WARN: 823 | szMsg = " "; 824 | break; 825 | case E_ERROR: 826 | szMsg = " "; 827 | break; 828 | case E_FATAL: 829 | szMsg = " "; 830 | break; 831 | case E_CRITICAL: 832 | szMsg = " "; 833 | break; 834 | } 835 | 836 | 837 | szMsg += buf; 838 | 839 | printf(szMsg.c_str()); 840 | 841 | } 842 | 843 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////// 2 | // 3 | // Part of ShaderToggler, a shader toggler add on for Reshade 5+ which allows you 4 | // to define groups of shaders to toggle them on/off with one key press 5 | // 6 | // (c) Frans 'Otis_Inf' Bouma. 7 | // 8 | // All rights reserved. 9 | // https://github.com/FransBouma/ShaderToggler 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met : 13 | // 14 | // * Redistributions of source code must retain the above copyright notice, this 15 | // list of conditions and the following disclaimer. 16 | // 17 | // * Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and / or other materials provided with the distribution. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ///////////////////////////////////////////////////////////////////////// 32 | 33 | #define IMGUI_DISABLE_INCLUDE_IMCONFIG_H 34 | #define ImTextureID unsigned long long // Change ImGui texture ID type to that of a 'reshade::api::resource_view' handle 35 | 36 | #include 37 | #include 38 | #include "crc32_hash.hpp" 39 | #include "ShaderManager.h" 40 | #include "CDataFile.h" 41 | #include "ToggleGroup.h" 42 | #include 43 | #include 44 | 45 | using namespace reshade::api; 46 | using namespace ShaderToggler; 47 | 48 | extern "C" __declspec(dllexport) const char *NAME = "Shader Toggler"; 49 | extern "C" __declspec(dllexport) const char *DESCRIPTION = "Add-on which allows you to define groups of game shaders to toggle on/off with one key press."; 50 | 51 | struct __declspec(uuid("038B03AA-4C75-443B-A695-752D80797037")) CommandListDataContainer { 52 | uint64_t activePixelShaderPipeline; 53 | uint64_t activeVertexShaderPipeline; 54 | uint64_t activeComputeShaderPipeline; 55 | }; 56 | 57 | #define FRAMECOUNT_COLLECTION_PHASE_DEFAULT 250; 58 | #define HASH_FILE_NAME "ShaderToggler.ini" 59 | 60 | static ShaderToggler::ShaderManager g_pixelShaderManager; 61 | static ShaderToggler::ShaderManager g_vertexShaderManager; 62 | static ShaderToggler::ShaderManager g_computeShaderManager; 63 | static KeyData g_keyCollector; 64 | static atomic_uint32_t g_activeCollectorFrameCounter = 0; 65 | static std::vector g_toggleGroups; 66 | static atomic_int g_toggleGroupIdKeyBindingEditing = -1; 67 | static atomic_int g_toggleGroupIdShaderEditing = -1; 68 | static float g_overlayOpacity = 1.0f; 69 | static int g_startValueFramecountCollectionPhase = FRAMECOUNT_COLLECTION_PHASE_DEFAULT; 70 | static std::string g_iniFileName = ""; 71 | 72 | /// 73 | /// Calculates a crc32 hash from the passed in shader bytecode. The hash is used to identity the shader in future runs. 74 | /// 75 | /// 76 | /// 77 | static uint32_t calculateShaderHash(void* shaderData) 78 | { 79 | if(nullptr==shaderData) 80 | { 81 | return 0; 82 | } 83 | 84 | const auto shaderDesc = *static_cast(shaderData); 85 | return compute_crc32(static_cast(shaderDesc.code), shaderDesc.code_size); 86 | } 87 | 88 | 89 | /// 90 | /// Adds a default group with VK_CAPITAL as toggle key. Only used if there aren't any groups defined in the ini file. 91 | /// 92 | void addDefaultGroup() 93 | { 94 | ToggleGroup toAdd("Default", ToggleGroup::getNewGroupId()); 95 | toAdd.setToggleKey(VK_CAPITAL, false, false, false); 96 | g_toggleGroups.push_back(toAdd); 97 | } 98 | 99 | 100 | /// 101 | /// Loads the defined hashes and groups from the shaderToggler.ini file. 102 | /// 103 | void loadShaderTogglerIniFile() 104 | { 105 | // Will assume it's started at the start of the application and therefore no groups are present. 106 | CDataFile iniFile; 107 | if(!iniFile.Load(g_iniFileName)) 108 | { 109 | // not there 110 | return; 111 | } 112 | int groupCounter = 0; 113 | const int numberOfGroups = iniFile.GetInt("AmountGroups", "General"); 114 | if(numberOfGroups==INT_MIN) 115 | { 116 | // old format file? 117 | addDefaultGroup(); 118 | groupCounter=-1; // enforce old format read for pre 1.0 ini file. 119 | } 120 | else 121 | { 122 | for(int i=0;i 136 | /// Saves the currently known toggle groups with their shader hashes to the shadertoggler.ini file 137 | /// 138 | void saveShaderTogglerIniFile() 139 | { 140 | // format: first section with # of groups, then per group a section with pixel and vertex shaders, as well as their name and key value. 141 | // groups are stored with "Group" + group counter, starting with 0. 142 | CDataFile iniFile; 143 | iniFile.SetInt("AmountGroups", g_toggleGroups.size(), "", "General"); 144 | 145 | int groupCounter = 0; 146 | for(const auto& group: g_toggleGroups) 147 | { 148 | group.saveState(iniFile, groupCounter); 149 | groupCounter++; 150 | } 151 | iniFile.SetFileName(g_iniFileName); 152 | iniFile.Save(); 153 | } 154 | 155 | 156 | static void onInitCommandList(command_list *commandList) 157 | { 158 | commandList->create_private_data(); 159 | } 160 | 161 | 162 | static void onDestroyCommandList(command_list *commandList) 163 | { 164 | commandList->destroy_private_data(); 165 | } 166 | 167 | static void onResetCommandList(command_list *commandList) 168 | { 169 | CommandListDataContainer &commandListData = commandList->get_private_data(); 170 | commandListData.activePixelShaderPipeline = -1; 171 | commandListData.activeVertexShaderPipeline = -1; 172 | commandListData.activeComputeShaderPipeline = -1; 173 | } 174 | 175 | 176 | static void onInitPipeline(device *device, pipeline_layout, uint32_t subobjectCount, const pipeline_subobject *subobjects, pipeline pipelineHandle) 177 | { 178 | // shader has been created, we will now create a hash and store it with the handle we got. 179 | for (uint32_t i = 0; i < subobjectCount; ++i) 180 | { 181 | switch (subobjects[i].type) 182 | { 183 | case pipeline_subobject_type::vertex_shader: 184 | g_vertexShaderManager.addHashHandlePair(calculateShaderHash(subobjects[i].data), pipelineHandle.handle); 185 | break; 186 | case pipeline_subobject_type::pixel_shader: 187 | g_pixelShaderManager.addHashHandlePair(calculateShaderHash(subobjects[i].data), pipelineHandle.handle); 188 | break; 189 | case pipeline_subobject_type::compute_shader: 190 | g_computeShaderManager.addHashHandlePair(calculateShaderHash(subobjects[i].data), pipelineHandle.handle); 191 | break; 192 | } 193 | } 194 | } 195 | 196 | 197 | static void onDestroyPipeline(device *device, pipeline pipelineHandle) 198 | { 199 | g_pixelShaderManager.removeHandle(pipelineHandle.handle); 200 | g_vertexShaderManager.removeHandle(pipelineHandle.handle); 201 | g_computeShaderManager.removeHandle(pipelineHandle.handle); 202 | } 203 | 204 | 205 | static void displayIsPartOfToggleGroup() 206 | { 207 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.0f, 1.0f)); 208 | ImGui::SameLine(); 209 | ImGui::Text(" Shader is part of this toggle group."); 210 | ImGui::PopStyleColor(); 211 | } 212 | 213 | 214 | static void displayShaderManagerInfo(ShaderManager& toDisplay, const char* shaderType) 215 | { 216 | if(toDisplay.isInHuntingMode()) 217 | { 218 | ImGui::Text("# of %s shaders active: %d. # of %s shaders in group: %d", shaderType, toDisplay.getAmountShaderHashesCollected(), shaderType, toDisplay.getMarkedShaderCount()); 219 | ImGui::Text("Current selected %s shader: %d / %d.", shaderType, toDisplay.getActiveHuntedShaderIndex(), toDisplay.getAmountShaderHashesCollected()); 220 | if(toDisplay.isHuntedShaderMarked()) 221 | { 222 | displayIsPartOfToggleGroup(); 223 | } 224 | } 225 | } 226 | 227 | static void displayShaderManagerStats(ShaderManager& toDisplay, const char* shaderType) 228 | { 229 | ImGui::Text("# of pipelines with %s shaders: %d. # of different %s shaders gathered: %d.", shaderType, toDisplay.getPipelineCount(), shaderType, toDisplay.getShaderCount()); 230 | } 231 | 232 | 233 | static void onReshadeOverlay(reshade::api::effect_runtime *runtime) 234 | { 235 | if(g_toggleGroupIdShaderEditing>=0) 236 | { 237 | ImGui::SetNextWindowBgAlpha(g_overlayOpacity); 238 | ImGui::SetNextWindowPos(ImVec2(10, 10)); 239 | if (!ImGui::Begin("ShaderTogglerInfo", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | 240 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings)) 241 | { 242 | ImGui::End(); 243 | return; 244 | } 245 | string editingGroupName = ""; 246 | for(auto& group:g_toggleGroups) 247 | { 248 | if(group.getId()==g_toggleGroupIdShaderEditing) 249 | { 250 | editingGroupName = group.getName(); 251 | break; 252 | } 253 | } 254 | 255 | displayShaderManagerStats(g_vertexShaderManager, "vertex"); 256 | displayShaderManagerStats(g_pixelShaderManager, "pixel"); 257 | displayShaderManagerStats(g_computeShaderManager, "compute"); 258 | 259 | if(g_activeCollectorFrameCounter > 0) 260 | { 261 | const uint32_t counterValue = g_activeCollectorFrameCounter; 262 | ImGui::Text("Collecting active shaders... frames to go: %d", counterValue); 263 | } 264 | else 265 | { 266 | if(g_vertexShaderManager.isInHuntingMode() || g_pixelShaderManager.isInHuntingMode() || g_computeShaderManager.isInHuntingMode()) 267 | { 268 | ImGui::Text("Editing the shaders for group: %s", editingGroupName.c_str()); 269 | } 270 | displayShaderManagerInfo(g_vertexShaderManager, "vertex"); 271 | displayShaderManagerInfo(g_pixelShaderManager, "pixel"); 272 | displayShaderManagerInfo(g_computeShaderManager, "compute"); 273 | } 274 | ImGui::End(); 275 | } 276 | } 277 | 278 | 279 | static void onBindPipeline(command_list* commandList, pipeline_stage stages, pipeline pipelineHandle) 280 | { 281 | if(nullptr != commandList && pipelineHandle.handle != 0) 282 | { 283 | const bool handleHasPixelShaderAttached = g_pixelShaderManager.isKnownHandle(pipelineHandle.handle); 284 | const bool handleHasVertexShaderAttached = g_vertexShaderManager.isKnownHandle(pipelineHandle.handle); 285 | const bool handleHasComputeShaderAttached = g_computeShaderManager.isKnownHandle(pipelineHandle.handle); 286 | if(!handleHasPixelShaderAttached && !handleHasVertexShaderAttached && !handleHasComputeShaderAttached) 287 | { 288 | // draw call with unknown handle, don't collect it 289 | return; 290 | } 291 | CommandListDataContainer& commandListData = commandList->get_private_data(); 292 | // always do the following code as that has to run for every bind on a pipeline: 293 | if(g_activeCollectorFrameCounter > 0) 294 | { 295 | // in collection mode 296 | if(handleHasPixelShaderAttached) 297 | { 298 | g_pixelShaderManager.addActivePipelineHandle(pipelineHandle.handle); 299 | } 300 | if(handleHasVertexShaderAttached) 301 | { 302 | g_vertexShaderManager.addActivePipelineHandle(pipelineHandle.handle); 303 | } 304 | if(handleHasComputeShaderAttached) 305 | { 306 | g_computeShaderManager.addActivePipelineHandle(pipelineHandle.handle); 307 | } 308 | } 309 | else 310 | { 311 | commandListData.activePixelShaderPipeline = handleHasPixelShaderAttached ? pipelineHandle.handle : commandListData.activePixelShaderPipeline; 312 | commandListData.activeVertexShaderPipeline = handleHasVertexShaderAttached ? pipelineHandle.handle : commandListData.activeVertexShaderPipeline; 313 | commandListData.activeComputeShaderPipeline = handleHasComputeShaderAttached ? pipelineHandle.handle : commandListData.activeComputeShaderPipeline; 314 | } 315 | if((stages & pipeline_stage::pixel_shader) == pipeline_stage::pixel_shader) 316 | { 317 | if(handleHasPixelShaderAttached) 318 | { 319 | if(g_activeCollectorFrameCounter > 0) 320 | { 321 | // in collection mode 322 | g_pixelShaderManager.addActivePipelineHandle(pipelineHandle.handle); 323 | } 324 | commandListData.activePixelShaderPipeline = pipelineHandle.handle; 325 | } 326 | } 327 | if((stages & pipeline_stage::vertex_shader) == pipeline_stage::vertex_shader) 328 | { 329 | if(handleHasVertexShaderAttached) 330 | { 331 | if(g_activeCollectorFrameCounter > 0) 332 | { 333 | // in collection mode 334 | g_vertexShaderManager.addActivePipelineHandle(pipelineHandle.handle); 335 | } 336 | commandListData.activeVertexShaderPipeline = pipelineHandle.handle; 337 | } 338 | } 339 | if((stages & pipeline_stage::compute_shader) == pipeline_stage::compute_shader) 340 | { 341 | if(handleHasComputeShaderAttached) 342 | { 343 | if(g_activeCollectorFrameCounter > 0) 344 | { 345 | // in collection mode 346 | g_computeShaderManager.addActivePipelineHandle(pipelineHandle.handle); 347 | } 348 | commandListData.activeComputeShaderPipeline = pipelineHandle.handle; 349 | } 350 | } 351 | } 352 | } 353 | 354 | 355 | /// 356 | /// This function will return true if the command list specified has one or more shader hashes which are currently marked to be hidden. Otherwise false. 357 | /// 358 | /// 359 | /// true if the draw call has to be blocked 360 | bool blockDrawCallForCommandList(command_list* commandList) 361 | { 362 | if(nullptr==commandList) 363 | { 364 | return false; 365 | } 366 | 367 | const CommandListDataContainer &commandListData = commandList->get_private_data(); 368 | uint32_t shaderHash = g_pixelShaderManager.getShaderHash(commandListData.activePixelShaderPipeline); 369 | bool blockCall = g_pixelShaderManager.isBlockedShader(shaderHash); 370 | for(auto& group : g_toggleGroups) 371 | { 372 | blockCall |= group.isBlockedPixelShader(shaderHash); 373 | } 374 | shaderHash = g_vertexShaderManager.getShaderHash(commandListData.activeVertexShaderPipeline); 375 | blockCall |= g_vertexShaderManager.isBlockedShader(shaderHash); 376 | for(auto& group : g_toggleGroups) 377 | { 378 | blockCall |= group.isBlockedVertexShader(shaderHash); 379 | } 380 | shaderHash = g_computeShaderManager.getShaderHash(commandListData.activeComputeShaderPipeline); 381 | blockCall |= g_computeShaderManager.isBlockedShader(shaderHash); 382 | for(auto& group : g_toggleGroups) 383 | { 384 | blockCall |= group.isBlockedComputeShader(shaderHash); 385 | } 386 | return blockCall; 387 | } 388 | 389 | 390 | static bool onDraw(command_list* commandList, uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) 391 | { 392 | // check if for this command list the active shader handles are part of the blocked set. If so, return true 393 | return blockDrawCallForCommandList(commandList); 394 | } 395 | 396 | 397 | static bool onDrawIndexed(command_list* commandList, uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance) 398 | { 399 | // same as onDraw 400 | return blockDrawCallForCommandList(commandList); 401 | } 402 | 403 | 404 | static bool onDrawOrDispatchIndirect(command_list* commandList, indirect_command type, resource buffer, uint64_t offset, uint32_t draw_count, uint32_t stride) 405 | { 406 | switch(type) 407 | { 408 | case indirect_command::unknown: 409 | case indirect_command::draw: 410 | case indirect_command::draw_indexed: 411 | case indirect_command::dispatch: 412 | // same as OnDraw 413 | return blockDrawCallForCommandList(commandList); 414 | // the rest aren't blocked 415 | } 416 | return false; 417 | } 418 | 419 | 420 | static void onReshadePresent(effect_runtime* runtime) 421 | { 422 | if(g_activeCollectorFrameCounter>0) 423 | { 424 | --g_activeCollectorFrameCounter; 425 | } 426 | 427 | for(auto& group: g_toggleGroups) 428 | { 429 | if(group.isToggleKeyPressed(runtime)) 430 | { 431 | group.toggleActive(); 432 | // if the group's shaders are being edited, it should toggle the ones currently marked. 433 | if(group.getId() == g_toggleGroupIdShaderEditing) 434 | { 435 | g_vertexShaderManager.toggleHideMarkedShaders(); 436 | g_pixelShaderManager.toggleHideMarkedShaders(); 437 | g_computeShaderManager.toggleHideMarkedShaders(); 438 | } 439 | } 440 | } 441 | 442 | // hardcoded hunting keys. 443 | // If Ctrl is pressed too, it'll step to the next marked shader (if any) 444 | // Numpad 1: previous pixel shader 445 | // Numpad 2: next pixel shader 446 | // Numpad 3: mark current pixel shader as part of the toggle group 447 | // Numpad 4: previous vertex shader 448 | // Numpad 5: next vertex shader 449 | // Numpad 6: mark current vertex shader as part of the toggle group 450 | // Numpad 7: previous compute shader 451 | // Numpad 8: next compute shader 452 | // Numpad 9: mark current compute shader as part of the toggle group 453 | if(runtime->is_key_pressed(VK_NUMPAD1)) 454 | { 455 | g_pixelShaderManager.huntPreviousShader(runtime->is_key_down(VK_CONTROL)); 456 | } 457 | if(runtime->is_key_pressed(VK_NUMPAD2)) 458 | { 459 | g_pixelShaderManager.huntNextShader(runtime->is_key_down(VK_CONTROL)); 460 | } 461 | if(runtime->is_key_pressed(VK_NUMPAD3)) 462 | { 463 | g_pixelShaderManager.toggleMarkOnHuntedShader(); 464 | } 465 | if(runtime->is_key_pressed(VK_NUMPAD4)) 466 | { 467 | g_vertexShaderManager.huntPreviousShader(runtime->is_key_down(VK_CONTROL)); 468 | } 469 | if(runtime->is_key_pressed(VK_NUMPAD5)) 470 | { 471 | g_vertexShaderManager.huntNextShader(runtime->is_key_down(VK_CONTROL)); 472 | } 473 | if(runtime->is_key_pressed(VK_NUMPAD6)) 474 | { 475 | g_vertexShaderManager.toggleMarkOnHuntedShader(); 476 | } 477 | if(runtime->is_key_pressed(VK_NUMPAD7)) 478 | { 479 | g_computeShaderManager.huntPreviousShader(runtime->is_key_down(VK_CONTROL)); 480 | } 481 | if(runtime->is_key_pressed(VK_NUMPAD8)) 482 | { 483 | g_computeShaderManager.huntNextShader(runtime->is_key_down(VK_CONTROL)); 484 | } 485 | if(runtime->is_key_pressed(VK_NUMPAD9)) 486 | { 487 | g_computeShaderManager.toggleMarkOnHuntedShader(); 488 | } 489 | } 490 | 491 | 492 | /// 493 | /// Function which marks the end of a keybinding editing cycle 494 | /// 495 | /// 496 | /// 497 | void endKeyBindingEditing(bool acceptCollectedBinding, ToggleGroup& groupEditing) 498 | { 499 | if (acceptCollectedBinding && g_toggleGroupIdKeyBindingEditing == groupEditing.getId() && g_keyCollector.isValid()) 500 | { 501 | groupEditing.setToggleKey(g_keyCollector); 502 | } 503 | g_toggleGroupIdKeyBindingEditing = -1; 504 | g_keyCollector.clear(); 505 | } 506 | 507 | 508 | /// 509 | /// Function which marks the start of a keybinding editing cycle for the passed in toggle group 510 | /// 511 | /// 512 | void startKeyBindingEditing(ToggleGroup& groupEditing) 513 | { 514 | if (g_toggleGroupIdKeyBindingEditing == groupEditing.getId()) 515 | { 516 | return; 517 | } 518 | if (g_toggleGroupIdKeyBindingEditing >= 0) 519 | { 520 | endKeyBindingEditing(false, groupEditing); 521 | } 522 | g_toggleGroupIdKeyBindingEditing = groupEditing.getId(); 523 | } 524 | 525 | 526 | /// 527 | /// Function which marks the end of a shader editing cycle for a given toggle group 528 | /// 529 | /// 530 | /// 531 | void endShaderEditing(bool acceptCollectedShaderHashes, ToggleGroup& groupEditing) 532 | { 533 | if(acceptCollectedShaderHashes && g_toggleGroupIdShaderEditing == groupEditing.getId()) 534 | { 535 | groupEditing.storeCollectedHashes(g_pixelShaderManager.getMarkedShaderHashes(), g_vertexShaderManager.getMarkedShaderHashes(), g_computeShaderManager.getMarkedShaderHashes()); 536 | g_pixelShaderManager.stopHuntingMode(); 537 | g_vertexShaderManager.stopHuntingMode(); 538 | g_computeShaderManager.stopHuntingMode(); 539 | } 540 | g_toggleGroupIdShaderEditing = -1; 541 | } 542 | 543 | 544 | /// 545 | /// Function which marks the start of a shader editing cycle for a given toggle group. 546 | /// 547 | /// 548 | void startShaderEditing(ToggleGroup& groupEditing) 549 | { 550 | if(g_toggleGroupIdShaderEditing==groupEditing.getId()) 551 | { 552 | return; 553 | } 554 | if(g_toggleGroupIdShaderEditing >= 0) 555 | { 556 | endShaderEditing(false, groupEditing); 557 | } 558 | g_toggleGroupIdShaderEditing = groupEditing.getId(); 559 | g_activeCollectorFrameCounter = g_startValueFramecountCollectionPhase; 560 | g_pixelShaderManager.startHuntingMode(groupEditing.getPixelShaderHashes()); 561 | g_vertexShaderManager.startHuntingMode(groupEditing.getVertexShaderHashes()); 562 | g_computeShaderManager.startHuntingMode(groupEditing.getComputeShaderHashes()); 563 | 564 | // after copying them to the managers, we can now clear the group's shader. 565 | groupEditing.clearHashes(); 566 | } 567 | 568 | 569 | static void showHelpMarker(const char* desc) 570 | { 571 | ImGui::TextDisabled("(?)"); 572 | if (ImGui::IsItemHovered()) 573 | { 574 | ImGui::BeginTooltip(); 575 | ImGui::PushTextWrapPos(450.0f); 576 | ImGui::TextUnformatted(desc); 577 | ImGui::PopTextWrapPos(); 578 | ImGui::EndTooltip(); 579 | } 580 | } 581 | 582 | 583 | static void displaySettings(reshade::api::effect_runtime* runtime) 584 | { 585 | if(g_toggleGroupIdKeyBindingEditing >= 0) 586 | { 587 | // a keybinding is being edited. Read current pressed keys into the collector, cumulatively; 588 | g_keyCollector.collectKeysPressed(runtime); 589 | } 590 | 591 | if(ImGui::CollapsingHeader("General info and help")) 592 | { 593 | ImGui::PushTextWrapPos(); 594 | ImGui::TextUnformatted("The Shader Toggler allows you to create one or more groups with shaders to toggle on/off. You can assign a keyboard shortcut (including using keys like Shift, Alt and Control) to each group, including a handy name. Each group can have one or more vertex or pixel shaders assigned to it. When you press the assigned keyboard shortcut, any draw calls using these shaders will be disabled, effectively hiding the elements in the 3D scene."); 595 | ImGui::TextUnformatted("\nThe following (hardcoded) keyboard shortcuts are used when you click a group's 'Change Shaders' button:"); 596 | ImGui::TextUnformatted("* Numpad 1 and Numpad 2: previous/next pixel shader"); 597 | ImGui::TextUnformatted("* Ctrl + Numpad 1 and Ctrl + Numpad 2: previous/next marked pixel shader in the group"); 598 | ImGui::TextUnformatted("* Numpad 3: mark/unmark the current pixel shader as being part of the group"); 599 | ImGui::TextUnformatted("* Numpad 4 and Numpad 5: previous/next vertex shader"); 600 | ImGui::TextUnformatted("* Ctrl + Numpad 4 and Ctrl + Numpad 5: previous/next marked vertex shader in the group"); 601 | ImGui::TextUnformatted("* Numpad 6: mark/unmark the current vertex shader as being part of the group"); 602 | ImGui::TextUnformatted("* Numpad 7 and Numpad 8: previous/next compute shader"); 603 | ImGui::TextUnformatted("* Ctrl + Numpad 7 and Ctrl + Numpad 8: previous/next marked compute shader in the group"); 604 | ImGui::TextUnformatted("* Numpad 9: mark/unmark the current compute shader as being part of the group"); 605 | ImGui::TextUnformatted("\nWhen you step through the shaders, the current shader is disabled in the 3D scene so you can see if that's the shader you were looking for."); 606 | ImGui::TextUnformatted("When you're done, make sure you click 'Save all toggle groups' to preserve the groups you defined so next time you start your game they're loaded in and you can use them right away."); 607 | ImGui::PopTextWrapPos(); 608 | } 609 | 610 | ImGui::AlignTextToFramePadding(); 611 | if(ImGui::CollapsingHeader("Shader selection parameters", ImGuiTreeNodeFlags_DefaultOpen)) 612 | { 613 | ImGui::AlignTextToFramePadding(); 614 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); 615 | ImGui::SliderFloat("Overlay opacity", &g_overlayOpacity, 0.2f, 1.0f); 616 | ImGui::AlignTextToFramePadding(); 617 | ImGui::SliderInt("# of frames to collect", &g_startValueFramecountCollectionPhase, 10, 1000); 618 | ImGui::SameLine(); 619 | showHelpMarker("This is the number of frames the addon will collect active shaders. Set this to a high number if the shader you want to mark is only used occasionally. Only shaders that are used in the frames collected can be marked."); 620 | ImGui::PopItemWidth(); 621 | } 622 | ImGui::Separator(); 623 | 624 | if(ImGui::CollapsingHeader("List of Toggle Groups", ImGuiTreeNodeFlags_DefaultOpen)) 625 | { 626 | if(ImGui::Button(" New ")) 627 | { 628 | addDefaultGroup(); 629 | } 630 | ImGui::Separator(); 631 | 632 | std::vector toRemove; 633 | for(auto& group : g_toggleGroups) 634 | { 635 | ImGui::PushID(group.getId()); 636 | ImGui::AlignTextToFramePadding(); 637 | if(ImGui::Button("X")) 638 | { 639 | toRemove.push_back(group); 640 | } 641 | ImGui::SameLine(); 642 | ImGui::Text(" %d ", group.getId()); 643 | ImGui::SameLine(); 644 | if(ImGui::Button("Edit")) 645 | { 646 | group.setEditing(true); 647 | } 648 | 649 | ImGui::SameLine(); 650 | if(g_toggleGroupIdShaderEditing >= 0) 651 | { 652 | if(g_toggleGroupIdShaderEditing == group.getId()) 653 | { 654 | if(ImGui::Button(" Done ")) 655 | { 656 | endShaderEditing(true, group); 657 | } 658 | } 659 | else 660 | { 661 | ImGui::BeginDisabled(true); 662 | ImGui::Button(" "); 663 | ImGui::EndDisabled(); 664 | } 665 | } 666 | else 667 | { 668 | if(ImGui::Button("Change shaders")) 669 | { 670 | ImGui::SameLine(); 671 | startShaderEditing(group); 672 | } 673 | } 674 | ImGui::SameLine(); 675 | ImGui::Text(" %s (%s%s)", group.getName().c_str(), group.getToggleKeyAsString().c_str(), group.isActive() ? ", is active" : ""); 676 | if(group.isActiveAtStartup()) 677 | { 678 | ImGui::SameLine(); 679 | ImGui::Text(" (Active at startup)"); 680 | } 681 | if(group.isEditing()) 682 | { 683 | ImGui::Separator(); 684 | ImGui::Text("Edit group %d", group.getId()); 685 | 686 | // Name of group 687 | char tmpBuffer[150]; 688 | const string& name = group.getName(); 689 | strncpy_s(tmpBuffer, 150, name.c_str(), name.size()); 690 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.7f); 691 | ImGui::AlignTextToFramePadding(); 692 | ImGui::Text("Name"); 693 | ImGui::SameLine(ImGui::GetWindowWidth() * 0.25f); 694 | ImGui::InputText("##Name", tmpBuffer, 149); 695 | group.setName(tmpBuffer); 696 | ImGui::PopItemWidth(); 697 | 698 | // Key binding of group 699 | bool isKeyEditing = false; 700 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); 701 | ImGui::AlignTextToFramePadding(); 702 | ImGui::Text("Key shortcut"); 703 | ImGui::SameLine(ImGui::GetWindowWidth() * 0.25f); 704 | string textBoxContents = (g_toggleGroupIdKeyBindingEditing == group.getId()) ? g_keyCollector.getKeyAsString() : group.getToggleKeyAsString(); // The 'press a key' is inside keycollector 705 | string toggleKeyName = group.getToggleKeyAsString(); 706 | ImGui::InputText("##Key shortcut", (char*)textBoxContents.c_str(), textBoxContents.size(), ImGuiInputTextFlags_ReadOnly); 707 | if(ImGui::IsItemClicked()) 708 | { 709 | startKeyBindingEditing(group); 710 | } 711 | if(g_toggleGroupIdKeyBindingEditing == group.getId()) 712 | { 713 | isKeyEditing = true; 714 | ImGui::SameLine(); 715 | if(ImGui::Button("OK")) 716 | { 717 | endKeyBindingEditing(true, group); 718 | } 719 | ImGui::SameLine(); 720 | if(ImGui::Button("Cancel")) 721 | { 722 | endKeyBindingEditing(false, group); 723 | } 724 | } 725 | ImGui::PopItemWidth(); 726 | 727 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.7f); 728 | ImGui::Text(" "); 729 | ImGui::SameLine(ImGui::GetWindowWidth() * 0.25f); 730 | bool isDefaultActive = group.isActiveAtStartup(); 731 | ImGui::Checkbox("Is active at startup", &isDefaultActive); 732 | group.setIsActiveAtStartup(isDefaultActive); 733 | ImGui::PopItemWidth(); 734 | 735 | if(!isKeyEditing) 736 | { 737 | if(ImGui::Button("OK")) 738 | { 739 | group.setEditing(false); 740 | g_toggleGroupIdKeyBindingEditing = -1; 741 | g_keyCollector.clear(); 742 | } 743 | } 744 | ImGui::Separator(); 745 | } 746 | 747 | ImGui::PopID(); 748 | } 749 | if(toRemove.size() > 0) 750 | { 751 | // switch off keybinding editing or shader editing, if in progress 752 | g_toggleGroupIdKeyBindingEditing = -1; 753 | g_keyCollector.clear(); 754 | g_toggleGroupIdShaderEditing = -1; 755 | g_pixelShaderManager.stopHuntingMode(); 756 | g_vertexShaderManager.stopHuntingMode(); 757 | } 758 | for(const auto& group : toRemove) 759 | { 760 | std::erase(g_toggleGroups, group); 761 | } 762 | 763 | ImGui::Separator(); 764 | if(g_toggleGroups.size() > 0) 765 | { 766 | if(ImGui::Button("Save all Toggle Groups")) 767 | { 768 | saveShaderTogglerIniFile(); 769 | } 770 | } 771 | } 772 | } 773 | 774 | 775 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD fdwReason, LPVOID) 776 | { 777 | switch (fdwReason) 778 | { 779 | case DLL_PROCESS_ATTACH: 780 | { 781 | if(!reshade::register_addon(hModule)) 782 | { 783 | return FALSE; 784 | } 785 | 786 | // We'll pass a nullptr for the module handle so we get the containing process' executable + path. We can't use the reshade's api as we don't have the runtime 787 | // and we can't use reshade's handle because under vulkan reshade is stored in a central space and therefore it won't get the folder of the exe (where the reshade dll is located as well). 788 | WCHAR buf[MAX_PATH]; 789 | const std::filesystem::path dllPath = GetModuleFileNameW(nullptr, buf, ARRAYSIZE(buf)) ? buf : std::filesystem::path(); // /shadertoggler.addon64 790 | const std::filesystem::path basePath = dllPath.parent_path(); // 791 | const std::string& hashFileName = HASH_FILE_NAME; 792 | g_iniFileName = (basePath / hashFileName).string(); // /shadertoggler.ini 793 | reshade::register_event(onInitPipeline); 794 | reshade::register_event(onInitCommandList); 795 | reshade::register_event(onDestroyCommandList); 796 | reshade::register_event(onResetCommandList); 797 | reshade::register_event(onDestroyPipeline); 798 | reshade::register_event(onReshadeOverlay); 799 | reshade::register_event(onReshadePresent); 800 | reshade::register_event(onBindPipeline); 801 | reshade::register_event(onDraw); 802 | reshade::register_event(onDrawIndexed); 803 | reshade::register_event(onDrawOrDispatchIndirect); 804 | reshade::register_overlay(nullptr, &displaySettings); 805 | loadShaderTogglerIniFile(); 806 | } 807 | break; 808 | case DLL_PROCESS_DETACH: 809 | reshade::unregister_event(onReshadePresent); 810 | reshade::unregister_event(onDestroyPipeline); 811 | reshade::unregister_event(onInitPipeline); 812 | reshade::unregister_event(onReshadeOverlay); 813 | reshade::unregister_event(onBindPipeline); 814 | reshade::unregister_event(onDraw); 815 | reshade::unregister_event(onDrawIndexed); 816 | reshade::unregister_event(onDrawOrDispatchIndirect); 817 | reshade::unregister_event(onInitCommandList); 818 | reshade::unregister_event(onDestroyCommandList); 819 | reshade::unregister_event(onResetCommandList); 820 | reshade::unregister_overlay(nullptr, &displaySettings); 821 | reshade::unregister_addon(hModule); 822 | break; 823 | } 824 | 825 | return TRUE; 826 | } 827 | -------------------------------------------------------------------------------- /src/Include/reshade_api_pipeline.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Patrick Mours 3 | * License: https://github.com/crosire/reshade#license 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "reshade_api_resource.hpp" 9 | 10 | namespace reshade::api 11 | { 12 | /// 13 | /// A list of flags that represent the available shader stages in the render pipeline. 14 | /// 15 | enum class shader_stage : uint32_t 16 | { 17 | vertex = 0x1, 18 | hull = 0x2, 19 | domain = 0x4, 20 | geometry = 0x8, 21 | pixel = 0x10, 22 | compute = 0x20, 23 | 24 | all = 0x7FFFFFFF, 25 | all_compute = compute, 26 | all_graphics = vertex | hull | domain | geometry | pixel 27 | }; 28 | RESHADE_DEFINE_ENUM_FLAG_OPERATORS(shader_stage); 29 | 30 | /// 31 | /// A list of flags that represent the available pipeline stages in the render pipeline. 32 | /// 33 | enum class pipeline_stage : uint32_t 34 | { 35 | vertex_shader = 0x8, 36 | hull_shader = 0x10, 37 | domain_shader = 0x20, 38 | geometry_shader = 0x40, 39 | pixel_shader = 0x80, 40 | compute_shader = 0x800, 41 | 42 | input_assembler = 0x2, 43 | stream_output = 0x4, 44 | rasterizer = 0x100, 45 | depth_stencil = 0x200, 46 | output_merger = 0x400, 47 | 48 | all = 0x7FFFFFFF, 49 | all_compute = compute_shader, 50 | all_graphics = vertex_shader | hull_shader | domain_shader | geometry_shader | pixel_shader | input_assembler | stream_output | rasterizer | depth_stencil | output_merger, 51 | all_shader_stages = vertex_shader | hull_shader | domain_shader | geometry_shader | pixel_shader | compute_shader 52 | }; 53 | RESHADE_DEFINE_ENUM_FLAG_OPERATORS(pipeline_stage); 54 | 55 | /// 56 | /// The available descriptor types. 57 | /// 58 | enum class descriptor_type : uint32_t 59 | { 60 | sampler = 0, 61 | sampler_with_resource_view = 1, 62 | shader_resource_view = 2, 63 | unordered_access_view = 3, 64 | constant_buffer = 6, 65 | shader_storage_buffer = 7 66 | }; 67 | 68 | /// 69 | /// The available pipeline layout parameter types. 70 | /// 71 | enum class pipeline_layout_param_type : uint32_t 72 | { 73 | push_constants = 1, 74 | push_descriptors = 2, 75 | descriptor_set = 0 76 | }; 77 | 78 | /// 79 | /// Describes a range of constants in a pipeline layout. 80 | /// 81 | struct constant_range 82 | { 83 | /// 84 | /// OpenGL uniform buffer binding index. 85 | /// 86 | uint32_t binding = 0; 87 | /// 88 | /// D3D10/D3D11/D3D12 constant buffer register index. 89 | /// 90 | uint32_t dx_register_index = 0; 91 | /// 92 | /// D3D12 constant buffer register space. 93 | /// 94 | uint32_t dx_register_space = 0; 95 | /// 96 | /// Number of constants in this range (in 32-bit values). 97 | /// 98 | uint32_t count = 0; 99 | /// 100 | /// Shader pipeline stages that can make use of the constants in this range. 101 | /// 102 | shader_stage visibility = shader_stage::all; 103 | }; 104 | 105 | /// 106 | /// Describes a range of descriptors in a descriptor set layout. 107 | /// 108 | struct descriptor_range 109 | { 110 | /// 111 | /// OpenGL/Vulkan binding index (layout(binding=X) in GLSL). 112 | /// In D3D this is equivalent to the offset (in descriptors) of this range in the descriptor set (since each binding can only have an array size of 1). 113 | /// 114 | uint32_t binding = 0; 115 | /// 116 | /// D3D9/D3D10/D3D11/D3D12 shader register index (register(xX) in HLSL). 117 | /// 118 | uint32_t dx_register_index = 0; 119 | /// 120 | /// D3D12 register space (register(..., spaceX) in HLSL). 121 | /// 122 | uint32_t dx_register_space = 0; 123 | /// 124 | /// Number of descriptors in this range. 125 | /// 126 | uint32_t count = 0; 127 | /// 128 | /// Shader pipeline stages that can make use of the descriptors in this range. 129 | /// 130 | shader_stage visibility = shader_stage::all; 131 | /// 132 | /// Size of the array in case this is an array binding. 133 | /// Only meaningful in Vulkan, in OpenGL and other APIs this has to be 1 (since each array element is a separate binding there). 134 | /// If this is less than the total number of descriptors specified in , then the remaining descriptors are assigned a separate binding (with an array size of 1), with the binding index incrementing with each additional descriptor. 135 | /// 136 | uint32_t array_size = 1; 137 | /// 138 | /// Type of the descriptors in this range. 139 | /// 140 | descriptor_type type = descriptor_type::sampler; 141 | }; 142 | 143 | /// 144 | /// Describes a single parameter in a pipeline layout. 145 | /// 146 | struct pipeline_layout_param 147 | { 148 | constexpr pipeline_layout_param() : push_descriptors() {} 149 | constexpr pipeline_layout_param(const constant_range &push_constants) : type(pipeline_layout_param_type::push_constants), push_constants(push_constants) {} 150 | constexpr pipeline_layout_param(const descriptor_range &push_descriptors) : type(pipeline_layout_param_type::push_descriptors), push_descriptors(push_descriptors) {} 151 | constexpr pipeline_layout_param(uint32_t count, const descriptor_range *ranges) : type(pipeline_layout_param_type::descriptor_set), descriptor_set({ count, ranges }) {} 152 | 153 | /// 154 | /// Type of the parameter. 155 | /// 156 | pipeline_layout_param_type type = pipeline_layout_param_type::push_descriptors; 157 | 158 | union 159 | { 160 | /// 161 | /// Used when parameter type is . 162 | /// 163 | constant_range push_constants; 164 | 165 | /// 166 | /// Used when parameter type is . 167 | /// 168 | descriptor_range push_descriptors; 169 | 170 | /// 171 | /// Used when parameter type is . 172 | /// 173 | struct 174 | { 175 | uint32_t count; 176 | const descriptor_range *ranges; 177 | } descriptor_set; 178 | }; 179 | }; 180 | 181 | /// 182 | /// An opaque handle to a pipeline layout object. 183 | /// In D3D12 this is a pointer to a 'ID3D12RootSignature' object, in Vulkan a 'VkPipelineLayout' handle. 184 | /// 185 | RESHADE_DEFINE_HANDLE(pipeline_layout); 186 | 187 | /// 188 | /// The fill mode to use when rendering triangles. 189 | /// 190 | enum class fill_mode : uint32_t 191 | { 192 | solid = 0, 193 | wireframe = 1, 194 | point = 2 195 | }; 196 | 197 | /// 198 | /// Indicates triangles facing a particular direction are not drawn. 199 | /// 200 | enum class cull_mode : uint32_t 201 | { 202 | none = 0, 203 | front = 1, 204 | back = 2, 205 | front_and_back = front | back 206 | }; 207 | RESHADE_DEFINE_ENUM_FLAG_OPERATORS(cull_mode); 208 | 209 | /// 210 | /// The available logic operations. 211 | /// 212 | enum class logic_op : uint32_t 213 | { 214 | clear = 0, 215 | bitwise_and = 1, 216 | bitwise_and_reverse = 2, 217 | copy = 3, 218 | bitwise_and_inverted = 4, 219 | noop = 5, 220 | bitwise_xor = 6, 221 | bitwise_or = 7, 222 | bitwise_nor = 8, 223 | equivalent = 9, 224 | invert = 10, 225 | bitwise_or_reverse = 11, 226 | copy_inverted = 12, 227 | bitwise_or_inverted = 13, 228 | bitwise_nand = 14, 229 | set = 15 230 | }; 231 | 232 | /// 233 | /// The available color or alpha blending operations. 234 | /// 235 | enum class blend_op : uint32_t 236 | { 237 | add = 0, 238 | subtract = 1, 239 | reverse_subtract = 2, 240 | min = 3, 241 | max = 4 242 | }; 243 | 244 | /// 245 | /// The available blend factors in color or alpha blending operations. 246 | /// 247 | enum class blend_factor : uint32_t 248 | { 249 | zero = 0, 250 | one = 1, 251 | source_color = 2, 252 | one_minus_source_color = 3, 253 | dest_color = 4, 254 | one_minus_dest_color = 5, 255 | source_alpha = 6, 256 | one_minus_source_alpha = 7, 257 | dest_alpha = 8, 258 | one_minus_dest_alpha = 9, 259 | constant_color = 10, 260 | one_minus_constant_color = 11, 261 | constant_alpha = 12, 262 | one_minus_constant_alpha = 13, 263 | source_alpha_saturate = 14, 264 | source1_color = 15, 265 | one_minus_source1_color = 16, 266 | source1_alpha = 17, 267 | one_minus_source1_alpha = 18 268 | }; 269 | 270 | /// 271 | /// The available stencil operations that can be performed during depth-stencil testing. 272 | /// 273 | enum class stencil_op : uint32_t 274 | { 275 | keep = 0, 276 | zero = 1, 277 | replace = 2, 278 | increment_saturate = 3, 279 | decrement_saturate = 4, 280 | invert = 5, 281 | increment = 6, 282 | decrement = 7 283 | }; 284 | 285 | /// 286 | /// Specifies how the pipeline interprets vertex data that is bound to the vertex input stage and subsequently renders it. 287 | /// 288 | enum class primitive_topology : uint32_t 289 | { 290 | undefined = 0, 291 | 292 | point_list = 1, 293 | line_list = 2, 294 | line_strip = 3, 295 | triangle_list = 4, 296 | triangle_strip = 5, 297 | triangle_fan = 6, 298 | line_list_adj = 10, 299 | line_strip_adj = 11, 300 | triangle_list_adj = 12, 301 | triangle_strip_adj = 13, 302 | 303 | patch_list_01_cp = 33, 304 | patch_list_02_cp, 305 | patch_list_03_cp, 306 | patch_list_04_cp, 307 | patch_list_05_cp, 308 | patch_list_06_cp, 309 | patch_list_07_cp, 310 | patch_list_08_cp, 311 | patch_list_09_cp, 312 | patch_list_10_cp, 313 | patch_list_11_cp, 314 | patch_list_12_cp, 315 | patch_list_13_cp, 316 | patch_list_14_cp, 317 | patch_list_15_cp, 318 | patch_list_16_cp, 319 | patch_list_17_cp, 320 | patch_list_18_cp, 321 | patch_list_19_cp, 322 | patch_list_20_cp, 323 | patch_list_21_cp, 324 | patch_list_22_cp, 325 | patch_list_23_cp, 326 | patch_list_24_cp, 327 | patch_list_25_cp, 328 | patch_list_26_cp, 329 | patch_list_27_cp, 330 | patch_list_28_cp, 331 | patch_list_29_cp, 332 | patch_list_30_cp, 333 | patch_list_31_cp, 334 | patch_list_32_cp 335 | }; 336 | 337 | /// 338 | /// Describes a shader object. 339 | /// 340 | struct shader_desc 341 | { 342 | /// 343 | /// Shader source code or binary. 344 | /// 345 | const void *code = nullptr; 346 | /// 347 | /// Size (in bytes) of the shader source or binary. 348 | /// 349 | size_t code_size = 0; 350 | /// 351 | /// Optional entry point name if the shader source or binary contains multiple entry points. 352 | /// Can be if it does not, or to use the default "main" entry point. 353 | /// 354 | const char *entry_point = nullptr; 355 | 356 | /// 357 | /// Number of entries in the and arrays. 358 | /// This is meaningful only when the shader binary is a SPIR-V module and is ignored otherwise. 359 | /// 360 | uint32_t spec_constants = 0; 361 | /// 362 | /// Pointer to an array of specialization constant indices. 363 | /// 364 | const uint32_t *spec_constant_ids = nullptr; 365 | /// 366 | /// Pointer to an array of constant values, one for each specialization constant index in . 367 | /// 368 | const uint32_t *spec_constant_values = nullptr; 369 | }; 370 | 371 | /// 372 | /// Describes a single element in the vertex layout for the input-assembler stage. 373 | /// 374 | struct input_element 375 | { 376 | /// 377 | /// GLSL attribute location associated with this element (layout(location = X)). 378 | /// 379 | uint32_t location = 0; 380 | /// 381 | /// HLSL semantic associated with this element. 382 | /// 383 | const char *semantic = nullptr; 384 | /// 385 | /// Optional index for the HLSL semantic (e.g. for "TEXCOORD1" set to "TEXCOORD" and to 1). 386 | /// 387 | uint32_t semantic_index = 0; 388 | /// 389 | /// Format of the element data. 390 | /// 391 | format format = format::unknown; 392 | /// 393 | /// Index of the vertex buffer binding. 394 | /// 395 | uint32_t buffer_binding = 0; 396 | /// 397 | /// Offset (in bytes) from the start of the vertex to this element. 398 | /// 399 | uint32_t offset = 0; 400 | /// 401 | /// Stride of the entire vertex (this has to be consistent for all elements per vertex buffer binding). 402 | /// Set to zero in case this is unknown. 403 | /// 404 | uint32_t stride = 0; 405 | /// 406 | /// Number of instances to draw using the same per-instance data before advancing by one element. 407 | /// This has to be consistent for all elements per vertex buffer binding. 408 | /// Set to zero to indicate that this element is per-vertex rather than per-instance. 409 | /// 410 | uint32_t instance_step_rate = 0; 411 | }; 412 | 413 | /// 414 | /// Describes the state of the stream-output stage. 415 | /// 416 | struct stream_output_desc 417 | { 418 | /// 419 | /// Index of the stream output stream to be sent to the rasterizer stage. 420 | /// 421 | uint32_t rasterized_stream = 0; 422 | }; 423 | 424 | /// 425 | /// Describes the state of the output-merger stage. 426 | /// 427 | struct blend_desc 428 | { 429 | /// 430 | /// Use alpha-to-coverage as a multisampling technique when setting a pixel to a render target. 431 | /// 432 | bool alpha_to_coverage_enable = false; 433 | /// 434 | /// Enable or disable blending for each render target. 435 | /// 436 | bool blend_enable[8] = { false, false, false, false, false, false, false, false }; 437 | /// 438 | /// Enable or disable a logical operation for each render target. 439 | /// 440 | bool logic_op_enable[8] = { false, false, false, false, false, false, false, false }; 441 | /// 442 | /// Source to use for the RGB value that the pixel shader outputs. 443 | /// 444 | blend_factor source_color_blend_factor[8] = { blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one }; 445 | /// 446 | /// Destination to use for the current RGB value in the render target. 447 | /// 448 | blend_factor dest_color_blend_factor[8] = { blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero }; 449 | /// 450 | /// Operation to use to combine and . 451 | /// 452 | blend_op color_blend_op[8] = { blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add }; 453 | /// 454 | /// Source to use for the alpha value that the pixel shader outputs. 455 | /// 456 | blend_factor source_alpha_blend_factor[8] = { blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one }; 457 | /// 458 | /// Destination to use for the current alpha value in the render target. 459 | /// 460 | blend_factor dest_alpha_blend_factor[8] = { blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero }; 461 | /// 462 | /// Operation to use to combine and . 463 | /// 464 | blend_op alpha_blend_op[8] = { blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add }; 465 | /// 466 | /// Constant RGBA value to use when or is . 467 | /// 468 | float blend_constant[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; 469 | /// 470 | /// Logical operation for each render target. Ignored if is . 471 | /// 472 | logic_op logic_op[8] = { logic_op::noop, logic_op::noop, logic_op::noop, logic_op::noop, logic_op::noop, logic_op::noop, logic_op::noop, logic_op::noop }; 473 | /// 474 | /// A write mask specifying which color components are written to each render target. Bitwise combination of 0x1 for red, 0x2 for green, 0x4 for blue and 0x8 for alpha. 475 | /// 476 | uint8_t render_target_write_mask[8] = { 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF }; 477 | }; 478 | 479 | /// 480 | /// Describes the state of the rasterizer stage. 481 | /// 482 | struct rasterizer_desc 483 | { 484 | /// 485 | /// Fill mode to use when rendering triangles. 486 | /// 487 | fill_mode fill_mode = fill_mode::solid; 488 | /// 489 | /// Triangles facing the specified direction are not drawn. 490 | /// 491 | cull_mode cull_mode = cull_mode::back; 492 | /// 493 | /// Determines if a triangle is front or back-facing. 494 | /// 495 | bool front_counter_clockwise = false; 496 | /// 497 | /// Depth value added to a given pixel. 498 | /// 499 | float depth_bias = 0.0f; 500 | /// 501 | /// Maximum depth bias of a pixel. 502 | /// 503 | float depth_bias_clamp = 0.0f; 504 | /// 505 | /// Scalar on the slope of a given pixel. 506 | /// 507 | float slope_scaled_depth_bias = 0.0f; 508 | /// 509 | /// Enable or disable clipping based on distance. 510 | /// 511 | bool depth_clip_enable = true; 512 | /// 513 | /// Enable or disable scissor testing (scissor rectangle culling). 514 | /// 515 | bool scissor_enable = false; 516 | /// 517 | /// Use the quadrilateral or alpha line anti-aliasing algorithm on multisample antialiasing render targets. 518 | /// 519 | bool multisample_enable = false; 520 | /// 521 | /// Enable or disable line antialiasing. Only applies if doing line drawing and is . 522 | /// 523 | bool antialiased_line_enable = false; 524 | /// 525 | /// Enable or disable conservative rasterization mode. 526 | /// 527 | uint32_t conservative_rasterization = 0; 528 | }; 529 | 530 | /// 531 | /// Describes the state of the depth-stencil stage. 532 | /// 533 | struct depth_stencil_desc 534 | { 535 | /// 536 | /// Enable or disable depth testing. 537 | /// 538 | bool depth_enable = true; 539 | /// 540 | /// Enable or disable writes to the depth-stencil buffer. 541 | /// 542 | bool depth_write_mask = true; 543 | /// 544 | /// Comparison function to use to compare new depth value from a fragment against current depth value in the depth-stencil buffer. 545 | /// 546 | compare_op depth_func = compare_op::less; 547 | /// 548 | /// Enable or disable stencil testing. 549 | /// 550 | bool stencil_enable = false; 551 | /// 552 | /// Mask applied to stencil values read from the depth-stencil buffer. 553 | /// 554 | uint8_t stencil_read_mask = 0xFF; 555 | /// 556 | /// Mask applied to stencil values written to the depth-stencil buffer. 557 | /// 558 | uint8_t stencil_write_mask = 0xFF; 559 | /// 560 | /// Reference value to perform against when doing stencil testing. 561 | /// 562 | uint8_t stencil_reference_value = 0; 563 | /// 564 | /// Comparison function to use to compare new stencil value from a fragment against current stencil value for pixels whose surface normal is facing towards the camera. 565 | /// 566 | compare_op front_stencil_func = compare_op::always; 567 | /// 568 | /// Stencil operation to perform when stencil testing and depth testing both pass for pixels whose surface normal is facing towards the camera. 569 | /// 570 | stencil_op front_stencil_pass_op = stencil_op::keep; 571 | /// 572 | /// Stencil operation to perform when stencil testing fails for pixels whose surface normal is towards the camera. 573 | /// 574 | stencil_op front_stencil_fail_op = stencil_op::keep; 575 | /// 576 | /// Stencil operation to perform when stencil testing passes and depth testing fails for pixels whose surface normal is facing towards the camera. 577 | /// 578 | stencil_op front_stencil_depth_fail_op = stencil_op::keep; 579 | /// 580 | /// Comparison function to use to compare new stencil value from a fragment against current stencil value for pixels whose surface normal is facing away from the camera. 581 | /// 582 | compare_op back_stencil_func = compare_op::always; 583 | /// 584 | /// Stencil operation to perform when stencil testing and depth testing both pass for pixels whose surface normal is facing away from the camera. 585 | /// 586 | stencil_op back_stencil_pass_op = stencil_op::keep; 587 | /// 588 | /// Stencil operation to perform when stencil testing fails for pixels whose surface normal is facing away from the camera. 589 | /// 590 | stencil_op back_stencil_fail_op = stencil_op::keep; 591 | /// 592 | /// Stencil operation to perform when stencil testing passes and depth testing fails for pixels whose surface normal is facing away from the camera. 593 | /// 594 | stencil_op back_stencil_depth_fail_op = stencil_op::keep; 595 | }; 596 | 597 | /// 598 | /// The available pipeline sub-object types. 599 | /// 600 | enum class pipeline_subobject_type 601 | { 602 | unknown, 603 | /// 604 | /// Vertex shader to use. 605 | /// Sub-object data is a pointer to a . 606 | /// 607 | /// 608 | /// 609 | vertex_shader, 610 | /// 611 | /// Hull shader to use. 612 | /// Sub-object data is a pointer to a . 613 | /// 614 | /// 615 | /// 616 | hull_shader, 617 | /// 618 | /// Domain shader to use. 619 | /// Sub-object data is a pointer to a . 620 | /// 621 | /// 622 | /// 623 | domain_shader, 624 | /// 625 | /// Geometry shader to use. 626 | /// Sub-object data is a pointer to a . 627 | /// 628 | /// 629 | /// 630 | geometry_shader, 631 | /// 632 | /// Pixel shader to use. 633 | /// Sub-object data is a pointer to a . 634 | /// 635 | /// 636 | /// 637 | pixel_shader, 638 | /// 639 | /// Compute shader to use. 640 | /// Sub-object data is a pointer to a . 641 | /// 642 | /// 643 | /// 644 | compute_shader, 645 | /// 646 | /// Vertex layout for the input-assembler stage. 647 | /// Sub-object data is a pointer to an array of . 648 | /// 649 | /// 650 | input_layout, 651 | /// 652 | /// State of the stream-output stage. 653 | /// Sub-object data is a pointer to a . 654 | /// 655 | /// 656 | stream_output_state, 657 | /// 658 | /// State of the output-merger stage. 659 | /// Sub-object data is a pointer to a . 660 | /// 661 | /// 662 | blend_state, 663 | /// 664 | /// State of the rasterizer stage. 665 | /// Sub-object data is a pointer to a . 666 | /// 667 | /// 668 | rasterizer_state, 669 | /// 670 | /// State of the depth-stencil stage. 671 | /// Sub-object data is a pointer to a . 672 | /// 673 | /// 674 | depth_stencil_state, 675 | /// 676 | /// Primitive topology to use when rendering. 677 | /// Sub-object data is a pointer to a value. 678 | /// 679 | primitive_topology, 680 | /// 681 | /// Format of the depth-stencil view that may be used with this pipeline. 682 | /// Sub-object data is a pointer to a value. 683 | /// 684 | depth_stencil_format, 685 | /// 686 | /// Formats of the render target views that may be used with this pipeline. 687 | /// Sub-object data is a pointer to an array of values. 688 | /// 689 | render_target_formats, 690 | /// 691 | /// Mask applied to the coverage mask for a fragment during rasterization. 692 | /// Sub-object data is a pointer to a 32-bit unsigned integer value. 693 | /// 694 | sample_mask, 695 | /// 696 | /// Number of samples used in rasterization. 697 | /// Sub-object data is a pointer to a 32-bit unsigned integer value. 698 | /// 699 | sample_count, 700 | /// 701 | /// Maximum number of viewports that may be bound via with this pipeline. 702 | /// Sub-object data is a pointer to a 32-bit unsigned integer value. 703 | /// 704 | viewport_count, 705 | /// 706 | /// States that may be dynamically updated via after binding this pipeline. 707 | /// Sub-object data is a pointer to an array of values. 708 | /// 709 | dynamic_pipeline_states, 710 | /// 711 | /// Maximum number of vertices a draw call with this pipeline will draw. 712 | /// Sub-object data is a pointer to a 32-bit unsigned integer value. 713 | /// 714 | max_vertex_count 715 | }; 716 | 717 | /// 718 | /// Describes a pipeline sub-object. 719 | /// 720 | struct pipeline_subobject 721 | { 722 | /// 723 | /// Type of the specified sub-object . 724 | /// 725 | pipeline_subobject_type type = pipeline_subobject_type::unknown; 726 | /// 727 | /// Number of sub-object descriptions. 728 | /// This should usually be 1, except for array sub-objects like (where this specifies the number of render targets) or . 729 | /// 730 | uint32_t count = 0; 731 | /// 732 | /// Pointer to an array of sub-object descriptions (which should be as large as the specified ). 733 | /// Depending on the sub-object this should be a pointer to a , , , or ... 734 | /// 735 | void *data = nullptr; 736 | }; 737 | 738 | /// 739 | /// An opaque handle to a pipeline state object. 740 | /// In D3D9, D3D10, D3D11 or D3D12 this is a pointer to a 'IDirect3D(...)Shader', 'ID3D10(...)(Shader/State)', 'ID3D11(...)(Shader/State)' or 'ID3D12PipelineState' object, in Vulkan a 'VkPipeline' handle. 741 | /// 742 | RESHADE_DEFINE_HANDLE(pipeline); 743 | 744 | /// 745 | /// A constant buffer resource descriptor. 746 | /// 747 | struct buffer_range 748 | { 749 | /// 750 | /// Constant buffer resource. 751 | /// 752 | resource buffer = { 0 }; 753 | /// 754 | /// Offset from the start of the buffer resource (in bytes). 755 | /// 756 | uint64_t offset = 0; 757 | /// 758 | /// Number of elements this range covers in the buffer resource (in bytes). 759 | /// Set to -1 (UINT64_MAX) to indicate that the whole buffer should be used. 760 | /// 761 | uint64_t size = UINT64_MAX; 762 | }; 763 | 764 | /// 765 | /// A combined sampler and resource view descriptor. 766 | /// 767 | struct sampler_with_resource_view 768 | { 769 | /// 770 | /// Sampler to sampler the shader resource view with. 771 | /// 772 | sampler sampler = { 0 }; 773 | /// 774 | /// Shader resource view. 775 | /// 776 | resource_view view = { 0 }; 777 | }; 778 | 779 | /// 780 | /// An opaque handle to a descriptor set. 781 | /// In Vulkan this is a 'VkDescriptorSet' handle. 782 | /// 783 | RESHADE_DEFINE_HANDLE(descriptor_set); 784 | 785 | /// 786 | /// All information needed to copy descriptors between descriptor sets. 787 | /// 788 | struct descriptor_set_copy 789 | { 790 | /// 791 | /// Descriptor set to copy from. 792 | /// 793 | descriptor_set source_set = { 0 }; 794 | /// 795 | /// Index of the binding in the source descriptor set. 796 | /// 797 | uint32_t source_binding = 0; 798 | /// 799 | /// Array index in the specified source binding to begin copying from. 800 | /// 801 | uint32_t source_array_offset = 0; 802 | /// 803 | /// Descriptor set to copy to. 804 | /// 805 | descriptor_set dest_set = { 0 }; 806 | /// 807 | /// Index of the binding in the destination descriptor set. 808 | /// 809 | uint32_t dest_binding = 0; 810 | /// 811 | /// Array index in the specified destination binding to begin copying to. 812 | /// 813 | uint32_t dest_array_offset = 0; 814 | /// 815 | /// Number of descriptors to copy. 816 | /// 817 | uint32_t count = 0; 818 | }; 819 | 820 | /// 821 | /// All information needed to update descriptors in a descriptor set. 822 | /// 823 | struct descriptor_set_update 824 | { 825 | /// 826 | /// Descriptor set to update. 827 | /// 828 | descriptor_set set = { 0 }; 829 | /// 830 | /// OpenGL/Vulkan binding index in the descriptor set. 831 | /// In D3D this is equivalent to the offset (in descriptors) from the start of the set. 832 | /// 833 | uint32_t binding = 0; 834 | /// 835 | /// Array index in the specified to begin updating at. 836 | /// Only meaningful in Vulkan, in OpenGL and other APIs this has to be 0 (since each GLSL array element gets a separate binding index). 837 | /// 838 | uint32_t array_offset = 0; 839 | /// 840 | /// Number of descriptors to update, starting at the specified . 841 | /// If the specified has fewer than array elements starting from , then the remainder will be used to update the subsequent binding starting at array element zero, recursively. 842 | /// 843 | uint32_t count = 0; 844 | /// 845 | /// Type of the specified . 846 | /// 847 | descriptor_type type = descriptor_type::sampler; 848 | /// 849 | /// Pointer to an array of descriptors to update in the set (which should be as large as the specified ). 850 | /// Depending on the descriptor this should be pointer to an array of , , or . 851 | /// 852 | const void *descriptors = nullptr; 853 | }; 854 | 855 | /// 856 | /// An opaque handle to a descriptor pool. 857 | /// In D3D12 this is a pointer to a 'ID3D12DescriptorHeap' object, in Vulkan a 'VkDescriptorPool' handle. 858 | /// 859 | RESHADE_DEFINE_HANDLE(descriptor_pool); 860 | 861 | /// 862 | /// The available query types. 863 | /// 864 | enum class query_type 865 | { 866 | occlusion = 0, 867 | binary_occlusion = 1, 868 | timestamp = 2, 869 | pipeline_statistics = 3, 870 | stream_output_statistics_0 = 4, 871 | stream_output_statistics_1, 872 | stream_output_statistics_2, 873 | stream_output_statistics_3 874 | }; 875 | 876 | /// 877 | /// An opaque handle to a query pool. 878 | /// In D3D12 this is a pointer to a 'ID3D12QueryHeap' object, in Vulkan a 'VkQueryPool' handle. 879 | /// 880 | RESHADE_DEFINE_HANDLE(query_pool); 881 | 882 | /// 883 | /// A list of all possible render pipeline states that can be set independent of pipeline state objects. 884 | /// Support for these varies between render APIs (e.g. modern APIs like D3D12 and Vulkan support much less dynamic states than D3D9). 885 | /// 886 | enum class dynamic_state 887 | { 888 | unknown = 0, 889 | 890 | alpha_test_enable = 15, 891 | alpha_reference_value = 24, 892 | alpha_func = 25, 893 | srgb_write_enable = 194, 894 | primitive_topology = 1000, 895 | sample_mask = 162, 896 | 897 | // Blend state 898 | 899 | alpha_to_coverage_enable = 1003, 900 | blend_enable = 27, 901 | logic_op_enable = 1004, 902 | source_color_blend_factor = 19, 903 | dest_color_blend_factor = 20, 904 | color_blend_op = 171, 905 | source_alpha_blend_factor = 207, 906 | dest_alpha_blend_factor = 208, 907 | alpha_blend_op = 209, 908 | logic_op = 1005, 909 | blend_constant = 193, 910 | render_target_write_mask = 168, 911 | 912 | // Rasterizer state 913 | 914 | fill_mode = 8, 915 | cull_mode = 22, 916 | front_counter_clockwise = 1001, 917 | depth_bias = 195, 918 | depth_bias_clamp = 1002, 919 | depth_bias_slope_scaled = 175, 920 | depth_clip_enable = 136, 921 | scissor_enable = 174, 922 | multisample_enable = 161, 923 | antialiased_line_enable = 176, 924 | 925 | // Depth-stencil state 926 | 927 | depth_enable = 7, 928 | depth_write_mask = 14, 929 | depth_func = 23, 930 | stencil_enable = 52, 931 | stencil_read_mask = 58, 932 | stencil_write_mask = 59, 933 | stencil_reference_value = 57, 934 | front_stencil_func = 56, 935 | front_stencil_pass_op = 55, 936 | front_stencil_fail_op = 53, 937 | front_stencil_depth_fail_op = 54, 938 | back_stencil_func = 189, 939 | back_stencil_pass_op = 188, 940 | back_stencil_fail_op = 186, 941 | back_stencil_depth_fail_op = 187 942 | }; 943 | 944 | /// 945 | /// Describes a rectangle. 946 | /// 947 | struct rect 948 | { 949 | int32_t left = 0; 950 | int32_t top = 0; 951 | int32_t right = 0; 952 | int32_t bottom = 0; 953 | 954 | constexpr uint32_t width() const { return right - left; } 955 | constexpr uint32_t height() const { return bottom - top; } 956 | }; 957 | 958 | /// 959 | /// Describes a render viewport. 960 | /// 961 | struct viewport 962 | { 963 | float x = 0.0f; 964 | float y = 0.0f; 965 | float width = 0.0f; 966 | float height = 0.0f; 967 | float min_depth = 0.0f; 968 | float max_depth = 1.0f; 969 | }; 970 | } 971 | --------------------------------------------------------------------------------