postprocessing.cpp (26132B)
1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "postprocessing.h" 5 #include "gpu_device.h" 6 #include "host.h" 7 #include "imgui_manager.h" 8 #include "postprocessing_shader.h" 9 #include "postprocessing_shader_fx.h" 10 #include "postprocessing_shader_glsl.h" 11 12 // TODO: Remove me 13 #include "core/host.h" 14 #include "core/host_interface_progress_callback.h" 15 #include "core/settings.h" 16 17 #include "IconsFontAwesome5.h" 18 #include "common/assert.h" 19 #include "common/error.h" 20 #include "common/file_system.h" 21 #include "common/log.h" 22 #include "common/path.h" 23 #include "common/progress_callback.h" 24 #include "common/small_string.h" 25 #include "common/string_util.h" 26 #include "common/timer.h" 27 #include "fmt/format.h" 28 29 Log_SetChannel(PostProcessing); 30 31 // TODO: ProgressCallbacks for shader compiling, it can be a bit slow. 32 // TODO: buffer width/height is wrong on resize, need to change it somehow. 33 34 namespace PostProcessing { 35 template<typename T> 36 static u32 ParseVector(std::string_view line, ShaderOption::ValueVector* values); 37 38 static TinyString ValueToString(ShaderOption::Type type, u32 vector_size, const ShaderOption::ValueVector& value); 39 40 static TinyString GetStageConfigSection(const char* section, u32 index); 41 static void CopyStageConfig(SettingsInterface& si, const char* section, u32 old_index, u32 new_index); 42 static void SwapStageConfig(SettingsInterface& si, const char* section, u32 lhs_index, u32 rhs_index); 43 static std::unique_ptr<Shader> TryLoadingShader(const std::string& shader_name, bool only_config, Error* error); 44 static SettingsInterface& GetLoadSettingsInterface(const char* section); 45 46 template<typename T> 47 ALWAYS_INLINE void ForAllChains(const T& F) 48 { 49 F(DisplayChain); 50 F(InternalChain); 51 } 52 53 Chain DisplayChain(Config::DISPLAY_CHAIN_SECTION); 54 Chain InternalChain(Config::INTERNAL_CHAIN_SECTION); 55 56 static Common::Timer s_timer; 57 58 static std::unordered_map<u64, std::unique_ptr<GPUSampler>> s_samplers; 59 static std::unique_ptr<GPUTexture> s_dummy_texture; 60 } // namespace PostProcessing 61 62 template<typename T> 63 u32 PostProcessing::ParseVector(std::string_view line, ShaderOption::ValueVector* values) 64 { 65 u32 index = 0; 66 size_t start = 0; 67 while (index < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS) 68 { 69 while (start < line.size() && std::isspace(line[start])) 70 start++; 71 72 if (start >= line.size()) 73 break; 74 75 size_t end = line.find(',', start); 76 if (end == std::string_view::npos) 77 end = line.size(); 78 79 const std::string_view component = line.substr(start, end - start); 80 T value = StringUtil::FromChars<T>(component).value_or(static_cast<T>(0)); 81 if constexpr (std::is_same_v<T, float>) 82 (*values)[index++].float_value = value; 83 else if constexpr (std::is_same_v<T, s32>) 84 (*values)[index++].int_value = value; 85 86 start = end + 1; 87 } 88 89 const u32 size = index; 90 91 for (; index < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS; index++) 92 { 93 if constexpr (std::is_same_v<T, float>) 94 (*values)[index++].float_value = 0.0f; 95 else if constexpr (std::is_same_v<T, s32>) 96 (*values)[index++].int_value = 0; 97 } 98 99 return size; 100 } 101 102 u32 PostProcessing::ShaderOption::ParseFloatVector(std::string_view line, ValueVector* values) 103 { 104 return ParseVector<float>(line, values); 105 } 106 107 u32 PostProcessing::ShaderOption::ParseIntVector(std::string_view line, ValueVector* values) 108 { 109 return ParseVector<s32>(line, values); 110 } 111 112 TinyString PostProcessing::ValueToString(ShaderOption::Type type, u32 vector_size, 113 const ShaderOption::ValueVector& value) 114 { 115 TinyString ret; 116 117 for (u32 i = 0; i < vector_size; i++) 118 { 119 if (i > 0) 120 ret.append(','); 121 122 switch (type) 123 { 124 case ShaderOption::Type::Bool: 125 ret.append((value[i].int_value != 0) ? "true" : "false"); 126 break; 127 128 case ShaderOption::Type::Int: 129 ret.append_format("{}", value[i].int_value); 130 break; 131 132 case ShaderOption::Type::Float: 133 ret.append_format("{}", value[i].float_value); 134 break; 135 136 default: 137 break; 138 } 139 } 140 141 return ret; 142 } 143 144 std::vector<std::pair<std::string, std::string>> PostProcessing::GetAvailableShaderNames() 145 { 146 std::vector<std::pair<std::string, std::string>> names; 147 148 FileSystem::FindResultsArray results; 149 FileSystem::FindFiles(Path::Combine(EmuFolders::Resources, "shaders").c_str(), "*.glsl", 150 FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS, &results); 151 FileSystem::FindFiles(EmuFolders::Shaders.c_str(), "*.glsl", 152 FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS | 153 FILESYSTEM_FIND_KEEP_ARRAY, 154 &results); 155 std::sort(results.begin(), results.end(), 156 [](const auto& lhs, const auto& rhs) { return lhs.FileName < rhs.FileName; }); 157 158 for (FILESYSTEM_FIND_DATA& fd : results) 159 { 160 size_t pos = fd.FileName.rfind('.'); 161 if (pos != std::string::npos && pos > 0) 162 fd.FileName.erase(pos); 163 164 #ifdef _WIN32 165 // swap any backslashes for forward slashes so the config is cross-platform 166 StringUtil::ReplaceAll(&fd.FileName, '\\', '/'); 167 #endif 168 169 if (std::none_of(names.begin(), names.end(), [&fd](const auto& other) { return fd.FileName == other.second; })) 170 { 171 std::string display_name = fmt::format(TRANSLATE_FS("PostProcessing", "{} [GLSL]"), fd.FileName); 172 names.emplace_back(std::move(display_name), std::move(fd.FileName)); 173 } 174 } 175 176 FileSystem::FindFiles(Path::Combine(EmuFolders::Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Shaders").c_str(), "*.fx", 177 FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS, &results); 178 FileSystem::FindFiles( 179 Path::Combine(EmuFolders::Resources, "shaders" FS_OSPATH_SEPARATOR_STR "reshade" FS_OSPATH_SEPARATOR_STR "Shaders") 180 .c_str(), 181 "*.fx", 182 FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS | FILESYSTEM_FIND_KEEP_ARRAY, 183 &results); 184 std::sort(results.begin(), results.end(), 185 [](const auto& lhs, const auto& rhs) { return lhs.FileName < rhs.FileName; }); 186 187 for (FILESYSTEM_FIND_DATA& fd : results) 188 { 189 size_t pos = fd.FileName.rfind('.'); 190 if (pos != std::string::npos && pos > 0) 191 fd.FileName.erase(pos); 192 193 #ifdef _WIN32 194 // swap any backslashes for forward slashes so the config is cross-platform 195 StringUtil::ReplaceAll(&fd.FileName, '\\', '/'); 196 #endif 197 198 if (std::none_of(names.begin(), names.end(), [&fd](const auto& other) { return fd.FileName == other.second; })) 199 { 200 std::string display_name = fmt::format(TRANSLATE_FS("PostProcessing", "{} [ReShade]"), fd.FileName); 201 names.emplace_back(std::move(display_name), std::move(fd.FileName)); 202 } 203 } 204 205 std::sort(names.begin(), names.end(), 206 [](const std::pair<std::string, std::string>& lhs, const std::pair<std::string, std::string>& rhs) { 207 return (StringUtil::Strcasecmp(lhs.first.c_str(), rhs.first.c_str()) < 0); 208 }); 209 210 return names; 211 } 212 213 TinyString PostProcessing::GetStageConfigSection(const char* section, u32 index) 214 { 215 return TinyString::from_format("{}/Stage{}", section, index + 1); 216 } 217 218 void PostProcessing::CopyStageConfig(SettingsInterface& si, const char* section, u32 old_index, u32 new_index) 219 { 220 const TinyString old_section = GetStageConfigSection(section, old_index); 221 const TinyString new_section = GetStageConfigSection(section, new_index); 222 223 si.ClearSection(new_section); 224 225 for (const auto& [key, value] : si.GetKeyValueList(old_section)) 226 si.SetStringValue(new_section, key.c_str(), value.c_str()); 227 } 228 229 void PostProcessing::SwapStageConfig(SettingsInterface& si, const char* section, u32 lhs_index, u32 rhs_index) 230 { 231 const TinyString lhs_section = GetStageConfigSection(section, lhs_index); 232 const TinyString rhs_section = GetStageConfigSection(section, rhs_index); 233 234 const std::vector<std::pair<std::string, std::string>> lhs_kvs = si.GetKeyValueList(lhs_section); 235 si.ClearSection(lhs_section); 236 237 const std::vector<std::pair<std::string, std::string>> rhs_kvs = si.GetKeyValueList(rhs_section); 238 si.ClearSection(rhs_section); 239 240 for (const auto& [key, value] : rhs_kvs) 241 si.SetStringValue(lhs_section, key.c_str(), value.c_str()); 242 243 for (const auto& [key, value] : lhs_kvs) 244 si.SetStringValue(rhs_section, key.c_str(), value.c_str()); 245 } 246 247 u32 PostProcessing::Config::GetStageCount(const SettingsInterface& si, const char* section) 248 { 249 return si.GetUIntValue(section, "StageCount", 0u); 250 } 251 252 std::string PostProcessing::Config::GetStageShaderName(const SettingsInterface& si, const char* section, u32 index) 253 { 254 return si.GetStringValue(GetStageConfigSection(section, index), "ShaderName"); 255 } 256 257 std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetStageOptions(const SettingsInterface& si, 258 const char* section, u32 index) 259 { 260 std::vector<PostProcessing::ShaderOption> ret; 261 262 const TinyString stage_section = GetStageConfigSection(section, index); 263 const std::string shader_name = si.GetStringValue(stage_section, "ShaderName"); 264 if (shader_name.empty()) 265 return ret; 266 267 std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, nullptr); 268 if (!shader) 269 return ret; 270 271 shader->LoadOptions(si, stage_section); 272 ret = shader->TakeOptions(); 273 return ret; 274 } 275 276 std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetShaderOptions(const std::string& shader_name, 277 Error* error) 278 { 279 std::vector<PostProcessing::ShaderOption> ret; 280 std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, error); 281 if (!shader) 282 return ret; 283 284 ret = shader->TakeOptions(); 285 return ret; 286 } 287 288 bool PostProcessing::Config::AddStage(SettingsInterface& si, const char* section, const std::string& shader_name, 289 Error* error) 290 { 291 std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, error); 292 if (!shader) 293 return false; 294 295 const u32 index = GetStageCount(si, section); 296 si.SetUIntValue(section, "StageCount", index + 1); 297 298 const TinyString stage_section = GetStageConfigSection(section, index); 299 si.SetStringValue(stage_section, "ShaderName", shader->GetName().c_str()); 300 301 #if 0 302 // Leave options unset for now. 303 for (const ShaderOption& option : shader->GetOptions()) 304 { 305 si.SetStringValue(section, option.name.c_str(), 306 ValueToString(option.type, option.vector_size, option.default_value)); 307 } 308 #endif 309 310 return true; 311 } 312 313 void PostProcessing::Config::RemoveStage(SettingsInterface& si, const char* section, u32 index) 314 { 315 const u32 stage_count = GetStageCount(si, section); 316 if (index >= stage_count) 317 return; 318 319 for (u32 i = index; i < (stage_count - 1); i++) 320 CopyStageConfig(si, section, i + 1, i); 321 322 si.ClearSection(GetStageConfigSection(section, stage_count - 1)); 323 si.SetUIntValue(section, "StageCount", stage_count - 1); 324 } 325 326 void PostProcessing::Config::MoveStageUp(SettingsInterface& si, const char* section, u32 index) 327 { 328 const u32 stage_count = GetStageCount(si, section); 329 if (index == 0 || index >= stage_count) 330 return; 331 332 SwapStageConfig(si, section, index, index - 1); 333 } 334 335 void PostProcessing::Config::MoveStageDown(SettingsInterface& si, const char* section, u32 index) 336 { 337 const u32 stage_count = GetStageCount(si, section); 338 if ((index + 1) >= stage_count) 339 return; 340 341 SwapStageConfig(si, section, index, index + 1); 342 } 343 344 void PostProcessing::Config::SetStageOption(SettingsInterface& si, const char* section, u32 index, 345 const ShaderOption& option) 346 { 347 const TinyString stage_section = GetStageConfigSection(section, index); 348 si.SetStringValue(stage_section, option.name.c_str(), ValueToString(option.type, option.vector_size, option.value)); 349 } 350 351 void PostProcessing::Config::UnsetStageOption(SettingsInterface& si, const char* section, u32 index, 352 const ShaderOption& option) 353 { 354 const TinyString stage_section = GetStageConfigSection(section, index); 355 si.DeleteValue(stage_section, option.name.c_str()); 356 } 357 358 void PostProcessing::Config::ClearStages(SettingsInterface& si, const char* section) 359 { 360 const u32 count = GetStageCount(si, section); 361 for (s32 i = static_cast<s32>(count - 1); i >= 0; i--) 362 si.ClearSection(GetStageConfigSection(section, static_cast<u32>(i))); 363 si.SetUIntValue(section, "StageCount", 0); 364 } 365 366 PostProcessing::Chain::Chain(const char* section) : m_section(section) 367 { 368 } 369 370 PostProcessing::Chain::~Chain() = default; 371 372 bool PostProcessing::Chain::IsActive() const 373 { 374 return m_enabled && !m_stages.empty(); 375 } 376 377 bool PostProcessing::Chain::IsInternalChain() const 378 { 379 return (this == &InternalChain); 380 } 381 382 void PostProcessing::Chain::ClearStagesWithError(const Error& error) 383 { 384 std::string msg = error.GetDescription(); 385 Host::AddIconOSDMessage( 386 "PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE, 387 fmt::format(TRANSLATE_FS("OSDMessage", "Failed to load post-processing chain: {}"), 388 msg.empty() ? TRANSLATE_SV("PostProcessing", "Unknown Error") : std::string_view(msg)), 389 Host::OSD_ERROR_DURATION); 390 m_stages.clear(); 391 } 392 393 void PostProcessing::Chain::LoadStages() 394 { 395 auto lock = Host::GetSettingsLock(); 396 SettingsInterface& si = GetLoadSettingsInterface(m_section); 397 398 m_enabled = si.GetBoolValue(m_section, "Enabled", false); 399 m_wants_depth_buffer = false; 400 401 const u32 stage_count = Config::GetStageCount(si, m_section); 402 if (stage_count == 0) 403 return; 404 405 Error error; 406 HostInterfaceProgressCallback progress; 407 progress.SetProgressRange(stage_count); 408 409 for (u32 i = 0; i < stage_count; i++) 410 { 411 std::string stage_name = Config::GetStageShaderName(si, m_section, i); 412 if (stage_name.empty()) 413 { 414 error.SetString(fmt::format("No stage name in stage {}.", i + 1)); 415 ClearStagesWithError(error); 416 return; 417 } 418 419 lock.unlock(); 420 progress.FormatStatusText("Loading shader {}...", stage_name); 421 422 std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error); 423 if (!shader) 424 { 425 ClearStagesWithError(error); 426 return; 427 } 428 429 lock.lock(); 430 shader->LoadOptions(si, GetStageConfigSection(m_section, i)); 431 m_stages.push_back(std::move(shader)); 432 433 progress.IncrementProgressValue(); 434 } 435 436 if (stage_count > 0) 437 DEV_LOG("Loaded {} post-processing stages.", stage_count); 438 439 // precompile shaders 440 if (!IsInternalChain() && g_gpu_device && g_gpu_device->GetWindowFormat() != GPUTexture::Format::Unknown) 441 { 442 CheckTargets(g_gpu_device->GetWindowFormat(), g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), 443 &progress); 444 } 445 446 // must be down here, because we need to compile first, triggered by CheckTargets() 447 for (std::unique_ptr<Shader>& shader : m_stages) 448 m_wants_depth_buffer |= shader->WantsDepthBuffer(); 449 m_needs_depth_buffer = m_enabled && m_wants_depth_buffer; 450 if (m_wants_depth_buffer) 451 DEV_LOG("Depth buffer is needed."); 452 } 453 454 void PostProcessing::Chain::ClearStages() 455 { 456 decltype(m_stages)().swap(m_stages); 457 } 458 459 void PostProcessing::Chain::UpdateSettings(std::unique_lock<std::mutex>& settings_lock) 460 { 461 SettingsInterface& si = GetLoadSettingsInterface(m_section); 462 463 m_enabled = si.GetBoolValue(m_section, "Enabled", false); 464 465 const u32 stage_count = Config::GetStageCount(si, m_section); 466 if (stage_count == 0) 467 { 468 m_stages.clear(); 469 return; 470 } 471 472 Error error; 473 474 m_stages.resize(stage_count); 475 476 HostInterfaceProgressCallback progress; 477 progress.SetProgressRange(stage_count); 478 479 const GPUTexture::Format prev_format = m_target_format; 480 m_wants_depth_buffer = false; 481 482 for (u32 i = 0; i < stage_count; i++) 483 { 484 std::string stage_name = Config::GetStageShaderName(si, m_section, i); 485 if (stage_name.empty()) 486 { 487 error.SetString(fmt::format("No stage name in stage {}.", i + 1)); 488 ClearStagesWithError(error); 489 return; 490 } 491 492 if (!m_stages[i] || stage_name != m_stages[i]->GetName()) 493 { 494 if (i < m_stages.size()) 495 m_stages[i].reset(); 496 497 // Force recompile. 498 m_target_format = GPUTexture::Format::Unknown; 499 500 settings_lock.unlock(); 501 502 std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error); 503 if (!shader) 504 { 505 ClearStagesWithError(error); 506 return; 507 } 508 509 if (i < m_stages.size()) 510 m_stages[i] = std::move(shader); 511 else 512 m_stages.push_back(std::move(shader)); 513 514 settings_lock.lock(); 515 } 516 517 m_stages[i]->LoadOptions(si, GetStageConfigSection(m_section, i)); 518 } 519 520 if (prev_format != GPUTexture::Format::Unknown) 521 CheckTargets(prev_format, m_target_width, m_target_height, &progress); 522 523 if (stage_count > 0) 524 { 525 s_timer.Reset(); 526 DEV_LOG("Loaded {} post-processing stages.", stage_count); 527 } 528 529 // must be down here, because we need to compile first, triggered by CheckTargets() 530 for (std::unique_ptr<Shader>& shader : m_stages) 531 m_wants_depth_buffer |= shader->WantsDepthBuffer(); 532 m_needs_depth_buffer = m_enabled && m_wants_depth_buffer; 533 if (m_wants_depth_buffer) 534 DEV_LOG("Depth buffer is needed."); 535 } 536 537 void PostProcessing::Chain::Toggle() 538 { 539 if (m_stages.empty()) 540 { 541 Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER, 542 TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."), 543 Host::OSD_QUICK_DURATION); 544 return; 545 } 546 547 const bool new_enabled = !m_enabled; 548 Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER, 549 new_enabled ? TRANSLATE_STR("OSDMessage", "Post-processing is now enabled.") : 550 TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."), 551 Host::OSD_QUICK_DURATION); 552 m_enabled = new_enabled; 553 m_needs_depth_buffer = new_enabled && m_wants_depth_buffer; 554 if (m_enabled) 555 s_timer.Reset(); 556 } 557 558 bool PostProcessing::Chain::CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height, 559 ProgressCallback* progress /* = nullptr */) 560 { 561 if (m_target_format == target_format && m_target_width == target_width && m_target_height == target_height) 562 return true; 563 564 // In case any allocs fail. 565 DestroyTextures(); 566 567 if (!(m_input_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, 568 GPUTexture::Type::RenderTarget, target_format)) || 569 !(m_output_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, 570 GPUTexture::Type::RenderTarget, target_format))) 571 { 572 DestroyTextures(); 573 return false; 574 } 575 576 if (!progress) 577 progress = ProgressCallback::NullProgressCallback; 578 579 progress->SetProgressRange(static_cast<u32>(m_stages.size())); 580 progress->SetProgressValue(0); 581 582 m_wants_depth_buffer = false; 583 584 for (size_t i = 0; i < m_stages.size(); i++) 585 { 586 Shader* const shader = m_stages[i].get(); 587 588 progress->FormatStatusText("Compiling {}...", shader->GetName()); 589 590 if (!shader->CompilePipeline(target_format, target_width, target_height, progress) || 591 !shader->ResizeOutput(target_format, target_width, target_height)) 592 { 593 ERROR_LOG("Failed to compile one or more post-processing shaders, disabling."); 594 Host::AddIconOSDMessage( 595 "PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE, 596 fmt::format("Failed to compile post-processing shader '{}'. Disabling post-processing.", shader->GetName())); 597 m_enabled = false; 598 return false; 599 } 600 601 progress->SetProgressValue(static_cast<u32>(i + 1)); 602 m_wants_depth_buffer |= shader->WantsDepthBuffer(); 603 } 604 605 m_target_format = target_format; 606 m_target_width = target_width; 607 m_target_height = target_height; 608 m_needs_depth_buffer = m_enabled && m_wants_depth_buffer; 609 return true; 610 } 611 612 void PostProcessing::Chain::DestroyTextures() 613 { 614 m_target_format = GPUTexture::Format::Unknown; 615 m_target_width = 0; 616 m_target_height = 0; 617 618 g_gpu_device->RecycleTexture(std::move(m_output_texture)); 619 g_gpu_device->RecycleTexture(std::move(m_input_texture)); 620 } 621 622 bool PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, 623 GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, 624 s32 native_height) 625 { 626 GL_SCOPE_FMT("{} Apply", m_section); 627 628 GPUTexture* output = m_output_texture.get(); 629 input_color->MakeReadyForSampling(); 630 if (input_depth) 631 input_depth->MakeReadyForSampling(); 632 633 for (const std::unique_ptr<Shader>& stage : m_stages) 634 { 635 const bool is_final = (stage.get() == m_stages.back().get()); 636 637 if (!stage->Apply(input_color, input_depth, is_final ? final_target : output, final_rect, orig_width, orig_height, 638 native_width, native_height, m_target_width, m_target_height)) 639 { 640 return false; 641 } 642 643 if (!is_final) 644 { 645 output->MakeReadyForSampling(); 646 input_color = output; 647 output = (output == m_output_texture.get()) ? m_input_texture.get() : m_output_texture.get(); 648 } 649 } 650 651 return true; 652 } 653 654 void PostProcessing::Initialize() 655 { 656 DisplayChain.LoadStages(); 657 InternalChain.LoadStages(); 658 s_timer.Reset(); 659 } 660 661 void PostProcessing::UpdateSettings() 662 { 663 auto lock = Host::GetSettingsLock(); 664 ForAllChains([&lock](Chain& chain) { chain.UpdateSettings(lock); }); 665 } 666 667 void PostProcessing::Shutdown() 668 { 669 g_gpu_device->RecycleTexture(std::move(s_dummy_texture)); 670 s_samplers.clear(); 671 ForAllChains([](Chain& chain) { 672 chain.ClearStages(); 673 chain.DestroyTextures(); 674 }); 675 } 676 677 bool PostProcessing::ReloadShaders() 678 { 679 if (!DisplayChain.HasStages() && !InternalChain.HasStages()) 680 { 681 Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER, 682 TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."), 683 Host::OSD_QUICK_DURATION); 684 return false; 685 } 686 687 ForAllChains([](Chain& chain) { 688 chain.ClearStages(); 689 chain.DestroyTextures(); 690 chain.LoadStages(); 691 }); 692 s_timer.Reset(); 693 694 Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER, 695 TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), Host::OSD_QUICK_DURATION); 696 return true; 697 } 698 699 std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name, 700 bool only_config, Error* error) 701 { 702 std::string filename; 703 std::optional<std::string> resource_str; 704 705 // Try reshade first. 706 filename = Path::Combine( 707 EmuFolders::Shaders, 708 fmt::format("reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name)); 709 if (FileSystem::FileExists(filename.c_str())) 710 { 711 std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>(); 712 if (shader->LoadFromFile(std::string(shader_name), filename.c_str(), only_config, error)) 713 return shader; 714 } 715 716 filename = Path::Combine(EmuFolders::Shaders, fmt::format("{}.glsl", shader_name)); 717 if (FileSystem::FileExists(filename.c_str())) 718 { 719 std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>(); 720 if (shader->LoadFromFile(std::string(shader_name), filename.c_str(), error)) 721 return shader; 722 } 723 724 filename = 725 fmt::format("shaders/reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name); 726 resource_str = Host::ReadResourceFileToString(filename.c_str(), true); 727 if (resource_str.has_value()) 728 { 729 std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>(); 730 if (shader->LoadFromString(std::string(shader_name), std::move(filename), std::move(resource_str.value()), 731 only_config, error)) 732 { 733 return shader; 734 } 735 } 736 737 filename = fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name); 738 resource_str = Host::ReadResourceFileToString(filename.c_str(), true); 739 if (resource_str.has_value()) 740 { 741 std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>(); 742 if (shader->LoadFromString(std::string(shader_name), std::move(resource_str.value()), error)) 743 return shader; 744 } 745 746 ERROR_LOG("Failed to load shader '{}'", shader_name); 747 return {}; 748 } 749 750 SettingsInterface& PostProcessing::GetLoadSettingsInterface(const char* section) 751 { 752 // If PostProcessing/Enable is set in the game settings interface, use that. 753 // Otherwise, use the base settings. 754 755 SettingsInterface* game_si = Host::Internal::GetGameSettingsLayer(); 756 if (game_si && game_si->ContainsValue(section, "Enabled")) 757 return *game_si; 758 else 759 return *Host::Internal::GetBaseSettingsLayer(); 760 } 761 762 const Common::Timer& PostProcessing::GetTimer() 763 { 764 return s_timer; 765 } 766 767 GPUSampler* PostProcessing::GetSampler(const GPUSampler::Config& config) 768 { 769 auto it = s_samplers.find(config.key); 770 if (it != s_samplers.end()) 771 return it->second.get(); 772 773 std::unique_ptr<GPUSampler> sampler = g_gpu_device->CreateSampler(config); 774 if (!sampler) 775 ERROR_LOG("Failed to create GPU sampler with config={:X}", config.key); 776 777 it = s_samplers.emplace(config.key, std::move(sampler)).first; 778 return it->second.get(); 779 } 780 781 GPUTexture* PostProcessing::GetDummyTexture() 782 { 783 if (s_dummy_texture) 784 return s_dummy_texture.get(); 785 786 const u32 zero = 0; 787 s_dummy_texture = g_gpu_device->FetchTexture(1, 1, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, 788 &zero, sizeof(zero)); 789 if (!s_dummy_texture) 790 ERROR_LOG("Failed to create dummy texture."); 791 792 return s_dummy_texture.get(); 793 }