├── .gitignore ├── LICENSE.md ├── README.md ├── draw_manager.cpp ├── draw_manager.hpp ├── draw_manager.sln ├── draw_manager.vcxproj ├── draw_manager.vcxproj.filters ├── font.cpp ├── font.hpp ├── impl ├── d3d11_manager.cpp ├── d3d11_manager.hpp ├── d3d9_manager.cpp ├── d3d9_manager.hpp ├── shaders.hpp ├── shaders │ ├── d3d11 │ │ ├── include │ │ │ ├── blur.hlsli │ │ │ ├── scissor_blur.hlsli │ │ │ └── types.hlsli │ │ ├── pixel │ │ │ ├── blur_x.hlsl │ │ │ ├── blur_y.hlsl │ │ │ ├── generic.hlsl │ │ │ ├── key.hlsl │ │ │ ├── scissor.hlsl │ │ │ ├── scissor_blur_x.hlsl │ │ │ ├── scissor_blur_y.hlsl │ │ │ └── scissor_key.hlsl │ │ └── vertex │ │ │ └── generic.hlsl │ └── d3d9 │ │ ├── include │ │ ├── blur.hlsli │ │ ├── scissor_blur.hlsli │ │ └── types.hlsli │ │ ├── pixel │ │ ├── blur_x.hlsl │ │ ├── blur_y.hlsl │ │ ├── key.hlsl │ │ ├── scissor.hlsl │ │ ├── scissor_blur_x.hlsl │ │ ├── scissor_blur_y.hlsl │ │ └── scissor_key.hlsl │ │ └── vertex │ │ └── generic.hlsl ├── shaders_dx11.hpp ├── tex_dict_dx11.cpp ├── tex_dict_dx11.hpp ├── tex_dict_dx9.cpp └── tex_dict_dx9.hpp ├── main_dx11.cpp ├── main_dx9.cpp ├── main_shared.cpp └── math.h /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUnit 46 | *.VisualState.xml 47 | TestResult.xml 48 | nunit-*.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | 63 | # StyleCop 64 | StyleCopReport.xml 65 | 66 | # Files built by Visual Studio 67 | *_i.c 68 | *_p.c 69 | *_h.h 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.iobj 74 | *.pch 75 | *.pdb 76 | *.ipdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *_wpftmp.csproj 87 | *.log 88 | *.vspscc 89 | *.vssscc 90 | .builds 91 | *.pidb 92 | *.svclog 93 | *.scc 94 | 95 | # Chutzpah Test files 96 | _Chutzpah* 97 | 98 | # Visual C++ cache files 99 | ipch/ 100 | *.aps 101 | *.ncb 102 | *.opendb 103 | *.opensdf 104 | *.sdf 105 | *.cachefile 106 | *.VC.db 107 | *.VC.VC.opendb 108 | 109 | # Visual Studio profiler 110 | *.psess 111 | *.vsp 112 | *.vspx 113 | *.sap 114 | 115 | # Visual Studio Trace Files 116 | *.e2e 117 | 118 | # TFS 2012 Local Workspace 119 | $tf/ 120 | 121 | # Guidance Automation Toolkit 122 | *.gpState 123 | 124 | # ReSharper is a .NET coding add-in 125 | _ReSharper*/ 126 | *.[Rr]e[Ss]harper 127 | *.DotSettings.user 128 | 129 | # JustCode is a .NET coding add-in 130 | .JustCode 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | 354 | external 355 | impl/shaders/cpp 356 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `2019` `T0b1-iOS(GitHub)` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | 28 | This software is based upon dear imgui by Omar Cornut(https://github.com/ocornut/imgui): 29 | ===================== 30 | 31 | The MIT License (MIT) 32 | ===================== 33 | 34 | Copyright (c) 2014-2019 Omar Cornut 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 40 | copies of the Software, and to permit persons to whom the Software is 41 | furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all 44 | copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 52 | SOFTWARE. 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # draw_manager 2 | 3 | #### basic multithreaded drawing 4 | 5 | Setting up 6 | ------ 7 | To get drawing to work you need to implement a class which extends draw_manager and implements the virtual functions. 8 | How to actually draw is pretty specific to your environment but to get an idea you can look at the d3d9 implementation provided or how ImGui does drawing, it's pretty similar. 9 | 10 | You also need to provide freetype headers & binaries(https://www.freetype.org/download.html) as well as stb_rectpack.h(https://github.com/nothings/stb/blob/master/stb_rect_pack.h) 11 | 12 | Usage for Drawing 13 | ------ 14 | 15 | ```cpp 16 | // Register buffer once somewhere 17 | static const auto buffer_idx = draw_manager->register_buffer(); 18 | 19 | // Each time you do a draw pass get a pointer to the buffer 20 | const auto buffer = draw_manager->get_buffer(buffer_idx); 21 | // do the drawing stuff 22 | // so for example 23 | buffer->rectangle_filled({0.f, 0.f}, draw_manager->get_screen_size(), math::color_rgba::white()); 24 | // then swap 25 | draw_manager->swap_buffers(buffer_idx); 26 | ``` 27 | 28 | Feature List 29 | ------ 30 | 31 | * drawing of primitives & text(freetype) into a vertex/index buffer consisting of triangles 32 | * multithreading as it operates like a swapchain 33 | * multiple independent buffers which can be sorted by priority & parent-child hierarchy 34 | * independent of actual drawing implementation(confirmed to work for d3d9 & csgo's surface) 35 | * supports bluring, color-keying & circle scissors if they are implemented 36 | 37 | Looks like this (after a resize and with the d3d9 implementation) 38 | ------ 39 | ![preview](https://i.imgur.com/OKl12dH.png) 40 | -------------------------------------------------------------------------------- /draw_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "draw_manager.hpp" 6 | 7 | using namespace util::draw; 8 | 9 | namespace 10 | { 11 | constexpr auto CIRCLE_POINT_COUNT = 64; 12 | 13 | bool circle_points_initialized = false; 14 | std::array circle_points; 15 | void init_circle_points(); 16 | } 17 | 18 | #pragma region util 19 | 20 | static inline float inv_length(const position& lhs, const float fail_value) 21 | { 22 | const auto d = lhs.x * lhs.x + lhs.y * lhs.y; 23 | if (d > 0.0f) 24 | return 1.0f / std::sqrt(d); 25 | return fail_value; 26 | } 27 | 28 | #pragma endregion 29 | 30 | #pragma region draw_buffer 31 | 32 | rect draw_buffer::cur_clip_rect() 33 | { 34 | if (clip_rect_stack.empty()) 35 | { 36 | return rect{ position{0.f, 0.f}, manager->get_screen_size() }; 37 | } 38 | return clip_rect_stack.back().first; 39 | } 40 | 41 | rect draw_buffer::cur_non_circle_clip_rect() 42 | { 43 | for (const auto& clip_entry : clip_rect_stack) 44 | { 45 | if (clip_entry.second) 46 | continue; 47 | return clip_entry.first; 48 | } 49 | 50 | return rect{ position{0.f, 0.f}, manager->get_screen_size() }; 51 | } 52 | 53 | rect draw_buffer::clip_rect_to_cur_rect(const rect& clip) 54 | { 55 | const auto cur_rect = cur_clip_rect(); 56 | rect new_clip{ 57 | std::max(cur_rect.xy.x, clip.xy.x), 58 | std::max(cur_rect.xy.y, clip.xy.y), 59 | std::min(cur_rect.z, clip.z), 60 | std::min(cur_rect.w, clip.w) 61 | }; 62 | return new_clip; 63 | } 64 | 65 | void draw_buffer::update_clip_rect() 66 | { 67 | if (!cmds.empty() && !clip_rect_stack.empty() 68 | && cur_clip_rect() == cmds.back().clip_rect.float_rect() 69 | && clip_rect_stack.back().second == cmds.back().circle_scissor) 70 | return; 71 | 72 | if (!cmds.empty()) 73 | { 74 | auto& cur_cmd = cmds.back(); 75 | if (!cur_cmd.elem_count) 76 | { 77 | cur_cmd.clip_rect = cur_clip_rect(); 78 | cur_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 79 | if (cur_cmd.circle_scissor) 80 | cur_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 81 | cur_cmd.tex_id = cur_tex_id(); 82 | cur_cmd.font_texture = (cur_cmd.tex_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr); 83 | cur_cmd.native_texture = !cmds.empty() && cmds.back().native_texture; 84 | return; 85 | } 86 | } 87 | //Allocate new command 88 | draw_cmd new_cmd = {}; 89 | new_cmd.clip_rect = cur_clip_rect(); 90 | new_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 91 | if (new_cmd.circle_scissor) 92 | new_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 93 | new_cmd.elem_count = 0; 94 | new_cmd.tex_id = cur_tex_id(); 95 | new_cmd.font_texture = (new_cmd.tex_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr); 96 | new_cmd.native_texture = !cmds.empty() && cmds.back().native_texture; 97 | if (!cmds.empty()) 98 | new_cmd.key_color = cmds.back().key_color; 99 | 100 | cmds.emplace_back(new_cmd); 101 | } 102 | 103 | void draw_buffer::push_font(font* font) 104 | { 105 | cur_font = font; 106 | font_stack.emplace_back(font); 107 | push_tex_id(font->container_atlas->tex_id, true); 108 | } 109 | 110 | void draw_buffer::pop_font() 111 | { 112 | assert(!font_stack.empty()); 113 | pop_tex_id(); 114 | font_stack.pop_back(); 115 | if (!font_stack.empty()) 116 | { 117 | cur_font = font_stack.back(); 118 | push_tex_id(cur_font->container_atlas->tex_id); 119 | return; 120 | } 121 | cur_font = nullptr; 122 | } 123 | 124 | void draw_buffer::update_tex_id(const bool force_font, const bool native_texture) 125 | { 126 | const auto cur_id = cur_tex_id(); 127 | if (!cmds.empty() && !tex_id_stack.empty() && cur_id == cmds.back().tex_id 128 | && !force_font && !native_texture) 129 | return; 130 | 131 | if (!cmds.empty()) 132 | { 133 | auto& cur_cmd = cmds.back(); 134 | if (!cur_cmd.elem_count) 135 | { 136 | cur_cmd.clip_rect = cur_clip_rect(); 137 | cur_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 138 | if (cur_cmd.circle_scissor) 139 | cur_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 140 | cur_cmd.tex_id = cur_tex_id(); 141 | cur_cmd.font_texture = (cur_cmd.tex_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr) || force_font; 142 | cur_cmd.native_texture = native_texture; 143 | return; 144 | } 145 | } 146 | 147 | //Allocate new cmd 148 | draw_cmd new_cmd = {}; 149 | new_cmd.clip_rect = cur_clip_rect(); 150 | new_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 151 | if (new_cmd.circle_scissor) 152 | new_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 153 | new_cmd.elem_count = 0; 154 | new_cmd.tex_id = cur_id; 155 | new_cmd.font_texture = 156 | (cur_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr) 157 | || force_font; 158 | new_cmd.native_texture = native_texture; 159 | 160 | if (!cmds.empty()) 161 | new_cmd.key_color = cmds.back().key_color; 162 | 163 | cmds.emplace_back(new_cmd); 164 | } 165 | 166 | size_t draw_buffer::force_new_cmd() 167 | { 168 | if (!cmds.empty() && cmds.back().elem_count == 0u) 169 | return (cmds.size() - 1); 170 | 171 | // Allocate a new command 172 | draw_cmd new_cmd = {}; 173 | new_cmd.clip_rect = cur_clip_rect(); 174 | new_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 175 | if (new_cmd.circle_scissor) 176 | new_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 177 | new_cmd.elem_count = 0; 178 | new_cmd.tex_id = cur_tex_id(); 179 | new_cmd.font_texture = (new_cmd.tex_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr); 180 | new_cmd.native_texture = !cmds.empty() && cmds.back().native_texture; 181 | 182 | if (!cmds.empty()) 183 | new_cmd.key_color = cmds.back().key_color; 184 | 185 | const auto new_idx = cmds.size(); 186 | cmds.emplace_back(new_cmd); 187 | 188 | return new_idx; 189 | } 190 | 191 | void draw_buffer::update_matrix_translate(const position& xy_translate, const size_t cmd_idx) 192 | { 193 | if (cmd_idx != -1) 194 | { 195 | if (cmds.size() <= cmd_idx) 196 | return; 197 | 198 | auto& cmd = cmds[cmd_idx]; 199 | cmd.matrix[0][3] += xy_translate.x; 200 | cmd.matrix[1][3] += xy_translate.y; 201 | return; 202 | } 203 | 204 | if (cmds.empty()) 205 | return; 206 | 207 | for (auto& cmd : cmds) 208 | { 209 | cmd.matrix[0][3] += xy_translate.x; 210 | cmd.matrix[1][3] += xy_translate.y; 211 | } 212 | } 213 | 214 | void draw_buffer::set_blur(const uint8_t strength, const uint8_t passes) 215 | { 216 | if (!cmds.empty() && cmds.back().blur_strength == strength && cmds.back().blur_pass_count == passes) 217 | return; 218 | 219 | //Allocate new command 220 | draw_cmd new_cmd = {}; 221 | new_cmd.clip_rect = cur_clip_rect(); 222 | new_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 223 | if (new_cmd.circle_scissor) 224 | new_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 225 | new_cmd.elem_count = 0; 226 | new_cmd.tex_id = cur_tex_id(); 227 | new_cmd.font_texture = (new_cmd.tex_id == manager->fonts->tex_id && manager->fonts->tex_id != nullptr); 228 | new_cmd.blur_strength = strength; 229 | new_cmd.blur_pass_count = passes; 230 | new_cmd.native_texture = !cmds.empty() && cmds.back().native_texture; 231 | if (!cmds.empty()) 232 | new_cmd.key_color = cmds.back().key_color; 233 | 234 | cmds.emplace_back(new_cmd); 235 | } 236 | 237 | void draw_buffer::set_key_color(const color col) 238 | { 239 | //Allocate new command 240 | draw_cmd new_cmd = {}; 241 | new_cmd.clip_rect = cur_clip_rect(); 242 | new_cmd.circle_scissor = clip_rect_stack.empty() ? false : clip_rect_stack.back().second; 243 | if (new_cmd.circle_scissor) 244 | new_cmd.circle_outer_clip = cur_non_circle_clip_rect(); 245 | new_cmd.elem_count = 0; 246 | new_cmd.tex_id = cur_tex_id(); 247 | new_cmd.font_texture = (new_cmd.tex_id == manager->fonts->tex_id 248 | && manager->fonts->tex_id != nullptr); 249 | new_cmd.key_color = col; 250 | new_cmd.native_texture = !cmds.empty() && cmds.back().native_texture; 251 | 252 | cmds.emplace_back(new_cmd); 253 | } 254 | 255 | void draw_buffer::triangle_filled(const position& p1, 256 | const position& p2, 257 | const position& p3, 258 | const pack_color col_p1, 259 | const pack_color col_p2, 260 | const pack_color col_p3, 261 | const bool anti_aliased) 262 | { 263 | reserve_primitives(3, 3); 264 | const position uv = { 1.f, 1.f }; 265 | write_vtx(p1, uv, col_p1); 266 | write_vtx(p2, uv, col_p2); 267 | write_vtx(p3, uv, col_p3); 268 | write_idx(cur_idx); 269 | write_idx(cur_idx + 1); 270 | write_idx(cur_idx + 2); 271 | cur_idx += 3; 272 | 273 | if(anti_aliased) 274 | { 275 | line(p1, p2, col_p1, col_p2, 1.f, true); 276 | line(p2, p3, col_p2, col_p3, 1.f, true); 277 | line(p3, p1, col_p3, col_p1, 1.f, true); 278 | } 279 | } 280 | 281 | void draw_buffer::rectangle_filled(const position& top_left, 282 | const position& bot_right, 283 | const pack_color col_top_left, 284 | const pack_color col_top_right, 285 | const pack_color col_bot_left, 286 | const pack_color col_bot_right) 287 | { 288 | triangle_filled({ top_left.x, bot_right.y }, top_left, bot_right, col_bot_left, col_top_left, col_bot_right); 289 | triangle_filled(top_left, { bot_right.x, top_left.y }, bot_right, col_top_left, col_top_right, col_bot_right); 290 | } 291 | 292 | void draw_buffer::rectangle(const position& top_left_pre, 293 | const position& bot_right_pre, 294 | const pos_type thickness, 295 | const pack_color col_top_left, 296 | const pack_color col_top_right, 297 | const pack_color col_bot_left, 298 | const pack_color col_bot_right, 299 | const bool clipped) 300 | { 301 | const auto top_left = (clipped 302 | ? position{ top_left_pre.x + thickness / 2, top_left_pre.y - thickness / 2 } 303 | : position{ top_left_pre.x, top_left_pre.y - thickness / 2 }); 304 | const auto bot_right = (clipped ? position{ bot_right_pre.x - thickness / 2, bot_right_pre.y } : bot_right_pre); 305 | 306 | reserve_path(5); 307 | push_path(top_left); 308 | push_path({ top_left.x, bot_right.y }); 309 | push_path(bot_right); 310 | push_path({ bot_right.x, top_left_pre.y }); 311 | push_path({ top_left.x + thickness / 2 - 1.f, top_left_pre.y }); 312 | 313 | pack_color colors[] = { col_top_left, col_bot_left, col_bot_right, col_top_right, col_top_left }; 314 | poly_line(path_data(), 5u, colors, thickness); 315 | clear_path(); 316 | } 317 | 318 | static inline void generate_circle_metadata(size_t& start_idx, size_t& point_count, size_t& skip_count, const pos_type radius, pos_type degrees, pos_type start_degree) 319 | { 320 | degrees = std::fabs(degrees); 321 | start_degree = std::fabs(start_degree); 322 | while (degrees > 360.f) 323 | degrees -= 360.f; 324 | while (start_degree > 360.f) 325 | start_degree -= 360.f; 326 | 327 | point_count = std::clamp(static_cast(degrees * (1 / 360.f) * circle_points.size()) + 1, static_cast(0u), circle_points.size()); 328 | // TODO: how do we properly calculate the index here? 329 | start_idx = std::clamp(static_cast((start_degree * (1 / 360.f)) * circle_points.size()), static_cast(0u), circle_points.size() - 1); 330 | #if 0 331 | // r = 1 -> 4 points so skip_count = CIRCLE_POINTS_COUNT, r = 100 -> all points so skip_count = 0 332 | skip_count = std::clamp(CIRCLE_POINT_COUNT - static_cast((radius - 1.f) * (CIRCLE_POINT_COUNT / 100.f) * 2.5f), 0, CIRCLE_POINT_COUNT); 333 | if (skip_count > 0) 334 | point_count /= skip_count; 335 | #else 336 | skip_count = 0; 337 | #endif 338 | } 339 | 340 | static inline void generate_circle_points(position* point_buf, const size_t start_idx, const size_t point_count, const size_t skip_count, const pos_type radius, const position& center) 341 | { 342 | auto cur_idx = std::clamp(start_idx, static_cast(0u), circle_points.size()); 343 | for (auto i = 0u; i < point_count; ++i) 344 | { 345 | if (cur_idx >= circle_points.size()) 346 | cur_idx -= circle_points.size(); 347 | 348 | point_buf[i] = circle_points[cur_idx] * radius + center; 349 | cur_idx += 1 + skip_count; 350 | } 351 | } 352 | 353 | void draw_buffer::circle_filled(const position& center, 354 | const pos_type radius, 355 | const pack_color inner_col, 356 | const pack_color outer_col, 357 | uint32_t parts, 358 | const pos_type degrees, 359 | pos_type start_degree, 360 | bool anti_aliasing) 361 | { 362 | size_t start_idx, point_count, skip_count; 363 | generate_circle_metadata(start_idx, point_count, skip_count, radius, degrees, start_degree); 364 | const auto points = reinterpret_cast(_alloca(sizeof(position) * point_count)); 365 | generate_circle_points(points, start_idx, point_count, skip_count, radius, center); 366 | //poly_fill(points, point_count, outer_col); 367 | fill_circle_impl(center, points, point_count, inner_col, outer_col); 368 | 369 | if (anti_aliasing) 370 | { 371 | poly_line(points, point_count, outer_col, 1.f, true); 372 | } 373 | } 374 | 375 | void draw_buffer::circle(const position& center, 376 | const pos_type radius, 377 | const pack_color col, 378 | const pos_type thickness, 379 | uint32_t parts, 380 | pos_type degrees, 381 | pos_type start_degree, const bool anti_aliasing) 382 | { 383 | size_t start_idx, point_count, skip_count; 384 | generate_circle_metadata(start_idx, point_count, skip_count, radius, degrees, start_degree); 385 | const auto points = reinterpret_cast(_alloca(sizeof(position) * point_count)); 386 | generate_circle_points(points, start_idx, point_count, skip_count, radius, center); 387 | 388 | // when doing poly_line here we have gaps in the circle so we just do this 389 | for (auto i = 0u; i < point_count - 1; ++i) 390 | { 391 | line(points[i], points[i + 1], col, thickness, anti_aliasing); 392 | } 393 | } 394 | 395 | void draw_buffer::fill_circle_impl(const position& center, 396 | position* vtx, 397 | const uint32_t vtx_count, 398 | const pack_color col_inner, 399 | const pack_color col_outer) 400 | { 401 | if (vtx_count < 2) 402 | return; 403 | const auto uv = manager->fonts->tex_uv_white_pixel; 404 | 405 | prim_reserve((vtx_count - 1) * 3, vtx_count + 1); 406 | write_vtx(center, uv, col_inner); 407 | const auto center_idx = cur_idx; 408 | cur_idx++; 409 | 410 | write_vtx(*vtx, uv, col_outer); 411 | for (auto i = 1u; i < vtx_count; i++) 412 | { 413 | write_vtx(vtx[i], uv, col_outer); 414 | write_idx(center_idx); 415 | write_idx(cur_idx + 1); 416 | write_idx(cur_idx); 417 | cur_idx++; 418 | } 419 | cur_idx++; 420 | } 421 | 422 | 423 | //TODO: Instead of dupe code, stack alloc points_count colors and fill with col in a inlined func? 424 | void draw_buffer::poly_line(position* points, 425 | const uint32_t points_count, 426 | const pack_color col, 427 | const pos_type thickness, 428 | const bool anti_aliased) 429 | { 430 | // from dear imgui 431 | if (points_count < 2) 432 | return; 433 | 434 | constexpr auto aa_size = 1.f; 435 | const auto uv = position{ 1.f, 1.f }; 436 | const auto thick_line = thickness > 1.f; 437 | const auto col_trans = pack_color{ col.r(), col.g(), col.b(), 0 }; 438 | const auto count = points_count - 1; 439 | 440 | if (anti_aliased) 441 | { 442 | const auto idx_count = thick_line ? count * 18 : count * 12; 443 | const auto vtx_count = thick_line ? points_count * 4 : points_count * 3; 444 | reserve_primitives(idx_count, vtx_count); 445 | 446 | const auto tmp_normals = reinterpret_cast(alloca( 447 | points_count * (thick_line ? 5 : 3) * sizeof(position))); 448 | const auto tmp_points = tmp_normals + points_count; 449 | 450 | for (auto i = 0u; i < count; ++i) 451 | { 452 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 453 | const auto delta = (points[j] - points[i]).normalize(); 454 | tmp_normals[i] = { delta.y, -delta.x }; 455 | } 456 | tmp_normals[points_count - 1] = tmp_normals[points_count - 2]; 457 | 458 | if (!thick_line) 459 | { 460 | tmp_points[0] = points[0] + tmp_normals[0] * aa_size; 461 | tmp_points[1] = points[0] - tmp_normals[0] * aa_size; 462 | tmp_points[(points_count - 1) * 2 + 0] = points[points_count - 1] + tmp_normals[points_count - 1] * aa_size; 463 | tmp_points[(points_count - 1) * 2 + 1] = points[points_count - 1] - tmp_normals[points_count - 1] * aa_size; 464 | 465 | auto idx = cur_idx; 466 | for (auto i = 0u; i < count; ++i) 467 | { 468 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 469 | const auto idx2 = (i + 1 == points_count) ? cur_idx : idx + 3; 470 | 471 | auto dm = ((tmp_normals[i] + tmp_normals[j]) * 0.5); 472 | // IM_FIXNORMAL2F 473 | auto len_sqr = dm.length_sqr(); 474 | if (len_sqr < 0.5f) 475 | len_sqr = 0.5f; 476 | dm *= (1.f / len_sqr); 477 | 478 | dm *= aa_size; 479 | 480 | const auto vtx_out = &tmp_points[j * 2]; 481 | vtx_out[0] = points[j] + dm; 482 | vtx_out[1] = points[j] - dm; 483 | 484 | write_idx(idx2); 485 | write_idx(idx); 486 | write_idx(idx + 2); 487 | write_idx(idx + 2); 488 | write_idx(idx2 + 2); 489 | write_idx(idx2); 490 | write_idx(idx2 + 1); 491 | write_idx(idx + 1); 492 | write_idx(idx); 493 | write_idx(idx); 494 | write_idx(idx2); 495 | write_idx(idx2 + 1); 496 | 497 | idx = idx2; 498 | } 499 | 500 | for (auto i = 0u; i < points_count; ++i) 501 | { 502 | write_vtx(points[i], uv, col); 503 | write_vtx(tmp_points[i * 2], uv, col_trans); 504 | write_vtx(tmp_points[i * 2 + 1], uv, col_trans); 505 | } 506 | } 507 | else 508 | { 509 | const auto half_inner_thickness = (thickness - aa_size) * 0.5f; 510 | tmp_points[0] = points[0] + tmp_normals[0] * (half_inner_thickness + aa_size); 511 | tmp_points[1] = points[0] + tmp_normals[0] * (half_inner_thickness); 512 | tmp_points[2] = points[0] - tmp_normals[0] * (half_inner_thickness); 513 | tmp_points[3] = points[0] - tmp_normals[0] * (half_inner_thickness + aa_size); 514 | tmp_points[(points_count - 1) * 4] = points[points_count - 1] + tmp_normals[points_count - 1] * ( 515 | half_inner_thickness + aa_size); 516 | tmp_points[(points_count - 1) * 4 + 1] = points[points_count - 1] + tmp_normals[points_count - 1] * ( 517 | half_inner_thickness); 518 | tmp_points[(points_count - 1) * 4 + 2] = points[points_count - 1] - tmp_normals[points_count - 1] * ( 519 | half_inner_thickness); 520 | tmp_points[(points_count - 1) * 4 + 3] = points[points_count - 1] - tmp_normals[points_count - 1] * ( 521 | half_inner_thickness + aa_size); 522 | 523 | auto idx = cur_idx; 524 | for (auto i = 0u; i < count; ++i) 525 | { 526 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 527 | const auto idx2 = (i + 1 == points_count) ? cur_idx : idx + 4; 528 | 529 | auto dm = (tmp_normals[i] + tmp_normals[j]) * 0.5f; 530 | // IM_FIXNORMAL2F 531 | auto len_sqr = dm.length_sqr(); 532 | if (len_sqr < 0.5f) 533 | len_sqr = 0.5f; 534 | dm *= (1.f / len_sqr); 535 | 536 | const auto dm_out = dm * (half_inner_thickness + aa_size); 537 | const auto dm_in = dm * half_inner_thickness; 538 | 539 | const auto vtx_out = &tmp_points[j * 4]; 540 | vtx_out[0] = points[j] + dm_out; 541 | vtx_out[1] = points[j] + dm_in; 542 | vtx_out[2] = points[j] - dm_in; 543 | vtx_out[3] = points[j] - dm_out; 544 | 545 | write_idx(idx2 + 1); 546 | write_idx(idx + 1); 547 | write_idx(idx + 2); 548 | write_idx(idx + 2); 549 | write_idx(idx2 + 2); 550 | write_idx(idx2 + 1); 551 | write_idx(idx2 + 1); 552 | write_idx(idx + 1); 553 | write_idx(idx); 554 | write_idx(idx); 555 | write_idx(idx2); 556 | write_idx(idx2 + 1); 557 | write_idx(idx2 + 2); 558 | write_idx(idx + 2); 559 | write_idx(idx + 3); 560 | write_idx(idx + 3); 561 | write_idx(idx2 + 3); 562 | write_idx(idx2 + 2); 563 | 564 | idx = idx2; 565 | } 566 | 567 | for (auto i = 0u; i < points_count; ++i) 568 | { 569 | write_vtx(tmp_points[i * 4], uv, col_trans); 570 | write_vtx(tmp_points[i * 4 + 1], uv, col); 571 | write_vtx(tmp_points[i * 4 + 2], uv, col); 572 | write_vtx(tmp_points[i * 4 + 3], uv, col_trans); 573 | } 574 | } 575 | cur_idx += vtx_count; 576 | } 577 | else 578 | { 579 | const auto idx_count = count * 6; 580 | const auto vtx_count = count * 4; 581 | reserve_primitives(idx_count, vtx_count); 582 | 583 | for (auto i = 0u; i < count; ++i) 584 | { 585 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 586 | const auto& p1 = points[i]; 587 | const auto& p2 = points[j]; 588 | 589 | auto d = (p2 - p1).normalize(); 590 | d *= (thickness * 0.5f); 591 | std::swap(d.x, d.y); 592 | d.x = -d.x; 593 | 594 | write_vtx(p1 + d, uv, col); 595 | write_vtx(p2 + d, uv, col); 596 | write_vtx(p2 - d, uv, col); 597 | write_vtx(p1 - d, uv, col); 598 | 599 | write_idx(cur_idx); 600 | write_idx(cur_idx + 1); 601 | write_idx(cur_idx + 2); 602 | write_idx(cur_idx); 603 | write_idx(cur_idx + 2); 604 | write_idx(cur_idx + 3); 605 | 606 | cur_idx += 4; 607 | } 608 | } 609 | } 610 | 611 | void draw_buffer::poly_line(position* points, 612 | const uint32_t points_count, 613 | const pack_color* cols, 614 | const pos_type thickness, 615 | const bool anti_aliased) 616 | { 617 | // from dear imgui 618 | if (points_count < 2) 619 | return; 620 | 621 | constexpr auto aa_size = 1.f; 622 | const auto uv = position{ 1.f, 1.f }; 623 | const auto thick_line = thickness > 1.f; 624 | //const auto col_trans = pack_color{ col.r(), col.g(), col.b(), 0 }; 625 | const auto count = points_count - 1; 626 | 627 | if (anti_aliased) 628 | { 629 | const auto idx_count = thick_line ? count * 18 : count * 12; 630 | const auto vtx_count = thick_line ? points_count * 4 : points_count * 3; 631 | reserve_primitives(idx_count, vtx_count); 632 | 633 | const auto tmp_normals = reinterpret_cast(alloca( 634 | points_count * (thick_line ? 5 : 3) * sizeof(position))); 635 | const auto tmp_points = tmp_normals + points_count; 636 | 637 | for (auto i = 0u; i < count; ++i) 638 | { 639 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 640 | const auto delta = (points[j] - points[i]).normalize(); 641 | tmp_normals[i] = { delta.y, -delta.x }; 642 | } 643 | tmp_normals[points_count - 1] = tmp_normals[points_count - 2]; 644 | 645 | if (!thick_line) 646 | { 647 | tmp_points[0] = points[0] + tmp_normals[0] * aa_size; 648 | tmp_points[1] = points[0] - tmp_normals[0] * aa_size; 649 | tmp_points[(points_count - 1) * 2 + 0] = points[points_count - 1] + tmp_normals[points_count - 1] * aa_size; 650 | tmp_points[(points_count - 1) * 2 + 1] = points[points_count - 1] - tmp_normals[points_count - 1] * aa_size; 651 | 652 | auto idx = cur_idx; 653 | for (auto i = 0u; i < count; ++i) 654 | { 655 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 656 | const auto idx2 = (i + 1 == points_count) ? cur_idx : idx + 3; 657 | 658 | auto dm = ((tmp_normals[i] + tmp_normals[j]) * 0.5); 659 | // IM_FIXNORMAL2F 660 | auto len_sqr = dm.length_sqr(); 661 | if (len_sqr < 0.5f) 662 | len_sqr = 0.5f; 663 | dm *= (1.f / len_sqr); 664 | 665 | dm *= aa_size; 666 | 667 | const auto vtx_out = &tmp_points[j * 2]; 668 | vtx_out[0] = points[j] + dm; 669 | vtx_out[1] = points[j] - dm; 670 | 671 | write_idx(idx2); 672 | write_idx(idx); 673 | write_idx(idx + 2); 674 | write_idx(idx + 2); 675 | write_idx(idx2 + 2); 676 | write_idx(idx2); 677 | write_idx(idx2 + 1); 678 | write_idx(idx + 1); 679 | write_idx(idx); 680 | write_idx(idx); 681 | write_idx(idx2); 682 | write_idx(idx2 + 1); 683 | 684 | idx = idx2; 685 | } 686 | 687 | for (auto i = 0u; i < points_count; ++i) 688 | { 689 | const auto col = cols[i]; 690 | const auto col_trans = pack_color{ col.r(), col.g(), col.b(), 0 }; 691 | write_vtx(points[i], uv, col); 692 | write_vtx(tmp_points[i * 2], uv, col_trans); 693 | write_vtx(tmp_points[i * 2 + 1], uv, col_trans); 694 | } 695 | } 696 | else 697 | { 698 | const auto half_inner_thickness = (thickness - aa_size) * 0.5f; 699 | tmp_points[0] = points[0] + tmp_normals[0] * (half_inner_thickness + aa_size); 700 | tmp_points[1] = points[0] + tmp_normals[0] * (half_inner_thickness); 701 | tmp_points[2] = points[0] - tmp_normals[0] * (half_inner_thickness); 702 | tmp_points[3] = points[0] - tmp_normals[0] * (half_inner_thickness + aa_size); 703 | tmp_points[(points_count - 1) * 4] = points[points_count - 1] + tmp_normals[points_count - 1] * ( 704 | half_inner_thickness + aa_size); 705 | tmp_points[(points_count - 1) * 4 + 1] = points[points_count - 1] + tmp_normals[points_count - 1] * ( 706 | half_inner_thickness); 707 | tmp_points[(points_count - 1) * 4 + 2] = points[points_count - 1] - tmp_normals[points_count - 1] * ( 708 | half_inner_thickness); 709 | tmp_points[(points_count - 1) * 4 + 3] = points[points_count - 1] - tmp_normals[points_count - 1] * ( 710 | half_inner_thickness + aa_size); 711 | 712 | auto idx = cur_idx; 713 | for (auto i = 0u; i < count; ++i) 714 | { 715 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 716 | const auto idx2 = (i + 1 == points_count) ? cur_idx : idx + 4; 717 | 718 | auto dm = (tmp_normals[i] + tmp_normals[j]) * 0.5f; 719 | dm.normalize(); 720 | const auto dm_out = dm * (half_inner_thickness + aa_size); 721 | const auto dm_in = dm * half_inner_thickness; 722 | 723 | const auto vtx_out = &tmp_points[j * 4]; 724 | vtx_out[0] = points[j] + dm_out; 725 | vtx_out[1] = points[j] + dm_in; 726 | vtx_out[2] = points[j] - dm_in; 727 | vtx_out[3] = points[j] - dm_out; 728 | 729 | write_idx(idx2 + 1); 730 | write_idx(idx + 1); 731 | write_idx(idx + 2); 732 | write_idx(idx + 2); 733 | write_idx(idx2 + 2); 734 | write_idx(idx2 + 1); 735 | write_idx(idx2 + 1); 736 | write_idx(idx + 1); 737 | write_idx(idx); 738 | write_idx(idx); 739 | write_idx(idx2); 740 | write_idx(idx2 + 1); 741 | write_idx(idx2 + 2); 742 | write_idx(idx + 2); 743 | write_idx(idx + 3); 744 | write_idx(idx + 3); 745 | write_idx(idx2 + 3); 746 | write_idx(idx2 + 2); 747 | 748 | idx = idx2; 749 | } 750 | 751 | for (auto i = 0u; i < points_count; ++i) 752 | { 753 | const auto col = cols[i]; 754 | const auto col_trans = pack_color{ col.r(), col.g(), col.b(), 0 }; 755 | write_vtx(tmp_points[i * 4], uv, col_trans); 756 | write_vtx(tmp_points[i * 4 + 1], uv, col); 757 | write_vtx(tmp_points[i * 4 + 2], uv, col); 758 | write_vtx(tmp_points[i * 4 + 3], uv, col_trans); 759 | } 760 | } 761 | cur_idx += vtx_count; 762 | } 763 | else 764 | { 765 | const auto idx_count = count * 6; 766 | const auto vtx_count = count * 4; 767 | reserve_primitives(idx_count, vtx_count); 768 | 769 | for (auto i = 0u; i < count; ++i) 770 | { 771 | const auto j = (i + 1 == points_count) ? 0 : i + 1; 772 | const auto& p1 = points[i]; 773 | const auto& p2 = points[j]; 774 | 775 | auto d = (p2 - p1).normalize(); 776 | d *= (thickness * 0.5f); 777 | std::swap(d.x, d.y); 778 | d.x = -d.x; 779 | 780 | const auto col = cols[i]; 781 | write_vtx(p1 + d, uv, col); 782 | write_vtx(p2 + d, uv, col); 783 | write_vtx(p2 - d, uv, col); 784 | write_vtx(p1 - d, uv, col); 785 | 786 | write_idx(cur_idx); 787 | write_idx(cur_idx + 1); 788 | write_idx(cur_idx + 2); 789 | write_idx(cur_idx); 790 | write_idx(cur_idx + 2); 791 | write_idx(cur_idx + 3); 792 | 793 | cur_idx += 4; 794 | } 795 | } 796 | } 797 | 798 | void draw_buffer::poly_fill(position* points, const uint32_t count, const pack_color* col) 799 | { 800 | const auto uv = manager->fonts->tex_uv_white_pixel; 801 | const auto idx_count = (count - 2) * 3; 802 | reserve_primitives(idx_count, count); 803 | for (auto i = 0u; i < count; i++) 804 | write_vtx(points[i], uv, col[i]); 805 | for (auto i = 2u; i < count; i++) 806 | { 807 | write_idx(cur_idx); 808 | write_idx(cur_idx + i - 1); 809 | write_idx(cur_idx + i); 810 | } 811 | cur_idx += count; 812 | } 813 | 814 | void draw_buffer::poly_fill(position* points, const uint32_t count, const pack_color col) 815 | { 816 | const auto uv = manager->fonts->tex_uv_white_pixel; 817 | const auto idx_count = (count - 2) * 3; 818 | reserve_primitives(idx_count, count); 819 | for (auto i = 0u; i < count; i++) 820 | write_vtx(points[i], uv, col); 821 | for (auto i = 2u; i < count; i++) 822 | { 823 | write_idx(cur_idx); 824 | write_idx(cur_idx + i - 1); 825 | write_idx(cur_idx + i); 826 | } 827 | cur_idx += count; 828 | } 829 | 830 | void draw_buffer::prim_reserve(uint32_t idx_count, uint32_t vtx_count) 831 | { 832 | reserve_primitives(idx_count, vtx_count); 833 | } 834 | 835 | void draw_buffer::prim_rect(const position& a, const position& c, const pack_color col) 836 | { 837 | position b{ c.x, a.y }, d{ a.x, c.y }, uv{ 1.f, 1.f }; 838 | auto idx = cur_idx; 839 | idx_write_ptr[0] = idx; 840 | idx_write_ptr[1] = (idx + 1); 841 | idx_write_ptr[2] = (idx + 2); 842 | idx_write_ptr[3] = idx; 843 | idx_write_ptr[4] = (idx + 2); 844 | idx_write_ptr[5] = (idx + 3); 845 | vtx_write_ptr[0].pos = a; 846 | vtx_write_ptr[0].uv = uv; 847 | vtx_write_ptr[0].col = col; 848 | vtx_write_ptr[1].pos = b; 849 | vtx_write_ptr[1].uv = uv; 850 | vtx_write_ptr[1].col = col; 851 | vtx_write_ptr[2].pos = c; 852 | vtx_write_ptr[2].uv = uv; 853 | vtx_write_ptr[2].col = col; 854 | vtx_write_ptr[3].pos = d; 855 | vtx_write_ptr[3].uv = uv; 856 | vtx_write_ptr[3].col = col; 857 | vtx_write_ptr += 4; 858 | cur_idx += 4; 859 | idx_write_ptr += 6; 860 | } 861 | 862 | void draw_buffer::prim_rect_uv(const position& a, 863 | const position& c, 864 | const position& uv_a, 865 | const position& uv_c, 866 | const pack_color col) 867 | { 868 | const position b{ c.x, a.y }, d{ a.x, c.y }, uv_b{ uv_c.x, uv_a.y }, uv_d{ uv_a.x, uv_c.y }; 869 | const auto idx = cur_idx; 870 | idx_write_ptr[0] = idx; 871 | idx_write_ptr[1] = (idx + 1); 872 | idx_write_ptr[2] = (idx + 2); 873 | idx_write_ptr[3] = idx; 874 | idx_write_ptr[4] = (idx + 2); 875 | idx_write_ptr[5] = (idx + 3); 876 | vtx_write_ptr[0].pos = a; 877 | vtx_write_ptr[0].uv = uv_a; 878 | vtx_write_ptr[0].col = col; 879 | vtx_write_ptr[1].pos = b; 880 | vtx_write_ptr[1].uv = uv_b; 881 | vtx_write_ptr[1].col = col; 882 | vtx_write_ptr[2].pos = c; 883 | vtx_write_ptr[2].uv = uv_c; 884 | vtx_write_ptr[2].col = col; 885 | vtx_write_ptr[3].pos = d; 886 | vtx_write_ptr[3].uv = uv_d; 887 | vtx_write_ptr[3].col = col; 888 | vtx_write_ptr += 4; 889 | cur_idx += 4; 890 | idx_write_ptr += 6; 891 | } 892 | 893 | void draw_buffer::prim_quad_uv(const position& tl, 894 | const position& tr, 895 | const position& bl, 896 | const position& br, 897 | const position& uv1, 898 | const position& uv2, 899 | const position& uv3, 900 | const position& uv4, 901 | const pack_color col) 902 | { 903 | const auto idx = cur_idx; 904 | idx_write_ptr[0] = idx; 905 | idx_write_ptr[1] = (idx + 1); 906 | idx_write_ptr[2] = (idx + 2); 907 | idx_write_ptr[3] = (idx + 1); 908 | idx_write_ptr[4] = (idx + 3); 909 | idx_write_ptr[5] = (idx + 2); 910 | 911 | vtx_write_ptr[0].pos = tl; 912 | vtx_write_ptr[0].uv = uv1; 913 | vtx_write_ptr[0].col = col; 914 | vtx_write_ptr[1].pos = tr; 915 | vtx_write_ptr[1].uv = uv2; 916 | vtx_write_ptr[1].col = col; 917 | vtx_write_ptr[2].pos = bl; 918 | vtx_write_ptr[2].uv = uv3; 919 | vtx_write_ptr[2].col = col; 920 | vtx_write_ptr[3].pos = br; 921 | vtx_write_ptr[3].uv = uv4; 922 | vtx_write_ptr[3].col = col; 923 | 924 | vtx_write_ptr += 4; 925 | cur_idx += 4; 926 | idx_write_ptr += 6; 927 | } 928 | 929 | void draw_buffer::text(font* font, 930 | const char* text, 931 | const position& top_left, 932 | const pack_color col, 933 | const bool outline, 934 | const position& bot_right) 935 | { 936 | if (col.a() == 0) 937 | return; 938 | 939 | if (font != nullptr) 940 | push_font(font); 941 | 942 | assert(cur_font); 943 | 944 | auto clip_rect = cur_clip_rect(); 945 | if (bot_right.x != -1.f && bot_right.y != -1.f) 946 | { 947 | clip_rect.z = bot_right.x; 948 | clip_rect.w = bot_right.y; 949 | } 950 | 951 | if (outline) 952 | { 953 | const auto col_out = color{ 0, 0, 0 }; 954 | auto copy_clip = clip_rect; 955 | copy_clip.xy = top_left; 956 | copy_clip.x -= 1.f; 957 | copy_clip.z -= 1.f; 958 | this->text(nullptr, text, copy_clip.xy, col_out, false, copy_clip.zw); 959 | copy_clip.x += 2.f; 960 | copy_clip.z += 2.f; 961 | if (copy_clip.z < 0) 962 | copy_clip.z = std::numeric_limits::max(); 963 | this->text(nullptr, text, copy_clip.xy, col_out, false, copy_clip.zw); 964 | copy_clip.x -= 1.f, 965 | copy_clip.z -= 1.f; 966 | copy_clip.y -= 1.f; 967 | copy_clip.w -= 1.f; 968 | this->text(nullptr, text, copy_clip.xy, col_out, false, copy_clip.zw); 969 | copy_clip.y += 2.f; 970 | copy_clip.w += 2.f; 971 | if (copy_clip.w < 0) 972 | copy_clip.w = std::numeric_limits::max(); 973 | this->text(nullptr, text, copy_clip.xy, col_out, false, copy_clip.zw); 974 | } 975 | 976 | cur_font->render_text(this, cur_font->font_size, top_left, col, clip_rect, text, text + strlen(text)); 977 | 978 | if (font != nullptr) 979 | pop_font(); 980 | } 981 | 982 | void draw_buffer::text(font* font, 983 | float target_size, 984 | const char* text, 985 | const position& top_left, 986 | const pack_color col, 987 | bool outline, 988 | const position& bot_right) 989 | { 990 | if (col.a() == 0) 991 | return; 992 | 993 | if (font != nullptr) 994 | push_font(font); 995 | 996 | assert(cur_font); 997 | 998 | auto clip_rect = cur_clip_rect(); 999 | if (bot_right.x != -1.f && bot_right.y != -1.f) 1000 | { 1001 | clip_rect.z = bot_right.x; 1002 | clip_rect.w = bot_right.y; 1003 | } 1004 | 1005 | if (outline) 1006 | { 1007 | const auto col_out = color{ 0, 0, 0 }; 1008 | auto copy_clip = clip_rect; 1009 | copy_clip.xy = top_left; 1010 | copy_clip.x -= 1.f; 1011 | copy_clip.z -= 1.f; 1012 | this->text(nullptr, target_size, text, copy_clip.xy, col_out, false, copy_clip.zw); 1013 | copy_clip.x += 2.f; 1014 | copy_clip.z += 2.f; 1015 | if (copy_clip.z < 0) 1016 | copy_clip.z = std::numeric_limits::max(); 1017 | this->text(nullptr, target_size, text, copy_clip.xy, col_out, false, copy_clip.zw); 1018 | copy_clip.x -= 1.f, 1019 | copy_clip.z -= 1.f; 1020 | copy_clip.y -= 1.f; 1021 | copy_clip.w -= 1.f; 1022 | this->text(nullptr, target_size, text, copy_clip.xy, col_out, false, copy_clip.zw); 1023 | copy_clip.y += 2.f; 1024 | copy_clip.w += 2.f; 1025 | if (copy_clip.w < 0) 1026 | copy_clip.w = std::numeric_limits::max(); 1027 | this->text(nullptr, target_size, text, copy_clip.xy, col_out, false, copy_clip.zw); 1028 | } 1029 | 1030 | cur_font->render_text(this, target_size, top_left, col, clip_rect, text, text + strlen(text)); 1031 | 1032 | if (font != nullptr) 1033 | pop_font(); 1034 | } 1035 | 1036 | 1037 | position draw_buffer::text_size(font* font, const char* text, const char* text_end) const 1038 | { 1039 | if (!font) 1040 | font = cur_font; 1041 | assert(font); 1042 | 1043 | if (!strlen(text)) 1044 | return position{ 0.f, font->font_size }; 1045 | 1046 | auto text_size = font->calc_text_size(font->font_size, FLT_MAX, -1.f, text, text_end); 1047 | 1048 | // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) 1049 | if (text_size.x > 0.0f) 1050 | text_size.x -= 1.0f; 1051 | text_size.x = std::roundf(text_size.x + 0.95f); 1052 | 1053 | return text_size; 1054 | } 1055 | 1056 | position draw_buffer::text_size(font* font, float target_size, const char* text, const char* text_end) const 1057 | { 1058 | if (!font) 1059 | font = cur_font; 1060 | assert(font); 1061 | 1062 | if (!strlen(text)) 1063 | return position{ 0.f, font->font_size }; 1064 | 1065 | auto text_size = font->calc_text_size(target_size, FLT_MAX, -1.f, text, text_end); 1066 | 1067 | // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) 1068 | if (text_size.x > 0.0f) 1069 | text_size.x -= 1.0f; 1070 | text_size.x = std::roundf(text_size.x + 0.95f); 1071 | 1072 | return text_size; 1073 | } 1074 | 1075 | rect draw_buffer::text_bounds(font* font, const char* text, const char* text_end) const 1076 | { 1077 | if (!font) 1078 | font = cur_font; 1079 | assert(font); 1080 | 1081 | if (!strlen(text)) 1082 | return rect{ 0.f, 0.f, 0.f, font->font_size }; 1083 | 1084 | auto text_bounds = font->calc_text_bounds(font->font_size, FLT_MAX, -1.f, text, text_end); 1085 | 1086 | // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) 1087 | if (text_bounds.z > 0.0f) 1088 | text_bounds.z -= 1.0f; 1089 | text_bounds.z = std::roundf(text_bounds.z + 0.95f); 1090 | 1091 | return text_bounds; 1092 | } 1093 | 1094 | rect draw_buffer::text_bounds(font* font, float target_size, const char* text, const char* text_end) const 1095 | { 1096 | if (!font) 1097 | font = cur_font; 1098 | assert(font); 1099 | 1100 | if (!strlen(text)) 1101 | return rect{ 0.f, 0.f, 0.f, target_size }; 1102 | 1103 | auto text_bounds = font->calc_text_bounds(target_size, FLT_MAX, -1.f, text, text_end); 1104 | 1105 | // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) 1106 | if (text_bounds.z > 0.0f) 1107 | text_bounds.z -= 1.0f * (target_size / font->font_size); 1108 | //text_bounds.z = std::roundf(text_bounds.z + 0.95f); 1109 | 1110 | return text_bounds; 1111 | } 1112 | 1113 | 1114 | void draw_buffer::reserve_primitives(const std::uint32_t idx_count, const std::uint32_t vtx_count) 1115 | { 1116 | const auto vtx_old_size = vertices.size(); 1117 | const auto idx_old_size = indices.size(); 1118 | vertices.resize(vtx_old_size + vtx_count); 1119 | indices.resize(idx_old_size + idx_count); 1120 | 1121 | vtx_write_ptr = &vertices[vtx_old_size]; 1122 | idx_write_ptr = &indices[idx_old_size]; 1123 | 1124 | cmds.back().elem_count += idx_count; 1125 | cmds.back().vtx_count += vtx_count; 1126 | } 1127 | 1128 | #pragma endregion 1129 | 1130 | #pragma region draw_manager 1131 | 1132 | size_t draw_manager::register_buffer(const size_t init_priority) 1133 | { 1134 | std::lock_guard g(_list_mutex); 1135 | auto new_idx = _buffer_list.size(); 1136 | if (!_free_buffers.empty()) 1137 | { 1138 | new_idx = _free_buffers.back(); 1139 | _free_buffers.erase(std::prev(_free_buffers.end())); 1140 | } 1141 | else 1142 | { 1143 | _buffer_list.emplace_back(buffer_node{}); 1144 | } 1145 | 1146 | auto& element = _buffer_list[new_idx]; 1147 | element.active_buffer = std::make_unique(this); 1148 | element.working_buffer = std::make_unique(this); 1149 | update_buffer_ptrs(); 1150 | 1151 | _priorities.emplace_back(std::make_pair(init_priority, new_idx)); 1152 | sort_priorities(); 1153 | 1154 | return new_idx; 1155 | } 1156 | 1157 | size_t draw_manager::register_child_buffer(size_t parent, size_t priority) 1158 | { 1159 | std::lock_guard g(_list_mutex); 1160 | auto new_idx = _buffer_list.size(); 1161 | if (!_free_buffers.empty()) 1162 | { 1163 | new_idx = _free_buffers.back(); 1164 | _free_buffers.erase(std::prev(_free_buffers.end())); 1165 | } 1166 | else 1167 | { 1168 | _buffer_list.emplace_back(buffer_node{}); 1169 | } 1170 | 1171 | auto& element = _buffer_list[new_idx]; 1172 | element.active_buffer = std::make_unique(this); 1173 | element.working_buffer = std::make_unique(this); 1174 | element.active_buffer->is_child_buffer = true; 1175 | element.working_buffer->is_child_buffer = true; 1176 | element.parent = parent; 1177 | auto& vec = _buffer_list[parent].child_buffers; 1178 | vec.emplace_back(std::make_pair(priority, new_idx)); 1179 | std::sort(vec.begin(), 1180 | vec.end(), 1181 | [](auto& first, auto& sec) -> bool 1182 | { 1183 | return first.first > sec.first; 1184 | }); 1185 | update_buffer_ptrs(); 1186 | 1187 | return new_idx; 1188 | } 1189 | 1190 | void draw_manager::update_child_priority(const size_t child_idx, const size_t new_priority) 1191 | { 1192 | std::lock_guard g(_list_mutex); 1193 | assert(child_idx < _buffer_list.size()); 1194 | 1195 | const auto& child = _buffer_list[child_idx]; 1196 | auto& parent = _buffer_list[child.parent]; 1197 | const auto it = std::find_if(parent.child_buffers.begin(), 1198 | parent.child_buffers.end(), 1199 | [child_idx](auto& pair) 1200 | { 1201 | return (pair.second == child_idx); 1202 | }); 1203 | if (it != parent.child_buffers.end()) 1204 | { 1205 | it->first = new_priority; 1206 | std::sort(parent.child_buffers.begin(), 1207 | parent.child_buffers.end(), 1208 | [](auto& first, auto& sec) -> bool 1209 | { 1210 | return first.first > sec.first; 1211 | }); 1212 | } 1213 | } 1214 | 1215 | void draw_manager::update_buffer_priority(const size_t buffer_idx, const size_t new_priority) 1216 | { 1217 | std::lock_guard g(_list_mutex); 1218 | assert(buffer_idx < _buffer_list.size()); 1219 | 1220 | const auto& node = _buffer_list[buffer_idx]; 1221 | 1222 | const auto it = std::find_if(_priorities.begin(), 1223 | _priorities.end(), 1224 | [buffer_idx](auto& pair) 1225 | { 1226 | return (pair.second == buffer_idx); 1227 | }); 1228 | if (it == _priorities.end()) 1229 | return; 1230 | 1231 | it->first = new_priority; 1232 | sort_priorities(); 1233 | } 1234 | 1235 | 1236 | void draw_manager::remove_buffer(const size_t idx) 1237 | { 1238 | std::lock_guard g(_list_mutex); 1239 | assert(idx < _buffer_list.size()); 1240 | 1241 | //Clear/Free child buffers 1242 | const auto free_buffer = [=](const size_t buf, auto& self_ref) -> void 1243 | { 1244 | auto& element = _buffer_list[buf]; 1245 | _free_buffers.emplace_back(buf); 1246 | element.is_free = true; 1247 | 1248 | //Clear buffers 1249 | element.active_buffer->clear_buffers(); 1250 | element.working_buffer->clear_buffers(); 1251 | element.active_buffer->is_child_buffer = false; 1252 | element.working_buffer->is_child_buffer = false; 1253 | 1254 | if (element.parent != -1) 1255 | { 1256 | auto& parent = _buffer_list[element.parent]; 1257 | element.parent = -1; 1258 | const auto it = std::find_if(parent.child_buffers.begin(), 1259 | parent.child_buffers.end(), 1260 | [buf](auto& pair) 1261 | { 1262 | return (pair.second == buf); 1263 | }); 1264 | if (it != parent.child_buffers.end()) 1265 | parent.child_buffers.erase(it); 1266 | } 1267 | 1268 | for (auto& child : element.child_buffers) 1269 | { 1270 | self_ref(child.second, self_ref); 1271 | } 1272 | }; 1273 | 1274 | free_buffer(idx, free_buffer); 1275 | } 1276 | 1277 | void draw_manager::update_buffer_ptrs() { } 1278 | 1279 | draw_buffer* draw_manager::get_buffer(const size_t idx) 1280 | { 1281 | std::lock_guard g(_list_mutex); 1282 | assert(idx < _buffer_list.size()); 1283 | return _buffer_list[idx].working_buffer.get(); 1284 | } 1285 | 1286 | void draw_manager::swap_buffers(const size_t idx) 1287 | { 1288 | std::lock_guard g(_list_mutex); 1289 | assert(idx < _buffer_list.size()); 1290 | 1291 | const auto swap_buffer = [=](const size_t buf, const auto& self_ref) -> void 1292 | { 1293 | auto& element = _buffer_list[buf]; 1294 | element.active_buffer.swap(element.working_buffer); 1295 | element.working_buffer->clear_buffers(); 1296 | for (auto& child : element.child_buffers) 1297 | self_ref(child.second, self_ref); 1298 | }; 1299 | 1300 | swap_buffer(idx, swap_buffer); 1301 | } 1302 | 1303 | font* draw_manager::add_font(const char* file, 1304 | const float size, 1305 | const bool italic, 1306 | const bool bold, 1307 | const GLYPH_RANGES range, 1308 | const int rasterizer_flags) const 1309 | { 1310 | auto font_cfg = font_config(); 1311 | if (italic) 1312 | font_cfg.rasterizer_flags |= OBLIQUE; 1313 | if (bold) 1314 | font_cfg.rasterizer_flags |= BOLD; 1315 | 1316 | font_cfg.rasterizer_flags |= rasterizer_flags; 1317 | 1318 | const font_wchar* ranges = nullptr; 1319 | if (range & GLYPH_RANGE_LATIN) 1320 | ranges = fonts->glyph_ranges_default(); 1321 | if (range & GLYPH_RANGE_JAPANESE) 1322 | ranges = fonts->glyph_ranges_japanese(); 1323 | 1324 | return fonts->add_font_from_ttf(file, size, &font_cfg, ranges); 1325 | } 1326 | 1327 | font* draw_manager::add_font(const char* file, 1328 | float size, 1329 | const font_wchar* glyph_ranges, 1330 | const bool italic, 1331 | const bool bold, 1332 | const int rasterizer_flags) const 1333 | { 1334 | auto font_cfg = font_config(); 1335 | if (italic) 1336 | font_cfg.rasterizer_flags |= OBLIQUE; 1337 | if (bold) 1338 | font_cfg.rasterizer_flags |= BOLD; 1339 | 1340 | font_cfg.rasterizer_flags |= rasterizer_flags; 1341 | 1342 | return fonts->add_font_from_ttf(file, size, &font_cfg, glyph_ranges); 1343 | } 1344 | 1345 | font* draw_manager::add_font_mem(uint8_t* data, 1346 | size_t data_size, 1347 | float font_size, 1348 | bool italic, 1349 | bool bold, 1350 | GLYPH_RANGES range, 1351 | const int rasterizer_flags) const 1352 | { 1353 | auto font_cfg = font_config(); 1354 | if (italic) 1355 | font_cfg.rasterizer_flags |= OBLIQUE; 1356 | if (bold) 1357 | font_cfg.rasterizer_flags |= BOLD; 1358 | 1359 | font_cfg.rasterizer_flags |= rasterizer_flags; 1360 | 1361 | const font_wchar* ranges = nullptr; 1362 | if (range & GLYPH_RANGE_LATIN) 1363 | ranges = fonts->glyph_ranges_default(); 1364 | if (range & GLYPH_RANGE_JAPANESE) 1365 | ranges = fonts->glyph_ranges_japanese(); 1366 | 1367 | return fonts->add_font_from_ttf_mem(data, data_size, font_size, &font_cfg, ranges); 1368 | } 1369 | 1370 | void draw_manager::remove_font(const font* font_ptr) const 1371 | { 1372 | fonts->remove_font(font_ptr); 1373 | } 1374 | 1375 | void draw_manager::update_matrix_translate(const size_t buffer, const position& xy_translate, const size_t cmd_idx) 1376 | { 1377 | std::lock_guard g(_list_mutex); 1378 | assert(buffer < _buffer_list.size()); 1379 | 1380 | auto& buffer_pair = _buffer_list[buffer]; 1381 | buffer_pair.active_buffer->update_matrix_translate(xy_translate, cmd_idx); 1382 | } 1383 | 1384 | void draw_manager::init() 1385 | { 1386 | fonts = std::make_unique(); 1387 | _buffer_list.reserve(1000); 1388 | init_circle_points(); 1389 | } 1390 | 1391 | 1392 | #pragma endregion 1393 | 1394 | #pragma region convenient_funcs 1395 | 1396 | void util::draw::rectangle_filled_rounded(draw_buffer* buf, 1397 | const position& top_left, 1398 | const position& bot_right, 1399 | const pos_type radius, 1400 | const pack_color col_top_left, 1401 | const pack_color col_top_right, 1402 | const pack_color col_bot_left, 1403 | const pack_color col_bot_right, 1404 | const uint8_t flags) 1405 | { 1406 | const auto corner_tl = position{ top_left.x + radius, top_left.y + radius }; 1407 | const auto corner_tr = position{ bot_right.x - radius, top_left.y + radius }; 1408 | const auto corner_bl = position{ top_left.x + radius, bot_right.y - radius }; 1409 | const auto corner_br = position{ bot_right.x - radius, bot_right.y - radius }; 1410 | buf->rectangle_filled(corner_tl, corner_br, col_top_left, col_top_right, col_bot_left, col_bot_right); 1411 | 1412 | buf->rectangle_filled({ top_left.x, top_left.y + radius }, 1413 | { top_left.x + radius, bot_right.y - radius }, 1414 | col_top_left, 1415 | col_top_left, 1416 | col_bot_left, 1417 | col_bot_left); //Left Side 1418 | buf->rectangle_filled({ corner_tl.x, bot_right.y - radius }, 1419 | { bot_right.x - radius, bot_right.y }, 1420 | col_bot_left, 1421 | col_bot_right, 1422 | col_bot_left, 1423 | col_bot_right); //Bot Side 1424 | buf->rectangle_filled(corner_tr, 1425 | { bot_right.x, corner_br.y }, 1426 | col_top_right, 1427 | col_top_right, 1428 | col_bot_right, 1429 | col_bot_right); //Right Side 1430 | buf->rectangle_filled({ corner_tl.x, top_left.y }, 1431 | corner_tr, 1432 | col_top_left, 1433 | col_top_right, 1434 | col_top_left, 1435 | col_top_right); 1436 | 1437 | if (flags & ROUND_RECT_TL) 1438 | buf->circle_filled(corner_tl, radius, col_top_left, col_top_left, 64, 90, 90); 1439 | else 1440 | buf->rectangle_filled(top_left, corner_tl, col_top_left); 1441 | 1442 | if (flags & ROUND_RECT_TR) 1443 | buf->circle_filled(corner_tr, radius, col_top_right, col_top_right, 64, 90, 180); 1444 | else 1445 | buf->rectangle_filled({ corner_tr.x, top_left.y }, { bot_right.x, corner_tr.y }, col_top_right); 1446 | 1447 | if (flags & ROUND_RECT_BR) 1448 | buf->circle_filled(corner_br, radius, col_bot_right, col_bot_right, 64, 90, 270); 1449 | else 1450 | buf->rectangle_filled(corner_br, bot_right, col_bot_right); 1451 | 1452 | if (flags & ROUND_RECT_BL) 1453 | buf->circle_filled(corner_bl, radius, col_bot_left, col_bot_left, 64, 90, 0); 1454 | else 1455 | buf->rectangle_filled({ top_left.x, corner_bl.y }, { corner_bl.x, bot_right.y }, col_bot_left); 1456 | 1457 | //buf->rectangle_filled( top_left, bot_right, col_top_left, col_top_right, col_bot_left, col_bot_right ); 1458 | } 1459 | 1460 | void util::draw::check_mark(draw_buffer* buf, position top_left, pos_type width, const pack_color col) 1461 | { 1462 | //From dear ImGui by ocornut 1463 | const auto thickness = std::max(width / 5.f, 1.f); 1464 | width -= thickness * 0.5f; 1465 | top_left += position{ thickness * 0.25f, thickness * 0.25f }; 1466 | 1467 | const auto third = width / 3.f; 1468 | const auto bx = top_left.x + third; 1469 | const auto by = top_left.y + width - third * 0.5f; 1470 | position pos[3] = { 1471 | position{bx - third, by - third}, 1472 | position{bx, by}, 1473 | position{bx + third * 2, by - third * 2} 1474 | }; 1475 | buf->poly_line(pos, 3, col, thickness); 1476 | } 1477 | 1478 | 1479 | #pragma endregion 1480 | 1481 | #pragma region circle_points 1482 | 1483 | namespace 1484 | { 1485 | void init_circle_points() 1486 | { 1487 | if (circle_points_initialized) 1488 | return; 1489 | 1490 | circle_points_initialized = true; 1491 | 1492 | std::array octant_buf{}; 1493 | const auto step = (math::PI * 0.25f) / CIRCLE_POINT_COUNT; 1494 | for (auto i = 0u; i <= CIRCLE_POINT_COUNT; ++i) 1495 | { 1496 | octant_buf[i] = position{ static_cast(std::cos(step * i)), static_cast(std::sin(step * i)) }; 1497 | } 1498 | 1499 | for (auto i = 0u; i < 8; ++i) 1500 | { 1501 | auto x_idx = 0; 1502 | auto y_idx = 1; 1503 | auto x_mult = 1.f; 1504 | auto y_mult = 1.f; 1505 | switch (i) 1506 | { 1507 | case 1: 1508 | case 2: 1509 | case 5: 1510 | case 6: 1511 | x_idx = 1; 1512 | y_idx = 0; 1513 | default: 1514 | break; 1515 | } 1516 | 1517 | if (i >= 4) 1518 | y_mult = -1.f; 1519 | if (i >= 2 && i <= 5) 1520 | x_mult = -1.f; 1521 | 1522 | const auto reverse = i % 2 == 1; 1523 | 1524 | for (auto j = 0u; j <= CIRCLE_POINT_COUNT; ++j) 1525 | { 1526 | auto& fill_pos = circle_points[j + i * (CIRCLE_POINT_COUNT + 1)]; 1527 | const auto& src_pos = octant_buf[reverse ? CIRCLE_POINT_COUNT - j : j]; 1528 | 1529 | fill_pos.x = src_pos[x_idx] * x_mult; 1530 | fill_pos.y = src_pos[y_idx] * y_mult; 1531 | } 1532 | } 1533 | std::rotate(circle_points.begin(), circle_points.begin() + (circle_points.size() - CIRCLE_POINT_COUNT * 2 - 2), circle_points.end()); 1534 | 1535 | for (auto i = 0u; i < circle_points.size(); ++i) 1536 | { 1537 | const auto& fill_pos = circle_points[i]; 1538 | std::printf("Circle Point %u: %.4f, %.4f\n", i, fill_pos.x, fill_pos.y); 1539 | } 1540 | } 1541 | } 1542 | 1543 | #pragma endregion 1544 | -------------------------------------------------------------------------------- /draw_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "font.hpp" 5 | 6 | #include 7 | #include 8 | 9 | enum ROUND_RECT_FLAG : uint8_t 10 | { 11 | ROUND_RECT_TL = (1 << 0), 12 | ROUND_RECT_BL = (1 << 1), 13 | ROUND_RECT_BR = (1 << 2), 14 | ROUND_RECT_TR = (1 << 3), 15 | 16 | ROUND_RECT_TOP = ROUND_RECT_TL | ROUND_RECT_TR, 17 | ROUND_RECT_BOT = ROUND_RECT_BR | ROUND_RECT_BL, 18 | ROUND_RECT_LEFT = ROUND_RECT_TL | ROUND_RECT_BL, 19 | ROUND_RECT_RIGHT = ROUND_RECT_TR | ROUND_RECT_BR, 20 | ROUND_RECT_ALL = ROUND_RECT_TOP | ROUND_RECT_BOT 21 | }; 22 | 23 | namespace util::draw 24 | { 25 | struct callback_data 26 | { 27 | virtual ~callback_data() = default; 28 | }; 29 | 30 | struct vecs 31 | { 32 | short x, y; 33 | }; 34 | 35 | struct clip_rect 36 | { 37 | union 38 | { 39 | struct 40 | { 41 | short x, y, z, w; 42 | }; 43 | 44 | struct 45 | { 46 | vecs xy, zw; 47 | }; 48 | }; 49 | 50 | rect float_rect() const 51 | { 52 | return rect{ 53 | static_cast(x), 54 | static_cast(y), 55 | static_cast(z), 56 | static_cast(w) 57 | }; 58 | } 59 | 60 | clip_rect() = default; 61 | 62 | clip_rect(const rect& r) 63 | { 64 | x = static_cast(std::lround(r.x)); 65 | y = static_cast(std::lround(r.y)); 66 | z = static_cast(std::lround(r.z)); 67 | w = static_cast(std::lround(r.w)); 68 | } 69 | 70 | bool operator!=(const clip_rect& o) const 71 | { 72 | return (o.x != x || o.y != y || o.z != z || o.w != w); 73 | } 74 | }; 75 | 76 | struct draw_buffer 77 | { 78 | using draw_index = std::uint32_t; 79 | 80 | struct draw_cmd 81 | { 82 | std::uint32_t elem_count; 83 | clip_rect clip_rect; 84 | draw::clip_rect circle_outer_clip; 85 | tex_id tex_id; 86 | bool font_texture = false; 87 | bool circle_scissor = false; 88 | bool native_texture = false; 89 | uint8_t blur_strength = 0; 90 | uint8_t blur_pass_count = 0; 91 | std::uint32_t vtx_count = 0; 92 | std::function callback = 93 | nullptr; 94 | //Callback that will be called if not null instead of drawing 95 | std::shared_ptr callback_data = nullptr; //Data for callback 96 | // If color matches it will be made transparent, alpha indicates enabling of the feature 97 | color key_color = { 0, 0, 0, 0 }; 98 | // TODO: optionally disable these with a define since their use case is limited and they just waste space in the draw_cmd? 99 | // This will be used as the model matrix, e.g. "model space to world space", basically a tool to transform all vertexes 100 | matrix matrix = { 101 | vec4f{1.f, 0.f, 0.f, 0.f}, 102 | {0.f, 1.f, 0.f, 0.f}, 103 | {0.f, 0.f, 1.f, 0.f}, 104 | {0.f, 0.f, 0.f, 1.f} 105 | }; 106 | }; 107 | 108 | struct draw_vertex 109 | { 110 | draw_vertex() = default; 111 | 112 | draw_vertex(const position& p, const position& u, const std::uint32_t c) 113 | : pos(p), 114 | uv(u), 115 | col(c) { } 116 | 117 | draw_vertex(const position& p, const position& u, const pack_color c) 118 | : pos(p), 119 | uv(u), 120 | col(c) { } 121 | 122 | position pos = {}; 123 | position uv = {}; 124 | pack_color col = 0u; //R8G8B8A8 125 | }; 126 | 127 | std::vector cmds = {}; 128 | std::vector vertices = {}; 129 | std::vector indices = {}; 130 | 131 | bool is_child_buffer = false; 132 | pos_type scaling_factor = 1.f; 133 | 134 | public: //Changed for now 135 | std::vector> clip_rect_stack = {}; 136 | std::vector tex_id_stack = {}; 137 | std::vector font_stack = {}; 138 | std::vector path = {}; 139 | draw_vertex* vtx_write_ptr = nullptr; 140 | draw_index* idx_write_ptr = nullptr; 141 | draw_index cur_idx = 0; 142 | font* cur_font = nullptr; 143 | draw_manager* manager = nullptr; 144 | 145 | public: 146 | 147 | draw_buffer(draw_manager* manager) 148 | : manager(manager) 149 | { 150 | update_clip_rect(); 151 | } 152 | 153 | std::pair vtx_idx_count() const 154 | { 155 | return { vertices.size(), indices.size() }; 156 | } 157 | 158 | void clear_buffers() 159 | { 160 | cmds = {}; 161 | vertices = {}; 162 | indices = {}; 163 | clip_rect_stack = {}; 164 | tex_id_stack = {}; 165 | font_stack = {}; 166 | path = {}; 167 | vtx_write_ptr = nullptr; 168 | idx_write_ptr = nullptr; 169 | cur_idx = 0; 170 | cur_font = nullptr; 171 | update_clip_rect(); 172 | } 173 | 174 | rect cur_clip_rect(); 175 | rect cur_non_circle_clip_rect(); 176 | rect clip_rect_to_cur_rect(const rect&); 177 | 178 | void push_clip_rect(const position& min, const position& max, const bool circle = false) //rvalue reference? 179 | { 180 | auto new_rect = rect{ min, max }; 181 | if (!circle) 182 | new_rect = clip_rect_to_cur_rect(new_rect); 183 | 184 | clip_rect_stack.emplace_back(std::make_pair(new_rect, circle)); 185 | update_clip_rect(); 186 | } 187 | 188 | void push_clip_rect(const pos_type min_x, 189 | const pos_type min_y, 190 | const pos_type max_x, 191 | const pos_type max_y, 192 | const bool circle = false) 193 | { 194 | auto new_rect = rect{ min_x, min_y, max_x, max_y }; 195 | if (!circle) 196 | new_rect = clip_rect_to_cur_rect(new_rect); 197 | 198 | clip_rect_stack.emplace_back(std::make_pair(new_rect, circle)); 199 | update_clip_rect(); 200 | } 201 | 202 | void push_clip_rect(const rect& clip_rect, const bool circle = false) 203 | { 204 | const auto new_rect = circle ? clip_rect : clip_rect_to_cur_rect(clip_rect); 205 | clip_rect_stack.emplace_back(std::make_pair(new_rect, circle)); 206 | update_clip_rect(); 207 | } 208 | 209 | void pop_clip_rect() 210 | { 211 | assert(!clip_rect_stack.empty()); 212 | clip_rect_stack.pop_back(); 213 | update_clip_rect(); 214 | } 215 | 216 | void update_clip_rect(); 217 | 218 | tex_id cur_tex_id() 219 | { 220 | return (tex_id_stack.empty() ? nullptr : tex_id_stack.back()); 221 | } 222 | 223 | void push_font(font* font); 224 | 225 | void push_tex_id(tex_id id, const bool force_font = false, const bool native_texture = false) 226 | { 227 | tex_id_stack.emplace_back(id); 228 | update_tex_id(force_font, native_texture); 229 | } 230 | 231 | void pop_tex_id() 232 | { 233 | assert(!tex_id_stack.empty()); 234 | tex_id_stack.pop_back(); 235 | update_tex_id(); 236 | } 237 | 238 | void pop_font(); 239 | void update_tex_id(bool force_font = false, bool native_texture = false); 240 | 241 | // This will force a new command to be started and return the index of it 242 | size_t force_new_cmd(); 243 | 244 | // This is special: This function allows to move all vertexes in a cmd(or all vertexes) by the x&y-values specified in xy_translate 245 | // This can be useful if you later want to move an already-swapped buffer without redoing all the computing 246 | // Warning: This will add to the current translation 247 | void update_matrix_translate(const position& xy_translate, const size_t cmd_idx = -1); 248 | 249 | void set_blur(uint8_t strength = 2, uint8_t passes = 1); 250 | void set_key_color(color col); 251 | 252 | //Drawing Funcs 253 | //Triangles 254 | void triangle_filled(const position& p1, 255 | const position& p2, 256 | const position& p3, 257 | const pack_color col_p1, 258 | const pack_color col_p2, 259 | const pack_color col_p3, 260 | const bool anti_aliased = false); 261 | 262 | void triangle_filled(const position& p1, 263 | const position& p2, 264 | const position& p3, 265 | const pack_color col_p1, 266 | const pack_color col_p2, 267 | const bool anti_aliased = false) 268 | { 269 | triangle_filled(p1, p2, p3, col_p1, col_p2, col_p2, anti_aliased); 270 | } 271 | 272 | void triangle_filled(const position& p1, const position& p2, const position& p3, const pack_color col, const bool anti_aliased = false) 273 | { 274 | triangle_filled(p1, p2, p3, col, col, col, anti_aliased); 275 | } 276 | 277 | void rectangle_filled(const position& top_left, 278 | const position& bot_right, 279 | const pack_color col_top_left, 280 | const pack_color col_top_right, 281 | const pack_color col_bot_left, 282 | const pack_color col_bot_right); 283 | 284 | void rectangle_filled(const position& top_left, 285 | const position& bot_right, 286 | const pack_color col) 287 | { 288 | rectangle_filled(top_left, bot_right, col, col, col, col); 289 | } 290 | 291 | void rectangle_filled(const position& top_left, 292 | const pos_type width, 293 | const pos_type height, 294 | const pack_color col_top_left, 295 | const pack_color col_top_right, 296 | const pack_color col_bot_left, 297 | const pack_color col_bot_right) 298 | { 299 | rectangle_filled(top_left, 300 | { top_left.x + width, top_left.y + height }, 301 | col_top_left, 302 | col_top_right, 303 | col_bot_left, 304 | col_bot_right); 305 | } 306 | 307 | void rectangle_filled(const position& top_left, 308 | const pos_type width, 309 | const pos_type height, 310 | const pack_color col) 311 | { 312 | rectangle_filled(top_left, { top_left.x + width, top_left.y + height }, col, col, col, col); 313 | } 314 | 315 | void rectangle(const position& top_left, 316 | const position& bot_right, 317 | const pos_type thickness, 318 | const pack_color col_top_left, 319 | const pack_color col_top_right, 320 | const pack_color col_bot_left, 321 | const pack_color col_bot_right, 322 | const bool clipped = false); 323 | 324 | void rectangle(const position& top_left, 325 | const position& bot_right, 326 | const pos_type thickness, 327 | const pack_color col, 328 | const bool clipped = false) 329 | { 330 | rectangle(top_left, bot_right, thickness, col, col, col, col, clipped); 331 | } 332 | 333 | void rectangle(const position& top_left, 334 | const pos_type width, 335 | const pos_type height, 336 | const pos_type thickness, 337 | const pack_color col_top_left, 338 | const pack_color col_top_right, 339 | const pack_color col_bot_left, 340 | const pack_color col_bot_right, 341 | const bool clipped = false) 342 | { 343 | rectangle(top_left, 344 | { top_left.x + width, top_left.y + height }, 345 | thickness, 346 | col_top_left, 347 | col_top_right, 348 | col_bot_left, 349 | col_bot_right, 350 | clipped); 351 | } 352 | 353 | void rectangle(const position& top_left, 354 | const pos_type width, 355 | const pos_type height, 356 | const pos_type thickness, 357 | const pack_color col, 358 | const bool clipped = false) 359 | { 360 | rectangle(top_left, { top_left.x + width, top_left.y + height }, thickness, col, col, col, col, clipped); 361 | } 362 | 363 | //degrees = counter-clockwise; start_degrees = clockwise; blame the performance 364 | void circle_filled(const position& center, 365 | const pos_type radius, 366 | const pack_color inner_col, 367 | const pack_color* outer_col, 368 | const uint32_t parts = 12, 369 | const pos_type degrees = 360.f, 370 | pos_type start_degree = 0.f); 371 | 372 | //degrees = counter-clockwise; start_degrees = clockwise 373 | void circle_filled(const position& center, 374 | const pos_type radius, 375 | const pack_color inner_col, 376 | const pack_color outer_col, 377 | const uint32_t parts = 12, 378 | const pos_type degrees = 360.f, 379 | pos_type start_degree = 0.f, 380 | bool anti_aliasing = true); 381 | //degrees = counter-clockwise; start_degrees = clockwise 382 | void circle_filled(const position& center, 383 | const pos_type radius, 384 | const pack_color col, 385 | const uint32_t parts = 12, 386 | bool anti_aliasing = true) 387 | { 388 | circle_filled(center, radius, col, col, parts, 360.f, 0, anti_aliasing); 389 | } 390 | 391 | void circle(const position& center, 392 | const pos_type radius, 393 | const pack_color col, 394 | const pos_type thickness, 395 | const uint32_t parts = 12, 396 | const pos_type degrees = 360.f, 397 | const pos_type start_degree = 0.f, 398 | bool anti_aliasing = true); 399 | 400 | private: 401 | 402 | void fill_circle_impl(const position& center, 403 | position* vtx, 404 | uint32_t vtx_count, 405 | pack_color col_inner, 406 | pack_color col_outer); 407 | public: 408 | 409 | void line(const position& p1, 410 | const position& p2, 411 | const pack_color col1, 412 | const pack_color col2, 413 | const pos_type thickness, 414 | const bool aa = false) 415 | { 416 | reserve_path(2); 417 | push_path(p1); 418 | push_path(p2); 419 | pack_color colors[] = { col1, col2 }; 420 | poly_line(path_data(), 2, colors, thickness, aa); 421 | clear_path(); 422 | } 423 | 424 | void line(const position& p1, 425 | const position& p2, 426 | const pack_color col, 427 | const pos_type thickness, 428 | const bool aa = false) 429 | { 430 | line(p1, p2, col, col, thickness, aa); 431 | } 432 | 433 | //Polystuff 434 | void poly_line(position* points, 435 | uint32_t count, 436 | pack_color col, 437 | pos_type thickness, 438 | bool anti_aliased = false/*, bool closed = false, bool anti_aliased = true*/); 439 | void poly_line(position* points, 440 | uint32_t count, 441 | const pack_color* col, 442 | pos_type thickness, 443 | bool anti_aliased = false/*, bool closed = false, bool anti_aliased = true*/); 444 | 445 | void poly_fill(position* points, uint32_t count, const pack_color* col); //TODO: Anti-Aliasing 446 | void poly_fill(position* points, uint32_t count, pack_color col); 447 | 448 | //Primitives(Call prim_reserve before calling the draw funcs) 449 | void prim_reserve(uint32_t idx_count, uint32_t vtx_count); 450 | void prim_rect(const position& p1, const position& p2, const pack_color col); 451 | void prim_rect_uv(const position& p1, 452 | const position& p2, 453 | const position& uv1, 454 | const position& uv2, 455 | const pack_color col); 456 | void prim_quad_uv(const position& p1, 457 | const position& p2, 458 | const position& p3, 459 | const position& p4, 460 | const position& uv1, 461 | const position& uv2, 462 | const position& uv3, 463 | const position& uv4, 464 | const pack_color col); 465 | 466 | //Not formatted and auto-clipped rn 467 | void text(font* font, 468 | const char* text, 469 | const position& top_left, 470 | const pack_color col, 471 | bool outline = false, 472 | const position& bot_right = position 473 | { -1.f, -1.f }); 474 | 475 | void text(const char* text, 476 | const position& top_left, 477 | const pack_color col, 478 | const bool outline = false, 479 | const position& bot_right = position{ 480 | -1.f, 481 | -1.f 482 | }) 483 | { 484 | this->text(nullptr, text, top_left, col, outline, bot_right); 485 | } 486 | 487 | position text_size(font* font, const char* text, const char* text_end = nullptr) const; 488 | 489 | position text_size(const char* text, const char* text_end = nullptr) const 490 | { 491 | return text_size(nullptr, text, text_end); 492 | } 493 | 494 | rect text_bounds(font* font, const char* text, const char* text_end = nullptr) const; 495 | 496 | rect text_bounds(const char* text, const char* text_end = nullptr) const 497 | { 498 | return text_bounds(nullptr, text, text_end); 499 | } 500 | 501 | void text(font* font, 502 | float target_size, 503 | const char* text, 504 | const position& top_left, 505 | const pack_color col, 506 | bool outline = false, 507 | const position& bot_right = position{ -1.f, -1.f }); 508 | 509 | void text(float target_size, 510 | const char* text, 511 | const position& top_left, 512 | const pack_color col, 513 | const bool outline = false, 514 | const position& bot_right = position{ -1.f, -1.f }) 515 | { 516 | this->text(nullptr, target_size, text, top_left, col, outline, bot_right); 517 | } 518 | 519 | position text_size(font* font, float target_size, const char* text, const char* text_end = nullptr) const; 520 | 521 | position text_size(float target_size, const char* text, const char* text_end = nullptr) const 522 | { 523 | return text_size(nullptr, target_size, text, text_end); 524 | } 525 | 526 | rect text_bounds(font* font, float target_size, const char* text, const char* text_end = nullptr) const; 527 | 528 | rect text_bounds(float target_size, const char* text, const char* text_end = nullptr) const 529 | { 530 | return text_bounds(nullptr, target_size, text, text_end); 531 | } 532 | 533 | private: 534 | void reserve_primitives(std::uint32_t idx_count, std::uint32_t vtx_count); 535 | 536 | void write_vtx(const position& p, const position& uv, const std::uint32_t col) 537 | { 538 | vtx_write_ptr->pos = p; 539 | vtx_write_ptr->uv = uv; 540 | vtx_write_ptr->col = col; 541 | vtx_write_ptr++; 542 | } 543 | 544 | void write_idx(const draw_index idx) 545 | { 546 | *idx_write_ptr = idx; 547 | idx_write_ptr++; 548 | } 549 | 550 | void reserve_path(const size_t size) 551 | { 552 | path.reserve(size); 553 | } 554 | 555 | void push_path(const position& pos) 556 | { 557 | path.emplace_back(pos); 558 | } 559 | 560 | position* path_data() 561 | { 562 | return path.data(); 563 | } 564 | 565 | void clear_path() 566 | { 567 | path.clear(); 568 | } 569 | }; 570 | 571 | struct draw_manager 572 | { 573 | protected: 574 | struct buffer_node 575 | { 576 | using child_array = std::vector>; 577 | std::unique_ptr active_buffer = nullptr; 578 | std::unique_ptr working_buffer = nullptr; 579 | child_array child_buffers = {}; // sorted low to high by size_t 580 | bool is_free = false; 581 | size_t parent = std::numeric_limits::max(); 582 | }; 583 | 584 | public: 585 | std::unique_ptr fonts = nullptr; 586 | 587 | size_t register_buffer(size_t init_priority = 0); 588 | size_t register_child_buffer(size_t parent, size_t priority); 589 | void update_child_priority(size_t child_idx, size_t new_priority); 590 | void update_buffer_priority(size_t buffer, size_t new_priority); 591 | void remove_buffer(size_t idx); 592 | void update_buffer_ptrs(); 593 | draw_buffer* get_buffer(const size_t); 594 | void swap_buffers(const size_t); 595 | 596 | virtual void update_screen_size(const position& screen_size) 597 | { 598 | this->_screen_size = screen_size; 599 | } 600 | 601 | const position& get_screen_size() const 602 | { 603 | return _screen_size; 604 | } 605 | 606 | //TODO: filename without backslashes translated to default windows font dir 607 | font* add_font(const char* file, 608 | float size, 609 | bool italic = false, 610 | bool bold = false, 611 | GLYPH_RANGES range = GLYPH_RANGE_LATIN, 612 | int rasterizer_flags = 0) 613 | const; 614 | font* add_font(const char* file, 615 | float size, 616 | const font_wchar* glyph_ranges, 617 | bool italic = false, 618 | bool bold = false, 619 | int rasterizer_flags = 0) const; 620 | 621 | // this does not copy the font data so keep the buffer alive until you remove the font :) 622 | font* add_font_mem(uint8_t* data, 623 | size_t data_size, 624 | float font_size, 625 | bool italic = false, 626 | bool bold = false, 627 | GLYPH_RANGES range = GLYPH_RANGE_LATIN, 628 | int rasterizer_flags = 0) 629 | const; 630 | void remove_font(const font*) const; 631 | 632 | // This will call update_matrix_translate on the currently active(!) buffer in the "swapchain", 633 | // use with care, may impact performance 634 | void update_matrix_translate(size_t buffer, const position& xy_translate, size_t cmd_idx = -1); 635 | 636 | 637 | // Texture Creation 638 | virtual tex_id create_texture(uint32_t width, uint32_t height) = 0; 639 | virtual bool set_texture_rgba(tex_id id, const uint8_t* rgba, uint32_t width, uint32_t height) = 0; 640 | // this function exists to fix bugs im too lazy to find out why they even happen 641 | // also it may not even do what the name suggests 642 | // the d3d9_manager feeds it directly to directx while the csgo impl converts the data to bgra 643 | virtual bool set_texture_rabg(tex_id id, const uint8_t* rabg, uint32_t width, uint32_t height) = 0; 644 | virtual bool texture_size(tex_id id, uint32_t& width, uint32_t& height) = 0; 645 | virtual bool delete_texture(tex_id id) = 0; 646 | 647 | virtual void draw() = 0; 648 | protected: 649 | std::vector _buffer_list = {}; 650 | std::vector> _priorities = {}; //(priority,idx) //TODO: Check performance 651 | std::vector _free_buffers = {}; 652 | std::mutex _list_mutex; 653 | position _screen_size = position{}; 654 | 655 | void sort_priorities() 656 | { 657 | std::sort(_priorities.begin(), 658 | _priorities.end(), 659 | [](auto& first, auto& sec) -> bool 660 | { 661 | return first.first < sec.first; 662 | }); 663 | } 664 | 665 | draw_manager() = default; 666 | void init(); 667 | }; 668 | 669 | 670 | //Draw Helper Funcs 671 | void rectangle_filled_rounded(draw_buffer* buf, 672 | const position& top_left, 673 | const position& bot_right, 674 | const pos_type radius, 675 | const pack_color col_top_left, 676 | const pack_color col_top_right, 677 | const pack_color col_bot_left, 678 | const pack_color col_bot_right, 679 | const uint8_t flags = ROUND_RECT_ALL); 680 | 681 | inline void rectangle_filled_rounded(draw_buffer* buf, 682 | const position& top_left, 683 | const position& bot_right, 684 | const pos_type radius, 685 | const pack_color col, 686 | const uint8_t flags = ROUND_RECT_ALL) 687 | { 688 | rectangle_filled_rounded(buf, top_left, bot_right, radius, col, col, col, col, flags); 689 | } 690 | 691 | inline void rectangle_filled_rounded(draw_buffer* buf, 692 | const position& top_left, 693 | const pos_type width, 694 | const pos_type height, 695 | const pos_type radius, 696 | const pack_color col_top_left, 697 | const pack_color col_top_right, 698 | const pack_color col_bot_left, 699 | const pack_color col_bot_right, 700 | const uint8_t flags = ROUND_RECT_ALL) 701 | { 702 | rectangle_filled_rounded(buf, 703 | top_left, 704 | { top_left.x + width, top_left.y + height }, 705 | radius, 706 | col_top_left, 707 | col_top_right, 708 | col_bot_left, 709 | col_bot_right, 710 | flags); 711 | } 712 | 713 | inline void rectangle_filled_rounded(draw_buffer* buf, 714 | const position& top_left, 715 | const pos_type width, 716 | const pos_type height, 717 | const pos_type radius, 718 | const pack_color col, 719 | const uint8_t flags = ROUND_RECT_ALL) 720 | { 721 | rectangle_filled_rounded(buf, 722 | top_left, 723 | { top_left.x + width, top_left.y + height }, 724 | radius, 725 | col, 726 | col, 727 | col, 728 | col, 729 | flags); 730 | } 731 | 732 | extern void check_mark(draw_buffer* buf, position top_left, pos_type width, const pack_color col); 733 | } 734 | -------------------------------------------------------------------------------- /draw_manager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33723.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "draw_manager", "draw_manager.vcxproj", "{8CE5580D-C1FE-4518-B95B-6C86091F0937}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug_DX11|x64 = Debug_DX11|x64 11 | Debug_DX11|x86 = Debug_DX11|x86 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release_DX11|x64 = Release_DX11|x64 15 | Release_DX11|x86 = Release_DX11|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug_DX11|x64.ActiveCfg = Debug_DX11|x64 21 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug_DX11|x64.Build.0 = Debug_DX11|x64 22 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug_DX11|x86.ActiveCfg = Debug_DX11|Win32 23 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug_DX11|x86.Build.0 = Debug_DX11|Win32 24 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug|x64.ActiveCfg = Debug|x64 25 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug|x64.Build.0 = Debug|x64 26 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug|x86.ActiveCfg = Debug|Win32 27 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Debug|x86.Build.0 = Debug|Win32 28 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release_DX11|x64.ActiveCfg = Release_DX11|x64 29 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release_DX11|x64.Build.0 = Release_DX11|x64 30 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release_DX11|x86.ActiveCfg = Release_DX11|Win32 31 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release_DX11|x86.Build.0 = Release_DX11|Win32 32 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release|x64.ActiveCfg = Release|x64 33 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release|x64.Build.0 = Release|x64 34 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release|x86.ActiveCfg = Release|Win32 35 | {8CE5580D-C1FE-4518-B95B-6C86091F0937}.Release|x86.Build.0 = Release|Win32 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {D6CFE3DD-3791-4572-97C2-1834F5D52B35} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /draw_manager.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /font.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "math.h" 6 | 7 | typedef struct FT_LibraryRec_ *FT_Library; 8 | typedef struct FT_FaceRec_ *FT_Face; 9 | typedef int32_t FT_Int32; 10 | typedef struct FT_BitmapGlyphRec_ *FT_BitmapGlyph; 11 | typedef struct FT_GlyphRec_ *FT_Glyph; 12 | 13 | namespace util::draw 14 | { 15 | using pos_type = float; 16 | using position = math::vec2f; 17 | using rect = math::vec4f; 18 | using color = math::color_rgba; 19 | using pack_color = math::color_rgba; 20 | using font_t = size_t; // 21 | using tex_id = void*; 22 | using matrix = math::matrix4x4f; 23 | using vec4f = math::vec4f; 24 | 25 | 26 | enum GLYPH_RANGES 27 | { 28 | GLYPH_RANGE_LATIN, 29 | GLYPH_RANGE_JAPANESE 30 | }; 31 | 32 | enum FONT_ATLAS_FLAGS : uint16_t 33 | { 34 | FONT_ATLAS_FLAGS_NONE = 0, 35 | FONT_ATLAS_FLAGS_NO_POWER_OF_TWO_HEIGHT = 1 << 0, 36 | FONT_ATLAS_FLAGS_NO_MOUSE_CURSORS = 1 << 1 37 | }; 38 | 39 | enum RASTERIZER_FLAGS 40 | { 41 | // By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. 42 | NO_HINTING = 1 << 0, 43 | // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. 44 | NO_AUTO_HINT = 1 << 1, 45 | // Disable auto-hinter. 46 | FORCE_AUTO_HINT = 1 << 2, 47 | // Indicates that the auto-hinter is preferred over the font's native hinter. 48 | LIGHT_HINTING = 1 << 3, 49 | // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. 50 | MONO_HINTING = 1 << 4, 51 | // Strong hinting algorithm that should only be used for monochrome output. 52 | BOLD = 1 << 5, 53 | // Styling: Should we artificially embolden the font? 54 | OBLIQUE = 1 << 6, 55 | // Styling: Should we slant the font, emulating italic style? 56 | NO_ANTIALIASING = 1 << 7 57 | // Disable anti-aliasing. Combine this with MonoHinting for best results! 58 | }; 59 | 60 | 61 | using font_wchar = uint16_t; 62 | using font_char = char; 63 | 64 | struct font_config; 65 | struct font_info; 66 | struct glyph_info; 67 | struct font_atlas; 68 | struct font; 69 | struct draw_manager; 70 | struct draw_buffer; 71 | 72 | struct font_config 73 | { 74 | void *font_data; 75 | uint32_t font_data_size; 76 | bool owned_by_atlas; 77 | font_t font_idx; 78 | 79 | float size_pixels; 80 | uint32_t oversample_h; 81 | uint32_t oversample_v; 82 | bool pixel_snap_h; 83 | 84 | position glyph_extra_spacing; 85 | position glyph_offset; 86 | const font_wchar *glyph_ranges; 87 | float glyph_min_advance; 88 | float glyph_max_advance; 89 | 90 | bool merge_mode; //Ignored for now 91 | uint32_t rasterizer_flags; 92 | float rasterizer_multiply; 93 | 94 | std::array name{}; 95 | std::shared_ptr dst_font; 96 | 97 | font_config(); 98 | }; 99 | 100 | struct font_info 101 | { 102 | uint32_t pixel_height; 103 | float ascender; 104 | float descender; 105 | float line_spacing; 106 | float line_gap; 107 | float max_advance_width; 108 | }; 109 | 110 | struct font_glyph 111 | { 112 | font_wchar codepoint; 113 | float advance_x; 114 | float x0, y0, x1, y1; 115 | float u0, v0, u1, v1; 116 | }; 117 | 118 | struct font 119 | { 120 | font_info info; 121 | uint32_t user_flags; 122 | FT_Library freetype_library{}; 123 | FT_Face freetype_face{}; 124 | FT_Int32 freetype_load_flags{}; 125 | 126 | //Other stuff 127 | float font_size; 128 | float scale; 129 | position display_offset; 130 | std::vector glyphs; 131 | std::vector index_advance_x; 132 | std::vector index_glyph_y; 133 | std::vector index_lookup; 134 | const font_glyph *fallback_glyph; 135 | float fallback_advance_x; 136 | font_wchar fallback_char; 137 | 138 | short config_data_count; 139 | font_config *config_data; 140 | font_atlas *container_atlas; 141 | float ascent, descent; 142 | bool dirty_lookup_tables; 143 | int metrics_total_surface; 144 | 145 | font(); 146 | ~font(); 147 | 148 | bool init(const font_config &cfg, uint32_t user_flags); 149 | void shutdown(); 150 | void set_pixel_height(uint32_t pixel_height); 151 | bool calc_glyph_info(uint32_t codepoint, 152 | glyph_info &glyph_info, 153 | FT_Glyph &ft_glyph, 154 | FT_BitmapGlyph &ft_bitmap) const; 155 | void blit_glyph(FT_BitmapGlyph ft_bitmap, 156 | uint8_t *dst, 157 | uint32_t dst_pitch, 158 | const unsigned char *multiply_table = nullptr) const; 159 | 160 | 161 | void clear_output_data(); 162 | void build_lookup_table(); 163 | void set_fallback_char(font_wchar c); 164 | const font_glyph* find_glyph(font_wchar c) const; 165 | const font_glyph* find_glyph_no_fallback(font_wchar c) const; 166 | 167 | float char_advance(font_wchar c) const 168 | { 169 | return (static_cast(c) < index_advance_x.size()) 170 | ? index_advance_x[static_cast(c)] 171 | : fallback_advance_x; 172 | } 173 | 174 | bool loaded() const 175 | { 176 | return container_atlas != nullptr; 177 | } 178 | 179 | const char* debug_name() const 180 | { 181 | return config_data ? config_data->name.data() : ""; 182 | } 183 | 184 | //TODO: FIX THIS SHIT 185 | position calc_text_size(float size, 186 | float max_width, 187 | float wrap_width, 188 | const char *text_begin, 189 | const char *text_end = nullptr, 190 | const char **remaining = nullptr) const; 191 | rect calc_text_bounds(float size, 192 | float max_width, 193 | float wrap_width, 194 | const char *text_begin, 195 | const char *text_end, 196 | const char **remaining = nullptr) const; 197 | const char* calc_word_wrap_pos(float scale, const char *text, const char *text_end, float wrap_width) const; 198 | void render_char(draw_buffer *draw_buffer, float size, position pos, pack_color col, font_wchar c) const; 199 | void render_text(draw_buffer *draw_buffer, 200 | float size, 201 | position pos, 202 | pack_color col, 203 | const rect &clip_rect, 204 | const char *text_begin, 205 | const char *text_end, 206 | float wrap_width = 0.0f, 207 | bool cpu_fine_clip = false) const; 208 | 209 | 210 | void grow_index(size_t new_size); 211 | void add_glyph(font_wchar c, 212 | float x0, 213 | float y0, 214 | float x1, 215 | float y1, 216 | float u0, 217 | float v0, 218 | float u1, 219 | float v1, 220 | float advance_x); 221 | void add_remap_char(font_wchar dst, font_wchar src, bool overwrite_dst = true); 222 | }; 223 | 224 | struct glyph_info 225 | { 226 | float width; 227 | float height; 228 | float offset_x; 229 | float offset_y; 230 | float advance_x; 231 | }; 232 | 233 | 234 | struct font_atlas 235 | { 236 | struct custom_rect 237 | { 238 | uint32_t id; 239 | uint16_t width, height; 240 | uint16_t x, y; 241 | float glyph_advance_x; 242 | position glyph_offset; 243 | font *font; 244 | 245 | custom_rect(); 246 | 247 | bool is_packed() const 248 | { 249 | return (x != 0xFFFF); 250 | } 251 | }; 252 | 253 | bool locked; 254 | bool has_updated; 255 | FONT_ATLAS_FLAGS flags; 256 | tex_id tex_id; 257 | uint32_t tex_desired_width; 258 | uint32_t tex_glyph_padding; 259 | 260 | 261 | uint8_t *tex_pixels_alpha_8; 262 | uint32_t *tex_pixels_rgba_32; 263 | uint32_t tex_width; 264 | uint32_t tex_height; 265 | position tex_uv_scale; 266 | position tex_uv_white_pixel; 267 | std::vector> fonts; 268 | std::vector custom_rects; 269 | std::vector config_data; 270 | std::array custom_rect_idx; 271 | std::mutex tex_mutex; 272 | 273 | 274 | font_atlas(); 275 | ~font_atlas(); 276 | 277 | void add_font_default() { } //TODO 278 | font* add_font(const font_config *font_cfg = nullptr); 279 | font* add_font_from_ttf(const char *file_name, 280 | float size_pixels, 281 | const font_config *font_cfg = nullptr, 282 | const font_wchar *glyph_ranges = nullptr); 283 | font* add_font_from_ttf_mem(void *font_data, 284 | size_t data_size, 285 | float size_pixels, 286 | const font_config *font_cfg = nullptr, 287 | const font_wchar *glyph_ranges = nullptr); 288 | //Skipped Compressed Fonts 289 | void remove_font(const font *); 290 | 291 | void clear_input_data(); 292 | void clear_tex_data(bool lock_tex = true); 293 | void clear_fonts(); 294 | void clear(); 295 | 296 | 297 | bool build(uint32_t extra_flags = 0); 298 | 299 | bool is_built() 300 | { 301 | return !fonts.empty() && (tex_pixels_alpha_8 != nullptr || tex_pixels_rgba_32 != nullptr); 302 | } 303 | 304 | void tex_data_as_alpha_8(uint8_t **out_pixels, 305 | uint32_t *out_width, 306 | uint32_t *out_height, 307 | uint32_t *out_bytes_per_pixel = nullptr); 308 | void tex_data_as_rgba_32(uint8_t **out_pixels, 309 | uint32_t *out_width, 310 | uint32_t *out_height, 311 | uint32_t *out_bytes_per_pixel = nullptr); 312 | 313 | void set_tex_id(const draw::tex_id id) 314 | { 315 | tex_id = id; 316 | } 317 | 318 | const font_wchar* glyph_ranges_default(); 319 | const font_wchar* glyph_ranges_japanese(); 320 | 321 | uint32_t add_custom_rect_regular(uint32_t id, uint16_t width, uint16_t height); 322 | uint32_t add_custom_rect_glyph(font *font, 323 | font_wchar id, 324 | uint16_t width, 325 | uint16_t height, 326 | float advance_x, 327 | const position &offset = position{0.f, 0.f}); 328 | 329 | void calc_custom_rect_uv(const custom_rect *rect, position *out_uv_min, position *out_uv_max); 330 | }; 331 | } 332 | -------------------------------------------------------------------------------- /impl/d3d11_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "d3d11_manager.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "shaders_dx11.hpp" 9 | 10 | using namespace util::draw; 11 | 12 | tex_dict_dx11 d3d11_manager::_tex_dict{}; 13 | 14 | struct d3d11_vertex { 15 | float pos[3]; 16 | union { 17 | uint8_t col[4]; 18 | uint32_t col_u32; 19 | }; 20 | float uv[2]; 21 | }; 22 | 23 | struct pix_size_buf { 24 | float size[4]; 25 | }; 26 | 27 | struct pix_scissor_buf { 28 | float circle_def[4]; 29 | float key_color[4]; 30 | }; 31 | 32 | struct pix_blur_buf { 33 | float sample_vec[4]; 34 | float weights[96]; 35 | float offsets[96]; 36 | }; 37 | 38 | d3d11_manager::d3d11_manager(ID3D11Device* dev, ID3D11DeviceContext* ctx) : _device_ptr(dev), _ctx(ctx) { 39 | init(); 40 | } 41 | 42 | static float gauss(float sigma, float x) { 43 | float tmp = 1.f / (std::sqrt(2 * 3.141592653589793) * sigma); 44 | float tmp2 = std::exp(-0.5 * ((x * x) / (sigma * sigma))); 45 | return tmp * tmp2; 46 | } 47 | 48 | void d3d11_manager::draw() { 49 | _tex_dict.process_update_queue(_ctx); 50 | std::scoped_lock g(_list_mutex, fonts->tex_mutex); 51 | fonts->locked = true; 52 | auto idx_count = 0u; 53 | auto vtx_count = 0u; 54 | 55 | for (const auto& node : _buffer_list) 56 | { 57 | const auto vtx_idx_count = node.active_buffer->vtx_idx_count(); 58 | vtx_count += vtx_idx_count.first; 59 | idx_count += vtx_idx_count.second; 60 | } 61 | 62 | if (!vtx_count || !idx_count) 63 | { 64 | fonts->locked = false; 65 | return; 66 | } 67 | 68 | if (!_dat.font_tex || fonts->has_updated) 69 | { 70 | create_font_texture(); 71 | } 72 | 73 | if (!_dat.vtx_buf || _dat._vtx_buf_size < vtx_count) { 74 | _dat._vtx_buf_size = vtx_count + 500; 75 | auto desc = D3D11_BUFFER_DESC{}; 76 | desc.Usage = D3D11_USAGE_DYNAMIC; 77 | desc.ByteWidth = _dat._vtx_buf_size * sizeof(d3d11_vertex); 78 | desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 79 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 80 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.vtx_buf.ReleaseAndGetAddressOf()) != S_OK) { 81 | fonts->locked = false; 82 | return; 83 | } 84 | } 85 | 86 | if (!_dat.idx_buf || _dat._idx_buf_size < idx_count) { 87 | _dat._idx_buf_size = idx_count + 1000; 88 | auto desc = D3D11_BUFFER_DESC{}; 89 | desc.Usage = D3D11_USAGE_DYNAMIC; 90 | desc.ByteWidth = _dat._idx_buf_size * sizeof(draw_buffer::draw_index); 91 | desc.BindFlags = D3D11_BIND_INDEX_BUFFER; 92 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 93 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.idx_buf.ReleaseAndGetAddressOf()) != S_OK) { 94 | fonts->locked = false; 95 | return; 96 | } 97 | } 98 | 99 | D3D11_MAPPED_SUBRESOURCE vtx_res, idx_res; 100 | if (_ctx->Map(_dat.vtx_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_res) != S_OK) { 101 | fonts->locked = false; 102 | return; 103 | } 104 | if (_ctx->Map(_dat.idx_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_res) != S_OK) { 105 | fonts->locked = false; 106 | return; 107 | } 108 | auto* vtx_dst = reinterpret_cast(vtx_res.pData); 109 | auto* idx_dst = reinterpret_cast(idx_res.pData); 110 | 111 | const auto copy_draw_data = [&](draw_buffer* buf_ptr) { 112 | auto* vtx_src = buf_ptr->vertices.data(); 113 | for (auto i = 0u; i < buf_ptr->vertices.size(); i++) 114 | { 115 | vtx_dst->pos[0] = vtx_src->pos.x; 116 | vtx_dst->pos[1] = vtx_src->pos.y; 117 | vtx_dst->pos[2] = 1.f; 118 | vtx_dst->col_u32 = vtx_src->col.as_abgr(); 119 | vtx_dst->uv[0] = vtx_src->uv.x; 120 | vtx_dst->uv[1] = vtx_src->uv.y; 121 | vtx_dst++; 122 | vtx_src++; 123 | } 124 | std::memcpy(idx_dst, buf_ptr->indices.data(), 125 | buf_ptr->indices.size() * sizeof(draw_buffer::draw_index)); 126 | idx_dst += buf_ptr->indices.size(); 127 | }; 128 | 129 | const auto copy_child_data = [=](const buffer_node::child_array& childs, 130 | const auto& self_ref) -> void { 131 | for (const auto& child : childs) 132 | { 133 | const auto& element = _buffer_list[child.second]; 134 | copy_draw_data(element.active_buffer.get()); 135 | if (!element.child_buffers.empty()) 136 | self_ref(element.child_buffers, self_ref); 137 | } 138 | }; 139 | 140 | if (vtx_count && idx_count) 141 | { 142 | for (const auto& prio_idx : _priorities) 143 | { 144 | const auto& node = _buffer_list[prio_idx.second]; 145 | 146 | copy_draw_data(node.active_buffer.get()); 147 | copy_child_data(node.child_buffers, copy_child_data); 148 | } 149 | } 150 | 151 | _ctx->Unmap(_dat.vtx_buf.Get(), 0); 152 | _ctx->Unmap(_dat.idx_buf.Get(), 0); 153 | 154 | if (!setup_render_data() || !setup_draw_state()) 155 | { 156 | fonts->locked = false; 157 | return; 158 | } 159 | 160 | // TODO 161 | uint32_t vtx_off = 0; 162 | uint32_t idx_off = 0; 163 | 164 | { 165 | D3D11_MAPPED_SUBRESOURCE res; 166 | if (_ctx->Map(_dat.pix_size_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res) != S_OK) { 167 | return; 168 | } 169 | 170 | auto* size_buf = reinterpret_cast(res.pData); 171 | *size_buf = pix_size_buf{ { _screen_size.x, _screen_size.y, 0.f, 0.f } }; 172 | _ctx->Unmap(_dat.pix_size_buf.Get(), 0); 173 | } 174 | 175 | { 176 | D3D11_MAPPED_SUBRESOURCE res; 177 | if (_ctx->Map(_dat.vtx_const_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res) != S_OK) { 178 | return; 179 | } 180 | 181 | std::memcpy(res.pData, _wvp, sizeof(_wvp)); 182 | _ctx->Unmap(_dat.vtx_const_buf.Get(), 0); 183 | } 184 | 185 | // make a copy of the render target 186 | ComPtr rt_view; 187 | _ctx->OMGetRenderTargets(1, rt_view.GetAddressOf(), nullptr); 188 | 189 | ComPtr rt_res; 190 | rt_view->GetResource(rt_res.GetAddressOf()); 191 | 192 | if (!_dat.buffer_copy || _screen_size.x != _dat.old_width || _screen_size.y != _dat.old_height) { 193 | // TODO: size check 194 | ComPtr rt_tex; 195 | rt_res.As(&rt_tex); 196 | 197 | D3D11_TEXTURE2D_DESC rt_desc; 198 | rt_tex->GetDesc(&rt_desc); 199 | 200 | _dat.old_width = _screen_size.x; 201 | _dat.old_height = _screen_size.y; 202 | 203 | auto desc = D3D11_TEXTURE2D_DESC{}; 204 | desc.Width = rt_desc.Width; 205 | desc.Height = rt_desc.Height; 206 | desc.MipLevels = 1; 207 | desc.ArraySize = 1; 208 | desc.Format = rt_desc.Format; 209 | desc.SampleDesc.Count = 1; 210 | desc.Usage = D3D11_USAGE_DEFAULT; 211 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 212 | 213 | ComPtr tex; 214 | if (_device_ptr->CreateTexture2D(&desc, nullptr, tex.GetAddressOf()) != S_OK) { 215 | return; 216 | } 217 | 218 | auto srv_desc = D3D11_SHADER_RESOURCE_VIEW_DESC{}; 219 | srv_desc.Format = desc.Format; 220 | srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 221 | srv_desc.Texture2D.MipLevels = 1; 222 | if (_device_ptr->CreateShaderResourceView(tex.Get(), &srv_desc, _dat.buffer_copy.ReleaseAndGetAddressOf()) != S_OK) { 223 | return; 224 | } 225 | 226 | auto sampler_desc = D3D11_SAMPLER_DESC{}; 227 | sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; 228 | sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_MIRROR; 229 | sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_MIRROR; 230 | sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; 231 | sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; 232 | if (_device_ptr->CreateSamplerState(&sampler_desc, _dat.buffer_copy_sampler.ReleaseAndGetAddressOf()) != S_OK) { 233 | return; 234 | } 235 | } 236 | 237 | ComPtr copy_res; 238 | { 239 | _dat.buffer_copy->GetResource(copy_res.GetAddressOf()); 240 | _ctx->CopyResource(copy_res.Get(), rt_res.Get()); 241 | } 242 | 243 | _ctx->PSSetSamplers(1, 1, _dat.buffer_copy_sampler.GetAddressOf()); 244 | _ctx->PSSetShaderResources(1, 1, _dat.buffer_copy.GetAddressOf()); 245 | 246 | const auto font_tex = fonts->tex_id; 247 | const auto draw_cmds = [&](draw_buffer* buf_ptr) { 248 | for (const auto& cmd : buf_ptr->cmds) 249 | { 250 | if (cmd.callback) 251 | { 252 | cmd.callback(&cmd); 253 | } 254 | else if (cmd.elem_count > 0) 255 | { 256 | RECT clip = { cmd.clip_rect.x, cmd.clip_rect.y, cmd.clip_rect.z, 257 | cmd.clip_rect.w }; 258 | pix_scissor_buf scissor_buf{}; 259 | if (cmd.circle_scissor) 260 | { 261 | // x,y = center; z = radius*radius; screenSpace 262 | D3D11_MAPPED_SUBRESOURCE res; 263 | if (_ctx->Map(_dat.pix_scissor_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res) != S_OK) { 264 | return; 265 | } 266 | 267 | auto* buf = reinterpret_cast(res.pData); 268 | scissor_buf.circle_def[0] = 269 | static_cast(cmd.clip_rect.x + cmd.clip_rect.z) * 0.5f; 270 | scissor_buf.circle_def[1] = 271 | static_cast(cmd.clip_rect.y + cmd.clip_rect.w) * 0.5f; 272 | scissor_buf.circle_def[2] = 273 | static_cast(cmd.clip_rect.z - cmd.clip_rect.x) * 0.5f; 274 | scissor_buf.circle_def[2] *= scissor_buf.circle_def[2]; 275 | std::memcpy(buf, &scissor_buf, sizeof(scissor_buf)); 276 | _ctx->Unmap(_dat.pix_scissor_buf.Get(), 0); 277 | 278 | _ctx->PSSetShader(_dat.scissor_pixel_shader.Get(), nullptr, 0); 279 | 280 | clip = { cmd.circle_outer_clip.x, cmd.circle_outer_clip.y, cmd.circle_outer_clip.z, 281 | cmd.circle_outer_clip.w }; 282 | } 283 | 284 | auto tex_id = cmd.tex_id; 285 | if (cmd.font_texture) 286 | tex_id = font_tex; 287 | else if (tex_id && !cmd.native_texture) 288 | tex_id = _tex_dict.texture(reinterpret_cast(tex_id)); 289 | 290 | if (!tex_id) { 291 | tex_id = _tex_dict.texture(_white_tex); 292 | } 293 | 294 | _ctx->RSSetScissorRects(1, &clip); 295 | _ctx->PSSetShaderResources(0, 1, reinterpret_cast(&tex_id)); 296 | 297 | // TODO: i dont use it so it's unsupported 298 | /*_device_ptr->SetTransform( 299 | D3DTS_WORLD, 300 | reinterpret_cast(cmd.matrix.matrix.data()));*/ 301 | if (cmd.blur_strength) 302 | { 303 | // TODO: maybe precompute common values? 304 | const auto sample_count = std::min(cmd.blur_strength, uint8_t(95 * 2)); 305 | const auto sigma = static_cast(sample_count) / 3.f; 306 | std::array weights; 307 | std::array offsets; 308 | weights[0] = gauss(sigma, 0); 309 | float sum = weights[0]; 310 | for (int i = 1; i <= sample_count / 2; ++i) { 311 | // combined computation courtesy of https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ 312 | const auto weight1 = gauss(sigma, i * 2); 313 | const auto weight2 = gauss(sigma, i * 2 + 1); 314 | const auto off1 = (float)i * 2; 315 | const auto off2 = off1 + 1; 316 | const auto combined_weight = weight1 + weight2; 317 | weights[i] = combined_weight; 318 | offsets[i] = ((off1 * weight1) + (off2 * weight2)) / combined_weight; 319 | 320 | sum += combined_weight * 2.f; 321 | } 322 | if (sample_count & 1) { 323 | weights[sample_count] = gauss(sigma, sample_count); 324 | offsets[sample_count] = sample_count; 325 | sum += weights[sample_count] * 2; 326 | } 327 | 328 | float sum_inv = 1.f / sum; 329 | for (int i = 0; i <= sample_count; ++i) { 330 | weights[i] *= sum_inv; 331 | } 332 | 333 | // we effectively half the original sample count 334 | const auto end_sample_count = (sample_count >> 1) + (sample_count & 1); 335 | float sample_vec[4] = { end_sample_count, 0, 0, 0 }; 336 | const auto f_count = (end_sample_count + 3) / 4; // round up 337 | 338 | { 339 | D3D11_MAPPED_SUBRESOURCE sub_res; 340 | if (_ctx->Map(_dat.pix_blur_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sub_res) != S_OK) { 341 | return; 342 | } 343 | 344 | auto* data = reinterpret_cast(sub_res.pData); 345 | std::memcpy(data->sample_vec, sample_vec, sizeof(sample_vec)); 346 | std::memcpy(data->weights, weights.data(), sizeof(float) * end_sample_count); 347 | std::memcpy(data->offsets, offsets.data(), sizeof(float) * end_sample_count); 348 | 349 | _ctx->Unmap(_dat.pix_blur_buf.Get(), 0); 350 | } 351 | 352 | for (auto i = 0; i < cmd.blur_pass_count; ++i) 353 | { 354 | // TODO: maybe cache min/max coords in the cmd so we can say somewhat accurately where we draw? 355 | _ctx->CopyResource(copy_res.Get(), rt_res.Get()); 356 | _ctx->PSSetShader(cmd.circle_scissor ? _dat.scissor_blur_x_shader.Get() : _dat.blur_x_pixel_shader.Get(), nullptr, 0); 357 | _ctx->DrawIndexed(cmd.elem_count, idx_off, vtx_off); 358 | 359 | _ctx->CopyResource(copy_res.Get(), rt_res.Get()); 360 | _ctx->PSSetShader(cmd.circle_scissor ? _dat.scissor_blur_y_shader.Get() : _dat.blur_y_pixel_shader.Get(), nullptr, 0); 361 | _ctx->DrawIndexed(cmd.elem_count, idx_off, vtx_off); 362 | } 363 | 364 | _ctx->PSSetShader(_dat.pix_shader.Get(), nullptr, 0); 365 | } 366 | else 367 | { 368 | if (cmd.key_color.a() != 0) 369 | { 370 | D3D11_MAPPED_SUBRESOURCE res; 371 | if (_ctx->Map(_dat.pix_scissor_buf.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res) != S_OK) { 372 | return; 373 | } 374 | 375 | scissor_buf.key_color[0] = cmd.key_color.r() / 255.f; 376 | scissor_buf.key_color[1] = cmd.key_color.g() / 255.f; 377 | scissor_buf.key_color[2] = cmd.key_color.b() / 255.f; 378 | scissor_buf.key_color[3] = 0.f; 379 | 380 | std::memcpy(res.pData, &scissor_buf, sizeof(scissor_buf)); 381 | _ctx->Unmap(_dat.pix_scissor_buf.Get(), 0); 382 | 383 | _ctx->PSSetShader(cmd.circle_scissor ? _dat.scissor_key_shader.Get() : _dat.key_shader.Get(), nullptr, 0); 384 | } 385 | 386 | _ctx->DrawIndexed(cmd.elem_count, idx_off, vtx_off); 387 | 388 | if (cmd.key_color.a() != 0) 389 | { 390 | _ctx->PSSetShader(_dat.pix_shader.Get(), nullptr, 0); 391 | } 392 | } 393 | 394 | if (cmd.circle_scissor) 395 | { 396 | _ctx->PSSetShader(_dat.pix_shader.Get(), nullptr, 0); 397 | } 398 | idx_off += cmd.elem_count; 399 | } 400 | } 401 | vtx_off += buf_ptr->vertices.size(); 402 | }; 403 | 404 | const auto draw_child_cmds = [=](const buffer_node::child_array& childs, 405 | const auto& self_ref) -> void { 406 | for (const auto child : childs) 407 | { 408 | const auto& element = _buffer_list[child.second]; 409 | draw_cmds(element.active_buffer.get()); 410 | if (!element.child_buffers.empty()) 411 | self_ref(element.child_buffers, self_ref); 412 | } 413 | }; 414 | 415 | for (const auto prio_idx : _priorities) 416 | { 417 | const auto& node = _buffer_list[prio_idx.second]; 418 | 419 | draw_cmds(node.active_buffer.get()); 420 | draw_child_cmds(node.child_buffers, draw_child_cmds); 421 | } 422 | 423 | destroy_draw_state(); 424 | fonts->locked = false; 425 | } 426 | 427 | bool d3d11_manager::create_font_texture() { 428 | uint8_t* pixels; 429 | uint32_t width, height, bytes_per_pixel; 430 | fonts->tex_data_as_rgba_32(&pixels, &width, &height, &bytes_per_pixel); 431 | 432 | if (pixels == nullptr) 433 | return true; 434 | 435 | _dat.font_tex = nullptr; 436 | 437 | { 438 | auto desc = D3D11_TEXTURE2D_DESC{}; 439 | desc.Width = width; 440 | desc.Height = height; 441 | desc.MipLevels = 1; 442 | desc.ArraySize = 1; 443 | desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 444 | desc.SampleDesc.Count = 1; 445 | //desc.SampleDesc.Quality = 0; 446 | desc.Usage = D3D11_USAGE_DEFAULT; 447 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 448 | 449 | auto sub_res = D3D11_SUBRESOURCE_DATA{}; 450 | sub_res.pSysMem = pixels; 451 | sub_res.SysMemPitch = width * 4; 452 | ComPtr tex; 453 | if (_device_ptr->CreateTexture2D(&desc, &sub_res, tex.GetAddressOf()) != S_OK) { 454 | return false; 455 | } 456 | 457 | auto shader_desc = D3D11_SHADER_RESOURCE_VIEW_DESC{}; 458 | shader_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 459 | shader_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 460 | shader_desc.Texture2D.MipLevels = 1; 461 | if (_device_ptr->CreateShaderResourceView(tex.Get(), &shader_desc, _dat.font_tex.ReleaseAndGetAddressOf()) != S_OK) { 462 | return false; 463 | } 464 | } 465 | 466 | // sampler 467 | { 468 | auto desc = D3D11_SAMPLER_DESC{}; 469 | desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; 470 | desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; 471 | desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; 472 | desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; 473 | desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; 474 | if (_device_ptr->CreateSamplerState(&desc, _dat.font_sampler.ReleaseAndGetAddressOf()) != S_OK) { 475 | return false; 476 | } 477 | } 478 | 479 | fonts->tex_id = _dat.font_tex.Get(); 480 | fonts->has_updated = false; 481 | return true; 482 | } 483 | 484 | bool d3d11_manager::setup_render_data() { 485 | if (_dat.valid) { 486 | return true; 487 | } 488 | 489 | // blend state 490 | { 491 | auto desc = D3D11_BLEND_DESC{}; 492 | desc.AlphaToCoverageEnable = false; 493 | desc.RenderTarget[0].BlendEnable = true; 494 | desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; 495 | desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; 496 | desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; 497 | desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; 498 | desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; 499 | desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; 500 | desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 501 | if (_device_ptr->CreateBlendState(&desc, _dat.bs.ReleaseAndGetAddressOf()) != S_OK) { 502 | return false; 503 | } 504 | } 505 | 506 | // rasterizer state 507 | { 508 | auto desc = D3D11_RASTERIZER_DESC{}; 509 | desc.FillMode = D3D11_FILL_SOLID; 510 | desc.CullMode = D3D11_CULL_NONE; 511 | desc.ScissorEnable = true; 512 | desc.DepthClipEnable = true; 513 | if (_device_ptr->CreateRasterizerState(&desc, _dat.rs.ReleaseAndGetAddressOf()) != S_OK) { 514 | return false; 515 | } 516 | } 517 | 518 | // depth-stencil state 519 | { 520 | auto desc = D3D11_DEPTH_STENCIL_DESC{}; 521 | desc.DepthEnable = false; 522 | desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; 523 | desc.DepthFunc = D3D11_COMPARISON_ALWAYS; 524 | desc.StencilEnable = false; 525 | desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; 526 | desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; 527 | desc.BackFace = desc.FrontFace; 528 | if (_device_ptr->CreateDepthStencilState(&desc, _dat.dss.ReleaseAndGetAddressOf()) != S_OK) { 529 | return false; 530 | } 531 | } 532 | 533 | // vertex shader 534 | { 535 | if (_device_ptr->CreateVertexShader(shaders::vertex::generic, sizeof(shaders::vertex::generic), nullptr, _dat.vtx_shader.ReleaseAndGetAddressOf()) != S_OK) { 536 | return false; 537 | } 538 | 539 | D3D11_INPUT_ELEMENT_DESC layout[] = { 540 | { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, (UINT)offsetof(d3d11_vertex, pos), D3D11_INPUT_PER_VERTEX_DATA, 0}, 541 | { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(d3d11_vertex, uv), D3D11_INPUT_PER_VERTEX_DATA, 0}, 542 | { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(d3d11_vertex, col), D3D11_INPUT_PER_VERTEX_DATA, 0}, 543 | }; 544 | if (_device_ptr->CreateInputLayout(layout, 3, shaders::vertex::generic, sizeof(shaders::vertex::generic), _dat.input_layout.ReleaseAndGetAddressOf()) != S_OK) { 545 | return false; 546 | } 547 | 548 | // vertex const buf 549 | auto desc = D3D11_BUFFER_DESC{}; 550 | desc.ByteWidth = sizeof(_wvp); 551 | desc.Usage = D3D11_USAGE_DYNAMIC; 552 | desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 553 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 554 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.vtx_const_buf.ReleaseAndGetAddressOf()) != S_OK) { 555 | return false; 556 | } 557 | } 558 | 559 | // pixel shader 560 | { 561 | if (_device_ptr->CreatePixelShader(shaders::pixel::generic, sizeof(shaders::pixel::generic), nullptr, _dat.pix_shader.ReleaseAndGetAddressOf()) != S_OK) { 562 | return false; 563 | } 564 | 565 | if (_device_ptr->CreatePixelShader(shaders::pixel::scissor, sizeof(shaders::pixel::scissor), nullptr, _dat.scissor_pixel_shader.ReleaseAndGetAddressOf()) != S_OK) { 566 | return false; 567 | } 568 | 569 | if (_device_ptr->CreatePixelShader(shaders::pixel::blur_x, sizeof(shaders::pixel::blur_x), nullptr, _dat.blur_x_pixel_shader.ReleaseAndGetAddressOf()) != S_OK) { 570 | return false; 571 | } 572 | 573 | if (_device_ptr->CreatePixelShader(shaders::pixel::blur_y, sizeof(shaders::pixel::blur_y), nullptr, _dat.blur_y_pixel_shader.ReleaseAndGetAddressOf()) != S_OK) { 574 | return false; 575 | } 576 | 577 | if (_device_ptr->CreatePixelShader(shaders::pixel::scissor_blur_x, sizeof(shaders::pixel::scissor_blur_x), nullptr, _dat.scissor_blur_x_shader.ReleaseAndGetAddressOf()) != S_OK) { 578 | return false; 579 | } 580 | 581 | if (_device_ptr->CreatePixelShader(shaders::pixel::scissor_blur_y, sizeof(shaders::pixel::scissor_blur_y), nullptr, _dat.scissor_blur_y_shader.ReleaseAndGetAddressOf()) != S_OK) { 582 | return false; 583 | } 584 | 585 | if (_device_ptr->CreatePixelShader(shaders::pixel::key, sizeof(shaders::pixel::key), nullptr, _dat.key_shader.ReleaseAndGetAddressOf()) != S_OK) { 586 | return false; 587 | } 588 | 589 | if (_device_ptr->CreatePixelShader(shaders::pixel::scissor_key, sizeof(shaders::pixel::scissor_key), nullptr, _dat.scissor_key_shader.ReleaseAndGetAddressOf()) != S_OK) { 590 | return false; 591 | } 592 | 593 | // pix consts bufs 594 | auto desc = D3D11_BUFFER_DESC{}; 595 | desc.ByteWidth = sizeof(pix_size_buf); 596 | desc.Usage = D3D11_USAGE_DYNAMIC; 597 | desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 598 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 599 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.pix_size_buf.ReleaseAndGetAddressOf()) != S_OK) { 600 | return false; 601 | } 602 | 603 | desc.ByteWidth = sizeof(pix_scissor_buf); 604 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.pix_scissor_buf.ReleaseAndGetAddressOf()) != S_OK) { 605 | return false; 606 | } 607 | 608 | desc.ByteWidth = sizeof(pix_blur_buf); 609 | if (_device_ptr->CreateBuffer(&desc, nullptr, _dat.pix_blur_buf.ReleaseAndGetAddressOf()) != S_OK) { 610 | return false; 611 | } 612 | } 613 | 614 | if (!_white_tex) { 615 | _white_tex = _tex_dict.create_texture(2, 2); 616 | uint8_t data[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; 617 | if (!_white_tex->set_tex_data(&_tex_dict, _device_ptr, data, 2, 2)) { 618 | return false; 619 | } 620 | } 621 | 622 | _dat.valid = true; 623 | return true; 624 | } 625 | 626 | bool d3d11_manager::setup_draw_state() { 627 | _bak.vp_count = _bak.scissor_count = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; 628 | _ctx->RSGetScissorRects(&_bak.scissor_count, _bak.scissors); 629 | _ctx->RSGetViewports(&_bak.vp_count, _bak.viewports); 630 | _ctx->RSGetState(&_bak.rs); 631 | _ctx->OMGetBlendState(&_bak.bs, _bak.blend_factor, &_bak.sample_mask); 632 | _ctx->OMGetDepthStencilState(&_bak.stencil_state, &_bak.stencil_ref); 633 | _ctx->PSGetShaderResources(0, 2, _bak.shader_res); 634 | _ctx->PSGetSamplers(0, 2, _bak.ps_ss); 635 | _ctx->PSGetConstantBuffers(0, 3, _bak.ps_consts_bufs); 636 | _bak.ps_inst_count = _bak.vs_inst_count = _bak.gs_inst_count = 256; 637 | _ctx->PSGetShader(&_bak.ps, _bak.ps_insts, &_bak.ps_inst_count); 638 | _ctx->VSGetShader(&_bak.vs, _bak.vs_insts, &_bak.vs_inst_count); 639 | _ctx->VSGetConstantBuffers(0, 1, &_bak.vs_const_buf); 640 | _ctx->GSGetShader(&_bak.gs, _bak.gs_insts, &_bak.gs_inst_count); 641 | 642 | _ctx->IAGetPrimitiveTopology(&_bak.prim_top); 643 | _ctx->IAGetIndexBuffer(&_bak.idx_buf, &_bak.idx_buf_fmt, &_bak.idx_buf_off); 644 | _ctx->IAGetVertexBuffers(0, 1, &_bak.vtx_buf, &_bak.vtx_buf_stride, &_bak.vtx_buf_off); 645 | _ctx->IAGetInputLayout(&_bak.input_layout); 646 | 647 | auto vp = D3D11_VIEWPORT{}; 648 | vp.Width = _screen_size.x; 649 | vp.Height = _screen_size.y; 650 | vp.MinDepth = 0.f; 651 | vp.MaxDepth = 1.f; 652 | _ctx->RSSetViewports(1, &vp); 653 | 654 | uint32_t stride = sizeof(d3d11_vertex); 655 | uint32_t off = 0; 656 | _ctx->IASetInputLayout(_dat.input_layout.Get()); 657 | _ctx->IASetVertexBuffers(0, 1, _dat.vtx_buf.GetAddressOf(), &stride, &off); 658 | _ctx->IASetIndexBuffer(_dat.idx_buf.Get(), sizeof(draw_buffer::draw_index) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); 659 | _ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 660 | _ctx->VSSetShader(_dat.vtx_shader.Get(), nullptr, 0); 661 | _ctx->VSSetConstantBuffers(0, 1, _dat.vtx_const_buf.GetAddressOf()); 662 | _ctx->PSSetConstantBuffers(0, 1, _dat.pix_size_buf.GetAddressOf()); 663 | _ctx->PSSetConstantBuffers(1, 1, _dat.pix_scissor_buf.GetAddressOf()); 664 | _ctx->PSSetConstantBuffers(2, 1, _dat.pix_blur_buf.GetAddressOf()); 665 | _ctx->PSSetShader(_dat.pix_shader.Get(), nullptr, 0); 666 | _ctx->PSSetSamplers(0, 1, _dat.font_sampler.GetAddressOf()); 667 | _ctx->GSSetShader(nullptr, nullptr, 0); 668 | _ctx->HSSetShader(nullptr, nullptr, 0); 669 | _ctx->DSSetShader(nullptr, nullptr, 0); 670 | _ctx->CSSetShader(nullptr, nullptr, 0); 671 | 672 | const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; 673 | _ctx->OMSetBlendState(_dat.bs.Get(), blend_factor, 0xFFFFFFFF); 674 | _ctx->OMSetDepthStencilState(_dat.dss.Get(), 0); 675 | _ctx->RSSetState(_dat.rs.Get()); 676 | return true; 677 | } 678 | 679 | template 680 | static void safe_release(type*& obj) 681 | { 682 | if (obj) 683 | { 684 | obj->Release(); 685 | obj = nullptr; 686 | } 687 | } 688 | 689 | void d3d11_manager::destroy_draw_state() { 690 | _ctx->RSSetScissorRects(_bak.scissor_count, _bak.scissors); 691 | _ctx->RSSetViewports(_bak.vp_count, _bak.viewports); 692 | _ctx->RSSetState(_bak.rs); 693 | _ctx->OMSetBlendState(_bak.bs, _bak.blend_factor, _bak.sample_mask); 694 | _ctx->OMSetDepthStencilState(_bak.stencil_state, _bak.stencil_ref); 695 | _ctx->PSSetShaderResources(0, 2, _bak.shader_res); 696 | _ctx->PSSetSamplers(0, 2, _bak.ps_ss); 697 | _ctx->PSSetShader(_bak.ps, _bak.ps_insts, _bak.ps_inst_count); 698 | _ctx->PSSetConstantBuffers(0, 3, _bak.ps_consts_bufs); 699 | _ctx->VSSetConstantBuffers(0, 1, &_bak.vs_const_buf); 700 | _ctx->VSSetShader(_bak.vs, _bak.vs_insts, _bak.vs_inst_count); 701 | _ctx->GSSetShader(_bak.gs, _bak.gs_insts, _bak.gs_inst_count); 702 | _ctx->IASetPrimitiveTopology(_bak.prim_top); 703 | _ctx->IASetIndexBuffer(_bak.idx_buf, _bak.idx_buf_fmt, _bak.idx_buf_off); 704 | _ctx->IASetVertexBuffers(0, 1, &_bak.vtx_buf, &_bak.vtx_buf_stride, &_bak.vtx_buf_off); 705 | _ctx->IASetInputLayout(_bak.input_layout); 706 | 707 | safe_release(_bak.rs); 708 | safe_release(_bak.bs); 709 | safe_release(_bak.stencil_state); 710 | safe_release(_bak.shader_res[0]); 711 | safe_release(_bak.shader_res[1]); 712 | safe_release(_bak.ps_ss[0]); 713 | safe_release(_bak.ps_ss[1]); 714 | safe_release(_bak.ps); 715 | safe_release(_bak.ps_consts_bufs[0]); 716 | safe_release(_bak.ps_consts_bufs[1]); 717 | safe_release(_bak.ps_consts_bufs[2]); 718 | safe_release(_bak.vs_const_buf); 719 | for (auto i = 0; i < _bak.ps_inst_count; ++i) 720 | safe_release(_bak.ps_insts[i]); 721 | safe_release(_bak.vs); 722 | for (auto i = 0; i < _bak.vs_inst_count; ++i) 723 | safe_release(_bak.vs_insts[i]); 724 | safe_release(_bak.gs); 725 | for (auto i = 0; i < _bak.gs_inst_count; ++i) 726 | safe_release(_bak.gs_insts[i]); 727 | safe_release(_bak.idx_buf); 728 | safe_release(_bak.vtx_buf); 729 | safe_release(_bak.input_layout); 730 | } 731 | 732 | void d3d11_manager::update_screen_size(const position& screen_size) 733 | { 734 | _screen_size = screen_size; 735 | std::memset(&_wvp, 0, sizeof(_wvp)); 736 | _wvp[0] = 2.f / _screen_size.x; 737 | _wvp[5] = 2.f / -_screen_size.y; 738 | _wvp[10] = 0.5f; 739 | _wvp[12] = -1.f; 740 | _wvp[13] = 1.f; 741 | _wvp[14] = 0.5f; 742 | _wvp[15] = 1.f; 743 | } 744 | 745 | tex_id d3d11_manager::create_texture(const uint32_t width, const uint32_t height) 746 | { 747 | return reinterpret_cast(_tex_dict.create_texture(width, height)); 748 | } 749 | 750 | // I'm doing something wrong so we ghetto-fix it 751 | static void copy_convert(const uint8_t* rgba, uint8_t* out, const size_t size) 752 | { 753 | auto buf = reinterpret_cast(out); 754 | for (auto i = 0u; i < (size / 4); ++i) 755 | { 756 | uint32_t v = rgba[i*4]; 757 | v |= rgba[i*4 + 1] << 8; 758 | v |= rgba[i*4 + 2] << 16; 759 | v |= rgba[i*4 + 3] << 24; 760 | *buf++ = v; 761 | } 762 | } 763 | 764 | bool d3d11_manager::set_texture_rgba(const tex_id id, const uint8_t* rgba, 765 | const uint32_t width, const uint32_t height) 766 | { 767 | assert(id != reinterpret_cast(0)); 768 | 769 | if (!id) 770 | return false; 771 | 772 | auto tmp_data = std::vector{}; 773 | tmp_data.resize(width * height * 4u); 774 | 775 | /*copy_convert(rgba, tmp_data.data(), 776 | width * height * sizeof(std::remove_pointer_t) 777 | * 4u);*/ 778 | 779 | return _tex_dict.set_tex_data(_device_ptr, 780 | reinterpret_cast(id), 781 | rgba, width, height); 782 | } 783 | 784 | // this is broken 785 | bool d3d11_manager::set_texture_rabg(const tex_id id, const uint8_t* rabg, 786 | const uint32_t width, const uint32_t height) 787 | { 788 | assert(id != reinterpret_cast(0)); 789 | 790 | if (!id) 791 | return false; 792 | 793 | return _tex_dict.set_tex_data( 794 | _device_ptr, reinterpret_cast(id), rabg, width, height); 795 | } 796 | 797 | bool d3d11_manager::texture_size(const tex_id id, uint32_t& width, 798 | uint32_t& height) 799 | { 800 | assert(id != reinterpret_cast(0)); 801 | if (!id) 802 | return false; 803 | 804 | return _tex_dict.texture_size(reinterpret_cast(id), width, 805 | height); 806 | } 807 | 808 | bool d3d11_manager::delete_texture(const tex_id id) 809 | { 810 | assert(id != reinterpret_cast(0)); 811 | if (!id) 812 | return false; 813 | 814 | _tex_dict.destroy_texture(reinterpret_cast(id)); 815 | return true; 816 | } 817 | -------------------------------------------------------------------------------- /impl/d3d11_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NOMINMAX 5 | #include 6 | #include 7 | 8 | #include "tex_dict_dx11.hpp" 9 | #include "../draw_manager.hpp" 10 | 11 | namespace util::draw { 12 | using Microsoft::WRL::ComPtr; 13 | 14 | struct d3d11_manager final : draw_manager { 15 | explicit d3d11_manager(ID3D11Device*, ID3D11DeviceContext*); 16 | 17 | void draw() override; 18 | 19 | void pre_reset() const {}; 20 | 21 | void post_reset() const {}; 22 | 23 | auto device_ptr() const { return _device_ptr; } 24 | 25 | tex_id create_texture(uint32_t width, uint32_t height) override; 26 | bool set_texture_rgba(tex_id id, const uint8_t* rgba, uint32_t width, 27 | uint32_t height) override; 28 | bool set_texture_rabg(tex_id id, const uint8_t* rabg, uint32_t width, 29 | uint32_t height) override; 30 | bool texture_size(tex_id id, uint32_t& width, uint32_t& height) override; 31 | bool delete_texture(tex_id id) override; 32 | 33 | void update_screen_size(const position& screen_size) override; 34 | 35 | protected: 36 | bool create_font_texture(); 37 | bool setup_draw_state(); 38 | void destroy_draw_state(); 39 | bool setup_render_data(); 40 | void destroy_render_data(); 41 | 42 | protected: 43 | ID3D11Device* _device_ptr = nullptr; 44 | ID3D11DeviceContext* _ctx = nullptr; 45 | static tex_dict_dx11 _tex_dict; 46 | tex_wrapper_dx11* _white_tex = nullptr; 47 | 48 | float _wvp[16]; 49 | 50 | struct { 51 | ComPtr vtx_buf; 52 | ComPtr idx_buf; 53 | ComPtr factory; 54 | ComPtr font_tex; 55 | ComPtr font_sampler; 56 | ComPtr input_layout; 57 | ComPtr vtx_shader; 58 | ComPtr vtx_const_buf; 59 | ComPtr pix_shader; 60 | ComPtr pix_size_buf, pix_scissor_buf, pix_blur_buf; 61 | ComPtr key_shader; 62 | ComPtr scissor_pixel_shader; 63 | ComPtr scissor_blur_x_shader; 64 | ComPtr scissor_blur_y_shader; 65 | ComPtr scissor_key_shader; 66 | ComPtr blur_x_pixel_shader; 67 | ComPtr blur_y_pixel_shader; 68 | ComPtr buffer_copy; 69 | float old_width = 0, old_height = 0; 70 | ComPtr buffer_copy_sampler; 71 | ComPtr rs; 72 | ComPtr bs; 73 | ComPtr dss; 74 | size_t _vtx_buf_size = 0, _idx_buf_size = 0; 75 | bool valid = false; 76 | } _dat{}; 77 | 78 | struct { 79 | uint32_t vp_count, scissor_count; 80 | D3D11_VIEWPORT viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 81 | D3D11_RECT scissors[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 82 | ID3D11RasterizerState* rs; 83 | ID3D11BlendState* bs; 84 | float blend_factor[4]; 85 | uint32_t sample_mask; 86 | uint32_t stencil_ref; 87 | ID3D11DepthStencilState* stencil_state; 88 | ID3D11ShaderResourceView* shader_res[2]; 89 | ID3D11SamplerState* ps_ss[2]; 90 | ID3D11PixelShader* ps; 91 | ID3D11VertexShader* vs; 92 | ID3D11GeometryShader* gs; 93 | uint32_t ps_inst_count, vs_inst_count, gs_inst_count; 94 | ID3D11ClassInstance* ps_insts[256], * vs_insts[256], * gs_insts[256]; 95 | D3D11_PRIMITIVE_TOPOLOGY prim_top; 96 | ID3D11Buffer* idx_buf, * vtx_buf, * vs_const_buf; 97 | ID3D11Buffer* ps_consts_bufs[3]; 98 | uint32_t idx_buf_off, vtx_buf_stride, vtx_buf_off; 99 | DXGI_FORMAT idx_buf_fmt; 100 | ID3D11InputLayout* input_layout; 101 | } _bak{}; 102 | }; 103 | } -------------------------------------------------------------------------------- /impl/d3d9_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../draw_manager.hpp" 6 | #include "d3d9_manager.hpp" 7 | 8 | #include 9 | 10 | #include "shaders.hpp" 11 | 12 | using namespace util::draw; 13 | 14 | constexpr auto D3DFVF_CUSTOM = (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); 15 | 16 | tex_dict_dx9 d3d9_manager::_tex_dict{}; 17 | d3d_shared_reset_data d3d9_manager::_r{}; 18 | 19 | struct d3d9_vertex 20 | { 21 | float pos[3]; 22 | D3DCOLOR col; 23 | float uv[2]; 24 | }; 25 | 26 | d3d9_manager::d3d9_manager(IDirect3DDevice9* device) : _device_ptr(device) 27 | { 28 | D3DXMatrixIdentity(&_identity); 29 | init(); 30 | }; 31 | 32 | float gauss(float sigma, float x) { 33 | float tmp = 1.f / (std::sqrt(2 * 3.141592653589793) * sigma); 34 | float tmp2 = std::exp(-0.5 * ((x * x) / (sigma * sigma))); 35 | return tmp * tmp2; 36 | } 37 | 38 | void d3d9_manager::draw() 39 | { 40 | //std::lock_guard g(list_mutex); 41 | std::scoped_lock g(_list_mutex, fonts->tex_mutex); 42 | fonts->locked = true; 43 | auto idx_count = 0u; 44 | auto vtx_count = 0u; 45 | 46 | for (const auto& node : _buffer_list) 47 | { 48 | const auto vtx_idx_count = node.active_buffer->vtx_idx_count(); 49 | vtx_count += vtx_idx_count.first; 50 | idx_count += vtx_idx_count.second; 51 | } 52 | 53 | if (!vtx_count || !idx_count) 54 | { 55 | fonts->locked = false; 56 | return; 57 | } 58 | 59 | if (!_r.font_texture || fonts->has_updated) 60 | { 61 | create_font_texture(); 62 | } 63 | 64 | if (!_vtx_buffer || _vtx_buf_size < vtx_count) 65 | { 66 | if (_vtx_buffer) 67 | _vtx_buffer->Release(); 68 | _vtx_buf_size = vtx_count + 500; 69 | if (_device_ptr->CreateVertexBuffer(_vtx_buf_size * sizeof(d3d9_vertex), 70 | D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 71 | D3DFVF_CUSTOM, D3DPOOL_DEFAULT, 72 | &_vtx_buffer, nullptr) 73 | != D3D_OK) 74 | return; 75 | } 76 | if (!_idx_buffer || _idx_buf_size < idx_count) 77 | { 78 | if (_idx_buffer) 79 | _idx_buffer->Release(); 80 | _idx_buf_size = idx_count + 1000; 81 | if (_device_ptr->CreateIndexBuffer( 82 | _idx_buf_size * sizeof(draw_buffer::draw_index), 83 | D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, 84 | D3DPOOL_DEFAULT, &_idx_buffer, nullptr) 85 | != D3D_OK) 86 | return; 87 | } 88 | 89 | //TODO: This is taken over from ImGui but it can be designed more performant 90 | d3d9_vertex* vtx_dest; 91 | draw_buffer::draw_index* idx_dest; 92 | if ((vtx_count && idx_count) 93 | && _vtx_buffer->Lock(0, vtx_count * sizeof(d3d9_vertex), 94 | reinterpret_cast(&vtx_dest), 95 | D3DLOCK_DISCARD) 96 | != D3D_OK) 97 | return; 98 | if ((vtx_count && idx_count) 99 | && _idx_buffer->Lock(0, idx_count * sizeof(draw_buffer::draw_index), 100 | reinterpret_cast(&idx_dest), 101 | D3DLOCK_DISCARD) 102 | != D3D_OK) 103 | { 104 | _vtx_buffer->Unlock(); 105 | return; 106 | } 107 | 108 | const auto copy_draw_data = [&](draw_buffer* buf_ptr) { 109 | auto* vtx_src = buf_ptr->vertices.data(); 110 | for (auto i = 0u; i < buf_ptr->vertices.size(); i++) 111 | { 112 | vtx_dest->pos[0] = vtx_src->pos.x; 113 | vtx_dest->pos[1] = vtx_src->pos.y; 114 | vtx_dest->pos[2] = 1.f; 115 | vtx_dest->col = vtx_src->col.as_argb(); 116 | vtx_dest->uv[0] = vtx_src->uv.x; 117 | vtx_dest->uv[1] = vtx_src->uv.y; 118 | vtx_dest++; 119 | vtx_src++; 120 | } 121 | std::memcpy(idx_dest, buf_ptr->indices.data(), 122 | buf_ptr->indices.size() * sizeof(draw_buffer::draw_index)); 123 | idx_dest += buf_ptr->indices.size(); 124 | }; 125 | 126 | const auto copy_child_data = [=](const buffer_node::child_array& childs, 127 | const auto& self_ref) -> void { 128 | for (const auto child : childs) 129 | { 130 | const auto& element = _buffer_list[child.second]; 131 | copy_draw_data(element.active_buffer.get()); 132 | if (!element.child_buffers.empty()) 133 | self_ref(element.child_buffers, self_ref); 134 | } 135 | }; 136 | 137 | if (vtx_count && idx_count) 138 | { 139 | for (const auto prio_idx : _priorities) 140 | { 141 | const auto& node = _buffer_list[prio_idx.second]; 142 | 143 | copy_draw_data(node.active_buffer.get()); 144 | copy_child_data(node.child_buffers, copy_child_data); 145 | } 146 | 147 | _idx_buffer->Unlock(); 148 | _vtx_buffer->Unlock(); 149 | } 150 | 151 | if (!setup_draw_state()) 152 | { 153 | fonts->locked = false; 154 | return; 155 | } 156 | 157 | setup_shader(); 158 | 159 | //Render 160 | std::uint32_t vtx_offset = 0; 161 | std::uint32_t idx_offset = 0; 162 | 163 | _device_ptr->SetVertexShader(_r.vertex_shader); 164 | 165 | D3DXVECTOR4 size_vec = { _screen_size.x, _screen_size.y, 0.f, 0.f }; 166 | 167 | _device_ptr->SetVertexShaderConstantF(9, _wvp, 4); 168 | _device_ptr->SetPixelShaderConstantF(4, size_vec, 1); 169 | 170 | IDirect3DSurface9* back_buffer = nullptr; 171 | _device_ptr->GetRenderTarget(0, &back_buffer); 172 | 173 | IDirect3DSurface9* target_surface = nullptr; 174 | if (!_r.buffer_copy) 175 | { 176 | D3DSURFACE_DESC buffer_desc; 177 | back_buffer->GetDesc(&buffer_desc); 178 | _device_ptr->CreateTexture(buffer_desc.Width, buffer_desc.Height, 0, 179 | D3DUSAGE_RENDERTARGET, buffer_desc.Format, 180 | D3DPOOL_DEFAULT, &_r.buffer_copy, nullptr); 181 | } 182 | 183 | _r.buffer_copy->GetSurfaceLevel(0, &target_surface); 184 | const auto res = _device_ptr->StretchRect(back_buffer, nullptr, target_surface, nullptr, 185 | D3DTEXF_NONE); 186 | 187 | IDirect3DBaseTexture9* bak_tex = nullptr; 188 | _device_ptr->GetTexture(1, &bak_tex); 189 | _device_ptr->SetTexture(1, _r.buffer_copy); 190 | _device_ptr->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); 191 | _device_ptr->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); 192 | 193 | _device_ptr->SetPixelShader(nullptr); 194 | _device_ptr->SetVertexShader(nullptr); 195 | 196 | const auto font_tex = fonts->tex_id; 197 | const auto draw_cmds = [&](draw_buffer* buf_ptr) { 198 | for (const auto& cmd : buf_ptr->cmds) 199 | { 200 | if (cmd.callback) 201 | { 202 | cmd.callback(&cmd); 203 | } 204 | else if (cmd.elem_count > 0) 205 | { 206 | RECT clip = { cmd.clip_rect.x, cmd.clip_rect.y, cmd.clip_rect.z, 207 | cmd.clip_rect.w }; 208 | if (cmd.circle_scissor) 209 | { 210 | // x,y = center; z = radius*radius; screenSpace 211 | D3DXVECTOR4 circle_def; 212 | circle_def.x = 213 | static_cast(cmd.clip_rect.x + cmd.clip_rect.z) * 0.5f; 214 | circle_def.y = 215 | static_cast(cmd.clip_rect.y + cmd.clip_rect.w) * 0.5f; 216 | circle_def.z = 217 | static_cast(cmd.clip_rect.z - cmd.clip_rect.x) * 0.5f; 218 | circle_def.z *= circle_def.z; 219 | _device_ptr->SetVertexShader(_r.vertex_shader); 220 | _device_ptr->SetPixelShader(_r.scissor_pixel_shader); 221 | _device_ptr->SetPixelShaderConstantF(5, circle_def, 1); 222 | 223 | clip = { cmd.circle_outer_clip.x, cmd.circle_outer_clip.y, cmd.circle_outer_clip.z, 224 | cmd.circle_outer_clip.w }; 225 | } 226 | 227 | auto tex_id = cmd.tex_id; 228 | if (cmd.font_texture) 229 | tex_id = font_tex; 230 | else if (tex_id && !cmd.native_texture) 231 | tex_id = _tex_dict.texture(reinterpret_cast(tex_id)); 232 | 233 | auto sampler_available = BOOL{ tex_id != nullptr }; 234 | _device_ptr->SetPixelShaderConstantB(1, &sampler_available, 1); 235 | 236 | _device_ptr->SetScissorRect(&clip); 237 | _device_ptr->SetTexture(/*texture_stage*/ 0u, 238 | reinterpret_cast(tex_id)); 239 | _device_ptr->SetTransform( 240 | D3DTS_WORLD, 241 | reinterpret_cast(cmd.matrix.matrix.data())); 242 | if (cmd.blur_strength) 243 | { 244 | _device_ptr->SetVertexShader(_r.vertex_shader); 245 | 246 | // TODO: maybe precompute common values? 247 | const auto sample_count = std::min(cmd.blur_strength, uint8_t(95 * 2)); 248 | const auto sigma = static_cast(sample_count) / 3.f; 249 | std::array weights; 250 | std::array offsets; 251 | weights[0] = gauss(sigma, 0); 252 | float sum = weights[0]; 253 | for (int i = 1; i <= sample_count / 2; ++i) { 254 | // combined computation courtesy of https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ 255 | const auto weight1 = gauss(sigma, i*2); 256 | const auto weight2 = gauss(sigma, i*2 + 1); 257 | const auto off1 = (float)i * 2; 258 | const auto off2 = off1 + 1; 259 | const auto combined_weight = weight1 + weight2; 260 | weights[i] = combined_weight; 261 | offsets[i] = ((off1 * weight1) + (off2 * weight2)) / combined_weight; 262 | 263 | sum += combined_weight * 2.f; 264 | } 265 | if (sample_count & 1) { 266 | weights[sample_count] = gauss(sigma, sample_count); 267 | offsets[sample_count] = sample_count; 268 | sum += weights[sample_count] * 2; 269 | } 270 | 271 | float sum_inv = 1.f / sum; 272 | for (int i = 0; i <= sample_count; ++i) { 273 | weights[i] *= sum_inv; 274 | } 275 | 276 | // we effectively half the original sample count 277 | const auto end_sample_count = (sample_count >> 1) + (sample_count & 1); 278 | float sample_vec[4] = { end_sample_count, 0, 0, 0 }; 279 | const auto f_count = (end_sample_count + 3) / 4; // round up 280 | _device_ptr->SetPixelShaderConstantF(13, sample_vec, 1); 281 | _device_ptr->SetPixelShaderConstantF(14, weights.data(), f_count); 282 | _device_ptr->SetPixelShaderConstantF(38, offsets.data(), f_count); 283 | 284 | _device_ptr->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); 285 | _device_ptr->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 286 | 287 | for (auto i = 0; i < cmd.blur_pass_count; ++i) 288 | { 289 | // TODO: maybe cache min/max coords in the cmd so we can say somewhat accurately where we draw? 290 | _device_ptr->StretchRect(back_buffer, &clip, target_surface, 291 | &clip, D3DTEXF_NONE); 292 | _device_ptr->SetPixelShader(cmd.circle_scissor ? _r.scissor_blur_x_shader : _r.blur_x_pixel_shader); 293 | _device_ptr->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vtx_offset, 0, 294 | cmd.vtx_count, idx_offset, 295 | cmd.elem_count / 3); 296 | 297 | _device_ptr->StretchRect(back_buffer, &clip, target_surface, 298 | &clip, D3DTEXF_NONE); 299 | _device_ptr->SetPixelShader(cmd.circle_scissor ? _r.scissor_blur_y_shader : _r.blur_y_pixel_shader); 300 | _device_ptr->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vtx_offset, 0, 301 | cmd.vtx_count, idx_offset, 302 | cmd.elem_count / 3); 303 | } 304 | 305 | _device_ptr->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); 306 | _device_ptr->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 307 | _device_ptr->SetPixelShader(nullptr); 308 | _device_ptr->SetVertexShader(nullptr); 309 | } 310 | else 311 | { 312 | if (cmd.key_color.a() != 0) 313 | { 314 | _device_ptr->SetVertexShader(_r.vertex_shader); 315 | D3DXVECTOR4 vec = { cmd.key_color.r() / 255.f, cmd.key_color.g() / 255.f, 316 | cmd.key_color.b() / 255.f, 0.f }; 317 | _device_ptr->SetPixelShaderConstantF(8, vec, 1); 318 | _device_ptr->SetPixelShader( 319 | cmd.circle_scissor ? _r.scissor_key_shader : _r.key_shader); 320 | } 321 | 322 | _device_ptr->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vtx_offset, 0, 323 | cmd.vtx_count, idx_offset, 324 | cmd.elem_count / 3); 325 | 326 | if (cmd.key_color.a() != 0) 327 | { 328 | _device_ptr->SetVertexShader(nullptr); 329 | _device_ptr->SetPixelShader(nullptr); 330 | } 331 | } 332 | 333 | if (cmd.circle_scissor) 334 | { 335 | _device_ptr->SetVertexShader(nullptr); 336 | _device_ptr->SetPixelShader(nullptr); 337 | } 338 | idx_offset += cmd.elem_count; 339 | } 340 | } 341 | vtx_offset += buf_ptr->vertices.size(); 342 | }; 343 | 344 | const auto draw_child_cmds = [=](const buffer_node::child_array& childs, 345 | const auto& self_ref) -> void { 346 | for (const auto child : childs) 347 | { 348 | const auto& element = _buffer_list[child.second]; 349 | draw_cmds(element.active_buffer.get()); 350 | if (!element.child_buffers.empty()) 351 | self_ref(element.child_buffers, self_ref); 352 | } 353 | }; 354 | 355 | for (const auto prio_idx : _priorities) 356 | { 357 | const auto& node = _buffer_list[prio_idx.second]; 358 | 359 | draw_cmds(node.active_buffer.get()); 360 | draw_child_cmds(node.child_buffers, draw_child_cmds); 361 | } 362 | 363 | _device_ptr->SetTexture(1, bak_tex); 364 | if (bak_tex) 365 | bak_tex->Release(); 366 | 367 | target_surface->Release(); 368 | back_buffer->Release(); 369 | 370 | destroy_draw_state(); 371 | fonts->locked = false; 372 | } 373 | 374 | void d3d9_manager::setup_shader() 375 | { 376 | if (!_r.vertex_shader) 377 | { 378 | _device_ptr->CreateVertexShader( 379 | reinterpret_cast(shaders::vertex::generic), 380 | &_r.vertex_shader); 381 | } 382 | 383 | if (!_r.key_shader) 384 | { 385 | _device_ptr->CreatePixelShader( 386 | reinterpret_cast(shaders::pixel::key), &_r.key_shader); 387 | } 388 | 389 | if (!_r.scissor_pixel_shader) 390 | { 391 | _device_ptr->CreatePixelShader( 392 | reinterpret_cast(shaders::pixel::scissor), 393 | &_r.scissor_pixel_shader); 394 | } 395 | 396 | if (!_r.scissor_blur_x_shader) 397 | { 398 | _device_ptr->CreatePixelShader( 399 | reinterpret_cast(shaders::pixel::scissor_blur_x), 400 | &_r.scissor_blur_x_shader); 401 | } 402 | 403 | if (!_r.scissor_blur_y_shader) 404 | { 405 | _device_ptr->CreatePixelShader( 406 | reinterpret_cast(shaders::pixel::scissor_blur_y), 407 | &_r.scissor_blur_y_shader); 408 | } 409 | 410 | if (!_r.scissor_key_shader) 411 | { 412 | _device_ptr->CreatePixelShader( 413 | reinterpret_cast(shaders::pixel::scissor_key), 414 | &_r.scissor_key_shader); 415 | } 416 | 417 | if (!_r.blur_x_pixel_shader) 418 | { 419 | _device_ptr->CreatePixelShader( 420 | reinterpret_cast(shaders::pixel::blur_x), 421 | &_r.blur_x_pixel_shader); 422 | } 423 | 424 | if (!_r.blur_y_pixel_shader) 425 | { 426 | _device_ptr->CreatePixelShader( 427 | reinterpret_cast(shaders::pixel::blur_y), 428 | &_r.blur_y_pixel_shader); 429 | } 430 | } 431 | 432 | bool d3d9_manager::setup_draw_state() 433 | { 434 | _device_ptr->GetRenderState(D3DRS_COLORWRITEENABLE, &_color_write_enable); 435 | _device_ptr->GetSamplerState(0ul, D3DSAMP_ADDRESSU, &_sampler_u); 436 | _device_ptr->GetSamplerState(0ul, D3DSAMP_ADDRESSV, &_sampler_v); 437 | _device_ptr->GetSamplerState(0ul, D3DSAMP_ADDRESSW, &_sampler_w); 438 | _device_ptr->GetSamplerState(0ul, D3DSAMP_SRGBTEXTURE, &_sampler_srgb); 439 | 440 | _device_ptr->GetViewport(&_bak.vp); 441 | _device_ptr->SetRenderState(D3DRS_COLORWRITEENABLE, 0xFFFFFFFF); 442 | _device_ptr->SetRenderState(D3DRS_SRGBWRITEENABLE, false); 443 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 444 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 445 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); 446 | _device_ptr->SetSamplerState(0ul, D3DSAMP_SRGBTEXTURE, 0ul); 447 | 448 | _device_ptr->GetVertexDeclaration(&_bak.vert_declaration); 449 | 450 | if (_device_ptr->CreateStateBlock(D3DSBT_ALL, &_r.state_block) != D3D_OK) 451 | return false; 452 | _r.state_block->Capture(); 453 | 454 | D3DVIEWPORT9 vp; 455 | vp.X = vp.Y = 0; 456 | vp.Width = static_cast(_screen_size.x); 457 | vp.Height = static_cast(_screen_size.y); 458 | vp.MinZ = 0.f; 459 | vp.MaxZ = 1.f; 460 | _device_ptr->SetViewport(&vp); 461 | 462 | _device_ptr->GetTexture(0, &_bak.texture); 463 | _device_ptr->GetVertexShader(&_bak.vert_shader); 464 | _device_ptr->GetPixelShader(&_bak.pixel_shader); 465 | _device_ptr->SetTexture(0, nullptr); 466 | _device_ptr->GetFVF(&_bak.fvf); 467 | _device_ptr->SetVertexShader(nullptr); 468 | _device_ptr->SetPixelShader(nullptr); 469 | _device_ptr->SetFVF(D3DFVF_CUSTOM); 470 | _device_ptr->SetStreamSource(0, _vtx_buffer, 0, sizeof(d3d9_vertex)); 471 | _device_ptr->SetIndices(_idx_buffer); 472 | 473 | _device_ptr->GetRenderState(D3DRS_MULTISAMPLEANTIALIAS, &_bak.aa_state); 474 | _device_ptr->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE); 475 | _device_ptr->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); 476 | _device_ptr->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); 477 | _device_ptr->SetRenderState(D3DRS_LIGHTING, false); 478 | _device_ptr->SetRenderState(D3DRS_ZENABLE, false); 479 | _device_ptr->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); 480 | _device_ptr->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); 481 | _device_ptr->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); 482 | _device_ptr->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); 483 | _device_ptr->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); 484 | _device_ptr->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); 485 | _device_ptr->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); 486 | _device_ptr->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 487 | 488 | _device_ptr->SetRenderState(D3DRS_SCISSORTESTENABLE, true); 489 | _device_ptr->SetRenderState(D3DRS_ALPHABLENDENABLE, true); 490 | _device_ptr->SetRenderState(D3DRS_ALPHATESTENABLE, false); 491 | _device_ptr->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); 492 | _device_ptr->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 493 | _device_ptr->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 494 | 495 | //Backup Transformation matrices 496 | _device_ptr->GetTransform(D3DTS_WORLD, &_bak.world); 497 | _device_ptr->GetTransform(D3DTS_VIEW, &_bak.view); 498 | _device_ptr->GetTransform(D3DTS_PROJECTION, &_bak.projection); 499 | 500 | _device_ptr->SetTransform(D3DTS_WORLD, &_identity); 501 | _device_ptr->SetTransform(D3DTS_VIEW, &_identity); 502 | _device_ptr->SetTransform(D3DTS_PROJECTION, &_projection); 503 | 504 | return true; 505 | } 506 | 507 | template 508 | static void safe_release(type*& obj) 509 | { 510 | if (obj) 511 | { 512 | obj->Release(); 513 | obj = nullptr; 514 | } 515 | } 516 | 517 | void d3d9_manager::destroy_draw_state() 518 | { 519 | _device_ptr->SetTransform(D3DTS_WORLD, &_bak.world); 520 | _device_ptr->SetTransform(D3DTS_VIEW, &_bak.view); 521 | _device_ptr->SetTransform(D3DTS_PROJECTION, &_bak.projection); 522 | 523 | if (_r.state_block) 524 | { 525 | _r.state_block->Apply(); 526 | _r.state_block->Release(); 527 | _r.state_block = nullptr; 528 | } 529 | 530 | _device_ptr->SetVertexDeclaration(_bak.vert_declaration); 531 | _device_ptr->SetPixelShader(_bak.pixel_shader); 532 | _device_ptr->SetVertexShader(_bak.vert_shader); 533 | _device_ptr->SetTexture(0, _bak.texture); 534 | _device_ptr->SetFVF(_bak.fvf); 535 | _device_ptr->SetViewport(&_bak.vp); 536 | _device_ptr->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, _bak.aa_state); 537 | 538 | _device_ptr->SetSamplerState(0ul, D3DSAMP_SRGBTEXTURE, _sampler_srgb); 539 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSW, _sampler_w); 540 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSV, _sampler_v); 541 | _device_ptr->SetSamplerState(0ul, D3DSAMP_ADDRESSU, _sampler_u); 542 | 543 | _device_ptr->SetRenderState(D3DRS_COLORWRITEENABLE, _color_write_enable); 544 | _device_ptr->SetRenderState(D3DRS_SRGBWRITEENABLE, true); 545 | 546 | safe_release(_bak.vert_declaration); 547 | safe_release(_bak.pixel_shader); 548 | safe_release(_bak.vert_shader); 549 | safe_release(_bak.texture); 550 | } 551 | 552 | bool d3d9_manager::create_font_texture() 553 | { 554 | uint8_t* pixels; 555 | uint32_t width, height, bytes_per_pixel; 556 | fonts->tex_data_as_rgba_32(&pixels, &width, &height, &bytes_per_pixel); 557 | 558 | if (pixels == nullptr) 559 | return true; 560 | 561 | if (_r.font_texture) 562 | _r.font_texture->Release(); 563 | 564 | _r.font_texture = nullptr; 565 | if (_device_ptr->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, 566 | D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, 567 | &_r.font_texture, nullptr) 568 | != D3D_OK) 569 | return false; 570 | 571 | D3DLOCKED_RECT locked_rect; 572 | if (_r.font_texture->LockRect(0, &locked_rect, nullptr, 0) != D3D_OK) 573 | return false; 574 | for (auto i = 0u; i < height; i++) 575 | std::memcpy(reinterpret_cast(locked_rect.pBits) 576 | + locked_rect.Pitch * i, 577 | pixels + (width * bytes_per_pixel) * i, 578 | (width * bytes_per_pixel)); 579 | 580 | _r.font_texture->UnlockRect(0); 581 | 582 | fonts->tex_id = _r.font_texture; 583 | fonts->has_updated = false; 584 | 585 | return true; 586 | } 587 | 588 | bool d3d9_manager::create_device_objects() 589 | { 590 | if (!_device_ptr) 591 | return false; 592 | if (!create_font_texture()) 593 | return false; 594 | 595 | _tex_dict.post_reset(_device_ptr); 596 | 597 | return true; 598 | } 599 | 600 | void d3d9_manager::invalidate_shader() 601 | { 602 | if (_r.vertex_shader) 603 | { 604 | _r.vertex_shader->Release(); 605 | _r.vertex_shader = nullptr; 606 | } 607 | if (_r.key_shader) 608 | { 609 | _r.key_shader->Release(); 610 | _r.key_shader = nullptr; 611 | } 612 | if (_r.scissor_pixel_shader) { 613 | _r.scissor_pixel_shader->Release(); 614 | _r.scissor_pixel_shader = nullptr; 615 | } 616 | if (_r.scissor_blur_x_shader) { 617 | _r.scissor_blur_x_shader->Release(); 618 | _r.scissor_blur_x_shader = nullptr; 619 | } 620 | if (_r.scissor_blur_y_shader) { 621 | _r.scissor_blur_y_shader->Release(); 622 | _r.scissor_blur_y_shader = nullptr; 623 | } 624 | if (_r.scissor_key_shader) 625 | { 626 | _r.scissor_key_shader->Release(); 627 | _r.scissor_key_shader = nullptr; 628 | } 629 | if (_r.blur_x_pixel_shader) { 630 | _r.blur_x_pixel_shader->Release(); 631 | _r.blur_x_pixel_shader = nullptr; 632 | } 633 | if (_r.blur_y_pixel_shader) 634 | { 635 | _r.blur_y_pixel_shader->Release(); 636 | _r.blur_y_pixel_shader = nullptr; 637 | } 638 | } 639 | 640 | bool d3d9_manager::invalidate_device_objects() 641 | { 642 | if (!_device_ptr) 643 | return false; 644 | if (_vtx_buffer) 645 | { 646 | _vtx_buffer->Release(); 647 | _vtx_buffer = nullptr; 648 | } 649 | if (_idx_buffer) 650 | { 651 | _idx_buffer->Release(); 652 | _idx_buffer = nullptr; 653 | } 654 | if (_r.font_texture) 655 | _r.font_texture->Release(); 656 | if (_r.state_block) 657 | { 658 | _r.state_block->Release(); 659 | _r.state_block = nullptr; 660 | } 661 | invalidate_shader(); 662 | 663 | if (_r.buffer_copy) 664 | { 665 | _r.buffer_copy->Release(); 666 | _r.buffer_copy = nullptr; 667 | } 668 | 669 | _r.font_texture = nullptr; 670 | fonts->tex_id = nullptr; 671 | _tex_dict.pre_reset(); 672 | 673 | return true; 674 | } 675 | 676 | tex_id d3d9_manager::create_texture(const uint32_t width, const uint32_t height) 677 | { 678 | return reinterpret_cast(_tex_dict.create_texture(width, height)); 679 | } 680 | 681 | // Idk why directx seems to be using RABG internally, mabye not, IDK!!! this is weird, fuck directx 682 | static void copy_convert(const uint8_t* rgba, uint8_t* out, const size_t size) 683 | { 684 | auto in = reinterpret_cast(rgba); 685 | auto buf = reinterpret_cast(out); 686 | for (auto i = 0u; i < (size / 4); ++i) 687 | { 688 | *buf++ = 689 | (*in & 0xFF00FF00) | ((*in & 0xFF0000) >> 16) | ((*in++ & 0xFF) << 16); 690 | } 691 | } 692 | 693 | bool d3d9_manager::set_texture_rgba(const tex_id id, const uint8_t* rgba, 694 | const uint32_t width, const uint32_t height) 695 | { 696 | assert(id != reinterpret_cast(0)); 697 | 698 | if (!id) 699 | return false; 700 | 701 | auto tmp_data = std::vector{}; 702 | tmp_data.resize(width * height * 4u); 703 | 704 | copy_convert(rgba, tmp_data.data(), 705 | width * height * sizeof(std::remove_pointer_t) 706 | * 4u); 707 | 708 | return _tex_dict.set_tex_data(_device_ptr, 709 | reinterpret_cast(id), 710 | tmp_data.data(), width, height); 711 | } 712 | 713 | bool d3d9_manager::set_texture_rabg(const tex_id id, const uint8_t* rabg, 714 | const uint32_t width, const uint32_t height) 715 | { 716 | assert(id != reinterpret_cast(0)); 717 | 718 | if (!id) 719 | return false; 720 | 721 | return _tex_dict.set_tex_data( 722 | _device_ptr, reinterpret_cast(id), rabg, width, height); 723 | } 724 | 725 | bool d3d9_manager::texture_size(const tex_id id, uint32_t& width, 726 | uint32_t& height) 727 | { 728 | assert(id != reinterpret_cast(0)); 729 | if (!id) 730 | return false; 731 | 732 | return _tex_dict.texture_size(reinterpret_cast(id), width, 733 | height); 734 | } 735 | 736 | bool d3d9_manager::delete_texture(const tex_id id) 737 | { 738 | assert(id != reinterpret_cast(0)); 739 | if (!id) 740 | return false; 741 | 742 | _tex_dict.destroy_texture(reinterpret_cast(id)); 743 | return true; 744 | } 745 | 746 | void d3d9_manager::update_screen_size(const position& screen_size) 747 | { 748 | _screen_size = screen_size; 749 | std::memset(&_projection, 0, sizeof(_projection)); 750 | _projection[0] = 2.f / _screen_size.x; 751 | _projection[5] = 2.f / -_screen_size.y; 752 | _projection[10] = 0.5f; 753 | _projection[12] = (_screen_size.x + 1.f) / -_screen_size.x; 754 | _projection[13] = (_screen_size.y + 1.f) / _screen_size.y; 755 | _projection[14] = 0.5f; 756 | _projection[15] = 1.f; 757 | 758 | D3DXMatrixIdentity(&_wvp); 759 | _wvp *= _identity; 760 | _wvp *= _projection; 761 | } 762 | -------------------------------------------------------------------------------- /impl/d3d9_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NOMINMAX 5 | #include 6 | #include 7 | 8 | #include "tex_dict_dx9.hpp" 9 | 10 | namespace util::draw 11 | { 12 | struct d3d_shared_reset_data 13 | { 14 | IDirect3DTexture9* font_texture = nullptr; 15 | IDirect3DStateBlock9* state_block = nullptr; 16 | 17 | IDirect3DVertexShader9* vertex_shader = nullptr; 18 | 19 | IDirect3DPixelShader9* key_shader = nullptr; 20 | 21 | IDirect3DPixelShader9* scissor_pixel_shader = nullptr; 22 | IDirect3DPixelShader9* scissor_blur_x_shader = nullptr; 23 | IDirect3DPixelShader9* scissor_blur_y_shader = nullptr; 24 | IDirect3DPixelShader9* scissor_key_shader = nullptr; 25 | 26 | IDirect3DPixelShader9* blur_x_pixel_shader = nullptr; 27 | IDirect3DPixelShader9* blur_y_pixel_shader = nullptr; 28 | 29 | IDirect3DTexture9* buffer_copy = nullptr; 30 | }; 31 | 32 | struct d3d9_manager : public draw_manager 33 | { 34 | explicit d3d9_manager(IDirect3DDevice9* device); 35 | 36 | void draw() override; 37 | 38 | void pre_reset() const {}; 39 | 40 | void post_reset() const {}; 41 | 42 | bool create_device_objects(); 43 | bool invalidate_device_objects(); 44 | 45 | auto device_ptr() const { return _device_ptr; } 46 | 47 | tex_id create_texture(uint32_t width, uint32_t height) override; 48 | bool set_texture_rgba(tex_id id, const uint8_t* rgba, uint32_t width, 49 | uint32_t height) override; 50 | bool set_texture_rabg(tex_id id, const uint8_t* rabg, uint32_t width, 51 | uint32_t height) override; 52 | bool texture_size(tex_id id, uint32_t& width, uint32_t& height) override; 53 | bool delete_texture(tex_id id) override; 54 | 55 | void update_screen_size(const position& screen_size) override; 56 | 57 | protected: 58 | bool create_font_texture(); 59 | bool setup_draw_state(); 60 | void destroy_draw_state(); 61 | void setup_shader(); 62 | void invalidate_shader(); 63 | 64 | protected: 65 | IDirect3DDevice9* _device_ptr = nullptr; 66 | static tex_dict_dx9 _tex_dict; 67 | static d3d_shared_reset_data _r; 68 | IDirect3DVertexBuffer9* _vtx_buffer = nullptr; 69 | IDirect3DIndexBuffer9* _idx_buffer = nullptr; 70 | 71 | D3DXMATRIX _identity; 72 | D3DXMATRIX _projection; 73 | D3DXMATRIX _wvp; 74 | 75 | struct 76 | { 77 | D3DMATRIX world{}, view{}, projection{}; 78 | IDirect3DVertexDeclaration9* vert_declaration = nullptr; 79 | IDirect3DVertexShader9* vert_shader{}; 80 | IDirect3DPixelShader9* pixel_shader{}; 81 | IDirect3DBaseTexture9* texture{}; 82 | unsigned long fvf{}; 83 | unsigned long aa_state; 84 | D3DVIEWPORT9 vp{}; 85 | } _bak; 86 | 87 | size_t _vtx_buf_size = 0, _idx_buf_size = 0; 88 | 89 | unsigned long _color_write_enable = 0ul; 90 | unsigned long _sampler_u{}, _sampler_v{}, _sampler_w{}, _sampler_srgb{}; 91 | }; 92 | } // namespace util::draw 93 | -------------------------------------------------------------------------------- /impl/shaders.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace shaders 4 | { 5 | namespace vertex 6 | { 7 | #include "shaders/cpp/vertex/generic.h" 8 | } 9 | 10 | namespace pixel 11 | { 12 | #include "shaders/cpp/pixel/blur_x.h" 13 | #include "shaders/cpp/pixel/blur_y.h" 14 | #include "shaders/cpp/pixel/key.h" 15 | #include "shaders/cpp/pixel/scissor.h" 16 | #include "shaders/cpp/pixel/scissor_blur_x.h" 17 | #include "shaders/cpp/pixel/scissor_blur_y.h" 18 | #include "shaders/cpp/pixel/scissor_key.h" 19 | } // namespace pixel 20 | 21 | } // namespace shaders 22 | -------------------------------------------------------------------------------- /impl/shaders/d3d11/include/blur.hlsli: -------------------------------------------------------------------------------- 1 | #include "types.hlsli" 2 | 3 | cbuffer sizeBuf : register(b0) 4 | { 5 | float4 dimension; 6 | } 7 | 8 | cbuffer blurBuf : register(b2) 9 | { 10 | float4 iteration_count; 11 | float4 blur_weights_packed[24]; 12 | float4 blur_offsets_packed[24]; 13 | } 14 | 15 | static float blur_weights[96] = (float[96]) blur_weights_packed; 16 | static float blur_offsets[96] = (float[96]) blur_offsets_packed; 17 | 18 | // we could make this even faster by using the weighted offsets from https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ 19 | float3 blur(float2 pos, bool x_dir) 20 | { 21 | float2 step = float2(1 / dimension.x, 1 / dimension.y); 22 | 23 | float3 col = rtCopyTex.Sample(rtCopySampler, pos).rgb * blur_weights[0]; 24 | float2 test = float2(ddx(pos).x, ddy(pos).y); 25 | 26 | int it = int(iteration_count.x); 27 | 28 | if (x_dir) 29 | { 30 | [loop] 31 | for (int i = 1; i < it; ++i) 32 | { 33 | col += rtCopyTex.SampleGrad(rtCopySampler, pos + float2(step.x * int(blur_offsets[i]), 0), test.x, test.y).rgb * blur_weights[i]; 34 | col += rtCopyTex.SampleGrad(rtCopySampler, pos - float2(step.x * int(blur_offsets[i]), 0), test.x, test.y).rgb * blur_weights[i]; 35 | } 36 | } 37 | else 38 | { 39 | [loop] 40 | for (int i = 1; i < it; ++i) 41 | { 42 | col += rtCopyTex.SampleGrad(rtCopySampler, pos + float2(0, step.y * int(blur_offsets[i])), test.x, test.y).rgb * blur_weights[i]; 43 | col += rtCopyTex.SampleGrad(rtCopySampler, pos - float2(0, step.y * int(blur_offsets[i])), test.x, test.y).rgb * blur_weights[i]; 44 | } 45 | } 46 | 47 | return col; 48 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/include/scissor_blur.hlsli: -------------------------------------------------------------------------------- 1 | #include "blur.hlsli" 2 | 3 | float4 scissor_blur(VS_OUTPUT IN, bool x_dir) 4 | { 5 | float4 col; 6 | 7 | float2 center = float2(scissor.x, scissor.y); 8 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 9 | float2 distVec = center - pos; 10 | float distSqr = dot(distVec, distVec); 11 | 12 | if (distSqr > scissor.z) 13 | { 14 | col = float4(0, 0, 0, 0); 15 | return col; 16 | } 17 | 18 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 19 | float alpha = saturate(distToEdge); 20 | 21 | float2 tex_coord = 22 | float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 23 | //if (overlay) 24 | //{ 25 | tex_coord.x += (1 / dimension.x); 26 | tex_coord.y += (1 / dimension.y); 27 | //} 28 | 29 | float3 result = blur(tex_coord, x_dir); 30 | col = float4(result, alpha); 31 | 32 | return col; 33 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/include/types.hlsli: -------------------------------------------------------------------------------- 1 | struct VS_INPUT { 2 | float3 pos : POSITION; 3 | float4 col : COLOR0; 4 | float2 uv : TEXCOORD0; 5 | }; 6 | 7 | struct VS_OUTPUT 8 | { 9 | float4 position : SV_POSITION; 10 | float4 hposition : TEXCOORD0; 11 | float4 color0 : COLOR0; 12 | float2 texcoord0 : TEXCOORD1; 13 | float2 screenPos : TEXCOORD2; 14 | }; 15 | 16 | cbuffer scissorBuf : register(b1) 17 | { 18 | float4 scissor; 19 | float4 key_color; 20 | } 21 | 22 | sampler curtex : register(s0); 23 | Texture2D texture0 : register(t0); 24 | 25 | sampler rtCopySampler : register(s1); 26 | Texture2D rtCopyTex : register(t1); -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/blur_x.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/blur.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_Target 4 | { 5 | float4 col; 6 | 7 | float2 tex_coord = float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 8 | col = float4(blur(tex_coord, true), 1); 9 | 10 | return col; 11 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/blur_y.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/blur.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET 4 | { 5 | float4 col; 6 | 7 | float2 tex_coord = float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 8 | col = float4(blur(tex_coord, false), 1); 9 | 10 | return col; 11 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/generic.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET 4 | { 5 | float4 col = IN.color0 * texture0.Sample(curtex, IN.texcoord0); 6 | return col; 7 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/key.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET 4 | { 5 | float4 col; 6 | 7 | col = IN.color0 * texture0.Sample(curtex, IN.texcoord0); 8 | 9 | if (col.r == key_color.r && col.g == key_color.g && col.b == key_color.b) 10 | { 11 | col = float4(0, 0, 0, 0); 12 | } 13 | 14 | return col; 15 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/scissor.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET 4 | { 5 | float4 color; 6 | 7 | float2 center = float2(scissor.x, scissor.y); 8 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 9 | float2 distVec = center - pos; 10 | float distSqr = dot(distVec, distVec); 11 | 12 | if (distSqr > scissor.z) 13 | { 14 | color = float4(0, 0, 0, 0); 15 | return color; 16 | } 17 | 18 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 19 | float alpha = saturate(distToEdge); 20 | 21 | color = IN.color0 * texture0.Sample(curtex, IN.texcoord0); 22 | color.a *= alpha; 23 | 24 | return color; 25 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/scissor_blur_x.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/scissor_blur.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET { 4 | return scissor_blur(IN, true); 5 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/scissor_blur_y.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/scissor_blur.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET { 4 | return scissor_blur(IN, false); 5 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/pixel/scissor_key.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | float4 main(VS_OUTPUT IN) : SV_TARGET 4 | { 5 | float4 col; 6 | 7 | float2 center = float2(scissor.x, scissor.y); 8 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 9 | float2 distVec = center - pos; 10 | float distSqr = dot(distVec, distVec); 11 | 12 | if (distSqr > scissor.z) 13 | { 14 | col = float4(0, 0, 0, 0); 15 | return col; 16 | } 17 | 18 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 19 | float alpha = saturate(distToEdge); 20 | 21 | col = IN.color0 * texture0.Sample(curtex, IN.texcoord0); 22 | 23 | if (col.r == key_color.r && col.g == key_color.g && col.b == key_color.b) 24 | { 25 | col = float4(0, 0, 0, 0); 26 | } else 27 | { 28 | col.a *= alpha; 29 | } 30 | 31 | return col; 32 | } -------------------------------------------------------------------------------- /impl/shaders/d3d11/vertex/generic.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | #pragma pack_matrix( row_major ) 4 | cbuffer vtxBuf : register(b0) 5 | { 6 | float4x4 worldViewProj; 7 | }; 8 | 9 | VS_OUTPUT main(VS_INPUT IN) 10 | { 11 | VS_OUTPUT OUT; 12 | 13 | float4 v = float4(IN.pos.x, IN.pos.y, IN.pos.z, 1.0f); 14 | //float4 tmp = v; 15 | float4 tmp = mul(v, worldViewProj); 16 | OUT.position = tmp; 17 | OUT.hposition = tmp; 18 | OUT.color0 = IN.col; 19 | OUT.texcoord0 = IN.uv; 20 | OUT.screenPos = float2(IN.pos.x, IN.pos.y); 21 | 22 | return OUT; 23 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/include/blur.hlsli: -------------------------------------------------------------------------------- 1 | float iteration_count : register(c13); 2 | float4 blur_weights_packed[24] : register(c14); 3 | float4 blur_offsets_packed[24] : register(c38); 4 | 5 | static float blur_weights[96] = (float[96]) blur_weights_packed; 6 | static float blur_offsets[96] = (float[96]) blur_offsets_packed; 7 | 8 | // we could make this even faster by using the weighted offsets from https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ 9 | float3 blur(float2 pos, bool x_dir) { 10 | float2 step = float2(1 / dimension.x, 1 / dimension.y); 11 | 12 | float3 col = tex2D(backbuffer, pos).rgb * blur_weights[0]; 13 | float2 test = float2(ddx(pos).x, ddy(pos).y); 14 | 15 | int it = int(iteration_count); 16 | 17 | if (x_dir) { 18 | [loop] for (int i = 1; i < it; ++i) { 19 | col += tex2Dgrad(backbuffer, pos + float2(step.x * int(blur_offsets[i]), 0), test.x, test.y).rgb * blur_weights[i]; 20 | col += tex2Dgrad(backbuffer, pos - float2(step.x * int(blur_offsets[i]), 0), test.x, test.y).rgb * blur_weights[i]; 21 | } 22 | } 23 | else { 24 | [loop] for (int i = 1; i < it; ++i) { 25 | col += tex2Dgrad(backbuffer, pos + float2(0, step.y * int(blur_offsets[i])), test.x, test.y).rgb * blur_weights[i]; 26 | col += tex2Dgrad(backbuffer, pos - float2(0, step.y * int(blur_offsets[i])), test.x, test.y).rgb * blur_weights[i]; 27 | } 28 | } 29 | 30 | return col; 31 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/include/scissor_blur.hlsli: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | #include "../include/blur.hlsli" 3 | 4 | PS_OUTPUT scissor_blur(VS_OUTPUT IN, bool x_dir) 5 | { 6 | PS_OUTPUT OUT; 7 | 8 | float2 center = float2(scissor.x, scissor.y); 9 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 10 | float2 distVec = center - pos; 11 | float distSqr = dot(distVec, distVec); 12 | 13 | if (distSqr > scissor.z) 14 | { 15 | OUT.color = float4(0, 0, 0, 0); 16 | return OUT; 17 | } 18 | 19 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 20 | float alpha = saturate(distToEdge); 21 | 22 | float2 tex_coord = 23 | float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 24 | //if (overlay) 25 | //{ 26 | tex_coord.x += (1 / dimension.x); 27 | tex_coord.y += (1 / dimension.y); 28 | //} 29 | 30 | float3 result = blur(tex_coord, x_dir); 31 | OUT.color = float4(result, alpha); 32 | 33 | return OUT; 34 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/include/types.hlsli: -------------------------------------------------------------------------------- 1 | struct VS_INPUT 2 | { 3 | float3 position : POSITION; 4 | float4 color0 : COLOR0; 5 | float2 texcoord0 : TEXCOORD0; 6 | }; 7 | 8 | struct VS_OUTPUT 9 | { 10 | float4 position : POSITION; 11 | float4 hposition : TEXCOORD0; 12 | float4 color0 : COLOR0; 13 | float2 texcoord0 : TEXCOORD1; 14 | float2 screenPos : TEXCOORD2; 15 | }; 16 | 17 | struct PS_OUTPUT 18 | { 19 | float4 color : COLOR; 20 | }; 21 | 22 | sampler curtex : register(s0); 23 | sampler backbuffer : register(s1); // backbuffer copy 24 | 25 | float4 dimension : register(c4); 26 | // x,y = center; z = radius*radius; screenSpace 27 | float4 scissor : register(c5); 28 | float4 key : register(c8); 29 | 30 | row_major float4x4 worldViewProj : register(c9); // 9-12 31 | 32 | /*bool overlay : register(b0);*/ 33 | bool samplerAvailable : register(b1); -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/blur_x.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | #include "../include/blur.hlsli" 3 | 4 | PS_OUTPUT main(VS_OUTPUT IN) 5 | { 6 | PS_OUTPUT OUT; 7 | 8 | float2 tex_coord = float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 9 | OUT.color = float4(blur(tex_coord, true), 1); 10 | 11 | return OUT; 12 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/blur_y.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | #include "../include/blur.hlsli" 3 | 4 | PS_OUTPUT main(VS_OUTPUT IN) 5 | { 6 | PS_OUTPUT OUT; 7 | 8 | float2 tex_coord = float2((IN.hposition.x + 1) / 2, (IN.hposition.y - 1) / -2); 9 | OUT.color = float4(blur(tex_coord, false), 1); 10 | 11 | return OUT; 12 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/key.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | PS_OUTPUT main(VS_OUTPUT IN) 4 | { 5 | PS_OUTPUT OUT; 6 | 7 | if (samplerAvailable) 8 | { 9 | OUT.color = tex2D(curtex, IN.texcoord0); 10 | OUT.color.a = 1; // really ghetto fix 11 | OUT.color *= IN.color0; 12 | } 13 | else 14 | { 15 | OUT.color = IN.color0; 16 | } 17 | 18 | if (OUT.color.r == key.r && OUT.color.g == key.g && OUT.color.b == key.b) 19 | { 20 | OUT.color = float4(0, 0, 0, 0); 21 | } 22 | 23 | return OUT; 24 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/scissor.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | PS_OUTPUT main(VS_OUTPUT IN) 4 | { 5 | PS_OUTPUT OUT; 6 | 7 | float2 center = float2(scissor.x, scissor.y); 8 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 9 | float2 distVec = center - pos; 10 | float distSqr = dot(distVec, distVec); 11 | 12 | if (distSqr > scissor.z) 13 | { 14 | OUT.color = float4(0, 0, 0, 0); 15 | return OUT; 16 | } 17 | 18 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 19 | float alpha = saturate(distToEdge); 20 | if (samplerAvailable) 21 | { 22 | OUT.color = tex2D(curtex, IN.texcoord0); 23 | } else 24 | { 25 | OUT.color = IN.color0; 26 | } 27 | OUT.color.a *= alpha; 28 | return OUT; 29 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/scissor_blur_x.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/scissor_blur.hlsli" 2 | 3 | PS_OUTPUT main(VS_OUTPUT IN) { 4 | return scissor_blur(IN, true); 5 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/scissor_blur_y.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/scissor_blur.hlsli" 2 | 3 | PS_OUTPUT main(VS_OUTPUT IN) { 4 | return scissor_blur(IN, false); 5 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/pixel/scissor_key.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | PS_OUTPUT main(VS_OUTPUT IN) 4 | { 5 | PS_OUTPUT OUT; 6 | 7 | float2 center = float2(scissor.x, scissor.y); 8 | float2 pos = float2(IN.screenPos.x, IN.screenPos.y); 9 | float2 distVec = center - pos; 10 | float distSqr = dot(distVec, distVec); 11 | 12 | if (distSqr > scissor.z) 13 | { 14 | OUT.color = float4(0, 0, 0, 0); 15 | return OUT; 16 | } 17 | 18 | float distToEdge = sqrt(scissor.z) - sqrt(distSqr); 19 | float alpha = saturate(distToEdge); 20 | 21 | if (samplerAvailable) 22 | { 23 | OUT.color = tex2D(curtex, IN.texcoord0); 24 | OUT.color.a = 1; // really ghetto fix 25 | OUT.color *= IN.color0; 26 | } else 27 | { 28 | OUT.color = IN.color0; 29 | } 30 | 31 | if (OUT.color.r == key.r && OUT.color.g == key.g && OUT.color.b == key.b) 32 | { 33 | OUT.color = float4(0, 0, 0, 0); 34 | } else 35 | { 36 | OUT.color.a *= alpha; 37 | } 38 | 39 | return OUT; 40 | } -------------------------------------------------------------------------------- /impl/shaders/d3d9/vertex/generic.hlsl: -------------------------------------------------------------------------------- 1 | #include "../include/types.hlsli" 2 | 3 | VS_OUTPUT main(VS_INPUT IN) 4 | { 5 | VS_OUTPUT OUT; 6 | 7 | float4 v = float4(IN.position.x, IN.position.y, IN.position.z, 1.0f); 8 | float4 tmp = mul(v, worldViewProj); 9 | OUT.position = tmp; 10 | OUT.hposition = tmp; 11 | OUT.color0 = IN.color0; 12 | OUT.texcoord0 = IN.texcoord0; 13 | OUT.screenPos = float2(IN.position.x, IN.position.y); 14 | 15 | return OUT; 16 | } -------------------------------------------------------------------------------- /impl/shaders_dx11.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace shaders 4 | { 5 | namespace vertex 6 | { 7 | #include "shaders/cpp/d3d11/vertex/generic.h" 8 | } 9 | 10 | namespace pixel 11 | { 12 | #include "shaders/cpp/d3d11/pixel/generic.h" 13 | #include "shaders/cpp/d3d11/pixel/scissor.h" 14 | #include "shaders/cpp/d3d11/pixel/blur_x.h" 15 | #include "shaders/cpp/d3d11/pixel/blur_y.h" 16 | #include "shaders/cpp/d3d11/pixel/scissor_blur_x.h" 17 | #include "shaders/cpp/d3d11/pixel/scissor_blur_y.h" 18 | #include "shaders/cpp/d3d11/pixel/key.h" 19 | #include "shaders/cpp/d3d11/pixel/scissor_key.h" 20 | } // namespace pixel 21 | 22 | } // namespace shaders 23 | -------------------------------------------------------------------------------- /impl/tex_dict_dx11.cpp: -------------------------------------------------------------------------------- 1 | #include "tex_dict_dx11.hpp" 2 | #include 3 | 4 | #include 5 | 6 | 7 | using namespace util::draw; 8 | using Microsoft::WRL::ComPtr; 9 | 10 | void tex_wrapper_dx11::create(ID3D11Device* device) 11 | { 12 | if (_texture) 13 | _texture->Release(); 14 | if (_res_view) 15 | _res_view->Release(); 16 | _texture = nullptr; 17 | _res_view = nullptr; 18 | 19 | auto desc = D3D11_TEXTURE2D_DESC{}; 20 | desc.Width = _size_x; 21 | desc.Height = _size_y; 22 | desc.MipLevels = 1; 23 | desc.ArraySize = 1; 24 | desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 25 | desc.SampleDesc.Count = 1; 26 | desc.Usage = D3D11_USAGE_DEFAULT; 27 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; 28 | desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; 29 | 30 | auto res = device->CreateTexture2D(&desc, nullptr, &_texture); 31 | 32 | assert(res == S_OK); 33 | if (res != S_OK) { 34 | return; 35 | } 36 | 37 | auto srv_desc = D3D11_SHADER_RESOURCE_VIEW_DESC{}; 38 | srv_desc.Format = desc.Format; 39 | srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 40 | srv_desc.Texture2D.MipLevels = 1; 41 | res = device->CreateShaderResourceView(_texture, &srv_desc, &_res_view); 42 | assert(res == S_OK); 43 | } 44 | 45 | bool tex_wrapper_dx11::set_tex_data(tex_dict_dx11* dict, ID3D11Device* device, const uint8_t* data, const uint32_t size_x, 46 | const uint32_t size_y) 47 | { 48 | _texture_data.resize(size_x * size_y * 4); 49 | std::copy(data, data + _texture_data.size(), _texture_data.begin()); 50 | 51 | auto changed = _size_x != size_x || _size_y != size_y; 52 | 53 | _size_x = size_x; 54 | _size_y = size_y; 55 | if (!_texture || changed) 56 | { 57 | create(device); 58 | } 59 | 60 | std::scoped_lock g{dict->_update_queue_lock}; 61 | if (std::find(dict->_update_queue.begin(), dict->_update_queue.end(), this) == dict->_update_queue.end()) { 62 | dict->_update_queue.push_back(this); 63 | } 64 | 65 | return true; 66 | } 67 | 68 | bool tex_wrapper_dx11::apply_tex_changes(ID3D11DeviceContext* ctx) { 69 | return copy_texture_data(ctx); 70 | } 71 | 72 | bool tex_wrapper_dx11::copy_texture_data(ID3D11DeviceContext* ctx) 73 | { 74 | auto box = D3D11_BOX{}; 75 | box.left = 0; 76 | box.top = 0; 77 | box.front = 0; 78 | box.back = 1; 79 | box.right = _size_x; 80 | box.bottom = _size_y; 81 | 82 | ctx->UpdateSubresource(_texture, 0, &box, _texture_data.data(), _size_x * 4, _size_x * _size_y * 4); 83 | 84 | return true; 85 | } 86 | 87 | void tex_dict_dx11::clear_textures() 88 | { 89 | std::scoped_lock g(_mutex); 90 | for (auto& tex : _textures) 91 | if (!tex.free) 92 | tex.clear_data(); 93 | _textures.clear(); 94 | } 95 | 96 | tex_wrapper_dx11* tex_dict_dx11::create_texture(uint32_t size_x, uint32_t size_y) 97 | { 98 | std::scoped_lock g(_mutex); 99 | 100 | _textures.emplace_front(); 101 | const auto ptr = &_textures.front(); 102 | _valid_elements.emplace_back(ptr); 103 | return ptr; 104 | } 105 | 106 | void tex_dict_dx11::destroy_texture(tex_wrapper_dx11* tex) 107 | { 108 | std::scoped_lock g(_mutex); 109 | if (!is_valid_tex(tex)) 110 | return; 111 | 112 | tex->clear_data(); 113 | _valid_elements.erase(std::find(_valid_elements.begin(), _valid_elements.end(), tex)); 114 | _textures.remove_if([tex](const auto& elem) { return &elem == tex; }); 115 | } 116 | 117 | void tex_dict_dx11::process_update_queue(ID3D11DeviceContext* ctx) { 118 | std::scoped_lock g{_update_queue_lock}; 119 | for (auto entry : _update_queue) { 120 | entry->apply_tex_changes(ctx); 121 | } 122 | _update_queue.clear(); 123 | } -------------------------------------------------------------------------------- /impl/tex_dict_dx11.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NOMINMAX 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace util::draw { 13 | struct tex_dict_dx11; 14 | 15 | struct tex_wrapper_dx11 16 | { 17 | tex_wrapper_dx11() noexcept = default; 18 | ~tex_wrapper_dx11() noexcept 19 | { 20 | if (_texture) 21 | _texture->Release(); 22 | if (_res_view) 23 | _res_view->Release(); 24 | _texture = nullptr; 25 | _res_view = nullptr; 26 | } 27 | 28 | tex_wrapper_dx11(const tex_wrapper_dx11&) = delete; 29 | tex_wrapper_dx11& operator=(const tex_wrapper_dx11&) = delete; 30 | 31 | tex_wrapper_dx11(tex_wrapper_dx11&&) noexcept = default; 32 | tex_wrapper_dx11& operator=(tex_wrapper_dx11&&) noexcept = default; 33 | 34 | // 4 channels 35 | bool set_tex_data(tex_dict_dx11* dict, ID3D11Device* device, const uint8_t* data, uint32_t size_x, uint32_t size_y); 36 | bool apply_tex_changes(ID3D11DeviceContext*); 37 | 38 | bool texture_size(uint32_t& width, uint32_t& height) const 39 | { 40 | width = _size_x; 41 | height = _size_y; 42 | return true; 43 | } 44 | 45 | void clear_data() 46 | { 47 | _texture_data.clear(); 48 | _size_x = 0; 49 | _size_y = 0; 50 | if (_texture) 51 | _texture->Release(); 52 | if (_res_view) 53 | _res_view->Release(); 54 | _texture = nullptr; 55 | _res_view = nullptr; 56 | } 57 | 58 | void invalidate() 59 | { 60 | if (_texture) 61 | _texture->Release(); 62 | if (_res_view) 63 | _res_view->Release(); 64 | _texture = nullptr; 65 | _res_view = nullptr; 66 | } 67 | 68 | void create(ID3D11Device* device); // <- for reset 69 | ID3D11ShaderResourceView* texture() const { return _res_view; } 70 | 71 | bool free = false; 72 | 73 | protected: 74 | bool copy_texture_data(ID3D11DeviceContext* ctx); 75 | 76 | ID3D11Texture2D* _texture = nullptr; 77 | ID3D11ShaderResourceView* _res_view = nullptr; 78 | std::vector _texture_data = {}; 79 | 80 | uint32_t _size_x = 0u, _size_y = 0u; 81 | }; 82 | 83 | struct tex_dict_dx11 { 84 | friend struct tex_wrapper_dx11; 85 | 86 | tex_dict_dx11() = default; 87 | ~tex_dict_dx11() { clear_textures(); } 88 | 89 | ID3D11ShaderResourceView* texture(const tex_wrapper_dx11* tex) 90 | { 91 | std::scoped_lock g(_mutex); 92 | if (!is_valid_tex(tex)) 93 | return nullptr; 94 | 95 | return tex->texture(); 96 | } 97 | 98 | bool set_tex_data(ID3D11Device* device, tex_wrapper_dx11* tex, 99 | const uint8_t* data, const uint32_t size_x, 100 | const uint32_t size_y) 101 | { 102 | std::scoped_lock g(_mutex); 103 | if (!is_valid_tex(tex)) 104 | return false; 105 | 106 | return tex->set_tex_data(this, device, data, size_x, size_y); 107 | } 108 | 109 | bool texture_size(const tex_wrapper_dx11* tex, uint32_t& width, uint32_t& height) 110 | { 111 | std::scoped_lock g(_mutex); 112 | if (!is_valid_tex(tex)) 113 | return false; 114 | 115 | return tex->texture_size(width, height); 116 | } 117 | 118 | bool is_valid_tex(const tex_wrapper_dx11* tex) 119 | { 120 | return std::find(_valid_elements.begin(), _valid_elements.end(), tex) != _valid_elements.end(); 121 | } 122 | 123 | tex_wrapper_dx11* create_texture(uint32_t size_x, uint32_t size_y); 124 | void destroy_texture(tex_wrapper_dx11* tex); 125 | 126 | // call from directx thread 127 | void clear_textures(); 128 | 129 | void pre_reset() 130 | { 131 | std::scoped_lock g(_mutex); 132 | for (auto& tex : _textures) 133 | tex.invalidate(); 134 | } 135 | 136 | void post_reset(ID3D11Device* device) 137 | { 138 | std::scoped_lock g(_mutex); 139 | for (auto& tex : _textures) 140 | if (!tex.free) 141 | tex.create(device); 142 | } 143 | 144 | void process_update_queue(ID3D11DeviceContext*); 145 | 146 | protected: 147 | std::mutex _mutex; 148 | std::forward_list _textures = {}; 149 | std::vector _valid_elements = {}; 150 | std::mutex _update_queue_lock; 151 | std::vector _update_queue{}; 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /impl/tex_dict_dx9.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #define NOMINMAX 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "tex_dict_dx9.hpp" 9 | 10 | using namespace util::draw; 11 | 12 | void d3d9_tex_wrapper::create(IDirect3DDevice9* device) 13 | { 14 | if (_texture) 15 | _texture->Release(); 16 | _texture = nullptr; 17 | 18 | const auto res = 19 | device->CreateTexture(_size_x, _size_y, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, 20 | D3DPOOL_DEFAULT, &_texture, nullptr); 21 | assert(res == D3D_OK); 22 | 23 | copy_texture_data(device); 24 | } 25 | 26 | bool d3d9_tex_wrapper::set_tex_data(IDirect3DDevice9* device, const uint8_t* data, const uint32_t size_x, 27 | const uint32_t size_y) 28 | { 29 | _texture_data.resize(size_x * size_y * 4); 30 | std::copy(data, data + _texture_data.size(), _texture_data.begin()); 31 | 32 | _size_x = size_x; 33 | _size_y = size_y; 34 | if (!_texture) 35 | { 36 | const auto res = 37 | device->CreateTexture(_size_x, _size_y, 1, 0, D3DFMT_A8R8G8B8, 38 | D3DPOOL_DEFAULT, &_texture, nullptr); 39 | assert(res == D3D_OK); 40 | } 41 | 42 | return copy_texture_data(device); 43 | } 44 | 45 | bool d3d9_tex_wrapper::copy_texture_data(IDirect3DDevice9* device) 46 | { 47 | IDirect3DTexture9* tmp_tex = nullptr; 48 | auto res = device->CreateTexture(_size_x, _size_y, 1, D3DUSAGE_DYNAMIC, 49 | D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &tmp_tex, 50 | nullptr); 51 | if (res != D3D_OK) 52 | { 53 | assert(0); 54 | return false; 55 | } 56 | 57 | D3DLOCKED_RECT rect; 58 | res = tmp_tex->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD); 59 | if (res != D3D_OK) 60 | { 61 | tmp_tex->Release(); 62 | assert(0); 63 | return false; 64 | } 65 | 66 | auto src = _texture_data.data(); 67 | auto dst = reinterpret_cast(rect.pBits); 68 | for (auto y = 0u; y < _size_y; ++y) 69 | { 70 | std::copy(src, src + (_size_x * 4), dst); 71 | 72 | src += _size_x * 4; 73 | dst += rect.Pitch; 74 | } 75 | 76 | res = tmp_tex->UnlockRect(0); 77 | if (res != D3D_OK) 78 | { 79 | tmp_tex->Release(); 80 | assert(0); 81 | return false; 82 | } 83 | 84 | res = device->UpdateTexture(tmp_tex, _texture); 85 | 86 | tmp_tex->Release(); 87 | assert(res == D3D_OK); 88 | return (res == D3D_OK); 89 | } 90 | 91 | void tex_dict_dx9::clear_textures() 92 | { 93 | std::scoped_lock g(_mutex); 94 | for (auto& tex : _textures) 95 | if (!tex.free) 96 | tex.clear_data(); 97 | _textures.clear(); 98 | } 99 | 100 | d3d9_tex_wrapper* tex_dict_dx9::create_texture(uint32_t size_x, uint32_t size_y) 101 | { 102 | std::scoped_lock g(_mutex); 103 | 104 | _textures.emplace_front(); 105 | const auto ptr = &_textures.front(); 106 | _valid_elements.emplace_back(ptr); 107 | return ptr; 108 | } 109 | 110 | void tex_dict_dx9::destroy_texture(d3d9_tex_wrapper* tex) 111 | { 112 | std::scoped_lock g(_mutex); 113 | if (!is_valid_tex(tex)) 114 | return; 115 | 116 | tex->clear_data(); 117 | _valid_elements.erase(std::find(_valid_elements.begin(), _valid_elements.end(), tex)); 118 | _textures.remove_if([tex](const auto& elem) { return &elem == tex; }); 119 | } -------------------------------------------------------------------------------- /impl/tex_dict_dx9.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace util::draw { 9 | struct d3d9_tex_wrapper 10 | { 11 | d3d9_tex_wrapper() noexcept = default; 12 | ~d3d9_tex_wrapper() noexcept 13 | { 14 | if (_texture) 15 | _texture->Release(); 16 | } 17 | 18 | d3d9_tex_wrapper(const d3d9_tex_wrapper&) = delete; 19 | d3d9_tex_wrapper& operator=(const d3d9_tex_wrapper&) = delete; 20 | 21 | d3d9_tex_wrapper(d3d9_tex_wrapper&&) noexcept = default; 22 | d3d9_tex_wrapper& operator=(d3d9_tex_wrapper&&) noexcept = default; 23 | 24 | // 4 channels 25 | bool set_tex_data(IDirect3DDevice9* device, const uint8_t* data, uint32_t size_x, uint32_t size_y); 26 | 27 | bool texture_size(uint32_t& width, uint32_t& height) const 28 | { 29 | width = _size_x; 30 | height = _size_y; 31 | return true; 32 | } 33 | 34 | void clear_data() 35 | { 36 | _texture_data.clear(); 37 | _size_x = 0; 38 | _size_y = 0; 39 | if (_texture) 40 | _texture->Release(); 41 | _texture = nullptr; 42 | } 43 | 44 | void invalidate() 45 | { 46 | if (_texture) 47 | _texture->Release(); 48 | _texture = nullptr; 49 | } 50 | 51 | void create(IDirect3DDevice9* device); // <- for reset 52 | IDirect3DTexture9* texture() const { return _texture; } 53 | 54 | bool free = false; 55 | 56 | protected: 57 | bool copy_texture_data(IDirect3DDevice9* device); 58 | 59 | IDirect3DTexture9* _texture = nullptr; 60 | std::vector _texture_data = {}; 61 | 62 | uint32_t _size_x = 0u, _size_y = 0u; 63 | }; 64 | 65 | struct tex_dict_dx9 { 66 | 67 | tex_dict_dx9() = default; 68 | ~tex_dict_dx9() { clear_textures(); } 69 | 70 | IDirect3DTexture9* texture(const d3d9_tex_wrapper* tex) 71 | { 72 | std::scoped_lock g(_mutex); 73 | if (!is_valid_tex(tex)) 74 | return nullptr; 75 | 76 | return tex->texture(); 77 | } 78 | 79 | bool set_tex_data(IDirect3DDevice9* device, d3d9_tex_wrapper* tex, 80 | const uint8_t* data, const uint32_t size_x, 81 | const uint32_t size_y) 82 | { 83 | std::scoped_lock g(_mutex); 84 | if (!is_valid_tex(tex)) 85 | return false; 86 | 87 | return tex->set_tex_data(device, data, size_x, size_y); 88 | } 89 | 90 | bool texture_size(const d3d9_tex_wrapper* tex, uint32_t& width, uint32_t& height) 91 | { 92 | std::scoped_lock g(_mutex); 93 | if (!is_valid_tex(tex)) 94 | return false; 95 | 96 | return tex->texture_size(width, height); 97 | } 98 | 99 | bool is_valid_tex(const d3d9_tex_wrapper* tex) 100 | { 101 | return std::find(_valid_elements.begin(), _valid_elements.end(), tex) != _valid_elements.end(); 102 | } 103 | 104 | d3d9_tex_wrapper* create_texture(uint32_t size_x, uint32_t size_y); 105 | void destroy_texture(d3d9_tex_wrapper* tex); 106 | 107 | // call from directx thread 108 | void clear_textures(); 109 | 110 | void pre_reset() 111 | { 112 | std::scoped_lock g(_mutex); 113 | for (auto& tex : _textures) 114 | tex.invalidate(); 115 | } 116 | 117 | void post_reset(IDirect3DDevice9* device) 118 | { 119 | std::scoped_lock g(_mutex); 120 | for (auto& tex : _textures) 121 | if (!tex.free) 122 | tex.create(device); 123 | } 124 | 125 | protected: 126 | std::mutex _mutex; 127 | std::forward_list _textures = {}; 128 | std::vector _valid_elements = {}; 129 | }; 130 | } 131 | -------------------------------------------------------------------------------- /main_dx11.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #define NOMINMAX 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "draw_manager.hpp" 13 | #include "impl/d3d11_manager.hpp" 14 | 15 | #include "math.h" 16 | 17 | LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam); 18 | 19 | bool create_window(); 20 | bool create_draw_context(); 21 | void destroy_draw_context(); 22 | 23 | void draw(); 24 | void user_thread_impl(); 25 | 26 | int window_size_x = 1280; 27 | int window_size_y = 720; 28 | 29 | WNDCLASS window_class = {}; 30 | HWND window_handle = nullptr; 31 | 32 | using Microsoft::WRL::ComPtr; 33 | ComPtr d3d11_device = nullptr; 34 | ComPtr d3d11_device_ctx = nullptr; 35 | ComPtr d3d11_rtview = nullptr; 36 | ComPtr d3d11_swapchain = nullptr; 37 | 38 | std::atomic fps = 0; 39 | auto fps_counter = 0; 40 | auto fps_counter_start_time = 0ull; 41 | bool reset_rt = false; 42 | 43 | std::unique_ptr draw_manager = nullptr; 44 | auto stop_user_thread = false; 45 | std::thread user_thread; 46 | 47 | int main() 48 | { 49 | if (!create_window()) 50 | return 1; 51 | 52 | ShowWindow(window_handle, SW_SHOW); 53 | 54 | if (!create_draw_context()) 55 | return 1; 56 | 57 | RECT rect; 58 | GetClientRect(window_handle, &rect); 59 | 60 | draw_manager = std::make_unique(d3d11_device.Get(), d3d11_device_ctx.Get()); 61 | draw_manager->update_screen_size(util::draw::position{ 62 | static_cast(rect.right - rect.left), 63 | static_cast(rect.bottom - rect.top) 64 | }); 65 | user_thread = std::thread{ user_thread_impl }; 66 | 67 | MSG msg; 68 | std::memset(&msg, 0, sizeof(MSG)); 69 | while (msg.message != WM_QUIT) 70 | { 71 | if (PeekMessage(&msg, nullptr, 0u, 0u, PM_REMOVE)) 72 | { 73 | TranslateMessage(&msg); 74 | DispatchMessage(&msg); 75 | continue; 76 | } 77 | 78 | draw(); 79 | } 80 | 81 | stop_user_thread = true; 82 | user_thread.join(); 83 | 84 | destroy_draw_context(); 85 | DestroyWindow(window_handle); 86 | UnregisterClass(window_class.lpszClassName, window_class.hInstance); 87 | 88 | return 0; 89 | } 90 | 91 | void draw() 92 | { 93 | using namespace std::chrono; 94 | 95 | const auto ms = duration_cast(steady_clock::now().time_since_epoch()).count(); 96 | if (fps_counter_start_time == 0ull) 97 | { 98 | fps_counter_start_time = ms; 99 | } 100 | else if (ms - fps_counter_start_time >= 1000ull) 101 | { 102 | fps = fps_counter; 103 | fps_counter = 0; 104 | fps_counter_start_time = ms; 105 | char buffer[128]; 106 | std::snprintf(buffer, 128, "D3D11 Test Window - FPS: %d", fps.load()); 107 | SetWindowTextA(window_handle, buffer); 108 | } 109 | 110 | if (reset_rt) { 111 | d3d11_rtview = nullptr; 112 | auto res = d3d11_swapchain->ResizeBuffers(2, window_size_x, window_size_y, DXGI_FORMAT_UNKNOWN, 0); 113 | if (res != S_OK) { 114 | std::fprintf(stderr, "ResizeBuffers failed: %d\n", res); 115 | exit(1); 116 | } 117 | 118 | ComPtr back_buffer; 119 | d3d11_swapchain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)); 120 | d3d11_device->CreateRenderTargetView(back_buffer.Get(), nullptr, 121 | d3d11_rtview.GetAddressOf()); 122 | } 123 | 124 | const FLOAT clear_col[4] = { 0.f, 0.f, 0.f, 1.f }; 125 | d3d11_device_ctx->ClearRenderTargetView(d3d11_rtview.Get(), clear_col); 126 | d3d11_device_ctx->OMSetRenderTargets(1, d3d11_rtview.GetAddressOf(), nullptr); 127 | draw_manager->draw(); 128 | 129 | const auto res = d3d11_swapchain->Present(0, 0); 130 | 131 | if (res != S_OK) 132 | { 133 | std::fprintf(stderr, "Present failed: %d\n", res); 134 | exit(1); 135 | } 136 | 137 | fps_counter++; 138 | } 139 | 140 | bool create_window() 141 | { 142 | const auto class_name = "Draw Manager"; 143 | 144 | window_class.lpfnWndProc = &window_proc; 145 | window_class.hInstance = GetModuleHandleA(nullptr); 146 | window_class.lpszClassName = class_name; 147 | 148 | if (!RegisterClassA(&window_class)) 149 | return false; 150 | 151 | window_handle = CreateWindowExA(0, 152 | class_name, 153 | "D3D11 Test Window - FPS: 0", 154 | WS_OVERLAPPEDWINDOW, 155 | CW_USEDEFAULT, 156 | CW_USEDEFAULT, 157 | window_size_x, 158 | window_size_y, 159 | nullptr, 160 | nullptr, 161 | GetModuleHandleA(nullptr), 162 | nullptr); 163 | 164 | return (window_handle != nullptr); 165 | } 166 | 167 | LRESULT __stdcall window_proc(const HWND wnd, const UINT msg, const WPARAM wparam, const LPARAM lparam) 168 | { 169 | switch (msg) 170 | { 171 | case WM_SIZE: 172 | { 173 | if (d3d11_device && wparam != SIZE_MINIMIZED) 174 | { 175 | window_size_x = LOWORD(lparam); 176 | window_size_y = HIWORD(lparam); 177 | reset_rt = true; 178 | } 179 | if (draw_manager && wparam != SIZE_MINIMIZED) 180 | { 181 | draw_manager->update_screen_size(util::draw::position{ 182 | static_cast(LOWORD(lparam)), 183 | static_cast(HIWORD(lparam)) 184 | }); 185 | } 186 | return 0; 187 | } 188 | case WM_DESTROY: 189 | PostQuitMessage(0); 190 | return 0; 191 | default: 192 | return DefWindowProcA(wnd, msg, wparam, lparam); 193 | } 194 | } 195 | 196 | bool create_draw_context() 197 | { 198 | DXGI_SWAP_CHAIN_DESC desc; 199 | std::memset(&desc, 0, sizeof(desc)); 200 | desc.BufferCount = 2; 201 | desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 202 | desc.BufferDesc.RefreshRate.Numerator = 60; 203 | desc.BufferDesc.RefreshRate.Denominator = 1; 204 | desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; 205 | desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 206 | desc.OutputWindow = window_handle; 207 | desc.SampleDesc.Count = 1; 208 | desc.Windowed = TRUE; 209 | desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; 210 | 211 | D3D_FEATURE_LEVEL feature_level; 212 | const D3D_FEATURE_LEVEL feature_levels[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0 }; 213 | if (const auto res = D3D11CreateDeviceAndSwapChain( 214 | nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, 215 | feature_levels, 2, D3D11_SDK_VERSION, &desc, 216 | d3d11_swapchain.GetAddressOf(), d3d11_device.GetAddressOf(), 217 | &feature_level, d3d11_device_ctx.GetAddressOf()); 218 | res != S_OK) 219 | return false; 220 | 221 | ComPtr back_buffer; 222 | d3d11_swapchain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)); 223 | d3d11_device->CreateRenderTargetView(back_buffer.Get(), nullptr, 224 | d3d11_rtview.GetAddressOf()); 225 | return true; 226 | } 227 | 228 | void destroy_draw_context() 229 | { 230 | d3d11_rtview = nullptr; 231 | d3d11_swapchain = nullptr; 232 | d3d11_device_ctx = nullptr; 233 | d3d11_device = nullptr; 234 | } 235 | -------------------------------------------------------------------------------- /main_dx9.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #define NOMINMAX 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "draw_manager.hpp" 12 | #include "impl/d3d9_manager.hpp" 13 | #include "math.h" 14 | 15 | LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam); 16 | 17 | bool create_window(); 18 | bool create_draw_context(); 19 | void destroy_draw_context(); 20 | 21 | void reset_d3d9_device(); 22 | 23 | void draw(); 24 | extern void user_thread_impl(); 25 | 26 | int window_size_x = 1280; 27 | int window_size_y = 720; 28 | 29 | WNDCLASS window_class = {}; 30 | HWND window_handle = nullptr; 31 | 32 | LPDIRECT3D9 d3d9_instance = nullptr; 33 | LPDIRECT3DDEVICE9 d3d9_device = nullptr; 34 | D3DPRESENT_PARAMETERS d3d9_present_params = {}; 35 | auto do_reset = false; 36 | 37 | std::atomic fps = 0; 38 | auto fps_counter = 0; 39 | auto fps_counter_start_time = 0ull; 40 | 41 | std::unique_ptr draw_manager = nullptr; 42 | auto stop_user_thread = false; 43 | extern std::atomic tex; 44 | extern std::atomic reset_tex; 45 | std::thread user_thread; 46 | 47 | int main() 48 | { 49 | if (!create_window()) 50 | return 1; 51 | 52 | ShowWindow(window_handle, SW_SHOW); 53 | 54 | if (!create_draw_context()) 55 | return 1; 56 | 57 | draw_manager = std::make_unique(d3d9_device); 58 | draw_manager->update_screen_size(util::draw::position{ 59 | static_cast(window_size_x), 60 | static_cast(window_size_y) 61 | }); 62 | user_thread = std::thread{user_thread_impl}; 63 | 64 | MSG msg; 65 | std::memset(&msg, 0, sizeof(MSG)); 66 | while (msg.message != WM_QUIT) 67 | { 68 | if (PeekMessage(&msg, nullptr, 0u, 0u, PM_REMOVE)) 69 | { 70 | TranslateMessage(&msg); 71 | DispatchMessage(&msg); 72 | continue; 73 | } 74 | 75 | draw(); 76 | } 77 | 78 | stop_user_thread = true; 79 | user_thread.join(); 80 | 81 | destroy_draw_context(); 82 | DestroyWindow(window_handle); 83 | UnregisterClass(window_class.lpszClassName, window_class.hInstance); 84 | 85 | return 0; 86 | } 87 | 88 | void draw() 89 | { 90 | using namespace std::chrono; 91 | 92 | const auto ms = duration_cast(steady_clock::now().time_since_epoch()).count(); 93 | if (fps_counter_start_time == 0ull) 94 | { 95 | fps_counter_start_time = ms; 96 | } 97 | else if (ms - fps_counter_start_time >= 1000ull) 98 | { 99 | fps = fps_counter; 100 | fps_counter = 0; 101 | fps_counter_start_time = ms; 102 | char buffer[128]; 103 | std::snprintf(buffer, 128, "D3D9 Test Window - FPS: %d", fps.load()); 104 | SetWindowTextA(window_handle, buffer); 105 | } 106 | 107 | d3d9_device->SetRenderState(D3DRS_ZENABLE, false); 108 | d3d9_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); 109 | d3d9_device->SetRenderState(D3DRS_SCISSORTESTENABLE, false); 110 | const auto clear_col = D3DCOLOR_RGBA(0, 0, 0, 255); 111 | d3d9_device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col, 1.f, 0); 112 | if (d3d9_device->BeginScene() >= 0) 113 | { 114 | draw_manager->draw(); 115 | d3d9_device->EndScene(); 116 | } 117 | 118 | const auto res = d3d9_device->Present(nullptr, nullptr, nullptr, nullptr); 119 | 120 | if (do_reset || (res == D3DERR_DEVICELOST && d3d9_device->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)) 121 | { 122 | reset_d3d9_device(); 123 | do_reset = false; 124 | } 125 | 126 | fps_counter++; 127 | } 128 | 129 | bool create_window() 130 | { 131 | const auto class_name = "Draw Manager"; 132 | 133 | window_class.lpfnWndProc = &window_proc; 134 | window_class.hInstance = GetModuleHandleA(nullptr); 135 | window_class.lpszClassName = class_name; 136 | 137 | if (!RegisterClassA(&window_class)) 138 | return false; 139 | 140 | window_handle = CreateWindowExA(0, 141 | class_name, 142 | "D3D9 Test Window - FPS: 0", 143 | WS_OVERLAPPEDWINDOW, 144 | CW_USEDEFAULT, 145 | CW_USEDEFAULT, 146 | window_size_x, 147 | window_size_y, 148 | nullptr, 149 | nullptr, 150 | GetModuleHandleA(nullptr), 151 | nullptr); 152 | 153 | return (window_handle != nullptr); 154 | } 155 | 156 | LRESULT __stdcall window_proc(const HWND wnd, const UINT msg, const WPARAM wparam, const LPARAM lparam) 157 | { 158 | switch (msg) 159 | { 160 | case WM_SIZE: 161 | { 162 | if (d3d9_device && wparam != SIZE_MINIMIZED) 163 | { 164 | d3d9_present_params.BackBufferWidth = LOWORD(lparam); 165 | d3d9_present_params.BackBufferHeight = HIWORD(lparam); 166 | do_reset = true; 167 | //reset_d3d9_device(); 168 | } 169 | if (draw_manager && wparam != SIZE_MINIMIZED) 170 | { 171 | draw_manager->update_screen_size(util::draw::position{ 172 | static_cast(LOWORD(lparam)), 173 | static_cast(HIWORD(lparam)) 174 | }); 175 | } 176 | return 0; 177 | } 178 | case WM_DESTROY: 179 | PostQuitMessage(0); 180 | return 0; 181 | default: 182 | return DefWindowProcA(wnd, msg, wparam, lparam); 183 | } 184 | } 185 | 186 | bool create_draw_context() 187 | { 188 | d3d9_instance = Direct3DCreate9(D3D_SDK_VERSION); 189 | if (!d3d9_instance) 190 | return false; 191 | 192 | std::memset(&d3d9_present_params, 0, sizeof(d3d9_present_params)); 193 | d3d9_present_params.Windowed = true; 194 | d3d9_present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; 195 | d3d9_present_params.BackBufferFormat = D3DFMT_A8R8G8B8; 196 | d3d9_present_params.EnableAutoDepthStencil = true; 197 | d3d9_present_params.AutoDepthStencilFormat = D3DFMT_D16; 198 | d3d9_present_params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 199 | d3d9_present_params.BackBufferWidth = window_size_x; 200 | d3d9_present_params.BackBufferHeight = window_size_y; 201 | 202 | if (d3d9_instance->CreateDevice(D3DADAPTER_DEFAULT, 203 | D3DDEVTYPE_HAL, 204 | window_handle, 205 | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, 206 | &d3d9_present_params, 207 | &d3d9_device) != D3D_OK) 208 | return false; 209 | 210 | return true; 211 | } 212 | 213 | void destroy_draw_context() 214 | { 215 | if (d3d9_device) 216 | d3d9_device->Release(); 217 | if (d3d9_instance) 218 | d3d9_instance->Release(); 219 | } 220 | 221 | void reset_d3d9_device() 222 | { 223 | reinterpret_cast(draw_manager.get())->invalidate_device_objects(); 224 | const auto res = d3d9_device->Reset(&d3d9_present_params); 225 | assert(res == D3D_OK); 226 | reinterpret_cast(draw_manager.get())->create_device_objects(); 227 | } 228 | -------------------------------------------------------------------------------- /main_shared.cpp: -------------------------------------------------------------------------------- 1 | #include "draw_manager.hpp" 2 | #include 3 | 4 | const auto font_path = R"(C:\Windows\Fonts\verdana.ttf)"; 5 | 6 | extern int window_size_x; 7 | extern int window_size_y; 8 | extern std::atomic fps; 9 | 10 | extern std::unique_ptr draw_manager; 11 | extern bool stop_user_thread; 12 | 13 | std::atomic tex; 14 | std::atomic reset_tex = false; 15 | 16 | static util::draw::tex_id create_texture() 17 | { 18 | struct pixel 19 | { 20 | uint8_t r, g, b, a; 21 | }; 22 | std::vector data; 23 | data.resize(200 * 200); 24 | 25 | const auto fill_rect = [&data](const auto min_x, 26 | const auto min_y, 27 | const auto max_x, 28 | const auto max_y, 29 | const math::color_rgba col) 30 | { 31 | for (int x = min_x; x < max_x; ++x) 32 | { 33 | for (auto y = min_y; y < max_y; ++y) 34 | { 35 | data[y * 200 + x] = { col.r(), col.g(), col.b(), col.a() }; 36 | } 37 | } 38 | }; 39 | fill_rect(0, 0, 100, 100, math::color_rgba::red()); 40 | fill_rect(100, 0, 200, 100, math::color_rgba::blue()); 41 | fill_rect(0, 100, 100, 200, math::color_rgba::green()); 42 | fill_rect(100, 100, 200, 200, math::color_rgba{ 226, 238, 37 }); 43 | 44 | const auto tex = draw_manager->create_texture(200, 200); 45 | draw_manager->set_texture_rgba(tex, reinterpret_cast(data.data()), 200, 200); 46 | 47 | return tex; 48 | } 49 | 50 | static auto buffer_idx = -1; 51 | 52 | void user_thread_impl() 53 | { 54 | using namespace std::chrono_literals; 55 | 56 | util::draw::font* font = nullptr; 57 | auto x = 0; 58 | char buffer[128]; 59 | tex = create_texture(); 60 | while (!stop_user_thread) 61 | { 62 | if (buffer_idx == -1) 63 | { 64 | buffer_idx = draw_manager->register_buffer(); 65 | font = draw_manager->add_font(font_path, 20.f, false, true); 66 | } 67 | 68 | const auto buf = draw_manager->get_buffer(buffer_idx); 69 | 70 | std::snprintf(buffer, 128, "Current FPS: %d", fps.load()); 71 | //buf->rectangle_filled({ 0, 0 }, { 100, 100 }, math::color_rgba::white()); 72 | 73 | const auto size = buf->text_size(font, buffer); 74 | buf->text(font, buffer, { 10, 10 }, math::color_rgba::white()); 75 | 76 | buf->rectangle({ 10, 10 }, { 10 + size.x, 10 + size.y }, 2.f, math::color_rgba::white()); 77 | 78 | buf->rectangle_filled({ 0, 120 }, { 100, 220 }, math::color_rgba::white()); 79 | 80 | buf->triangle_filled({ 0, 350 }, 81 | { 100, 230 }, 82 | { 200, 350 }, 83 | math::color_rgba::red(), 84 | math::color_rgba::blue(), 85 | math::color_rgba::green(), true); 86 | 87 | static auto degrees = 360.f; 88 | buf->circle_filled({ 400, 200 }, 25, math::color_rgba{ 0, 0, 0, 0 }, math::color_rgba::blue(), 64, degrees, 90.f); 89 | /*degrees -= 1.f; 90 | if (degrees < 0.f) 91 | degrees = 360.f;*/ 92 | 93 | buf->push_tex_id(tex); 94 | buf->prim_reserve(6, 4); 95 | buf->prim_rect_uv({ 400, 400 }, { 600, 600 }, { 0, 0 }, { 1, 1 }, math::color_rgba::white()); 96 | 97 | buf->prim_reserve(6, 4); 98 | buf->prim_rect_uv({ 610, 400 }, { 810, 600 }, { 0, 0 }, { 1, 1 }, math::color_rgba::white()); 99 | buf->pop_tex_id(); 100 | 101 | buf->set_blur(180); 102 | buf->rectangle_filled({ 610, 400 }, { 810, 600 }, math::color_rgba::white()); 103 | buf->set_blur(0); 104 | 105 | buf->set_key_color(math::color_rgba::green()); 106 | buf->push_tex_id(tex); 107 | buf->prim_reserve(6, 4); 108 | buf->prim_rect_uv({ 820, 400 }, { 1020, 600 }, { 0, 0 }, { 1, 1 }, math::color_rgba::white()); 109 | buf->pop_tex_id(); 110 | buf->set_key_color(math::color_rgba{ 0, 0, 0, 0 }); 111 | 112 | buf->push_clip_rect({ 1040, 410 }, { 1220, 590 }); 113 | buf->push_clip_rect({ 1030, 400 }, { 1230, 600 }, true); 114 | buf->push_tex_id(tex); 115 | 116 | buf->set_key_color(math::color_rgba::green()); 117 | buf->prim_reserve(6, 4); 118 | buf->prim_rect_uv({ 1030, 400 }, { 1230, 600 }, { 0, 0 }, { 1, 1 }, math::color_rgba::white()); 119 | buf->set_key_color(math::color_rgba{ 0, 0, 0, 0 }); 120 | 121 | buf->pop_tex_id(); 122 | 123 | buf->text(font, buffer, { 1030, 410 }, math::color_rgba::white()); 124 | buf->pop_clip_rect(); 125 | buf->pop_clip_rect(); 126 | 127 | buf->push_clip_rect({ window_size_x - 100.f, window_size_y - 100.f }, { window_size_x + 100.f, window_size_y + 100.f }, true); 128 | buf->push_tex_id(tex); 129 | 130 | buf->set_key_color(math::color_rgba::green()); 131 | buf->prim_reserve(6, 4); 132 | buf->prim_rect_uv({ window_size_x - 100.f, window_size_y - 100.f }, { window_size_x + 100.f, window_size_y + 100.f }, { 0, 0 }, { 1, 1 }, math::color_rgba::white()); 133 | buf->set_key_color(math::color_rgba{ 0, 0, 0, 0 }); 134 | 135 | buf->pop_tex_id(); 136 | buf->pop_clip_rect(); 137 | 138 | util::draw::position positions[4] = { 139 | {600, 50}, 140 | {700, 150}, 141 | {800, 70}, 142 | {900, 200} 143 | }; 144 | buf->poly_line(positions, 4, math::color_rgba::white(), 1.f, true); 145 | 146 | buf->line({ 600, 150 }, { 900, 250 }, math::color_rgba::white(), 2.f, true); 147 | 148 | buf->circle({ 100, 550 }, 100, math::color_rgba::white(), 3.f, 64); 149 | 150 | draw_manager->swap_buffers(buffer_idx); 151 | 152 | x++; 153 | std::this_thread::sleep_for(16ms); 154 | } 155 | } -------------------------------------------------------------------------------- /math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // just some classes to make the renderer runnable 8 | namespace math 9 | { 10 | template 11 | static constexpr T PI = T(3.14159265358979323846L); 12 | 13 | template 14 | constexpr T rad2deg(T rad) 15 | { 16 | return rad * T(180) / PI; 17 | } 18 | 19 | template 20 | constexpr T deg2rad(T deg) 21 | { 22 | return deg * PI / T(180); 23 | } 24 | 25 | struct vec2f 26 | { 27 | float x, y; 28 | 29 | vec2f() 30 | : x(0.f), 31 | y(0.f) {} 32 | 33 | vec2f(const float x, const float y) 34 | : x(x), 35 | y(y) {} 36 | 37 | float dot(const vec2f &o) const 38 | { 39 | return (x * o.x) + (y * o.y); 40 | } 41 | 42 | float dot() const 43 | { 44 | return dot(*this); 45 | } 46 | 47 | float length_sqr() const 48 | { 49 | return dot(); 50 | } 51 | 52 | float length() const 53 | { 54 | return sqrtf(length_sqr()); 55 | } 56 | 57 | float reciprocal_length() const 58 | { 59 | return 1.f / length(); 60 | } 61 | 62 | vec2f& normalize() 63 | { 64 | return (*this *= reciprocal_length()); 65 | } 66 | 67 | vec2f normalized() const 68 | { 69 | auto r{*this}; 70 | return r.normalize(); 71 | } 72 | 73 | bool operator==(const vec2f &o) const 74 | { 75 | return (x == o.x && y == o.y); 76 | } 77 | 78 | vec2f& operator+=(const float v) 79 | { 80 | x += v; 81 | y += v; 82 | return *this; 83 | } 84 | 85 | vec2f& operator-=(const float v) 86 | { 87 | x -= v; 88 | y -= v; 89 | return *this; 90 | } 91 | 92 | vec2f& operator/=(const float v) 93 | { 94 | x /= v; 95 | y /= v; 96 | return *this; 97 | } 98 | 99 | vec2f& operator*=(const float v) 100 | { 101 | x *= v; 102 | y *= v; 103 | return *this; 104 | } 105 | 106 | vec2f& operator+=(const vec2f &o) 107 | { 108 | x += o.x; 109 | y += o.y; 110 | return *this; 111 | } 112 | 113 | vec2f& operator-=(const vec2f &o) 114 | { 115 | x -= o.x; 116 | y -= o.y; 117 | return *this; 118 | } 119 | 120 | vec2f& operator/=(const vec2f &o) 121 | { 122 | x /= o.x; 123 | y /= o.y; 124 | return *this; 125 | } 126 | 127 | vec2f& operator*=(const vec2f &o) 128 | { 129 | x *= o.x; 130 | y *= o.y; 131 | return *this; 132 | } 133 | 134 | vec2f operator+(const float v) const 135 | { 136 | auto r{*this}; 137 | r.x += v; 138 | r.y += v; 139 | return r; 140 | } 141 | 142 | vec2f operator-(const float v) const 143 | { 144 | auto r{*this}; 145 | r.x -= v; 146 | r.y -= v; 147 | return r; 148 | } 149 | 150 | vec2f operator*(const float v) const 151 | { 152 | auto r{*this}; 153 | r.x *= v; 154 | r.y *= v; 155 | return r; 156 | } 157 | 158 | vec2f operator/(const float v) const 159 | { 160 | auto r{*this}; 161 | r.x /= v; 162 | r.y /= v; 163 | return r; 164 | } 165 | 166 | vec2f operator+(const vec2f &o) const 167 | { 168 | auto r{*this}; 169 | r.x += o.x; 170 | r.y += o.y; 171 | return r; 172 | } 173 | 174 | vec2f operator-(const vec2f &o) const 175 | { 176 | auto r{*this}; 177 | r.x -= o.x; 178 | r.y -= o.y; 179 | return r; 180 | } 181 | 182 | vec2f operator*(const vec2f &o) const 183 | { 184 | auto r{*this}; 185 | r.x *= o.x; 186 | r.y *= o.y; 187 | return r; 188 | } 189 | 190 | vec2f operator/(const vec2f &o) const 191 | { 192 | auto r{*this}; 193 | r.x /= o.x; 194 | r.y /= o.y; 195 | return r; 196 | } 197 | 198 | const float& operator[](const size_t idx) const 199 | { 200 | return reinterpret_cast(this)[idx]; 201 | } 202 | 203 | float& operator[](const size_t idx) 204 | { 205 | return reinterpret_cast(this)[idx]; 206 | } 207 | }; 208 | 209 | inline vec2f operator*(const float v, const vec2f &o) 210 | { 211 | auto r{o}; 212 | r.x *= v; 213 | r.y *= v; 214 | return r; 215 | } 216 | 217 | struct vec4f 218 | { 219 | union 220 | { 221 | struct 222 | { 223 | float x, y, z, w; 224 | }; 225 | 226 | struct 227 | { 228 | vec2f xy; 229 | vec2f zw; 230 | }; 231 | }; 232 | 233 | vec4f() 234 | : x(0.f), 235 | y(0.f), 236 | z(0.f), 237 | w(0.f) {} 238 | 239 | vec4f(const float x, const float y, const float z, const float w) 240 | : x(x), 241 | y(y), 242 | z(z), 243 | w(w) {} 244 | 245 | vec4f(const vec2f &xy, const vec2f &zw) 246 | : x(xy.x), 247 | y(xy.y), 248 | z(zw.x), 249 | w(zw.y) {} 250 | 251 | float operator[](const size_t idx) const 252 | { 253 | return (&x)[idx]; 254 | } 255 | 256 | float& operator[](const size_t idx) 257 | { 258 | return (&x)[idx]; 259 | } 260 | 261 | bool operator==(const vec4f &o) const 262 | { 263 | return (xy == o.xy && zw == o.zw); 264 | } 265 | }; 266 | 267 | struct color_rgba 268 | { 269 | uint8_t _r, _g, _b, _a; 270 | 271 | //color_rgba(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a = 255u) : _r(r), _g(g), _b(b), _a(a) {} 272 | color_rgba(const int r, const int g, const int b, const int a = 255u) 273 | : _r(r), 274 | _g(g), 275 | _b(b), 276 | _a(a) {} 277 | 278 | color_rgba(const uint32_t val) 279 | { 280 | _r = (val >> 24); 281 | _g = (val >> 16) & 0xFF; 282 | _b = (val >> 8) & 0xFF; 283 | _a = val & 0xFF; 284 | } 285 | 286 | uint32_t as_argb() const 287 | { 288 | return (_a << 24) | (_r << 16) | (_g << 8) | _b; 289 | } 290 | 291 | uint32_t as_abgr() const 292 | { 293 | return (_a << 24) | (_b << 16) | (_g << 8) | _r; 294 | } 295 | 296 | operator uint32_t() const 297 | { 298 | return (_r << 24) | (_g << 16) | (_b << 8) | _a; 299 | } 300 | 301 | uint8_t r() const 302 | { 303 | return _r; 304 | } 305 | 306 | uint8_t g() const 307 | { 308 | return _g; 309 | } 310 | 311 | uint8_t b() const 312 | { 313 | return _b; 314 | } 315 | 316 | uint8_t a() const 317 | { 318 | return _a; 319 | } 320 | 321 | static color_rgba white() 322 | { 323 | return color_rgba{255, 255, 255}; 324 | } 325 | 326 | static color_rgba black() 327 | { 328 | return color_rgba{0, 0, 0}; 329 | } 330 | 331 | static color_rgba red() 332 | { 333 | return color_rgba{235, 64, 52}; 334 | } 335 | 336 | static color_rgba green() 337 | { 338 | return color_rgba{52, 217, 77}; 339 | } 340 | 341 | static color_rgba blue() 342 | { 343 | return color_rgba{34, 108, 199}; 344 | } 345 | }; 346 | 347 | struct matrix4x4f 348 | { 349 | std::array matrix = {}; 350 | 351 | matrix4x4f() = default; 352 | 353 | matrix4x4f(const vec4f &row1, const vec4f &row2, const vec4f &row3, const vec4f &row4) 354 | : matrix{row1, row2, row3, row4} {} 355 | 356 | const vec4f& operator[](const size_t idx) const 357 | { 358 | return matrix[idx]; 359 | } 360 | 361 | vec4f& operator[](const size_t idx) 362 | { 363 | return matrix[idx]; 364 | } 365 | }; 366 | } 367 | --------------------------------------------------------------------------------