Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reshade::api::display in order to implement HDR display mastering metadata. #336

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ReShade.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@
<ClCompile Include="source\dxgi\dxgi.cpp" />
<ClCompile Include="source\dxgi\dxgi_d3d10.cpp" />
<ClCompile Include="source\dxgi\dxgi_device.cpp" />
<ClCompile Include="source\dxgi\dxgi_impl_display.cpp" />
<ClCompile Include="source\dxgi\dxgi_swapchain.cpp" />
<ClCompile Include="source\hook.cpp" />
<ClCompile Include="source\hook_manager.cpp" />
Expand Down Expand Up @@ -718,6 +719,7 @@
<ClInclude Include="source\dll_log.hpp" />
<ClInclude Include="source\dll_resources.hpp" />
<ClInclude Include="source\dxgi\dxgi_device.hpp" />
<ClInclude Include="source\dxgi\dxgi_impl_display.hpp" />
<ClInclude Include="source\dxgi\dxgi_swapchain.hpp" />
<ClInclude Include="source\hook.hpp" />
<ClInclude Include="source\hook_manager.hpp" />
Expand Down
9 changes: 9 additions & 0 deletions ReShade.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
<Filter Include="resources\shaders">
<UniqueIdentifier>{9232c43b-559d-4435-b309-c8bbf4e6477b}</UniqueIdentifier>
</Filter>
<Filter Include="api\dxgi">
<UniqueIdentifier>{e8eebdc2-4ab7-47d5-b053-e2dda568614e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="examples\09-depth\generic_depth_addon.cpp">
Expand Down Expand Up @@ -423,6 +426,9 @@
<ClCompile Include="source\windows\ws2_32.cpp">
<Filter>hooks\windows</Filter>
</ClCompile>
<ClCompile Include="source\dxgi\dxgi_impl_display.cpp">
<Filter>api\dxgi</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\reshade.hpp">
Expand Down Expand Up @@ -722,6 +728,9 @@
<ClInclude Include="source\vulkan\vulkan_impl_type_convert.hpp">
<Filter>api\vulkan</Filter>
</ClInclude>
<ClInclude Include="source\dxgi\dxgi_impl_display.hpp">
<Filter>api\dxgi</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\resource.rc">
Expand Down
27 changes: 19 additions & 8 deletions deps/sk_hdr_png/include/sk_hdr_png.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
#pragma once

#include "reshade_api.hpp"
//#include "reshade_api_display.hpp"
#include "reshade_api_display.hpp"
#include <bitset>
#include <com_ptr.hpp>

#include <cmath>
Expand Down Expand Up @@ -310,8 +311,8 @@ namespace sk_hdr_png
};
};

bool write_image_to_disk (const wchar_t* image_path, unsigned int width, unsigned int height, const void* pixels, int quantization_bits, format fmt, bool use_clipboard);//, reshade::api::display* display);
bool write_hdr_chunks (const wchar_t* image_path, unsigned int width, unsigned int height, const float* luminance_array, int quantization_bits);//, reshade::api::display* display);
bool write_image_to_disk (const wchar_t* image_path, unsigned int width, unsigned int height, const void* pixels, int quantization_bits, format fmt, bool use_clipboard, reshade::api::display* display);
bool write_hdr_chunks (const wchar_t* image_path, unsigned int width, unsigned int height, const float* luminance_array, int quantization_bits, reshade::api::display* display);
cLLi_Payload calculate_content_light_info (const float* luminance, unsigned int width, unsigned int height);
bool copy_to_clipboard (const wchar_t* image_path);
bool remove_chunk (const char* chunk_name, void* data, size_t& size);
Expand Down Expand Up @@ -586,7 +587,7 @@ sk_hdr_png::remove_chunk (const char* chunk_name, void* data, size_t& size)
}

