addon.cpp things - clshortfuse/renodx GitHub Wiki

Swap chain proxy (= Final shader)

Example: addon.cpp / final pixel / final vertex

Required in addon.cpp:

      renodx::mods::swapchain::use_resource_cloning = true;
      //renodx::mods::swapchain::swapchain_proxy_compatibility_mode = true;
      renodx::mods::swapchain::swap_chain_proxy_vertex_shader = __swap_chain_proxy_vertex_shader;
      renodx::mods::swapchain::swap_chain_proxy_pixel_shader = __swap_chain_proxy_pixel_shader;

  renodx::mods::swapchain::Use(fdw_reason, &shader_injection);

Note: compatibility_mode allows REST to access resources drawn on swapchain. Otherwise, cloning is preferred for efficiency (& possibly quality with 10bit swapchain).

swap_chain_proxy_vertex_shader.vs_X_X.hlsl content:

void main(uint id : SV_VERTEXID, out float4 pos : SV_POSITION,
          out float2 uv : TEXCOORD0) {
  uv.x = (id == 1) ? 2.0 : 0.0;
  uv.y = (id == 2) ? 2.0 : 0.0;
  pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}

swap_chain_proxy_pixel_shader.ps_X_X.hlsl content:

Texture2D t0 : register(t0);
SamplerState s0 : register(s0);
float4 main(float4 vpos : SV_POSITION, float2 uv : TEXCOORD0) : SV_TARGET {
  float4 color = t0.Sample(s0, uv);
  //color.rgb = ...;
  color.a = 1.f;
  return color;
}

Shader_hash based upgrade

Here is what's required to upgrade targets based on shader hashes. Everything happens in addon.cpp file.

The following functions can be placed in namespace. Ignore wrapping blocs, they're just indicators:

//----what usually preceeds in addon.cpp----//
#include "../../utils/settings.hpp"
#include "./shared.h"

