Skip to content
Voosh edited this page Jan 5, 2025 · 6 revisions

Swap chain proxy (= Final shader)

Example: addon.cpp / final pixel / final vertex

Required in addon.cpp:

      renodx::mods::swapchain::use_resource_cloning = true; // OR 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 (example).

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

//----what usually preceeds in addon.cpp----//
void OnPresetOff() {
  [...]
  renodx::utils::settings::UpdateSetting("colorGradeLUTScaling", 0.f);
  renodx::utils::settings::UpdateSetting("fxBloom", 50.f);
}
//----what usually preceeds in addon.cpp----//
bool HandlePreDraw(reshade::api::command_list* cmd_list, bool is_dispatch = false) {
  const auto& shader_state = cmd_list->get_private_data<renodx::utils::shader::CommandListData>();

  auto pixel_shader_hash = shader_state.GetCurrentPixelShaderHash(); // only if targetting Pixel shader
  auto vertex_shader_hash = shader_state.GetCurrentVertexShaderHash();  // only if targetting Vertex shader
  if (
      !is_dispatch
      && (pixel_shader_hash == 0xFFFFFFFF  // Set your shader type and hash here.
       && pixel_shader_hash == 0xFFFFFFFA  // Add as many as you need.
       && vertex_shader_hash == 0xFFFFFFFB // They don't have to be included in the project
       && vertex_shader_hash == 0xFFFFFFFC // as decompiled shaders.
          )) {
    auto& swapchain_state = cmd_list->get_private_data<renodx::utils::swapchain::CommandListData>();

    bool changed = false;
    const uint32_t render_target_count = swapchain_state.current_render_targets.size();
    for (uint32_t i = 0; i < render_target_count; i++) {
      auto render_target = swapchain_state.current_render_targets[i];
      if (render_target.handle == 0) continue;
      if (renodx::mods::swapchain::ActivateCloneHotSwap(cmd_list->get_device(), render_target)) {
/*         //----uncomment for logging----//
        std::stringstream s;
        s << "Upgrading RTV: ";
        s << reinterpret_cast<void*>(render_target.handle);
        s << ", shader: ";
        s << PRINT_CRC32(pixel_shader_hash);
        s << ")";
        reshade::log::message(reshade::log::level::debug, s.str().c_str());
*/         //----uncomment for logging----//
        changed = true;
      }
    }
    if (changed) {
      // Change render targets to desired
      renodx::mods::swapchain::RewriteRenderTargets(
          cmd_list,
          render_target_count,
          swapchain_state.current_render_targets.data(),
          swapchain_state.current_depth_stencil);
      renodx::mods::swapchain::FlushDescriptors(cmd_list);
    }
  } else {
    renodx::mods::swapchain::DiscardDescriptors(cmd_list);
  }

  return false;
}

bool OnDraw(reshade::api::command_list* cmd_list, uint32_t vertex_count,
            uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) {
  return HandlePreDraw(cmd_list);
}

bool OnDrawIndexed(reshade::api::command_list* cmd_list, uint32_t index_count,
                   uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance) {
  return HandlePreDraw(cmd_list);
}

bool OnDrawOrDispatchIndirect(reshade::api::command_list* cmd_list, reshade::api::indirect_command type,
                              reshade::api::resource buffer, uint64_t offset, uint32_t draw_count, uint32_t stride) {
  return HandlePreDraw(cmd_list);
}

bool OnDispatch(reshade::api::command_list* cmd_list,
                uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) {
  return HandlePreDraw(cmd_list, true);
}
//----what usually follows in addon.cpp----//
}  // namespace
extern "C" __declspec(dllexport) constexpr const char* NAME = "RenoDX";
extern "C" __declspec(dllexport) constexpr const char* DESCRIPTION = "RenoDX for Name of the Game";
//----what usually follows in addon.cpp----//

Then you need the following mod and upgrade settings. We also load above's functions. 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,
      });

      reshade::register_event<reshade::addon_event::draw>(OnDraw);
      reshade::register_event<reshade::addon_event::draw_indexed>(OnDrawIndexed);
      reshade::register_event<reshade::addon_event::draw_or_dispatch_indirect>(OnDrawOrDispatchIndirect);
      reshade::register_event<reshade::addon_event::dispatch>(OnDispatch);
//----what usually follows in addon.cpp----//
      break;
    case DLL_PROCESS_DETACH:
      reshade::unregister_addon(h_module);
      break;
  }
//----what usually follows in addon.cpp----//
Clone this wiki locally