Providing game data to ReShade

More
2 weeks 6 hours ago - 2 weeks 5 hours ago #1 by doodlum Providing game data to ReShade was created by doodlum
I want to be able to provide game data to ReShade so that shaders can make use of it. I'm wondering about how I could go about doing that, and if there are any good practices I should follow, such as some way of indicating to shaders of the presence of such data.

Right now, any game render target could be provided to ReShade, as well as the main camera information (view, position, whatever), motion vectors, normals and specular. 

The lighting and weather information, game tonemapping/color correction parameters, and ENB variables can be provided, perhaps as constant buffers?

The world cubemap (the sky + lod) should be possible to provide. For another project I want to cache light source information, so that might be possible alongside the shadow map. I don't know if any of that's useful though.

Information about how best to provide these bits of data would be appreciated. These are also D3D11 resources (the render targets) although I guess I can just capture them via ReShade if they aren't compatible.

What actually are the render effect arguments? I don't really understand why there are two RTVs. Can they be given HDR buffers?
 
Last edit: 2 weeks 5 hours ago by doodlum.

Please Log in or Create an account to join the conversation.

More
1 week 6 days ago #2 by crosire Replied by crosire on topic Providing game data to ReShade
To provide data to effects, you can use the "set_[...]()" methods of the "reshade::api::effect_runtime" class ( crosire.github.io/reshade-docs/structres...effect__runtime.html ). Typically one would register a callback with the "reshade::addon_even::reshade_begin_effects" event (which is called right before ReShade renders effects) and in that callback use the provided effect runtime pointer to set/update any effect variables one wants. In your case you are AFAIK already choosing to overwrite when ReShade renders effects somewhere else during the frame by calling "reshade::api::effect_runtime::render_effects()", so you could instead set/update any effect variables right before calling that directly, instead of going the event callback route (since "reshade::addon_event::reshade_begin_effects" would just be called from within the "render_effects()" call anyway).


There are various ways to choose which effect variables should be updated, e.g. find those with a fixed name, or with a specific annotation on them, ...
For uniform variables for example, in ReShade it's common practise to have them ask for specific data via a "source" annotation (e.g. "uniform MyVarName < source = "fill_this_with_x";" in the ReShade FX effect code), so in an add-on you would then enumerate all uniform variables, filter them by those which have that annotation set to "fill_this_with_x" and update those with the appropriate data, like so (see also crosire.github.io/reshade-docs/structres...9781b8f0a3f2eac4209a ):
reshade::api::effect_runtime *runtime = ...;
float example_vector_to_provide_to_effects[3] = { x, y, z };

runtime->enumerate_uniform_variables(nullptr, [](reshade::api::effect_runtime *runtime, reshade::api::effect_uniform_variable variable) {
    char annotation_value[32];
    if (runtime->get_annotation_string_from_uniform_variable(variable, "source", annotation_value))
    {
        if (strcmp(annotation_value, "fill_this_with_x") == 0)
        {
            runtime->set_uniform_value_float(variable, example_vector_to_provide_to_effects, 3);
        }
    }
});
Alternatively to do something like ENB, where variables are expected to have a fixed name, can either search for a specific one in a specific effect file via "reshade::api::effect_runtime::find_uniform_variable()" or update them all:
reshade::api::effect_runtime *runtime = ...;
float width = ..., height = ...;

runtime->enumerate_uniform_variables(nullptr, [](effect_runtime *runtime, effect_uniform_variable variable) {
    char variable_name[32];
    if (runtime->get_uniform_variable_name(variable, variable_name))
    {
        if (strcmp(variable_name, "ScreenSize") == 0)
        {
            runtime->set_uniform_value_float(variable, width, 1.0f / width, width / height, height / width);
        }
    }
});


Textures are handled a bit different, since ReShade FX has explicit language syntax to specify that a texture should come from an external source (via "texture MyVarName : MY_TEXTURE_SEMANTIC;", where "MY_TEXTURE_SEMANTIC" describes what that texture should be filled with, similar to "COLOR" and "DEPTH" ReShade already provides). As such there is a special method "reshade::api::effect_runtime::update_texture_bindings()" to automatically update all texture variables with a specific semantic to a texture of your choosing (see also crosire.github.io/reshade-docs/structres...62e2e9d5d5090e495cca ):
reshade::api::effect_runtime *runtime = ...;
reshade::api::resource_view shader_resource_view = ...; // This is a SRV to the texture you want to use. In case of D3D11, "reshade::api::resource_view" is equivalent to "ID3D11ShaderResourceView", so if you already have a SRV to the texture can just reinterpret_cast that "ID3D11ShaderResourceView" pointer to "reshade::api::resource_view" and use it here directly. Otherwise can use "reshade::api::device::create_resource_view" or "ID3D11Device::CreateShaderResourceView" to create one for the texture in question.
reshade::api::resource_view srgb_shader_resource_view = ...; // If the texture has a format that supports sRGB, ideally provide one SRV with the non-sRGB format variant and one with, so that the "SRGBTexture" sampler state in ReShade FX works. Otherwise just leave this zero.

runtime->update_texture_bindings("MY_TEXTURE_SEMANTIC", shader_resource_view, srgb_shader_resource_view);