namespace {

ShaderInjectData shader_injection;
//----what usually preceeds in addon.cpp----//

// DX11 Only
#define UpgradeRTVReplaceShader(value)       \
  {                                          \
      value,                                 \
      {                                      \
          .crc32 = value,                    \
          .code = __##value,                 \
          .on_draw = [](auto* cmd_list) {                                                             \
            auto rtvs = renodx::utils::swapchain::GetRenderTargets(cmd_list);                         \
            bool changed = false;                                                                     \
            for (auto rtv : rtvs) {                                                                   \
              changed = renodx::mods::swapchain::ActivateCloneHotSwap(cmd_list->get_device(), rtv);   \
            }                                                                                         \
            if (changed) {                                                                            \
              renodx::mods::swapchain::FlushDescriptors(cmd_list);                                    \
              renodx::mods::swapchain::RewriteRenderTargets(cmd_list, rtvs.size(), rtvs.data(), {0}); \
            }                                                                                         \
            return true; }, \
      },                                     \
  }

#define UpgradeRTVShader(value)              \
  {                                          \
      value,                                 \
      {                                      \
          .crc32 = value,                    \
          .on_draw = [](auto* cmd_list) {                                                           \
            auto rtvs = renodx::utils::swapchain::GetRenderTargets(cmd_list);                       \
            bool changed = false;                                                                   \
            for (auto rtv : rtvs) {                                                                 \
              changed = renodx::mods::swapchain::ActivateCloneHotSwap(cmd_list->get_device(), rtv); \
            }                                                                                       \
            if (changed) {                                                                          \
              renodx::mods::swapchain::FlushDescriptors(cmd_list);                                  \
              renodx::mods::swapchain::RewriteRenderTargets(cmd_list, rtvs.size(), rtvs.data(), {0});      \
            }                                                                                       \
            return true; }, \
      },                                     \
  }

renodx::mods::shader::CustomShaders custom_shaders = {
    CustomShaderEntry(0xFFFFFFFF),
    CustomShaderEntry(0xFFFFFFF),

    UpgradeRTVReplaceShader(0xFFFFFFFF),      // tonemapper > upgrade RTV & replace shader

    UpgradeRTVShader(0xFFFFFFFF),      // clamping resource 1 > upgrade RTV only
    UpgradeRTVShader(0xFFFFFFFF),      // clamping resource 2
  };

//----what usually follows in addon.cpp----//
renodx::utils::settings::Settings settings = {
    new renodx::utils::settings::Setting{
//----what usually follows in addon.cpp----//

Then you need resource cloning enabled and hotswap on corresponding resource upgrade. Again ignore wrapping blocs.

//----what usually preceeds in addon.cpp----//
BOOL APIENTRY DllMain(HMODULE h_module, DWORD fdw_reason, LPVOID lpv_reserved) {
  switch (fdw_reason) {
    case DLL_PROCESS_ATTACH:
      if (!reshade::register_addon(h_module)) return FALSE;
//----what usually preceeds in addon.cpp----//
      renodx::mods::swapchain::use_resource_cloning = true;

      renodx::mods::swapchain::swap_chain_upgrade_targets.push_back({
          .old_format = reshade::api::format::r8g8b8a8_unorm,  // adjust to your needs
          .new_format = reshade::api::format::r16g16b16a16_float,
          //.ignore_size = true, // usual stuff can be used, but only both below are required
          .use_resource_view_cloning = true,
          .use_resource_view_hot_swap = true,
      });
//----what usually follows in addon.cpp----//
      break;
    case DLL_PROCESS_DETACH:
      reshade::unregister_addon(h_module);
      break;
  }
//----what usually follows in addon.cpp----//

Perceptual film grain

In addon.cpp:

// at the top
#include "../../utils/random.hpp"

// slider
        renodx::templates::settings::CreateSetting({
            .key = "FxGrainStrength",
            .binding = &shader_injection.custom_grain_strength,
            .default_value = 0.f,
            .label = "Grain Strength",
            .section = "Effects",
            .parse = [](float value) { return value * 0.01f; },
        }),

// in DllMain:

renodx::utils::random::binds.push_back(&shader_injection.custom_random);

renodx::utils::random::Use(fdw_reason);

In shared.h:

#define CUSTOM_GRAIN_STRENGTH             shader_injection.custom_grain_strength
#define CUSTOM_RANDOM                          shader_injection.custom_random

// Must be 32bit aligned
// Should be 4x32
struct ShaderInjectData {
  float custom_grain_strength;
  float custom_random;
};

In shader.hlsl:

float3 grained_color = renodx::effects::ApplyFilmGrain(
        linearColor.rgb,
        TEXCOORD.xy,
        CUSTOM_RANDOM,
        CUSTOM_GRAIN_STRENGTH * 0.03f);

Supporting both SDR and HDR with swapchain upgrades

This will be going over the minimum amount of alterations needed to get a functioning and decently polished SDR/HDR toggle in your addon. In shared.h:

#define RENODX_SWAP_CHAIN_OUTPUT_PRESET        shader_injection.swap_chain_output_preset
struct ShaderInjectData {
	// ...
  float swap_chain_output_preset;
	// ...
};

In addon.cpp Note that some of these are simply suggestions, and the needs of your mod may vary. Pay attention to the functional differences and it will be easy to adapt it to any other settings you may need to include/exclude. At the top of your addon.cpp, before settings are defined:

float output_mode = 1.f;

// These pointers are key for our ability to update values based on output mode
renodx::utils::settings::Setting* output_mode_setting = nullptr;
renodx::utils::settings::Setting* force_display_hdr_setting = nullptr;
renodx::utils::settings::Setting* tone_map_type_setting = nullptr;
renodx::utils::settings::Setting* tone_map_peak_nits_setting = nullptr;
renodx::utils::settings::Setting* tone_map_game_nits_setting = nullptr;
renodx::utils::settings::Setting* tone_map_ui_nits_setting = nullptr;
renodx::utils::settings::Setting* tone_map_gamma_correction_setting = nullptr;

reshade::api::swapchain* tracked_swapchain = nullptr;
std::optional<reshade::api::color_space> next_color_space = std::nullopt;

void HandleOutputModeChange() {
  float output_mode = output_mode_setting->GetValue();
  bool is_10bit = renodx::mods::swapchain::target_format == reshade::api::format::r10g10b10a2_unorm;
  if (output_mode == 0.f) {
    if (is_10bit) {
      next_color_space = reshade::api::color_space::srgb_nonlinear;
      shader_injection.swap_chain_output_preset = 0.f;
      shader_injection.peak_white_nits = 1.f;
      shader_injection.diffuse_white_nits = 1.f;
      shader_injection.graphics_white_nits = 1.f;
      shader_injection.gamma_correction = 0.f;
    } else {
      shader_injection.swap_chain_output_preset = 2.f;
      shader_injection.peak_white_nits = 80.f;
      shader_injection.diffuse_white_nits = 80.f;
      shader_injection.graphics_white_nits = 80.f;
      shader_injection.gamma_correction = 0;
    }
  } else {
    if (is_10bit) {
      next_color_space = reshade::api::color_space::hdr10_st2084;
    }
    shader_injection.swap_chain_output_preset = 1.f;
    shader_injection.peak_white_nits = tone_map_peak_nits_setting->GetValue();
    shader_injection.diffuse_white_nits = tone_map_game_nits_setting->GetValue();
    shader_injection.graphics_white_nits = tone_map_ui_nits_setting->GetValue();
    shader_injection.gamma_correction = tone_map_gamma_correction_setting->GetValue();
  }
}

bool IsHDREnabled() { return shader_injection.swap_chain_output_preset == 1.f; }

Add your output mode setting You will also need to update the sliders you have created pointers for by setting the pointers equal to the setting

        output_mode_setting = new renodx::utils::settings::Setting{
        .key = "OutputMode",
        .value_type = renodx::utils::settings::SettingValueType::INTEGER,
        .default_value = 1.f,
        .can_reset = false,
        .label = "Output Mode",
        .labels = {"SDR", "HDR"},
        .on_change_value = [](float previous, float current) { HandleOutputModeChange(); },
    },

For sliders you want to hide in SDR mode, set .is_enabled to &IsHDREnabled Example:

    },
    tone_map_game_nits_setting = new renodx::utils::settings::Setting{
        .key = "ToneMapGameNits",
        .binding = &shader_injection.diffuse_white_nits,
        .default_value = 203.f,
        .label = "Game Brightness",
        .section = "Tone Mapping",
        .tooltip = "Sets the value of 100% white in nits",
        .min = 48.f,
        .max = 500.f,
        .is_enabled = &IsHDREnabled,
        .parse = [](float value) {
          return (output_mode_setting->GetValue() == 0.f)
                     ? 203.f
                     : value;
        },
    },

After your settings in addon.cpp, you will need the following function. If you already use OnPresent, you will need to add this functionality to it.

void OnPresent(reshade::api::command_queue* queue,
               reshade::api::swapchain* swapchain,
               const reshade::api::rect* source_rect,
               const reshade::api::rect* dest_rect,
               uint32_t dirty_rect_count,
               const reshade::api::rect* dirty_rects) {
  auto* device = queue->get_device();

  auto* data = renodx::utils::data::Get<renodx::mods::swapchain::DeviceData>(device);
  if (data == nullptr) return;
  if (!data->upgraded_swapchains.contains(swapchain)) return;
  if (tracked_swapchain != swapchain) {
    tracked_swapchain = swapchain;
    HandleOutputModeChange();
  } else if (next_color_space.has_value()) {
    renodx::utils::swapchain::ChangeColorSpace(tracked_swapchain, next_color_space.value());
    next_color_space = std::nullopt;
  }
}

In DllMain, register the present event and use HDR10

BOOL APIENTRY DllMain(HMODULE h_module, DWORD fdw_reason, LPVOID lpv_reserved) {
  switch (fdw_reason) {
    case DLL_PROCESS_ATTACH:
		// ...
	  renodx::mods::swapchain::SetUseHDR10();
          renodx::utils::settings::on_preset_changed_callbacks.emplace_back(&HandleOutputModeChange); // SDR presets
          reshade::register_event<reshade::addon_event::present>(OnPresent);
		// ...
      break;
    case DLL_PROCESS_DETACH:
		// ...
      reshade::unregister_event<reshade::addon_event::present>(OnPresent);
		// ...
      break;
  }