bool
sk_hdr_png::write_hdr_chunks (const wchar_t* image_path, unsigned int width, unsigned int height, const float* luminance, int quantization_bits)//, reshade::api::display* display)
sk_hdr_png::write_hdr_chunks (const wchar_t* image_path, unsigned int width, unsigned int height, const float* luminance, int quantization_bits, reshade::api::display* display)
{
if (image_path == nullptr || width == 0 || height == 0 || quantization_bits < 6)
{
Expand Down Expand Up @@ -706,7 +707,6 @@ sk_hdr_png::write_hdr_chunks (const wchar_t* image_path, unsigned int width, uns
sbit_chunk.write(fPNG);
chrm_chunk.write(fPNG);

#if 0
///
/// Mastering metadata can be added, provided you are able to read this info
/// from the user's EDID.
Expand Down Expand Up @@ -744,7 +744,6 @@ sk_hdr_png::write_hdr_chunks (const wchar_t* image_path, unsigned int width, uns
Chunk mdcv_chunk = {sizeof(mdcv_data), {'m','D','C','v'}, &mdcv_data};
mdcv_chunk.write(fPNG);
}
#endif

// Write the remainder of the original file
fwrite(insert_ptr, size - insert_pos, 1, fPNG);
Expand Down Expand Up @@ -814,8 +813,20 @@ sk_hdr_png::copy_to_clipboard (const wchar_t* image_path)
}

bool
sk_hdr_png::write_image_to_disk (const wchar_t* image_path, unsigned int width, unsigned int height, const void* pixels, int quantization_bits, format fmt, bool use_clipboard)//, reshade::api::display* display)
sk_hdr_png::write_image_to_disk (const wchar_t* image_path, unsigned int width, unsigned int height, const void* pixels, int quantization_bits, format fmt, bool use_clipboard, reshade::api::display* display)
{
if (fmt == format::r16g16b16a16_float)
{
int cpu_info [4] = { };

CpuIdEx(cpu_info, 1, 0);
if (!std::bitset<32>(cpu_info[2])[29])
{
reshade::log::message(reshade::log::level::error, "CPU does not support AVX/F16C, required for scRGB screenshots!");
return false;
}
}

using namespace DirectX;
using namespace DirectX::PackedVector;

Expand Down Expand Up @@ -988,7 +999,7 @@ sk_hdr_png::write_image_to_disk (const wchar_t* image_path, unsigned int width,
if (SUCCEEDED(hr)) hr = encoder->Commit();
if (SUCCEEDED(hr))
{
hr = write_hdr_chunks(image_path, width, height, luminance, quantization_bits/*, display*/) ? S_OK : E_FAIL;
hr = write_hdr_chunks(image_path, width, height, luminance, quantization_bits, display) ? S_OK : E_FAIL;

if (SUCCEEDED(hr))
{
Expand Down
7 changes: 7 additions & 0 deletions include/reshade_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace reshade { namespace api
/// </remarks>
RESHADE_DEFINE_HANDLE(effect_uniform_variable);

struct display;

/// <summary>
/// Input source for events triggered by user input.
/// </summary>
Expand Down Expand Up @@ -806,5 +808,10 @@ namespace reshade { namespace api
/// </summary>
/// <param name="effect_name">File name of the effect file that should be reloaded.</param>
virtual void reload_effect_next_frame(const char *effect_name) = 0;

/// <summary>
/// Gets the active display, which may change between frames or return nullptr if the swapchain is offscreen.
/// </summary>
virtual display* get_active_display() const = 0;
};
} }
1 change: 1 addition & 0 deletions include/reshade_api_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ namespace reshade { namespace api
/// For <see cref="command_list"/> this will be a pointer to a 'ID3D11DeviceContext' (when recording), 'ID3D11CommandList' (when executing) or 'ID3D12GraphicsCommandList' object or a 'VkCommandBuffer' handle.<br/>
/// For <see cref="command_queue"/> this will be a pointer to a 'ID3D11DeviceContext' or 'ID3D12CommandQueue' object or a 'VkQueue' handle.<br/>
/// For <see cref="swapchain"/> this will be a pointer to a 'IDirect3DSwapChain9' or 'IDXGISwapChain' object or a 'HDC' or 'VkSwapchainKHR' handle.
/// For <see cref="display"/> this will be a pointer to a 'IDXGIOutput'; DXGI is used even for runtimes that do not have a DXGI-based swapchain.
/// </remarks>
virtual uint64_t get_native() const = 0;

Expand Down
153 changes: 153 additions & 0 deletions include/reshade_api_display.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (C) 2024 Patrick Mours
* SPDX-License-Identifier: BSD-3-Clause OR MIT
*/

#pragma once

#include "reshade_api_device.hpp"
#include <unordered_map>
#include <memory>

namespace reshade { namespace api
{
/// <summary>
/// Describes quantities defined precisely as the ratio of two integers, such as refresh rates.
/// </summary>
struct rational
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with other parts of the API, I'd forego this type. swapchain_desc::fullscreen_refresh_rate e.g. is a floating point value describing the refresh rate, not a rational type (and there is some logic at https://github.com/crosire/reshade/blob/main/source/dxgi/dxgi.cpp#L21 to convert between the two), so it would be strange if the display type suddenly expressed the refresh rate in a different manner.

{
uint32_t numerator = 0;
uint32_t denomenator = 0;

constexpr float as_float() const { return denomenator != 0 ? static_cast <float>(numerator)/static_cast<float>(denomenator) : 0.0f; }
};

/// <summary>
/// Describes the colorimetry of a colorspace.
/// </summary>
struct colorimetry
{
float red [2] = { 0.0f, 0.0f };
float green [2] = { 0.0f, 0.0f };
float blue [2] = { 0.0f, 0.0f };
float white [2] = { 0.0f, 0.0f };
};

/// <summary>
/// Describes the dynamic range of a display or image.
/// </summary>
struct luminance_levels
{
float min_nits = 0.0f;
float max_nits = 0.0f;
float max_avg_nits = 0.0f;
};

/// <summary>
/// An output display.
/// <para>Functionally equivalent to a 'IDXGIOutput'.</para>
/// </summary>
struct __declspec(novtable) display : public api_object
{
/// <summary>
/// Indicates if cached properties are valid or potentially stale (i.e. refresh rate or resolution have changed).
/// </summary>
/// <remarks>
/// If this returns false, wait one frame and call runtime::get_active_display() to get updated data.
/// </remarks>
virtual bool is_current() const = 0;

using monitor = void*;

/// <summary>
/// Gets the handle of the monitor this display encapsulates.
/// </summary>
virtual monitor get_monitor() const = 0;

/// <summary>
/// Gets the (GDI) device name (i.e. \\DISPLAY1) of the the monitor; do not use this as a persistent display identifier!
/// </summary>
/// <remarks>
/// This device name is not valid as a persistent display identifier for storage in configuration files, it may not refer to the same display device after a reboot.
/// </remarks>
virtual const wchar_t* get_device_name() const = 0;

/// <summary>
/// Gets the device path (i.e. \\?\DISPLAY#DELA1E4#5&d93f871&0&UID33025#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}) of the the monitor.
/// </summary>
/// <remarks>
/// The device path uniquely identifies a specific monitor (down to its serial number and the port it is attached to) and remains valid across system reboots.
/// This identifier is suitable for persistent user-defined display configuration, partial name matches can be used to identify instances of the same monitor when the port it attaches to is unimportant.
/// </remarks>
virtual const wchar_t* get_device_path() const = 0;

/// <summary>
/// Gets the human readable name (i.e. Dell AW3423DW) of the the monitor.
/// </summary>
virtual const wchar_t* get_display_name() const = 0;

/// <summary>
/// Gets the size and location of the display on the desktop.
/// </summary>
virtual rect get_desktop_coords() const = 0;

/// <summary>
/// Gets the current color depth of the display.
/// </summary>
virtual uint32_t get_color_depth() const = 0;

/// <summary>
/// Gets the current refresh rate of the display.
/// </summary>
virtual rational get_refresh_rate() const = 0;

/// <summary>
/// Gets the current color space used for desktop composition.
/// </summary>
/// <remarks>
/// This is independent from swap chain colorspace, it identifies a display as HDR capable even when not rendering in HDR.
/// </remarks>
virtual color_space get_color_space() const = 0;

/// <summary>
/// Gets the display's reported native colorimetry (the data provided are often invalid or placeholders).
/// </summary>
/// <remarks>
/// Users running Windows 11 or newer are encouraged to run "Windows HDR Calibration" to ensure the values reported to ReShade and games are accurate.
/// </remarks>
virtual colorimetry get_colorimetry() const = 0;

/// <summary>
/// Gets the display's light output capabilities (the data provided are often invalid or placeholders).
/// </summary>
/// <remarks>
/// Users running Windows 11 or newer are encouraged to run "Windows HDR Calibration" to ensure the values reported to ReShade and games are accurate.
/// </remarks>
virtual luminance_levels get_luminance_caps() const = 0;

/// <summary>
/// Gets the desktop compositor's whitelevel for mapping SDR content to HDR.
/// </summary>
virtual float get_sdr_white_nits() const = 0;

/// <summary>
/// Checks if HDR is supported on the display, even if it is not currently enabled.
/// </summary>
virtual bool is_hdr_supported() const = 0;

/// <summary>
/// Checks if HDR is enabled on the display.
/// </summary>
virtual bool is_hdr_enabled() const = 0;

/// <summary>
/// Enables HDR on the display.
/// </summary>
/// <remarks>
/// Be aware that this is a global display setting, and will not automatically revert to its original state when the application exits.
/// </remarks>
virtual bool enable_hdr(bool enable) = 0;
};

using display_cache = std::unordered_map<display::monitor, std::unique_ptr<display>>;
} }
10 changes: 9 additions & 1 deletion include/reshade_events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1705,8 +1705,14 @@ namespace reshade
/// </remarks>
reshade_overlay_technique,

/// <summary>
/// Called when the active display changes for a runtime.
/// <para>Callback function signature: <c>void (api::effect_runtime *runtime, api::display *display)</c></para>
/// </summary>
display_change = 95,

#if RESHADE_ADDON
max = 95 // Last value used internally by ReShade to determine number of events in this enum
max = 96 // Last value used internally by ReShade to determine number of events in this enum
#endif
};

Expand Down Expand Up @@ -1848,4 +1854,6 @@ namespace reshade

RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::reshade_overlay_uniform_variable, bool, api::effect_runtime *runtime, api::effect_uniform_variable variable);
RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::reshade_overlay_technique, bool, api::effect_runtime *runtime, api::effect_technique technique);

RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::display_change, void, api::effect_runtime *runtime, api::display *display);
}
Loading