postprocessing_shader_fx.cpp (62975B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "postprocessing_shader_fx.h" 5 #include "image.h" 6 #include "input_manager.h" 7 #include "shadergen.h" 8 9 // TODO: Remove me 10 #include "core/host.h" 11 #include "core/settings.h" 12 13 #include "common/assert.h" 14 #include "common/bitutils.h" 15 #include "common/error.h" 16 #include "common/file_system.h" 17 #include "common/log.h" 18 #include "common/path.h" 19 #include "common/progress_callback.h" 20 #include "common/string_util.h" 21 22 #include "effect_codegen.hpp" 23 #include "effect_parser.hpp" 24 #include "effect_preprocessor.hpp" 25 26 #include "fmt/format.h" 27 28 #include <bitset> 29 #include <cctype> 30 #include <cmath> 31 #include <cstring> 32 #include <sstream> 33 34 Log_SetChannel(ReShadeFXShader); 35 36 static constexpr s32 DEFAULT_BUFFER_WIDTH = 3840; 37 static constexpr s32 DEFAULT_BUFFER_HEIGHT = 2160; 38 39 static RenderAPI GetRenderAPI() 40 { 41 return g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::D3D11; 42 } 43 44 static bool PreprocessorFileExistsCallback(const std::string& path) 45 { 46 if (Path::IsAbsolute(path)) 47 return FileSystem::FileExists(path.c_str()); 48 49 return Host::ResourceFileExists(path.c_str(), true); 50 } 51 52 static bool PreprocessorReadFileCallback(const std::string& path, std::string& data) 53 { 54 std::optional<std::string> rdata; 55 if (Path::IsAbsolute(path)) 56 rdata = FileSystem::ReadFileToString(path.c_str()); 57 else 58 rdata = Host::ReadResourceFileToString(path.c_str(), true); 59 if (!rdata.has_value()) 60 return false; 61 62 data = std::move(rdata.value()); 63 return true; 64 } 65 66 static std::unique_ptr<reshadefx::codegen> CreateRFXCodegen() 67 { 68 const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false; 69 const bool uniforms_to_spec_constants = false; 70 const RenderAPI rapi = GetRenderAPI(); 71 72 switch (rapi) 73 { 74 case RenderAPI::None: 75 case RenderAPI::D3D11: 76 case RenderAPI::D3D12: 77 { 78 return std::unique_ptr<reshadefx::codegen>( 79 reshadefx::create_codegen_hlsl(50, debug_info, uniforms_to_spec_constants)); 80 } 81 82 case RenderAPI::Vulkan: 83 case RenderAPI::Metal: 84 { 85 return std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_glsl( 86 false, true, debug_info, uniforms_to_spec_constants, false, (rapi == RenderAPI::Vulkan))); 87 } 88 89 case RenderAPI::OpenGL: 90 case RenderAPI::OpenGLES: 91 default: 92 { 93 return std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_glsl( 94 (rapi == RenderAPI::OpenGLES), false, debug_info, uniforms_to_spec_constants, false, true)); 95 } 96 } 97 } 98 99 static GPUTexture::Format MapTextureFormat(reshadefx::texture_format format) 100 { 101 static constexpr GPUTexture::Format s_mapping[] = { 102 GPUTexture::Format::Unknown, // unknown 103 GPUTexture::Format::R8, // r8 104 GPUTexture::Format::R16, // r16 105 GPUTexture::Format::R16F, // r16f 106 GPUTexture::Format::R32I, // r32i 107 GPUTexture::Format::R32U, // r32u 108 GPUTexture::Format::R32F, // r32f 109 GPUTexture::Format::RG8, // rg8 110 GPUTexture::Format::RG16, // rg16 111 GPUTexture::Format::RG16F, // rg16f 112 GPUTexture::Format::RG32F, // rg32f 113 GPUTexture::Format::RGBA8, // rgba8 114 GPUTexture::Format::RGBA16, // rgba16 115 GPUTexture::Format::RGBA16F, // rgba16f 116 GPUTexture::Format::RGBA32F, // rgba32f 117 GPUTexture::Format::RGB10A2, // rgb10a2 118 }; 119 DebugAssert(static_cast<u32>(format) < std::size(s_mapping)); 120 return s_mapping[static_cast<u32>(format)]; 121 } 122 123 static GPUSampler::Config MapSampler(const reshadefx::sampler_info& si) 124 { 125 GPUSampler::Config config = GPUSampler::GetNearestConfig(); 126 127 switch (si.filter) 128 { 129 case reshadefx::filter_mode::min_mag_mip_point: 130 config.min_filter = GPUSampler::Filter::Nearest; 131 config.mag_filter = GPUSampler::Filter::Nearest; 132 config.mip_filter = GPUSampler::Filter::Nearest; 133 break; 134 135 case reshadefx::filter_mode::min_mag_point_mip_linear: 136 config.min_filter = GPUSampler::Filter::Nearest; 137 config.mag_filter = GPUSampler::Filter::Nearest; 138 config.mip_filter = GPUSampler::Filter::Linear; 139 break; 140 141 case reshadefx::filter_mode::min_point_mag_linear_mip_point: 142 config.min_filter = GPUSampler::Filter::Linear; 143 config.mag_filter = GPUSampler::Filter::Linear; 144 config.mip_filter = GPUSampler::Filter::Nearest; 145 break; 146 147 case reshadefx::filter_mode::min_point_mag_mip_linear: 148 config.min_filter = GPUSampler::Filter::Nearest; 149 config.mag_filter = GPUSampler::Filter::Linear; 150 config.mip_filter = GPUSampler::Filter::Linear; 151 break; 152 153 case reshadefx::filter_mode::min_linear_mag_mip_point: 154 config.min_filter = GPUSampler::Filter::Linear; 155 config.mag_filter = GPUSampler::Filter::Nearest; 156 config.mip_filter = GPUSampler::Filter::Nearest; 157 break; 158 159 case reshadefx::filter_mode::min_linear_mag_point_mip_linear: 160 config.min_filter = GPUSampler::Filter::Linear; 161 config.mag_filter = GPUSampler::Filter::Nearest; 162 config.mip_filter = GPUSampler::Filter::Linear; 163 break; 164 165 case reshadefx::filter_mode::min_mag_linear_mip_point: 166 config.min_filter = GPUSampler::Filter::Linear; 167 config.mag_filter = GPUSampler::Filter::Linear; 168 config.mip_filter = GPUSampler::Filter::Nearest; 169 break; 170 171 case reshadefx::filter_mode::min_mag_mip_linear: 172 config.min_filter = GPUSampler::Filter::Linear; 173 config.mag_filter = GPUSampler::Filter::Linear; 174 config.mip_filter = GPUSampler::Filter::Linear; 175 break; 176 177 default: 178 break; 179 } 180 181 static constexpr auto map_address_mode = [](const reshadefx::texture_address_mode m) { 182 switch (m) 183 { 184 case reshadefx::texture_address_mode::wrap: 185 return GPUSampler::AddressMode::Repeat; 186 case reshadefx::texture_address_mode::mirror: 187 return GPUSampler::AddressMode::MirrorRepeat; 188 case reshadefx::texture_address_mode::clamp: 189 return GPUSampler::AddressMode::ClampToEdge; 190 case reshadefx::texture_address_mode::border: 191 default: 192 return GPUSampler::AddressMode::ClampToBorder; 193 } 194 }; 195 196 config.address_u = map_address_mode(si.address_u); 197 config.address_v = map_address_mode(si.address_v); 198 config.address_w = map_address_mode(si.address_w); 199 200 return config; 201 } 202 203 static GPUPipeline::BlendState MapBlendState(const reshadefx::pass_info& pi) 204 { 205 static constexpr auto map_blend_op = [](const reshadefx::pass_blend_op o) { 206 switch (o) 207 { 208 case reshadefx::pass_blend_op::add: 209 return GPUPipeline::BlendOp::Add; 210 case reshadefx::pass_blend_op::subtract: 211 return GPUPipeline::BlendOp::Subtract; 212 case reshadefx::pass_blend_op::reverse_subtract: 213 return GPUPipeline::BlendOp::ReverseSubtract; 214 case reshadefx::pass_blend_op::min: 215 return GPUPipeline::BlendOp::Min; 216 case reshadefx::pass_blend_op::max: 217 default: 218 return GPUPipeline::BlendOp::Max; 219 } 220 }; 221 static constexpr auto map_blend_factor = [](const reshadefx::pass_blend_factor f) { 222 switch (f) 223 { 224 case reshadefx::pass_blend_factor::zero: 225 return GPUPipeline::BlendFunc::Zero; 226 case reshadefx::pass_blend_factor::one: 227 return GPUPipeline::BlendFunc::One; 228 case reshadefx::pass_blend_factor::source_color: 229 return GPUPipeline::BlendFunc::SrcColor; 230 case reshadefx::pass_blend_factor::one_minus_source_color: 231 return GPUPipeline::BlendFunc::InvSrcColor; 232 case reshadefx::pass_blend_factor::dest_color: 233 return GPUPipeline::BlendFunc::DstColor; 234 case reshadefx::pass_blend_factor::one_minus_dest_color: 235 return GPUPipeline::BlendFunc::InvDstColor; 236 case reshadefx::pass_blend_factor::source_alpha: 237 return GPUPipeline::BlendFunc::SrcAlpha; 238 case reshadefx::pass_blend_factor::one_minus_source_alpha: 239 return GPUPipeline::BlendFunc::InvSrcAlpha; 240 case reshadefx::pass_blend_factor::dest_alpha: 241 default: 242 return GPUPipeline::BlendFunc::DstAlpha; 243 } 244 }; 245 246 GPUPipeline::BlendState bs = GPUPipeline::BlendState::GetNoBlendingState(); 247 bs.enable = (pi.blend_enable[0] != 0); 248 bs.blend_op = map_blend_op(pi.blend_op[0]); 249 bs.src_blend = map_blend_factor(pi.src_blend[0]); 250 bs.dst_blend = map_blend_factor(pi.dest_blend[0]); 251 bs.alpha_blend_op = map_blend_op(pi.blend_op_alpha[0]); 252 bs.src_alpha_blend = map_blend_factor(pi.src_blend_alpha[0]); 253 bs.dst_alpha_blend = map_blend_factor(pi.dest_blend_alpha[0]); 254 bs.write_mask = pi.color_write_mask[0]; 255 return bs; 256 } 257 258 static GPUPipeline::Primitive MapPrimitive(reshadefx::primitive_topology topology) 259 { 260 switch (topology) 261 { 262 case reshadefx::primitive_topology::point_list: 263 return GPUPipeline::Primitive::Points; 264 case reshadefx::primitive_topology::line_list: 265 return GPUPipeline::Primitive::Lines; 266 case reshadefx::primitive_topology::line_strip: 267 Panic("Unhandled line strip"); 268 case reshadefx::primitive_topology::triangle_list: 269 return GPUPipeline::Primitive::Triangles; 270 case reshadefx::primitive_topology::triangle_strip: 271 default: 272 return GPUPipeline::Primitive::TriangleStrips; 273 } 274 } 275 276 PostProcessing::ReShadeFXShader::ReShadeFXShader() = default; 277 278 PostProcessing::ReShadeFXShader::~ReShadeFXShader() 279 { 280 for (Texture& tex : m_textures) 281 g_gpu_device->RecycleTexture(std::move(tex.texture)); 282 } 283 284 bool PostProcessing::ReShadeFXShader::LoadFromFile(std::string name, std::string filename, bool only_config, 285 Error* error) 286 { 287 std::optional<std::string> data = FileSystem::ReadFileToString(filename.c_str(), error); 288 if (!data.has_value()) 289 { 290 ERROR_LOG("Failed to read '{}'.", filename); 291 return false; 292 } 293 294 return LoadFromString(std::move(name), std::move(filename), std::move(data.value()), only_config, error); 295 } 296 297 bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::string filename, std::string code, 298 bool only_config, Error* error) 299 { 300 DebugAssert(only_config || g_gpu_device); 301 302 m_name = std::move(name); 303 m_filename = std::move(filename); 304 305 // Reshade's preprocessor expects this. 306 if (code.empty() || code.back() != '\n') 307 code.push_back('\n'); 308 309 reshadefx::module temp_module; 310 if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(), 311 only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), &temp_module, 312 std::move(code), error)) 313 { 314 return false; 315 } 316 317 if (!CreateOptions(temp_module, error)) 318 return false; 319 320 // check limits 321 if (!temp_module.techniques.empty()) 322 { 323 bool has_passes = false; 324 for (const reshadefx::technique_info& tech : temp_module.techniques) 325 { 326 for (const reshadefx::pass_info& pi : tech.passes) 327 { 328 has_passes = true; 329 330 u32 max_rt = 0; 331 for (u32 i = 0; i < std::size(pi.render_target_names); i++) 332 { 333 if (pi.render_target_names[i].empty()) 334 break; 335 336 max_rt = std::max(max_rt, i); 337 } 338 339 if (max_rt > GPUDevice::MAX_RENDER_TARGETS) 340 { 341 Error::SetString(error, fmt::format("Too many render targets ({}) in pass {}, only {} are supported.", max_rt, 342 pi.name, GPUDevice::MAX_RENDER_TARGETS)); 343 return false; 344 } 345 346 if (pi.samplers.size() > GPUDevice::MAX_TEXTURE_SAMPLERS) 347 { 348 Error::SetString(error, fmt::format("Too many samplers ({}) in pass {}, only {} are supported.", 349 pi.samplers.size(), pi.name, GPUDevice::MAX_TEXTURE_SAMPLERS)); 350 return false; 351 } 352 } 353 } 354 if (!has_passes) 355 { 356 Error::SetString(error, "No passes defined in file."); 357 return false; 358 } 359 } 360 361 // Might go invalid when creating pipelines. 362 m_valid = true; 363 return true; 364 } 365 366 bool PostProcessing::ReShadeFXShader::IsValid() const 367 { 368 return m_valid; 369 } 370 371 bool PostProcessing::ReShadeFXShader::WantsDepthBuffer() const 372 { 373 return m_wants_depth_buffer; 374 } 375 376 bool PostProcessing::ReShadeFXShader::CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod, 377 std::string code, Error* error) 378 { 379 reshadefx::preprocessor pp; 380 pp.set_include_callbacks(PreprocessorFileExistsCallback, PreprocessorReadFileCallback); 381 382 if (Path::IsAbsolute(m_filename)) 383 { 384 // we're a real file, so include that directory 385 pp.add_include_path(std::string(Path::GetDirectory(m_filename))); 386 } 387 else 388 { 389 // we're a resource, include the resource subdirectory, if there is one 390 if (std::string_view resdir = Path::GetDirectory(m_filename); !resdir.empty()) 391 pp.add_include_path(std::string(resdir)); 392 } 393 394 // root of the user directory, and resources 395 pp.add_include_path(Path::Combine(EmuFolders::Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Shaders")); 396 pp.add_include_path("shaders/reshade/Shaders"); 397 398 pp.add_macro_definition("__RESHADE__", "50901"); 399 pp.add_macro_definition("BUFFER_WIDTH", StringUtil::ToChars(buffer_width)); // TODO: can we make these uniforms? 400 pp.add_macro_definition("BUFFER_HEIGHT", StringUtil::ToChars(buffer_height)); 401 pp.add_macro_definition("BUFFER_RCP_WIDTH", StringUtil::ToChars(1.0f / static_cast<float>(buffer_width))); 402 pp.add_macro_definition("BUFFER_RCP_HEIGHT", StringUtil::ToChars(1.0f / static_cast<float>(buffer_height))); 403 pp.add_macro_definition("BUFFER_COLOR_BIT_DEPTH", "32"); 404 pp.add_macro_definition("RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN", "0"); 405 pp.add_macro_definition("RESHADE_DEPTH_INPUT_IS_LOGARITHMIC", "0"); 406 pp.add_macro_definition("RESHADE_DEPTH_LINEARIZATION_FAR_PLANE", "1000.0"); 407 pp.add_macro_definition("RESHADE_DEPTH_INPUT_IS_REVERSED", "0"); 408 409 switch (GetRenderAPI()) 410 { 411 case RenderAPI::D3D11: 412 case RenderAPI::D3D12: 413 pp.add_macro_definition("__RENDERER__", "0x0B000"); 414 break; 415 416 case RenderAPI::OpenGL: 417 case RenderAPI::OpenGLES: 418 case RenderAPI::Vulkan: 419 case RenderAPI::Metal: 420 pp.add_macro_definition("__RENDERER__", "0x14300"); 421 break; 422 423 default: 424 UnreachableCode(); 425 break; 426 } 427 428 if (!pp.append_string(std::move(code), m_filename)) 429 { 430 Error::SetString(error, fmt::format("Failed to preprocess:\n{}", pp.errors())); 431 return false; 432 } 433 434 std::unique_ptr<reshadefx::codegen> cg = CreateRFXCodegen(); 435 if (!cg) 436 return false; 437 438 reshadefx::parser parser; 439 if (!parser.parse(pp.output(), cg.get())) 440 { 441 Error::SetString(error, fmt::format("Failed to parse:\n{}", parser.errors())); 442 return false; 443 } 444 445 cg->write_result(*mod); 446 return true; 447 } 448 449 static bool HasAnnotationWithName(const reshadefx::uniform_info& uniform, const std::string_view annotation_name) 450 { 451 for (const reshadefx::annotation& an : uniform.annotations) 452 { 453 if (an.name == annotation_name) 454 return true; 455 } 456 457 return false; 458 } 459 460 static std::string_view GetStringAnnotationValue(const std::vector<reshadefx::annotation>& annotations, 461 const std::string_view annotation_name, 462 const std::string_view default_value) 463 { 464 for (const reshadefx::annotation& an : annotations) 465 { 466 if (an.name != annotation_name) 467 continue; 468 469 if (an.type.base != reshadefx::type::t_string) 470 continue; 471 472 return an.value.string_data; 473 } 474 475 return default_value; 476 } 477 478 static bool GetBooleanAnnotationValue(const std::vector<reshadefx::annotation>& annotations, 479 const std::string_view annotation_name, bool default_value) 480 { 481 for (const reshadefx::annotation& an : annotations) 482 { 483 if (an.name != annotation_name) 484 continue; 485 486 if (an.type.base != reshadefx::type::t_bool) 487 continue; 488 489 return (an.value.as_int[0] != 0); 490 } 491 492 return default_value; 493 } 494 495 static PostProcessing::ShaderOption::ValueVector 496 GetVectorAnnotationValue(const reshadefx::uniform_info& uniform, const std::string_view annotation_name, 497 const PostProcessing::ShaderOption::ValueVector& default_value) 498 { 499 PostProcessing::ShaderOption::ValueVector vv = default_value; 500 for (const reshadefx::annotation& an : uniform.annotations) 501 { 502 if (an.name != annotation_name) 503 continue; 504 505 const u32 components = std::min<u32>(an.type.components(), PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS); 506 507 if (an.type.base == uniform.type.base || (an.type.is_integral() && uniform.type.is_integral())) // int<->uint 508 { 509 if (components > 0) 510 std::memcpy(&vv[0].float_value, &an.value.as_float[0], sizeof(float) * components); 511 512 break; 513 } 514 else if (an.type.base == reshadefx::type::t_string) 515 { 516 // Convert from string. 517 if (uniform.type.base == reshadefx::type::t_float) 518 { 519 if (an.value.string_data == "BUFFER_WIDTH") 520 vv[0].float_value = DEFAULT_BUFFER_WIDTH; 521 else if (an.value.string_data == "BUFFER_HEIGHT") 522 vv[0].float_value = DEFAULT_BUFFER_HEIGHT; 523 else 524 vv[0].float_value = StringUtil::FromChars<float>(an.value.string_data).value_or(1000.0f); 525 } 526 else if (uniform.type.base == reshadefx::type::t_int) 527 { 528 if (an.value.string_data == "BUFFER_WIDTH") 529 vv[0].int_value = DEFAULT_BUFFER_WIDTH; 530 else if (an.value.string_data == "BUFFER_HEIGHT") 531 vv[0].int_value = DEFAULT_BUFFER_HEIGHT; 532 else 533 vv[0].int_value = StringUtil::FromChars<s32>(an.value.string_data).value_or(1000); 534 } 535 else 536 { 537 ERROR_LOG("Unhandled string value for '{}' (annotation type: {}, uniform type {})", uniform.name, 538 an.type.description(), uniform.type.description()); 539 } 540 541 break; 542 } 543 else if (an.type.base == reshadefx::type::t_int) 544 { 545 // Convert from int. 546 if (uniform.type.base == reshadefx::type::t_float) 547 { 548 for (u32 i = 0; i < components; i++) 549 vv[i].float_value = static_cast<float>(an.value.as_int[i]); 550 } 551 else if (uniform.type.base == reshadefx::type::t_bool) 552 { 553 for (u32 i = 0; i < components; i++) 554 vv[i].int_value = (an.value.as_int[i] != 0) ? 1 : 0; 555 } 556 } 557 else if (an.type.base == reshadefx::type::t_float) 558 { 559 // Convert from float. 560 if (uniform.type.base == reshadefx::type::t_int) 561 { 562 for (u32 i = 0; i < components; i++) 563 vv[i].int_value = static_cast<int>(an.value.as_float[i]); 564 } 565 else if (uniform.type.base == reshadefx::type::t_bool) 566 { 567 for (u32 i = 0; i < components; i++) 568 vv[i].int_value = (an.value.as_float[i] != 0.0f) ? 1 : 0; 569 } 570 } 571 572 break; 573 } 574 575 return vv; 576 } 577 578 bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod, Error* error) 579 { 580 for (const reshadefx::uniform_info& ui : mod.uniforms) 581 { 582 SourceOptionType so; 583 if (!GetSourceOption(ui, &so, error)) 584 return false; 585 if (so != SourceOptionType::None) 586 { 587 DEV_LOG("Add source based option {} at offset {} ({})", static_cast<u32>(so), ui.offset, ui.name); 588 589 SourceOption sopt; 590 sopt.source = so; 591 sopt.offset = ui.offset; 592 593 const ShaderOption::ValueVector min = 594 GetVectorAnnotationValue(ui, "min", ShaderOption::MakeFloatVector(0, 0, 0, 0)); 595 const ShaderOption::ValueVector max = 596 GetVectorAnnotationValue(ui, "max", ShaderOption::MakeFloatVector(1, 1, 1, 1)); 597 const ShaderOption::ValueVector smoothing = 598 GetVectorAnnotationValue(ui, "smoothing", ShaderOption::MakeFloatVector(0)); 599 const ShaderOption::ValueVector step = 600 GetVectorAnnotationValue(ui, "step", ShaderOption::MakeFloatVector(0, 1, 0, 0)); 601 602 sopt.min = min[0].float_value; 603 sopt.max = max[0].float_value; 604 sopt.smoothing = smoothing[0].float_value; 605 std::memcpy(&sopt.step[0], &step[0].float_value, sizeof(sopt.value)); 606 std::memcpy(&sopt.value[0], &ui.initializer_value.as_float[0], sizeof(sopt.value)); 607 608 m_source_options.push_back(std::move(sopt)); 609 continue; 610 } 611 612 ShaderOption opt; 613 opt.name = ui.name; 614 opt.category = GetStringAnnotationValue(ui.annotations, "ui_category", std::string_view()); 615 opt.tooltip = GetStringAnnotationValue(ui.annotations, "ui_tooltip", std::string_view()); 616 617 if (!GetBooleanAnnotationValue(ui.annotations, "hidden", false)) 618 { 619 opt.ui_name = GetStringAnnotationValue(ui.annotations, "ui_label", std::string_view()); 620 if (opt.ui_name.empty()) 621 opt.ui_name = ui.name; 622 } 623 624 const std::string_view ui_type = GetStringAnnotationValue(ui.annotations, "ui_type", std::string_view()); 625 626 switch (ui.type.base) 627 { 628 case reshadefx::type::t_float: 629 opt.type = ShaderOption::Type::Float; 630 break; 631 632 case reshadefx::type::t_int: 633 case reshadefx::type::t_uint: 634 opt.type = ShaderOption::Type::Int; 635 break; 636 637 case reshadefx::type::t_bool: 638 opt.type = ShaderOption::Type::Bool; 639 break; 640 641 default: 642 Error::SetString(error, fmt::format("Unhandled uniform type {} ({})", static_cast<u32>(ui.type.base), ui.name)); 643 return false; 644 } 645 646 opt.buffer_offset = ui.offset; 647 opt.buffer_size = ui.size; 648 opt.vector_size = ui.type.components(); 649 if (opt.vector_size == 0 || opt.vector_size > ShaderOption::MAX_VECTOR_COMPONENTS) 650 { 651 Error::SetString(error, 652 fmt::format("Unhandled vector size {} ({})", static_cast<u32>(ui.type.components()), ui.name)); 653 return false; 654 } 655 656 opt.min_value = GetVectorAnnotationValue(ui, "ui_min", opt.default_value); 657 opt.max_value = GetVectorAnnotationValue(ui, "ui_max", opt.default_value); 658 ShaderOption::ValueVector default_step = {}; 659 switch (opt.type) 660 { 661 case ShaderOption::Type::Float: 662 { 663 for (u32 i = 0; i < opt.vector_size; i++) 664 { 665 const float range = opt.max_value[i].float_value - opt.min_value[i].float_value; 666 default_step[i].float_value = range / 100.0f; 667 } 668 } 669 break; 670 671 case ShaderOption::Type::Int: 672 { 673 for (u32 i = 0; i < opt.vector_size; i++) 674 { 675 const s32 range = opt.max_value[i].int_value - opt.min_value[i].int_value; 676 default_step[i].int_value = std::max(range / 100, 1); 677 } 678 } 679 break; 680 681 default: 682 break; 683 } 684 opt.step_value = GetVectorAnnotationValue(ui, "ui_step", default_step); 685 686 // set a default maximum based on step if there isn't one 687 if (!HasAnnotationWithName(ui, "ui_max") && HasAnnotationWithName(ui, "ui_step")) 688 { 689 for (u32 i = 0; i < opt.vector_size; i++) 690 { 691 switch (opt.type) 692 { 693 case ShaderOption::Type::Float: 694 opt.max_value[i].float_value = opt.min_value[i].float_value + (opt.step_value[i].float_value * 100.0f); 695 break; 696 case ShaderOption::Type::Int: 697 opt.max_value[i].int_value = opt.min_value[i].int_value + (opt.step_value[i].int_value * 100); 698 break; 699 default: 700 break; 701 } 702 } 703 } 704 705 if (ui.has_initializer_value) 706 { 707 std::memcpy(&opt.default_value[0].float_value, &ui.initializer_value.as_float[0], 708 sizeof(float) * opt.vector_size); 709 } 710 else 711 { 712 opt.default_value = {}; 713 } 714 715 // Assume default if user doesn't set it. 716 opt.value = opt.default_value; 717 718 if (!ui_type.empty() && opt.vector_size > 1) 719 { 720 WARNING_LOG("Uniform '{}' has UI type of '{}' but is vector not scalar ({}), ignoring", opt.name, ui_type, 721 opt.vector_size); 722 } 723 else if (!ui_type.empty()) 724 { 725 if ((ui_type == "combo" || ui_type == "radio") && opt.type == ShaderOption::Type::Int) 726 { 727 const std::string_view ui_values = GetStringAnnotationValue(ui.annotations, "ui_items", std::string_view()); 728 729 size_t start_pos = 0; 730 while (start_pos < ui_values.size()) 731 { 732 size_t end_pos = start_pos; 733 while (end_pos < ui_values.size() && ui_values[end_pos] != '\0') 734 end_pos++; 735 736 const size_t len = end_pos - start_pos; 737 if (len > 0) 738 opt.choice_options.emplace_back(ui_values.substr(start_pos, len)); 739 start_pos = end_pos + 1; 740 } 741 742 // update max if it hasn't been specified 743 const size_t num_choices = opt.choice_options.size(); 744 if (num_choices > 0) 745 opt.max_value[0].int_value = std::max(static_cast<s32>(num_choices - 1), opt.max_value[0].int_value); 746 } 747 } 748 749 OptionList::iterator iter = std::find_if(m_options.begin(), m_options.end(), 750 [&opt](const ShaderOption& it) { return it.category == opt.category; }); 751 if (iter != m_options.end()) 752 { 753 // insert at the end of this category 754 while (iter != m_options.end() && iter->category == opt.category) 755 ++iter; 756 } 757 m_options.insert(iter, std::move(opt)); 758 } 759 760 m_uniforms_size = mod.total_uniform_size; 761 DEV_LOG("{}: {} options", m_filename, m_options.size()); 762 return true; 763 } 764 765 bool PostProcessing::ReShadeFXShader::GetSourceOption(const reshadefx::uniform_info& ui, SourceOptionType* si, 766 Error* error) 767 { 768 // TODO: Rewrite these to a lookup table instead, this if chain is terrible. 769 const std::string_view source = GetStringAnnotationValue(ui.annotations, "source", {}); 770 if (!source.empty()) 771 { 772 if (source == "timer") 773 { 774 if (ui.type.base != reshadefx::type::t_float || ui.type.components() > 1) 775 { 776 Error::SetString( 777 error, fmt::format("Unexpected type '{}' for timer source in uniform '{}'", ui.type.description(), ui.name)); 778 return false; 779 } 780 781 *si = SourceOptionType::Timer; 782 return true; 783 } 784 else if (source == "framecount") 785 { 786 if ((!ui.type.is_integral() && !ui.type.is_floating_point()) || ui.type.components() > 1) 787 { 788 Error::SetString( 789 error, fmt::format("Unexpected type '{}' for timer source in uniform '{}'", ui.type.description(), ui.name)); 790 return false; 791 } 792 793 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::FrameCountF : SourceOptionType::FrameCount; 794 return true; 795 } 796 else if (source == "frametime") 797 { 798 if (ui.type.base != reshadefx::type::t_float || ui.type.components() > 1) 799 { 800 Error::SetString( 801 error, fmt::format("Unexpected type '{}' for timer source in uniform '{}'", ui.type.description(), ui.name)); 802 return false; 803 } 804 805 *si = SourceOptionType::FrameTime; 806 return true; 807 } 808 else if (source == "pingpong") 809 { 810 if (!ui.type.is_floating_point() || ui.type.components() < 2) 811 { 812 Error::SetString(error, fmt::format("Unexpected type '{}' for pingpong source in uniform '{}'", 813 ui.type.description(), ui.name)); 814 return false; 815 } 816 817 *si = SourceOptionType::PingPong; 818 return true; 819 } 820 else if (source == "mousepoint") 821 { 822 if (!ui.type.is_floating_point() || ui.type.components() < 2) 823 { 824 Error::SetString(error, fmt::format("Unexpected type '{}' for mousepoint source in uniform '{}'", 825 ui.type.description(), ui.name)); 826 return false; 827 } 828 829 *si = SourceOptionType::MousePoint; 830 return true; 831 } 832 else if (source == "mousebutton") 833 { 834 WARNING_LOG("Ignoring mousebutton source in uniform '{}', not supported.", ui.name); 835 *si = SourceOptionType::Zero; 836 return true; 837 } 838 else if (source == "random") 839 { 840 if ((!ui.type.is_floating_point() && !ui.type.is_integral()) || ui.type.components() != 1) 841 { 842 Error::SetString(error, fmt::format("Unexpected type '{}' ({} components) for random source in uniform '{}'", 843 ui.type.description(), ui.type.components(), ui.name)); 844 return false; 845 } 846 847 // TODO: This is missing min/max handling. 848 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::RandomF : SourceOptionType::Random; 849 return true; 850 } 851 else if (source == "overlay_active") 852 { 853 *si = SourceOptionType::Zero; 854 return true; 855 } 856 else if (source == "has_depth") 857 { 858 *si = SourceOptionType::HasDepth; 859 return true; 860 } 861 else if (source == "bufferwidth") 862 { 863 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::BufferWidthF : SourceOptionType::BufferWidth; 864 return true; 865 } 866 else if (source == "bufferheight") 867 { 868 *si = 869 (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::BufferHeightF : SourceOptionType::BufferHeight; 870 return true; 871 } 872 else if (source == "internalwidth") 873 { 874 *si = 875 (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::InternalWidthF : SourceOptionType::InternalWidth; 876 return true; 877 } 878 else if (source == "internalheight") 879 { 880 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::InternalHeightF : 881 SourceOptionType::InternalHeight; 882 return true; 883 } 884 else if (source == "nativewidth") 885 { 886 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::NativeWidthF : SourceOptionType::NativeWidth; 887 return true; 888 } 889 else if (source == "nativeheight") 890 { 891 *si = 892 (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::NativeHeightF : SourceOptionType::NativeHeight; 893 return true; 894 } 895 else if (source == "upscale_multiplier") 896 { 897 if (!ui.type.is_floating_point() || ui.type.components() != 1) 898 { 899 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 900 source, ui.name)); 901 return false; 902 } 903 904 *si = SourceOptionType::UpscaleMultiplier; 905 return true; 906 } 907 else if (source == "viewportx") 908 { 909 if (!ui.type.is_floating_point() || ui.type.components() != 1) 910 { 911 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 912 source, ui.name)); 913 return false; 914 } 915 916 *si = SourceOptionType::ViewportX; 917 return true; 918 } 919 else if (source == "viewporty") 920 { 921 if (!ui.type.is_floating_point() || ui.type.components() != 1) 922 { 923 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 924 source, ui.name)); 925 return false; 926 } 927 928 *si = SourceOptionType::ViewportY; 929 return true; 930 } 931 else if (source == "viewportwidth") 932 { 933 if (!ui.type.is_floating_point() || ui.type.components() != 1) 934 { 935 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 936 source, ui.name)); 937 return false; 938 } 939 940 *si = SourceOptionType::ViewportWidth; 941 return true; 942 } 943 else if (source == "viewportheight") 944 { 945 if (!ui.type.is_floating_point() || ui.type.components() != 1) 946 { 947 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 948 source, ui.name)); 949 return false; 950 } 951 952 *si = SourceOptionType::ViewportHeight; 953 return true; 954 } 955 else if (source == "viewportoffset") 956 { 957 if (!ui.type.is_floating_point() || ui.type.components() != 2) 958 { 959 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 960 source, ui.name)); 961 return false; 962 } 963 964 *si = SourceOptionType::ViewportOffset; 965 return true; 966 } 967 else if (source == "viewportsize") 968 { 969 if (!ui.type.is_floating_point() || ui.type.components() != 2) 970 { 971 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 972 source, ui.name)); 973 return false; 974 } 975 976 *si = SourceOptionType::ViewportSize; 977 return true; 978 } 979 else if (source == "internal_pixel_size") 980 { 981 if (!ui.type.is_floating_point() || ui.type.components() != 2) 982 { 983 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 984 source, ui.name)); 985 return false; 986 } 987 988 *si = SourceOptionType::InternalPixelSize; 989 return true; 990 } 991 else if (source == "normalized_internal_pixel_size") 992 { 993 if (!ui.type.is_floating_point() || ui.type.components() != 2) 994 { 995 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 996 source, ui.name)); 997 return false; 998 } 999 1000 *si = SourceOptionType::InternalNormPixelSize; 1001 return true; 1002 } 1003 else if (source == "native_pixel_size") 1004 { 1005 if (!ui.type.is_floating_point() || ui.type.components() != 2) 1006 { 1007 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 1008 source, ui.name)); 1009 return false; 1010 } 1011 1012 *si = SourceOptionType::NativePixelSize; 1013 return true; 1014 } 1015 else if (source == "normalized_native_pixel_size") 1016 { 1017 if (!ui.type.is_floating_point() || ui.type.components() != 2) 1018 { 1019 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 1020 source, ui.name)); 1021 return false; 1022 } 1023 1024 *si = SourceOptionType::NativeNormPixelSize; 1025 return true; 1026 } 1027 else if (source == "buffer_to_viewport_ratio") 1028 { 1029 if (!ui.type.is_floating_point() || ui.type.components() != 2) 1030 { 1031 Error::SetString(error, fmt::format("Unexpected type '{}' for {} source in uniform '{}'", ui.type.description(), 1032 source, ui.name)); 1033 return false; 1034 } 1035 1036 *si = SourceOptionType::BufferToViewportRatio; 1037 return true; 1038 } 1039 else 1040 { 1041 Error::SetString(error, fmt::format("Unknown source '{}' in uniform '{}'", source, ui.name)); 1042 return false; 1043 } 1044 } 1045 1046 if (ui.has_initializer_value) 1047 { 1048 if (ui.initializer_value.string_data == "BUFFER_WIDTH") 1049 { 1050 *si = (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::BufferWidthF : SourceOptionType::BufferWidth; 1051 return true; 1052 } 1053 else if (ui.initializer_value.string_data == "BUFFER_HEIGHT") 1054 { 1055 *si = 1056 (ui.type.base == reshadefx::type::t_float) ? SourceOptionType::BufferHeightF : SourceOptionType::BufferHeight; 1057 return true; 1058 } 1059 } 1060 1061 *si = SourceOptionType::None; 1062 return true; 1063 } 1064 1065 bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer_format, reshadefx::module& mod, 1066 Error* error) 1067 { 1068 u32 total_passes = 0; 1069 for (const reshadefx::technique_info& tech : mod.techniques) 1070 total_passes += static_cast<u32>(tech.passes.size()); 1071 if (total_passes == 0) 1072 { 1073 Error::SetString(error, "No passes defined."); 1074 return false; 1075 } 1076 1077 m_passes.reserve(total_passes); 1078 1079 // Named render targets. 1080 for (const reshadefx::texture_info& ti : mod.textures) 1081 { 1082 Texture tex; 1083 1084 if (!ti.semantic.empty()) 1085 { 1086 DEV_LOG("Ignoring semantic {} texture {}", ti.semantic, ti.unique_name); 1087 continue; 1088 } 1089 if (ti.render_target) 1090 { 1091 tex.rt_scale = 1.0f; 1092 tex.format = MapTextureFormat(ti.format); 1093 DEV_LOG("Creating render target '{}' {}", ti.unique_name, GPUTexture::GetFormatName(tex.format)); 1094 } 1095 else 1096 { 1097 const std::string_view source = GetStringAnnotationValue(ti.annotations, "source", {}); 1098 if (source.empty()) 1099 { 1100 Error::SetString(error, fmt::format("Non-render target texture '{}' is missing source.", ti.unique_name)); 1101 return false; 1102 } 1103 1104 RGBA8Image image; 1105 if (const std::string image_path = 1106 Path::Combine(EmuFolders::Shaders, Path::Combine("reshade" FS_OSPATH_SEPARATOR_STR "Textures", source)); 1107 !image.LoadFromFile(image_path.c_str())) 1108 { 1109 // Might be a base file/resource instead. 1110 const std::string resource_name = Path::Combine("shaders/reshade/Textures", source); 1111 if (std::optional<DynamicHeapArray<u8>> resdata = Host::ReadResourceFile(resource_name.c_str(), true); 1112 !resdata.has_value() || !image.LoadFromBuffer(resource_name.c_str(), resdata->data(), resdata->size())) 1113 { 1114 Error::SetString(error, fmt::format("Failed to load image '{}' (from '{}')", source, image_path).c_str()); 1115 return false; 1116 } 1117 } 1118 1119 tex.rt_scale = 0.0f; 1120 tex.texture = g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, 1121 GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch()); 1122 if (!tex.texture) 1123 { 1124 Error::SetString( 1125 error, fmt::format("Failed to create {}x{} texture ({})", image.GetWidth(), image.GetHeight(), source)); 1126 return false; 1127 } 1128 1129 DEV_LOG("Loaded {}x{} texture ({})", image.GetWidth(), image.GetHeight(), source); 1130 } 1131 1132 tex.reshade_name = ti.unique_name; 1133 m_textures.push_back(std::move(tex)); 1134 } 1135 1136 for (reshadefx::technique_info& tech : mod.techniques) 1137 { 1138 for (reshadefx::pass_info& pi : tech.passes) 1139 { 1140 const bool is_final = (&tech == &mod.techniques.back() && &pi == &tech.passes.back()); 1141 1142 Pass pass; 1143 pass.num_vertices = pi.num_vertices; 1144 1145 if (is_final) 1146 { 1147 pass.render_targets.push_back(OUTPUT_COLOR_TEXTURE); 1148 } 1149 else if (!pi.render_target_names[0].empty()) 1150 { 1151 for (const std::string& rtname : pi.render_target_names) 1152 { 1153 if (rtname.empty()) 1154 break; 1155 1156 TextureID rt = static_cast<TextureID>(m_textures.size()); 1157 for (u32 i = 0; i < static_cast<u32>(m_textures.size()); i++) 1158 { 1159 if (m_textures[i].reshade_name == rtname) 1160 { 1161 rt = static_cast<TextureID>(i); 1162 break; 1163 } 1164 } 1165 if (rt == static_cast<TextureID>(m_textures.size())) 1166 { 1167 Error::SetString(error, 1168 fmt::format("Unknown texture '{}' used as render target in pass '{}'", rtname, pi.name)); 1169 return false; 1170 } 1171 1172 pass.render_targets.push_back(rt); 1173 } 1174 } 1175 else 1176 { 1177 Texture new_rt; 1178 new_rt.rt_scale = 1.0f; 1179 new_rt.format = backbuffer_format; 1180 pass.render_targets.push_back(static_cast<TextureID>(m_textures.size())); 1181 m_textures.push_back(std::move(new_rt)); 1182 } 1183 1184 u32 texture_slot = 0; 1185 for (const reshadefx::sampler_info& si : pi.samplers) 1186 { 1187 Sampler sampler; 1188 sampler.slot = texture_slot++; 1189 sampler.reshade_name = si.unique_name; 1190 1191 sampler.texture_id = static_cast<TextureID>(m_textures.size()); 1192 for (const reshadefx::texture_info& ti : mod.textures) 1193 { 1194 if (ti.unique_name == si.texture_name) 1195 { 1196 // found the texture, now look for our side of it 1197 if (ti.semantic == "COLOR") 1198 { 1199 sampler.texture_id = INPUT_COLOR_TEXTURE; 1200 break; 1201 } 1202 else if (ti.semantic == "DEPTH") 1203 { 1204 sampler.texture_id = INPUT_DEPTH_TEXTURE; 1205 m_wants_depth_buffer = true; 1206 break; 1207 } 1208 else if (!ti.semantic.empty()) 1209 { 1210 Error::SetString(error, fmt::format("Unknown semantic {} in texture {}", ti.semantic, ti.name)); 1211 return false; 1212 } 1213 1214 // must be a render target, or another texture 1215 for (u32 i = 0; i < static_cast<u32>(m_textures.size()); i++) 1216 { 1217 if (m_textures[i].reshade_name == si.texture_name) 1218 { 1219 // hook it up 1220 sampler.texture_id = static_cast<TextureID>(i); 1221 break; 1222 } 1223 } 1224 1225 break; 1226 } 1227 } 1228 if (sampler.texture_id == static_cast<TextureID>(m_textures.size())) 1229 { 1230 Error::SetString( 1231 error, fmt::format("Unknown texture {} (sampler {}) in pass {}", si.texture_name, si.name, pi.name)); 1232 return false; 1233 } 1234 1235 DEV_LOG("Pass {} Texture {} => {}", pi.name, si.texture_name, sampler.texture_id); 1236 1237 sampler.sampler = GetSampler(MapSampler(si)); 1238 if (!sampler.sampler) 1239 { 1240 Error::SetString(error, "Failed to create sampler."); 1241 return false; 1242 } 1243 1244 pass.samplers.push_back(std::move(sampler)); 1245 } 1246 1247 #ifdef _DEBUG 1248 pass.name = std::move(pi.name); 1249 #endif 1250 m_passes.push_back(std::move(pass)); 1251 } 1252 } 1253 1254 return true; 1255 } 1256 1257 const char* PostProcessing::ReShadeFXShader::GetTextureNameForID(TextureID id) const 1258 { 1259 if (id == INPUT_COLOR_TEXTURE) 1260 return "Input Color Texture / Backbuffer"; 1261 else if (id == INPUT_DEPTH_TEXTURE) 1262 return "Input Depth Texture"; 1263 else if (id == OUTPUT_COLOR_TEXTURE) 1264 return "Output Color Texture"; 1265 else if (id < 0 || static_cast<size_t>(id) >= m_textures.size()) 1266 return "UNKNOWN"; 1267 else 1268 return m_textures[static_cast<size_t>(id)].reshade_name.c_str(); 1269 } 1270 1271 GPUTexture* PostProcessing::ReShadeFXShader::GetTextureByID(TextureID id, GPUTexture* input_color, 1272 GPUTexture* input_depth, GPUTexture* final_target) const 1273 { 1274 if (id < 0) 1275 { 1276 if (id == INPUT_COLOR_TEXTURE) 1277 { 1278 return input_color; 1279 } 1280 else if (id == INPUT_DEPTH_TEXTURE) 1281 { 1282 return input_depth ? input_depth : GetDummyTexture(); 1283 } 1284 else if (id == OUTPUT_COLOR_TEXTURE) 1285 { 1286 return final_target; 1287 } 1288 else 1289 { 1290 Panic("Unexpected reserved texture ID"); 1291 return nullptr; 1292 } 1293 } 1294 1295 if (static_cast<size_t>(id) >= m_textures.size()) 1296 Panic("Unexpected texture ID"); 1297 1298 return m_textures[static_cast<size_t>(id)].texture.get(); 1299 } 1300 1301 bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height, 1302 ProgressCallback* progress) 1303 { 1304 const RenderAPI api = g_gpu_device->GetRenderAPI(); 1305 const bool needs_main_defn = (api != RenderAPI::D3D11 && api != RenderAPI::D3D12); 1306 1307 m_valid = false; 1308 m_textures.clear(); 1309 m_passes.clear(); 1310 m_wants_depth_buffer = false; 1311 1312 std::string fxcode; 1313 if (!PreprocessorReadFileCallback(m_filename, fxcode)) 1314 { 1315 ERROR_LOG("Failed to re-read shader for pipeline: '{}'", m_filename); 1316 return false; 1317 } 1318 1319 // Reshade's preprocessor expects this. 1320 if (fxcode.empty() || fxcode.back() != '\n') 1321 fxcode.push_back('\n'); 1322 1323 Error error; 1324 reshadefx::module mod; 1325 if (!CreateModule(width, height, &mod, std::move(fxcode), &error)) 1326 { 1327 ERROR_LOG("Failed to create module for '{}': {}", m_name, error.GetDescription()); 1328 return false; 1329 } 1330 1331 DebugAssert(!mod.techniques.empty()); 1332 1333 if (!CreatePasses(format, mod, &error)) 1334 { 1335 ERROR_LOG("Failed to create passes for '{}': {}", m_name, error.GetDescription()); 1336 return false; 1337 } 1338 1339 const std::string_view code(mod.code.data(), mod.code.size()); 1340 1341 auto get_shader = [api, needs_main_defn, &code](const std::string& name, const std::span<Sampler> samplers, 1342 GPUShaderStage stage) { 1343 std::string real_code; 1344 if (needs_main_defn) 1345 { 1346 // dFdx/dFdy are not defined in the vertex shader. 1347 const char* defns = 1348 (stage == GPUShaderStage::Vertex) ? "#define dFdx(x) x\n#define dFdy(x) x\n#define discard\n" : ""; 1349 const char* precision = (api == RenderAPI::OpenGLES) ? 1350 "precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n" : 1351 ""; 1352 1353 TinyString version_string = "#version 460 core\n"; 1354 #ifdef ENABLE_OPENGL 1355 if (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES) 1356 version_string = ShaderGen::GetGLSLVersionString(api, ShaderGen::GetGLSLVersion(api)); 1357 #endif 1358 real_code = fmt::format("{}\n#define ENTRY_POINT_{}\n{}\n{}\n{}", version_string, name, defns, precision, code); 1359 1360 for (const Sampler& sampler : samplers) 1361 { 1362 std::string decl = fmt::format("binding = /*SAMPLER:{}*/0", sampler.reshade_name); 1363 std::string replacement = fmt::format("binding = {}", sampler.slot); 1364 StringUtil::ReplaceAll(&real_code, decl, replacement); 1365 } 1366 } 1367 else 1368 { 1369 real_code = std::string(code); 1370 1371 for (const Sampler& sampler : samplers) 1372 { 1373 std::string decl = fmt::format("__{}_t : register( t0);", sampler.reshade_name); 1374 std::string replacement = 1375 fmt::format("__{}_t : register({}t{});", sampler.reshade_name, (sampler.slot < 10) ? " " : "", sampler.slot); 1376 StringUtil::ReplaceAll(&real_code, decl, replacement); 1377 1378 decl = fmt::format("__{}_s : register( s0);", sampler.reshade_name); 1379 replacement = 1380 fmt::format("__{}_s : register({}s{});", sampler.reshade_name, (sampler.slot < 10) ? " " : "", sampler.slot); 1381 StringUtil::ReplaceAll(&real_code, decl, replacement); 1382 } 1383 } 1384 1385 // FileSystem::WriteStringToFile("D:\\foo.txt", real_code); 1386 1387 Error error; 1388 std::unique_ptr<GPUShader> sshader = g_gpu_device->CreateShader( 1389 stage, ShaderGen::GetShaderLanguageForAPI(api), real_code, &error, needs_main_defn ? "main" : name.c_str()); 1390 if (!sshader) 1391 ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription()); 1392 1393 return sshader; 1394 }; 1395 1396 GPUPipeline::GraphicsConfig plconfig; 1397 plconfig.layout = GPUPipeline::Layout::MultiTextureAndUBO; 1398 plconfig.primitive = GPUPipeline::Primitive::Triangles; 1399 plconfig.depth_format = GPUTexture::Format::Unknown; 1400 plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState(); 1401 plconfig.depth = GPUPipeline::DepthState::GetNoTestsState(); 1402 plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState(); 1403 plconfig.samples = 1; 1404 plconfig.per_sample_shading = false; 1405 plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags; 1406 1407 progress->PushState(); 1408 1409 size_t total_passes = 0; 1410 for (const reshadefx::technique_info& tech : mod.techniques) 1411 total_passes += tech.passes.size(); 1412 progress->SetProgressRange(static_cast<u32>(total_passes)); 1413 progress->SetProgressValue(0); 1414 1415 u32 passnum = 0; 1416 for (const reshadefx::technique_info& tech : mod.techniques) 1417 { 1418 for (const reshadefx::pass_info& info : tech.passes) 1419 { 1420 DebugAssert(passnum < m_passes.size()); 1421 Pass& pass = m_passes[passnum++]; 1422 1423 auto vs = get_shader(info.vs_entry_point, pass.samplers, GPUShaderStage::Vertex); 1424 auto fs = get_shader(info.ps_entry_point, pass.samplers, GPUShaderStage::Fragment); 1425 if (!vs || !fs) 1426 { 1427 progress->PopState(); 1428 return false; 1429 } 1430 1431 for (size_t i = 0; i < pass.render_targets.size(); i++) 1432 { 1433 plconfig.color_formats[i] = 1434 ((pass.render_targets[i] >= 0) ? m_textures[pass.render_targets[i]].format : format); 1435 } 1436 for (size_t i = pass.render_targets.size(); i < GPUDevice::MAX_RENDER_TARGETS; i++) 1437 plconfig.color_formats[i] = GPUTexture::Format::Unknown; 1438 plconfig.depth_format = GPUTexture::Format::Unknown; 1439 1440 plconfig.blend = MapBlendState(info); 1441 plconfig.primitive = MapPrimitive(info.topology); 1442 plconfig.vertex_shader = vs.get(); 1443 plconfig.fragment_shader = fs.get(); 1444 plconfig.geometry_shader = nullptr; 1445 if (!plconfig.vertex_shader || !plconfig.fragment_shader) 1446 { 1447 progress->PopState(); 1448 return false; 1449 } 1450 1451 pass.pipeline = g_gpu_device->CreatePipeline(plconfig); 1452 if (!pass.pipeline) 1453 { 1454 ERROR_LOG("Failed to create pipeline for pass '{}'", info.name); 1455 progress->PopState(); 1456 return false; 1457 } 1458 1459 progress->SetProgressValue(passnum); 1460 } 1461 } 1462 1463 progress->PopState(); 1464 1465 m_valid = true; 1466 return true; 1467 } 1468 1469 bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height) 1470 { 1471 m_valid = false; 1472 1473 for (Texture& tex : m_textures) 1474 { 1475 if (tex.rt_scale == 0.0f) 1476 continue; 1477 1478 g_gpu_device->RecycleTexture(std::move(tex.texture)); 1479 1480 const u32 t_width = std::max(static_cast<u32>(static_cast<float>(width) * tex.rt_scale), 1u); 1481 const u32 t_height = std::max(static_cast<u32>(static_cast<float>(height) * tex.rt_scale), 1u); 1482 tex.texture = g_gpu_device->FetchTexture(t_width, t_height, 1, 1, 1, GPUTexture::Type::RenderTarget, tex.format); 1483 if (!tex.texture) 1484 { 1485 ERROR_LOG("Failed to create {}x{} texture", t_width, t_height); 1486 return {}; 1487 } 1488 } 1489 1490 m_valid = true; 1491 return true; 1492 } 1493 1494 bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, 1495 GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, 1496 s32 native_height, u32 target_width, u32 target_height) 1497 { 1498 GL_PUSH_FMT("PostProcessingShaderFX {}", m_name); 1499 1500 m_frame_count++; 1501 1502 // Reshade always draws at full size. 1503 g_gpu_device->SetViewportAndScissor(GSVector4i(0, 0, target_width, target_height)); 1504 1505 if (m_uniforms_size > 0) 1506 { 1507 GL_SCOPE_FMT("Uniforms: {} bytes", m_uniforms_size); 1508 1509 u8* uniforms = static_cast<u8*>(g_gpu_device->MapUniformBuffer(m_uniforms_size)); 1510 for (const ShaderOption& opt : m_options) 1511 { 1512 DebugAssert((opt.buffer_offset + opt.buffer_size) <= m_uniforms_size); 1513 std::memcpy(uniforms + opt.buffer_offset, &opt.value[0].float_value, opt.buffer_size); 1514 } 1515 for (const SourceOption& so : m_source_options) 1516 { 1517 u8* dst = uniforms + so.offset; 1518 switch (so.source) 1519 { 1520 case SourceOptionType::Zero: 1521 { 1522 const u32 value = 0; 1523 std::memcpy(dst, &value, sizeof(value)); 1524 } 1525 break; 1526 1527 case SourceOptionType::HasDepth: 1528 { 1529 const u32 value = BoolToUInt32(input_depth != nullptr); 1530 std::memcpy(dst, &value, sizeof(value)); 1531 } 1532 break; 1533 1534 case SourceOptionType::Timer: 1535 { 1536 const float value = static_cast<float>(PostProcessing::GetTimer().GetTimeMilliseconds()); 1537 std::memcpy(dst, &value, sizeof(value)); 1538 } 1539 break; 1540 1541 case SourceOptionType::FrameTime: 1542 { 1543 const float value = static_cast<float>(m_frame_timer.GetTimeMilliseconds()); 1544 std::memcpy(dst, &value, sizeof(value)); 1545 } 1546 break; 1547 1548 case SourceOptionType::FrameCount: 1549 { 1550 std::memcpy(dst, &m_frame_count, sizeof(m_frame_count)); 1551 } 1552 break; 1553 1554 case SourceOptionType::FrameCountF: 1555 { 1556 const float value = static_cast<float>(m_frame_count); 1557 std::memcpy(dst, &value, sizeof(value)); 1558 } 1559 break; 1560 1561 case SourceOptionType::PingPong: 1562 { 1563 float increment = so.step[1] == 0 ? 1564 so.step[0] : 1565 (so.step[0] + std::fmod(static_cast<float>(std::rand()), so.step[1] - so.step[0] + 1)); 1566 1567 std::array<float, 2> value = {so.value[0].float_value, so.value[1].float_value}; 1568 if (value[1] >= 0) 1569 { 1570 increment = std::max(increment - std::max(0.0f, so.smoothing - (so.max - value[0])), 0.05f); 1571 increment *= static_cast<float>(m_frame_timer.GetTimeMilliseconds() * 1e-9); 1572 1573 if ((value[0] += increment) >= so.max) 1574 { 1575 value[0] = so.max; 1576 value[1] = -1; 1577 } 1578 } 1579 else 1580 { 1581 increment = std::max(increment - std::max(0.0f, so.smoothing - (value[0] - so.min)), 0.05f); 1582 increment *= static_cast<float>(m_frame_timer.GetTimeMilliseconds() * 1e-9); 1583 1584 if ((value[0] -= increment) <= so.min) 1585 { 1586 value[0] = so.min; 1587 value[1] = +1; 1588 } 1589 } 1590 1591 std::memcpy(dst, value.data(), sizeof(value)); 1592 } 1593 break; 1594 1595 case SourceOptionType::MousePoint: 1596 { 1597 const std::pair<float, float> mpos = InputManager::GetPointerAbsolutePosition(0); 1598 std::memcpy(dst, &mpos.first, sizeof(float)); 1599 std::memcpy(dst + sizeof(float), &mpos.second, sizeof(float)); 1600 } 1601 break; 1602 1603 case SourceOptionType::Random: 1604 { 1605 const s32 rv = m_random() % 32767; // reshade uses rand(), which on some platforms has a 0x7fff maximum. 1606 std::memcpy(dst, &rv, sizeof(rv)); 1607 } 1608 break; 1609 case SourceOptionType::RandomF: 1610 { 1611 const float rv = (m_random() - m_random.min()) / static_cast<float>(m_random.max() - m_random.min()); 1612 std::memcpy(dst, &rv, sizeof(rv)); 1613 } 1614 break; 1615 1616 case SourceOptionType::BufferWidth: 1617 case SourceOptionType::BufferHeight: 1618 { 1619 const s32 value = (so.source == SourceOptionType::BufferWidth) ? static_cast<s32>(target_width) : 1620 static_cast<s32>(target_height); 1621 std::memcpy(dst, &value, sizeof(value)); 1622 } 1623 break; 1624 1625 case SourceOptionType::BufferWidthF: 1626 case SourceOptionType::BufferHeightF: 1627 { 1628 const float value = (so.source == SourceOptionType::BufferWidthF) ? static_cast<float>(target_width) : 1629 static_cast<float>(target_height); 1630 std::memcpy(dst, &value, sizeof(value)); 1631 } 1632 break; 1633 1634 case SourceOptionType::InternalWidth: 1635 case SourceOptionType::InternalHeight: 1636 { 1637 const s32 value = (so.source == SourceOptionType::InternalWidth) ? static_cast<s32>(orig_width) : 1638 static_cast<s32>(orig_height); 1639 std::memcpy(dst, &value, sizeof(value)); 1640 } 1641 break; 1642 1643 case SourceOptionType::InternalWidthF: 1644 case SourceOptionType::InternalHeightF: 1645 { 1646 const float value = (so.source == SourceOptionType::InternalWidthF) ? static_cast<float>(orig_width) : 1647 static_cast<float>(orig_height); 1648 std::memcpy(dst, &value, sizeof(value)); 1649 } 1650 break; 1651 1652 case SourceOptionType::NativeWidth: 1653 case SourceOptionType::NativeHeight: 1654 { 1655 const s32 value = (so.source == SourceOptionType::NativeWidth) ? static_cast<s32>(native_width) : 1656 static_cast<s32>(native_height); 1657 std::memcpy(dst, &value, sizeof(value)); 1658 } 1659 break; 1660 1661 case SourceOptionType::NativeWidthF: 1662 case SourceOptionType::NativeHeightF: 1663 { 1664 const float value = (so.source == SourceOptionType::NativeWidthF) ? static_cast<float>(native_width) : 1665 static_cast<float>(native_height); 1666 std::memcpy(dst, &value, sizeof(value)); 1667 } 1668 break; 1669 1670 case SourceOptionType::UpscaleMultiplier: 1671 { 1672 const float value = static_cast<float>(orig_width) / static_cast<float>(native_width); 1673 std::memcpy(dst, &value, sizeof(value)); 1674 } 1675 break; 1676 1677 case SourceOptionType::ViewportX: 1678 { 1679 const float value = static_cast<float>(final_rect.left); 1680 std::memcpy(dst, &value, sizeof(value)); 1681 } 1682 break; 1683 1684 case SourceOptionType::ViewportY: 1685 { 1686 const float value = static_cast<float>(final_rect.top); 1687 std::memcpy(dst, &value, sizeof(value)); 1688 } 1689 break; 1690 1691 case SourceOptionType::ViewportWidth: 1692 { 1693 const float value = static_cast<float>(final_rect.width()); 1694 std::memcpy(dst, &value, sizeof(value)); 1695 } 1696 break; 1697 1698 case SourceOptionType::ViewportHeight: 1699 { 1700 const float value = static_cast<float>(final_rect.height()); 1701 std::memcpy(dst, &value, sizeof(value)); 1702 } 1703 break; 1704 1705 case SourceOptionType::ViewportOffset: 1706 { 1707 GSVector4::storel(dst, GSVector4(final_rect)); 1708 } 1709 break; 1710 1711 case SourceOptionType::ViewportSize: 1712 { 1713 const float value[2] = {static_cast<float>(final_rect.width()), static_cast<float>(final_rect.height())}; 1714 std::memcpy(dst, &value, sizeof(value)); 1715 } 1716 break; 1717 1718 case SourceOptionType::InternalPixelSize: 1719 { 1720 const float value[2] = {static_cast<float>(final_rect.width()) / static_cast<float>(orig_width), 1721 static_cast<float>(final_rect.height()) / static_cast<float>(orig_height)}; 1722 std::memcpy(dst, value, sizeof(value)); 1723 } 1724 break; 1725 1726 case SourceOptionType::InternalNormPixelSize: 1727 { 1728 const float value[2] = {(static_cast<float>(final_rect.width()) / static_cast<float>(orig_width)) / 1729 static_cast<float>(target_width), 1730 (static_cast<float>(final_rect.height()) / static_cast<float>(orig_height)) / 1731 static_cast<float>(target_height)}; 1732 std::memcpy(dst, value, sizeof(value)); 1733 } 1734 break; 1735 1736 case SourceOptionType::NativePixelSize: 1737 { 1738 const float value[2] = {static_cast<float>(final_rect.width()) / static_cast<float>(native_width), 1739 static_cast<float>(final_rect.height()) / static_cast<float>(native_height)}; 1740 std::memcpy(dst, value, sizeof(value)); 1741 } 1742 break; 1743 1744 case SourceOptionType::NativeNormPixelSize: 1745 { 1746 const float value[2] = {(static_cast<float>(final_rect.width()) / static_cast<float>(native_width)) / 1747 static_cast<float>(target_width), 1748 (static_cast<float>(final_rect.height()) / static_cast<float>(native_height)) / 1749 static_cast<float>(target_height)}; 1750 std::memcpy(dst, value, sizeof(value)); 1751 } 1752 break; 1753 1754 case SourceOptionType::BufferToViewportRatio: 1755 { 1756 const float value[2] = {static_cast<float>(target_width) / static_cast<float>(final_rect.width()), 1757 static_cast<float>(target_height) / static_cast<float>(final_rect.height())}; 1758 std::memcpy(dst, value, sizeof(value)); 1759 } 1760 break; 1761 1762 default: 1763 UnreachableCode(); 1764 break; 1765 } 1766 } 1767 g_gpu_device->UnmapUniformBuffer(m_uniforms_size); 1768 } 1769 1770 for (const Pass& pass : m_passes) 1771 { 1772 GL_SCOPE_FMT("Draw pass {}", pass.name.c_str()); 1773 DebugAssert(!pass.render_targets.empty()); 1774 1775 // Sucks doing this twice, but we need to set the RT first (for DX11), and transition layouts (for VK). 1776 for (const Sampler& sampler : pass.samplers) 1777 { 1778 GPUTexture* const tex = GetTextureByID(sampler.texture_id, input_color, input_depth, final_target); 1779 if (tex) 1780 tex->MakeReadyForSampling(); 1781 } 1782 1783 if (pass.render_targets.size() == 1 && pass.render_targets[0] == OUTPUT_COLOR_TEXTURE && !final_target) 1784 { 1785 // Special case: drawing to final buffer. 1786 if (!g_gpu_device->BeginPresent(false)) 1787 { 1788 GL_POP(); 1789 return false; 1790 } 1791 } 1792 else 1793 { 1794 std::array<GPUTexture*, GPUDevice::MAX_RENDER_TARGETS> render_targets; 1795 for (size_t i = 0; i < pass.render_targets.size(); i++) 1796 { 1797 GL_INS_FMT("Render Target {}: ID {} [{}]", i, pass.render_targets[i], 1798 GetTextureNameForID(pass.render_targets[i])); 1799 render_targets[i] = GetTextureByID(pass.render_targets[i], input_color, input_depth, final_target); 1800 DebugAssert(render_targets[i]); 1801 } 1802 1803 g_gpu_device->SetRenderTargets(render_targets.data(), static_cast<u32>(pass.render_targets.size()), nullptr); 1804 } 1805 1806 g_gpu_device->SetPipeline(pass.pipeline.get()); 1807 1808 // Set all inputs first, before the render pass starts. 1809 std::bitset<GPUDevice::MAX_TEXTURE_SAMPLERS> bound_textures = {}; 1810 for (const Sampler& sampler : pass.samplers) 1811 { 1812 // Can't bind the RT as a sampler. 1813 if (std::any_of(pass.render_targets.begin(), pass.render_targets.end(), 1814 [&sampler](TextureID rt) { return rt == sampler.texture_id; })) 1815 { 1816 GL_INS_FMT("Not binding RT sampler {}: ID {} [{}]", sampler.slot, sampler.texture_id, 1817 GetTextureNameForID(sampler.texture_id)); 1818 continue; 1819 } 1820 1821 GL_INS_FMT("Texture Sampler {}: ID {} [{}]", sampler.slot, sampler.texture_id, 1822 GetTextureNameForID(sampler.texture_id)); 1823 g_gpu_device->SetTextureSampler( 1824 sampler.slot, GetTextureByID(sampler.texture_id, input_color, input_depth, final_target), sampler.sampler); 1825 bound_textures[sampler.slot] = true; 1826 } 1827 1828 // Ensure RT wasn't left bound as a previous output, it breaks VK/DX12. 1829 // TODO: Maybe move this into the backend? Not sure... 1830 for (u32 i = 0; i < GPUDevice::MAX_TEXTURE_SAMPLERS; i++) 1831 { 1832 if (!bound_textures[i]) 1833 g_gpu_device->SetTextureSampler(i, nullptr, nullptr); 1834 } 1835 1836 g_gpu_device->Draw(pass.num_vertices, 0); 1837 } 1838 1839 // Don't leave any textures bound. 1840 for (u32 i = 0; i < GPUDevice::MAX_TEXTURE_SAMPLERS; i++) 1841 g_gpu_device->SetTextureSampler(i, nullptr, nullptr); 1842 1843 GL_POP(); 1844 m_frame_timer.Reset(); 1845 return true; 1846 }