8213 lines
238 KiB
C++
8213 lines
238 KiB
C++
/*
|
|
* Based on xcompmgr by Keith Packard et al.
|
|
* http://cgit.freedesktop.org/xorg/app/xcompmgr/
|
|
* Original xcompmgr legal notices follow:
|
|
*
|
|
* Copyright © 2003 Keith Packard
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation, and that the name of Keith Packard not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Keith Packard makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
|
|
/* Modified by Matthew Hawn. I don't know what to say here so follow what it
|
|
* says above. Not that I can really do anything about it
|
|
*/
|
|
|
|
#include "xwayland_ctx.hpp"
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/xfixeswire.h>
|
|
#include <cstdint>
|
|
#include <drm_mode.h>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <queue>
|
|
#include <variant>
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#if defined(__linux__)
|
|
#include <sys/prctl.h>
|
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
|
#include <sys/procctl.h>
|
|
#endif
|
|
#include <sys/socket.h>
|
|
#include <sys/resource.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <spawn.h>
|
|
#include <signal.h>
|
|
#include <linux/input-event-codes.h>
|
|
#include <X11/Xmu/CurUtil.h>
|
|
|
|
#include "steamcompmgr_shared.hpp"
|
|
|
|
#include "main.hpp"
|
|
#include "wlserver.hpp"
|
|
#include "drm.hpp"
|
|
#include "rendervulkan.hpp"
|
|
#include "steamcompmgr.hpp"
|
|
#include "vblankmanager.hpp"
|
|
#include "sdlwindow.hpp"
|
|
#include "log.hpp"
|
|
#include "defer.hpp"
|
|
|
|
#if HAVE_PIPEWIRE
|
|
#include "pipewire.hpp"
|
|
#endif
|
|
|
|
#if HAVE_OPENVR
|
|
#include "vr_session.hpp"
|
|
#endif
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include <stb_image.h>
|
|
#include <stb_image_write.h>
|
|
|
|
#define GPUVIS_TRACE_IMPLEMENTATION
|
|
#include "gpuvis_trace_utils.h"
|
|
|
|
|
|
static LogScope xwm_log("xwm");
|
|
|
|
bool g_bWasPartialComposite = false;
|
|
|
|
///
|
|
// Color Mgmt
|
|
//
|
|
|
|
gamescope_color_mgmt_tracker_t g_ColorMgmt{};
|
|
|
|
static gamescope_color_mgmt_luts g_ColorMgmtLutsOverride[ EOTF_Count ];
|
|
static lut3d_t g_ColorMgmtLooks[ EOTF_Count ];
|
|
|
|
|
|
gamescope_color_mgmt_luts g_ColorMgmtLuts[ EOTF_Count ];
|
|
|
|
gamescope_color_mgmt_luts g_ScreenshotColorMgmtLuts[ EOTF_Count ];
|
|
|
|
static lut1d_t g_tmpLut1d;
|
|
static lut3d_t g_tmpLut3d;
|
|
|
|
bool g_bForceHDRSupportDebug = false;
|
|
extern float g_flInternalDisplayBrightnessNits;
|
|
extern float g_flHDRItmSdrNits;
|
|
extern float g_flHDRItmTargetNits;
|
|
|
|
extern std::atomic<uint64_t> g_lastVblank;
|
|
|
|
static std::shared_ptr<wlserver_ctm> s_scRGB709To2020Matrix;
|
|
|
|
std::string clipboard;
|
|
std::string primarySelection;
|
|
|
|
std::string g_reshade_effect{};
|
|
uint32_t g_reshade_technique_idx = 0;
|
|
|
|
bool g_bSteamIsActiveWindow = false;
|
|
|
|
uint64_t timespec_to_nanos(struct timespec& spec)
|
|
{
|
|
return spec.tv_sec * 1'000'000'000ul + spec.tv_nsec;
|
|
}
|
|
|
|
static void
|
|
update_runtime_info();
|
|
|
|
static uint64_t g_SteamCompMgrLimitedAppRefreshCycle = 16'666'666;
|
|
static uint64_t g_SteamCompMgrAppRefreshCycle = 16'666'666;
|
|
|
|
static const gamescope_color_mgmt_t k_ScreenshotColorMgmt =
|
|
{
|
|
.enabled = true,
|
|
.displayColorimetry = displaycolorimetry_709,
|
|
.displayEOTF = EOTF_Gamma22,
|
|
.outputEncodingColorimetry = displaycolorimetry_709,
|
|
.outputEncodingEOTF = EOTF_Gamma22,
|
|
};
|
|
|
|
//#define COLOR_MGMT_MICROBENCH
|
|
// sudo cpupower frequency-set --governor performance
|
|
|
|
static void
|
|
create_color_mgmt_luts(const gamescope_color_mgmt_t& newColorMgmt, gamescope_color_mgmt_luts outColorMgmtLuts[ EOTF_Count ])
|
|
{
|
|
const displaycolorimetry_t& displayColorimetry = newColorMgmt.displayColorimetry;
|
|
const displaycolorimetry_t& outputEncodingColorimetry = newColorMgmt.outputEncodingColorimetry;
|
|
|
|
for ( uint32_t nInputEOTF = 0; nInputEOTF < EOTF_Count; nInputEOTF++ )
|
|
{
|
|
if (!outColorMgmtLuts[nInputEOTF].vk_lut1d)
|
|
outColorMgmtLuts[nInputEOTF].vk_lut1d = vulkan_create_1d_lut(s_nLutSize1d);
|
|
|
|
if (!outColorMgmtLuts[nInputEOTF].vk_lut3d)
|
|
outColorMgmtLuts[nInputEOTF].vk_lut3d = vulkan_create_3d_lut(s_nLutEdgeSize3d, s_nLutEdgeSize3d, s_nLutEdgeSize3d);
|
|
|
|
if ( g_ColorMgmtLutsOverride[nInputEOTF].HasLuts() )
|
|
{
|
|
memcpy(g_ColorMgmtLuts[nInputEOTF].lut1d, g_ColorMgmtLutsOverride[nInputEOTF].lut1d, sizeof(g_ColorMgmtLutsOverride[nInputEOTF].lut1d));
|
|
memcpy(g_ColorMgmtLuts[nInputEOTF].lut3d, g_ColorMgmtLutsOverride[nInputEOTF].lut3d, sizeof(g_ColorMgmtLutsOverride[nInputEOTF].lut3d));
|
|
}
|
|
else
|
|
{
|
|
displaycolorimetry_t inputColorimetry{};
|
|
colormapping_t colorMapping{};
|
|
|
|
tonemapping_t tonemapping{};
|
|
tonemapping.bUseShaper = true;
|
|
|
|
EOTF inputEOTF = static_cast<EOTF>( nInputEOTF );
|
|
float flGain = 1.f;
|
|
lut3d_t * pLook = g_ColorMgmtLooks[nInputEOTF].lutEdgeSize > 0 ? &g_ColorMgmtLooks[nInputEOTF] : nullptr;
|
|
|
|
if ( inputEOTF == EOTF_Gamma22 )
|
|
{
|
|
flGain = newColorMgmt.flSDRInputGain;
|
|
if ( newColorMgmt.outputEncodingEOTF == EOTF_Gamma22 )
|
|
{
|
|
// G22 -> G22. Does not matter what the g22 mult is
|
|
tonemapping.g22_luminance = 1.f;
|
|
// xwm_log.infof("G22 -> G22");
|
|
}
|
|
else if ( newColorMgmt.outputEncodingEOTF == EOTF_PQ )
|
|
{
|
|
// G22 -> PQ. SDR content going on an HDR output
|
|
tonemapping.g22_luminance = newColorMgmt.flSDROnHDRBrightness;
|
|
// xwm_log.infof("G22 -> PQ");
|
|
}
|
|
|
|
// The final display colorimetry is used to build the output mapping, as we want a gamut-aware handling
|
|
// for sdrGamutWideness indepdendent of the output encoding (for SDR data), and when mapping SDR -> PQ output
|
|
// we only want to utilize a portion of the gamut the actual display can reproduce
|
|
buildSDRColorimetry( &inputColorimetry, &colorMapping, newColorMgmt.sdrGamutWideness, displayColorimetry );
|
|
}
|
|
else if ( inputEOTF == EOTF_PQ )
|
|
{
|
|
flGain = newColorMgmt.flHDRInputGain;
|
|
if ( newColorMgmt.outputEncodingEOTF == EOTF_Gamma22 )
|
|
{
|
|
// PQ -> G22 Leverage the display's native brightness
|
|
tonemapping.g22_luminance = newColorMgmt.flInternalDisplayBrightness;
|
|
|
|
// Determine the tonemapping parameters
|
|
// Use the external atoms if provided
|
|
tonemap_info_t source = newColorMgmt.hdrTonemapSourceMetadata;
|
|
tonemap_info_t dest = newColorMgmt.hdrTonemapDisplayMetadata;
|
|
// Otherwise, rely on the Vulkan source info and the EDID
|
|
// TODO: If source is invalid, use the provided metadata.
|
|
// TODO: If hdrTonemapDisplayMetadata is invalid, use the one provided by the display
|
|
|
|
// Adjust the source brightness range by the requested HDR input gain
|
|
dest.flBlackPointNits /= flGain;
|
|
dest.flWhitePointNits /= flGain;
|
|
|
|
if ( source.BIsValid() && dest.BIsValid() )
|
|
{
|
|
tonemapping.eOperator = newColorMgmt.hdrTonemapOperator;
|
|
tonemapping.eetf2390.init( source, newColorMgmt.hdrTonemapDisplayMetadata );
|
|
}
|
|
else
|
|
{
|
|
tonemapping.eOperator = ETonemapOperator_None;
|
|
}
|
|
/*
|
|
xwm_log.infof("PQ -> 2.2 - g22_luminance %f gain %f", tonemapping.g22_luminance, flGain );
|
|
xwm_log.infof("source %f %f", source.flBlackPointNits, source.flWhitePointNits );
|
|
xwm_log.infof("dest %f %f", dest.flBlackPointNits, dest.flWhitePointNits );
|
|
xwm_log.infof("operator %d", (int) tonemapping.eOperator );*/
|
|
}
|
|
else if ( newColorMgmt.outputEncodingEOTF == EOTF_PQ )
|
|
{
|
|
// PQ -> PQ passthrough (though this does apply gain)
|
|
// TODO: should we manipulate the output static metadata to reflect the gain factor?
|
|
tonemapping.g22_luminance = 1.f;
|
|
// xwm_log.infof("PQ -> PQ");
|
|
}
|
|
|
|
buildPQColorimetry( &inputColorimetry, &colorMapping, displayColorimetry );
|
|
}
|
|
|
|
calcColorTransform( &g_tmpLut1d, s_nLutSize1d, &g_tmpLut3d, s_nLutEdgeSize3d, inputColorimetry, inputEOTF,
|
|
outputEncodingColorimetry, newColorMgmt.outputEncodingEOTF,
|
|
newColorMgmt.outputVirtualWhite, newColorMgmt.chromaticAdaptationMode,
|
|
colorMapping, newColorMgmt.nightmode, tonemapping, pLook, flGain );
|
|
|
|
// Create quantized output luts
|
|
for ( size_t i=0, end = g_tmpLut1d.dataR.size(); i<end; ++i )
|
|
{
|
|
outColorMgmtLuts[nInputEOTF].lut1d[4*i+0] = drm_quantize_lut_value( g_tmpLut1d.dataR[i] );
|
|
outColorMgmtLuts[nInputEOTF].lut1d[4*i+1] = drm_quantize_lut_value( g_tmpLut1d.dataG[i] );
|
|
outColorMgmtLuts[nInputEOTF].lut1d[4*i+2] = drm_quantize_lut_value( g_tmpLut1d.dataB[i] );
|
|
outColorMgmtLuts[nInputEOTF].lut1d[4*i+3] = 0;
|
|
}
|
|
|
|
for ( size_t i=0, end = g_tmpLut3d.data.size(); i<end; ++i )
|
|
{
|
|
outColorMgmtLuts[nInputEOTF].lut3d[4*i+0] = drm_quantize_lut_value( g_tmpLut3d.data[i].r );
|
|
outColorMgmtLuts[nInputEOTF].lut3d[4*i+1] = drm_quantize_lut_value( g_tmpLut3d.data[i].g );
|
|
outColorMgmtLuts[nInputEOTF].lut3d[4*i+2] = drm_quantize_lut_value( g_tmpLut3d.data[i].b );
|
|
outColorMgmtLuts[nInputEOTF].lut3d[4*i+3] = 0;
|
|
}
|
|
}
|
|
|
|
outColorMgmtLuts[nInputEOTF].bHasLut1D = true;
|
|
outColorMgmtLuts[nInputEOTF].bHasLut3D = true;
|
|
|
|
vulkan_update_luts(outColorMgmtLuts[nInputEOTF].vk_lut1d, outColorMgmtLuts[nInputEOTF].vk_lut3d, outColorMgmtLuts[nInputEOTF].lut1d, outColorMgmtLuts[nInputEOTF].lut3d);
|
|
}
|
|
}
|
|
|
|
int g_nAsyncFlipsEnabled = 0;
|
|
int g_nSteamMaxHeight = 0;
|
|
bool g_bVRRCapable_CachedValue = false;
|
|
bool g_bVRRInUse_CachedValue = false;
|
|
bool g_bSupportsST2084_CachedValue = false;
|
|
bool g_bForceHDR10OutputDebug = false;
|
|
bool g_bHDREnabled = false;
|
|
bool g_bHDRItmEnable = false;
|
|
int g_nCurrentRefreshRate_CachedValue = 0;
|
|
|
|
static void
|
|
update_color_mgmt()
|
|
{
|
|
// update pending native display colorimetry
|
|
if ( !BIsNested() )
|
|
{
|
|
drm_get_native_colorimetry( &g_DRM,
|
|
&g_ColorMgmt.pending.displayColorimetry, &g_ColorMgmt.pending.displayEOTF,
|
|
&g_ColorMgmt.pending.outputEncodingColorimetry, &g_ColorMgmt.pending.outputEncodingEOTF );
|
|
}
|
|
else if (g_bForceHDR10OutputDebug)
|
|
{
|
|
g_ColorMgmt.pending.displayColorimetry = displaycolorimetry_2020;
|
|
g_ColorMgmt.pending.displayEOTF = EOTF_PQ;
|
|
g_ColorMgmt.pending.outputEncodingColorimetry = displaycolorimetry_2020;
|
|
g_ColorMgmt.pending.outputEncodingEOTF = EOTF_PQ;
|
|
}
|
|
else
|
|
{
|
|
g_ColorMgmt.pending.displayColorimetry = displaycolorimetry_709;
|
|
g_ColorMgmt.pending.displayEOTF = EOTF_Gamma22;
|
|
g_ColorMgmt.pending.outputEncodingColorimetry = displaycolorimetry_709;
|
|
g_ColorMgmt.pending.outputEncodingEOTF = EOTF_Gamma22;
|
|
}
|
|
|
|
#ifdef COLOR_MGMT_MICROBENCH
|
|
struct timespec t0, t1;
|
|
#else
|
|
// check if any part of our color mgmt stack is dirty
|
|
if ( g_ColorMgmt.pending == g_ColorMgmt.current && g_ColorMgmt.serial != 0 )
|
|
return;
|
|
#endif
|
|
|
|
#ifdef COLOR_MGMT_MICROBENCH
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &t0);
|
|
#endif
|
|
|
|
if (g_ColorMgmt.pending.enabled)
|
|
{
|
|
create_color_mgmt_luts(g_ColorMgmt.pending, g_ColorMgmtLuts);
|
|
}
|
|
else
|
|
{
|
|
for ( uint32_t i = 0; i < EOTF_Count; i++ )
|
|
g_ColorMgmtLuts[i].reset();
|
|
}
|
|
|
|
#ifdef COLOR_MGMT_MICROBENCH
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &t1);
|
|
#endif
|
|
|
|
#ifdef COLOR_MGMT_MICROBENCH
|
|
double delta = (timespec_to_nanos(t1) - timespec_to_nanos(t0)) / 1000000.0;
|
|
|
|
static uint32_t iter = 0;
|
|
static const uint32_t iter_count = 120;
|
|
static double accum = 0;
|
|
|
|
accum += delta;
|
|
|
|
if (iter++ == iter_count)
|
|
{
|
|
printf("update_color_mgmt: %.3fms\n", accum / iter_count);
|
|
|
|
iter = 0;
|
|
accum = 0;
|
|
}
|
|
#endif
|
|
|
|
static uint32_t s_NextColorMgmtSerial = 0;
|
|
|
|
g_ColorMgmt.serial = ++s_NextColorMgmtSerial;
|
|
g_ColorMgmt.current = g_ColorMgmt.pending;
|
|
}
|
|
|
|
static void
|
|
update_screenshot_color_mgmt()
|
|
{
|
|
create_color_mgmt_luts(k_ScreenshotColorMgmt, g_ScreenshotColorMgmtLuts);
|
|
}
|
|
|
|
bool set_color_sdr_gamut_wideness( float flVal )
|
|
{
|
|
if ( g_ColorMgmt.pending.sdrGamutWideness == flVal )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.sdrGamutWideness = flVal;
|
|
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
|
|
bool set_internal_display_brightness( float flVal )
|
|
{
|
|
if ( flVal < 1.f )
|
|
{
|
|
flVal = 500.f;
|
|
}
|
|
|
|
if ( g_ColorMgmt.pending.flInternalDisplayBrightness == flVal )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.flInternalDisplayBrightness = flVal;
|
|
g_flInternalDisplayBrightnessNits = flVal;
|
|
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
|
|
bool set_sdr_on_hdr_brightness( float flVal )
|
|
{
|
|
if ( flVal < 1.f )
|
|
{
|
|
flVal = 203.f;
|
|
}
|
|
|
|
if ( g_ColorMgmt.pending.flSDROnHDRBrightness == flVal )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.flSDROnHDRBrightness = flVal;
|
|
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
|
|
bool set_hdr_input_gain( float flVal )
|
|
{
|
|
if ( flVal < 0.f )
|
|
{
|
|
flVal = 1.f;
|
|
}
|
|
|
|
if ( g_ColorMgmt.pending.flHDRInputGain == flVal )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.flHDRInputGain = flVal;
|
|
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
|
|
bool set_sdr_input_gain( float flVal )
|
|
{
|
|
if ( flVal < 0.f )
|
|
{
|
|
flVal = 1.f;
|
|
}
|
|
|
|
if ( g_ColorMgmt.pending.flSDRInputGain == flVal )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.flSDRInputGain = flVal;
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
|
|
bool set_color_nightmode( const nightmode_t &nightmode )
|
|
{
|
|
if ( g_ColorMgmt.pending.nightmode == nightmode )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.nightmode = nightmode;
|
|
|
|
return g_ColorMgmt.pending.enabled;
|
|
}
|
|
bool set_color_mgmt_enabled( bool bEnabled )
|
|
{
|
|
if ( g_ColorMgmt.pending.enabled == bEnabled )
|
|
return false;
|
|
|
|
g_ColorMgmt.pending.enabled = bEnabled;
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::shared_ptr<CVulkanTexture> s_MuraCorrectionImage[DRM_SCREEN_TYPE_COUNT];
|
|
static std::shared_ptr<wlserver_ctm> s_MuraCTMBlob[DRM_SCREEN_TYPE_COUNT];
|
|
static float g_flMuraScale = 1.0f;
|
|
static bool g_bMuraCompensationDisabled = false;
|
|
|
|
bool is_mura_correction_enabled()
|
|
{
|
|
return s_MuraCorrectionImage[drm_get_screen_type( &g_DRM )] != nullptr && !g_bMuraCompensationDisabled;
|
|
}
|
|
|
|
void update_mura_ctm()
|
|
{
|
|
s_MuraCTMBlob[DRM_SCREEN_TYPE_INTERNAL] = nullptr;
|
|
if (s_MuraCorrectionImage[DRM_SCREEN_TYPE_INTERNAL] == nullptr)
|
|
return;
|
|
|
|
static constexpr float kMuraMapScale = 0.0625f;
|
|
static constexpr float kMuraOffset = -127.0f / 255.0f;
|
|
|
|
// Mura's influence scales non-linearly with brightness, so we have an additional scale
|
|
// on top of the scale factor for the underlying mura map.
|
|
const float flScale = g_flMuraScale * kMuraMapScale;
|
|
glm::mat3x4 mura_scale_offset = glm::mat3x4
|
|
{
|
|
flScale, 0, 0, kMuraOffset * flScale,
|
|
0, flScale, 0, kMuraOffset * flScale,
|
|
0, 0, 0, 0, // No mura comp for blue channel.
|
|
};
|
|
s_MuraCTMBlob[DRM_SCREEN_TYPE_INTERNAL] = drm_create_ctm(&g_DRM, mura_scale_offset);
|
|
}
|
|
|
|
bool g_bMuraDebugFullColor = false;
|
|
|
|
bool set_mura_overlay( const char *path )
|
|
{
|
|
xwm_log.infof("[josh mura correction] Setting mura correction image to: %s", path);
|
|
s_MuraCorrectionImage[DRM_SCREEN_TYPE_INTERNAL] = nullptr;
|
|
update_mura_ctm();
|
|
|
|
std::string red_path = std::string(path) + "_red.png";
|
|
std::string green_path = std::string(path) + "_green.png";
|
|
|
|
int red_w, red_h, red_comp;
|
|
unsigned char *red_data = stbi_load(red_path.c_str(), &red_w, &red_h, &red_comp, 1);
|
|
int green_w, green_h, green_comp;
|
|
unsigned char *green_data = stbi_load(green_path.c_str(), &green_w, &green_h, &green_comp, 1);
|
|
if (!red_data || !green_data || red_w != green_w || red_h != green_h || red_comp != green_comp || red_comp != 1 || green_comp != 1)
|
|
{
|
|
xwm_log.infof("[josh mura correction] Couldn't load mura correction image, disabling mura correction.");
|
|
return true;
|
|
}
|
|
|
|
int w = red_w;
|
|
int h = red_h;
|
|
unsigned char *data = (unsigned char*)malloc(red_w * red_h * 4);
|
|
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
data[(y * w * 4) + (x * 4) + 0] = g_bMuraDebugFullColor ? 255 : red_data[y * w + x];
|
|
data[(y * w * 4) + (x * 4) + 1] = g_bMuraDebugFullColor ? 255 : green_data[y * w + x];
|
|
data[(y * w * 4) + (x * 4) + 2] = 127; // offset of 0.
|
|
data[(y * w * 4) + (x * 4) + 3] = 0; // Make alpha = 0 so we act as addtive.
|
|
}
|
|
}
|
|
free(red_data);
|
|
free(green_data);
|
|
|
|
CVulkanTexture::createFlags texCreateFlags;
|
|
texCreateFlags.bFlippable = !BIsNested();
|
|
texCreateFlags.bSampled = true;
|
|
s_MuraCorrectionImage[DRM_SCREEN_TYPE_INTERNAL] = vulkan_create_texture_from_bits(w, h, w, h, DRM_FORMAT_ABGR8888, texCreateFlags, (void*)data);
|
|
free(data);
|
|
|
|
xwm_log.infof("[josh mura correction] Loaded new mura correction image!");
|
|
|
|
update_mura_ctm();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool set_mura_scale(float new_scale)
|
|
{
|
|
bool diff = g_flMuraScale != new_scale;
|
|
g_flMuraScale = new_scale;
|
|
update_mura_ctm();
|
|
return diff;
|
|
}
|
|
|
|
bool set_color_3dlut_override(const char *path)
|
|
{
|
|
int nLutIndex = EOTF_Gamma22;
|
|
g_ColorMgmt.pending.externalDirtyCtr++;
|
|
g_ColorMgmtLutsOverride[nLutIndex].bHasLut3D = false;
|
|
|
|
FILE *f = fopen(path, "rb");
|
|
if (!f) {
|
|
return true;
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
long fsize = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
size_t elems = fsize / sizeof(uint16_t);
|
|
|
|
if (elems == 0) {
|
|
return true;
|
|
}
|
|
|
|
fread(g_ColorMgmtLutsOverride[nLutIndex].lut3d, elems, sizeof(uint16_t), f);
|
|
g_ColorMgmtLutsOverride[nLutIndex].bHasLut3D = true;
|
|
|
|
return true;
|
|
} |