Now as for "reshade::api::effect_runtime::render_effects()", the RTVs provided to that function are what ReShade will consider the "default backbuffer" while rendering. Meaning any passes that do not have a render target specified in ReShade FX effect code will end up in those RTVs (the former when the "SRGBWriteEnable" pass state is false, the latter when it is true), plus ReShade will automatically copy the data in those RTVs and make it available to textures with the "COLOR" semantic (like the commonly used ReShade::BackBuffer specified in ReShade.fxh). In case of D3D11 "reshade::api::resource_view" is equivalent to "ID3D11RenderTargetView" in this case, so if you already have the appropriate render target pointer, can reinterpret_cast them and pass directly. The format of these RTVs doesn't matter, they can be given HDR buffers, yes, ReShade will deal with that. The only thing ReShade expects is that these have the same width and height as the swapchain, otherwise effects may behave wrong (since "BUFFER_WIDTH" and "BUFFER_HEIGHT" in ReShade FX are not updated, see also crosire.github.io/reshade-docs/structres...5dacc2fdc84b1eed47c9 ).


In general I really encourage checking out crosire.github.io/reshade-docs/structres...effect__runtime.html to get a feel for all the methods available to communicate with the ReShade effect runtime.
The following user(s) said Thank You: doodlum

Please Log in or Create an account to join the conversation.

More
1 week 6 days ago #3 by crosire Replied by crosire on topic Providing game data to ReShade
Oh, and the presence of some specific data can be communicated to ReShade FX effects e.g. by setting a uniform bool value to true when available and false when not (like the Generic Depth add-on does to tell effects whether a depth buffer was found via uniform variables with a "bufready_depth" annotation: github.com/crosire/reshade/blob/79cb97bb...neric_depth.cpp#L341 ), or even set a special preprocessor definition and have all effects recompiled with that definition using "reshade::api::effect_runtime::set_preprocessor_definition" ( crosire.github.io/reshade-docs/structres...5e5dab3d21731f9dd572 ).

Note: "update_texture_bindings" and "set_preprocessor_definition" are expensive operations (the former synchronizes CPU/GPU, the later recompiles all effects), so do not call them every frame, but only when things change!!
The following user(s) said Thank You: doodlum

Please Log in or Create an account to join the conversation.

More
1 week 6 days ago #4 by doodlum Replied by doodlum on topic Providing game data to ReShade
Wow, this is extremely in-depth and helpful. Thank you so much.

Similarly to the annotations you referenced, would it be possible to have annotations which a shader can use to specify where it's ideally executed? It looks like to do so, I'd have to takeover the rendering of specific shaders. It would be cool if that for any game with "extended" support, shaders could declare where they would prefer to be run, such as before transparency. My understanding is that to render at unique points, users have to disable the shader in the main reshade window and enable it with custom UI (i.e. groups)

Please Log in or Create an account to join the conversation.

More
1 week 6 days ago - 1 week 6 days ago #5 by crosire Replied by crosire on topic Providing game data to ReShade
The problem is that ReShade is not really designed to be able to run subsets of effects at different times. The user interface presents techniques in a big list, with the notion that they will be looped through and executed from top to bottom.
So an add-on that wants to group effects into groups that can be rendered independently at different points would already make the ReShade user interface for effect selection moot, and would require an interface to configure of its own. At that point it's easier to just replace the effect rendering loop in ReShade entirely and ignore whatever is configured in the main ReShade window, hence why there is only an API to render techniques individually ("reshade::api::effect_runtime::render_technique()", to find out which techniques exist would use "reshade::api::effect_runtime::enumerate_techniques()"), which can be used to replace what ReShade does.
Last edit: 1 week 6 days ago by crosire.

Please Log in or Create an account to join the conversation.

More
10 hours 54 minutes ago #6 by doodlum Replied by doodlum on topic Providing game data to ReShade
reshade::api::resource_view shader_resource_view = (reshade::api::resource_view)(int64_t)&rt.RTV;
reshade::api::resource_view srgb_shader_resource_view = (reshade::api::resource_view)(int64_t)0;
runtime->update_texture_bindings(RTNames[n], shader_resource_view, srgb_shader_resource_view);
        
So far I've ended up with this. Reinterpret cast just does not work, the compiler complains. I'm assuming it is just a pointer.

I don't understand how I assign a namespace to the texture, or if it already exists.

Please Log in or Create an account to join the conversation.

More
9 hours 30 minutes ago - 7 hours 5 minutes ago #7 by crosire Replied by crosire on topic Providing game data to ReShade
ID3D11ShaderResourceView *d3d_srv = ...;
reshade::api::resource_view reshade_srv = { reinterpret_cast<uintptr_t>(d3d_srv) };
runtime->update_texture_bindings("BLUB", reshade_srv, reshade::api::resource_view { 0 });


Don't pass in a ID3D11RenderTargetView into "update_texture_bindings"! It specifically requires a SRV rather than a RTV.

I'm not sure what you mean by namespace. The string you pass to "update_texture_bindings" is the semantic by which you would reference the texture in an effect. E.g. "update_texture_bindings("BLUB", ...)" in add-on => "texture WhatEverVariableNameYouWant : BLUB;" in ReShade FX.
Last edit: 7 hours 5 minutes ago by crosire.

Please Log in or Create an account to join the conversation.

More
9 hours 18 minutes ago - 9 hours 17 minutes ago #8 by doodlum Replied by doodlum on topic Providing game data to ReShade
Thanks for the clarification.

.. I got confused about the RTVs vs SRVs , assuming ReShade just does something to the RTV to make a new SRV. I was testing with SRVs though, hence my other post.

 
Last edit: 9 hours 17 minutes ago by doodlum.

Please Log in or Create an account to join the conversation.

We use cookies

We use cookies on our website. Some of them are essential for the operation of the site, while others help us to improve this site and the user experience (tracking cookies). You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.