mirror of https://github.com/xbmc/xbmc
[GSoC 2017] RetroPlayer: Video Shaders
Includes the following fixes from KOPRajs: * Calculate separate POT (Power-Of-Two) texture width and height * Update video shader preset if size of the source has changed * Fix multipass shaders texture scaling and filtering * Use optimal FBO texture sizes * Clean up multipass rendering logic * Set correct destination rectangle size for the last pass
This commit is contained in:
parent
b7126ff52b
commit
801dfd409d
|
|
@ -132,6 +132,7 @@ cmake_install.cmake
|
||||||
/addons/kodi.binary.instance.inputstream/addon.xml
|
/addons/kodi.binary.instance.inputstream/addon.xml
|
||||||
/addons/kodi.binary.instance.peripheral/addon.xml
|
/addons/kodi.binary.instance.peripheral/addon.xml
|
||||||
/addons/kodi.binary.instance.pvr/addon.xml
|
/addons/kodi.binary.instance.pvr/addon.xml
|
||||||
|
/addons/kodi.binary.instance.shaderpreset/addon.xml
|
||||||
/addons/kodi.binary.instance.screensaver/addon.xml
|
/addons/kodi.binary.instance.screensaver/addon.xml
|
||||||
/addons/kodi.binary.instance.vfs/addon.xml
|
/addons/kodi.binary.instance.vfs/addon.xml
|
||||||
/addons/kodi.binary.instance.videocodec/addon.xml
|
/addons/kodi.binary.instance.videocodec/addon.xml
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon id="kodi.binary.instance.shaderpreset" version="@ADDON_INSTANCE_VERSION_SHADERPRESET@" provider-name="Team Kodi">
|
||||||
|
<backwards-compatibility abi="@ADDON_INSTANCE_VERSION_SHADERPRESET_MIN@"/>
|
||||||
|
<requires>
|
||||||
|
<import addon="xbmc.core" version="0.1.0"/>
|
||||||
|
</requires>
|
||||||
|
</addon>
|
||||||
|
|
@ -11,7 +11,7 @@ xbmc/cores/RetroPlayer/playback cores/RetroPlaye
|
||||||
xbmc/cores/RetroPlayer/process cores/RetroPlayer/process
|
xbmc/cores/RetroPlayer/process cores/RetroPlayer/process
|
||||||
xbmc/cores/RetroPlayer/rendering cores/RetroPlayer/rendering
|
xbmc/cores/RetroPlayer/rendering cores/RetroPlayer/rendering
|
||||||
xbmc/cores/RetroPlayer/rendering/VideoRenderers cores/RetroPlayer/rendering/VideoRenderers
|
xbmc/cores/RetroPlayer/rendering/VideoRenderers cores/RetroPlayer/rendering/VideoRenderers
|
||||||
xbmc/cores/RetroPlayer/rendering/VideoShaders cores/RetroPlayer/rendering/VideoShaders
|
|
||||||
xbmc/cores/RetroPlayer/savestates cores/RetroPlayer/savestates
|
xbmc/cores/RetroPlayer/savestates cores/RetroPlayer/savestates
|
||||||
|
xbmc/cores/RetroPlayer/shaders cores/RetroPlayer/shaders
|
||||||
xbmc/cores/RetroPlayer/streams cores/RetroPlayer/streams
|
xbmc/cores/RetroPlayer/streams cores/RetroPlayer/streams
|
||||||
xbmc/cores/RetroPlayer/streams/memory cores/RetroPlayer/streams/memory
|
xbmc/cores/RetroPlayer/streams/memory cores/RetroPlayer/streams/memory
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
xbmc/cores/RetroPlayer/process/windows cores/RetroPlayer/process/windows
|
xbmc/cores/RetroPlayer/process/windows cores/RetroPlayer/process/windows
|
||||||
xbmc/cores/RetroPlayer/rendering/VideoShaders/windows cores/RetroPlayer/rendering/VideoShaders/windows
|
xbmc/cores/RetroPlayer/shaders/windows cores/RetroPlayer/shaders/windows
|
||||||
xbmc/cores/VideoPlayer/Process/windows cores/VideoPlayer/Process/windows
|
xbmc/cores/VideoPlayer/Process/windows cores/VideoPlayer/Process/windows
|
||||||
xbmc/cores/VideoPlayer/VideoRenderers/windows cores/VideoPlayer/VideoRenderers/windows
|
xbmc/cores/VideoPlayer/VideoRenderers/windows cores/VideoPlayer/VideoRenderers/windows
|
||||||
xbmc/input/touch input/touch
|
xbmc/input/touch input/touch
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,16 @@ IF EXIST BUILD_WIN32\addons\game.libretro.* (
|
||||||
SET /A Counter = !Counter! + 1
|
SET /A Counter = !Counter! + 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
FOR /F "tokens=*" %%P IN ('dir /B /AD BUILD_WIN32\addons\game.shader.*') DO (
|
||||||
|
FOR /f "delims=<" %%N in ('powershell.exe -ExecutionPolicy Unrestricted -command "& {[xml]$a = get-content BUILD_WIN32\addons\%%P\addon.xml;$a.addon.name}"') do (
|
||||||
|
ECHO Section "%%N" SecGameAddons!Counter! >> game-addons.nsi
|
||||||
|
ECHO SectionIn 1 2 >> game-addons.nsi
|
||||||
|
ECHO SetOutPath "$INSTDIR\addons\%%P" >> game-addons.nsi
|
||||||
|
ECHO File /r "${app_root}\addons\%%P\*.*" >> game-addons.nsi
|
||||||
|
ECHO SectionEnd >> game-addons.nsi
|
||||||
|
SET /A Counter = !Counter! + 1
|
||||||
|
)
|
||||||
|
)
|
||||||
ECHO SectionGroupEnd >> game-addons.nsi
|
ECHO SectionGroupEnd >> game-addons.nsi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
<addon>kodi.binary.instance.peripheral</addon>
|
<addon>kodi.binary.instance.peripheral</addon>
|
||||||
<addon>kodi.binary.instance.pvr</addon>
|
<addon>kodi.binary.instance.pvr</addon>
|
||||||
<addon>kodi.binary.instance.screensaver</addon>
|
<addon>kodi.binary.instance.screensaver</addon>
|
||||||
|
<addon>kodi.binary.instance.shaderpreset</addon>
|
||||||
<addon>kodi.binary.instance.vfs</addon>
|
<addon>kodi.binary.instance.vfs</addon>
|
||||||
<addon>kodi.binary.instance.videocodec</addon>
|
<addon>kodi.binary.instance.videocodec</addon>
|
||||||
<addon>kodi.binary.instance.visualization</addon>
|
<addon>kodi.binary.instance.visualization</addon>
|
||||||
|
|
@ -52,6 +53,7 @@
|
||||||
<addon>xbmc.metadata</addon>
|
<addon>xbmc.metadata</addon>
|
||||||
<addon>xbmc.python</addon>
|
<addon>xbmc.python</addon>
|
||||||
<addon>xbmc.webinterface</addon>
|
<addon>xbmc.webinterface</addon>
|
||||||
|
<addon optional="true">game.shader.presets</addon>
|
||||||
<addon optional="true">inputstream.adaptive</addon>
|
<addon optional="true">inputstream.adaptive</addon>
|
||||||
<addon optional="true">peripheral.joystick</addon>
|
<addon optional="true">peripheral.joystick</addon>
|
||||||
<addon optional="true">service.xbmc.versioncheck</addon>
|
<addon optional="true">service.xbmc.versioncheck</addon>
|
||||||
|
|
|
||||||
|
|
@ -200,9 +200,9 @@ bool CServiceManager::InitStageThree(const std::shared_ptr<CProfileManager>& pro
|
||||||
// Peripherals depends on strings being loaded before stage 3
|
// Peripherals depends on strings being loaded before stage 3
|
||||||
m_peripherals->Initialise();
|
m_peripherals->Initialise();
|
||||||
|
|
||||||
m_gameServices =
|
m_gameServices = std::make_unique<GAME::CGameServices>(
|
||||||
std::make_unique<GAME::CGameServices>(*m_gameControllerManager, *m_gameRenderManager,
|
*m_gameControllerManager, *m_gameRenderManager, *m_peripherals, *profileManager,
|
||||||
*m_peripherals, *profileManager, *m_inputManager);
|
*m_inputManager, *m_addonMgr);
|
||||||
|
|
||||||
m_contextMenuManager->Init();
|
m_contextMenuManager->Init();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ AddonPtr CAddonBuilder::Generate(const AddonInfoPtr& info, AddonType type)
|
||||||
return std::make_shared<CAddonDll>(info, type);
|
return std::make_shared<CAddonDll>(info, type);
|
||||||
case AddonType::GAMEDLL:
|
case AddonType::GAMEDLL:
|
||||||
return std::make_shared<GAME::CGameClient>(info);
|
return std::make_shared<GAME::CGameClient>(info);
|
||||||
|
case AddonType::SHADERDLL:
|
||||||
|
return std::make_shared<CAddonDll>(info, type);
|
||||||
case AddonType::PLUGIN:
|
case AddonType::PLUGIN:
|
||||||
case AddonType::SCRIPT:
|
case AddonType::SCRIPT:
|
||||||
return std::make_shared<CPluginSource>(info, type);
|
return std::make_shared<CPluginSource>(info, type);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
namespace ADDON
|
namespace ADDON
|
||||||
{
|
{
|
||||||
|
|
||||||
const std::vector<AddonType> ADDONS_TO_CACHE = {AddonType::GAMEDLL};
|
const std::vector<AddonType> ADDONS_TO_CACHE = {AddonType::GAMEDLL, AddonType::SHADERDLL};
|
||||||
|
|
||||||
CBinaryAddonCache::~CBinaryAddonCache()
|
CBinaryAddonCache::~CBinaryAddonCache()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ set(SOURCES Addon.cpp
|
||||||
Scraper.cpp
|
Scraper.cpp
|
||||||
ScreenSaver.cpp
|
ScreenSaver.cpp
|
||||||
Service.cpp
|
Service.cpp
|
||||||
|
ShaderPreset.cpp
|
||||||
Skin.cpp
|
Skin.cpp
|
||||||
UISoundsResource.cpp
|
UISoundsResource.cpp
|
||||||
VFSEntry.cpp
|
VFSEntry.cpp
|
||||||
|
|
@ -65,6 +66,7 @@ set(HEADERS Addon.h
|
||||||
Scraper.h
|
Scraper.h
|
||||||
ScreenSaver.h
|
ScreenSaver.h
|
||||||
Service.h
|
Service.h
|
||||||
|
ShaderPreset.h
|
||||||
Skin.h
|
Skin.h
|
||||||
UISoundsResource.h
|
UISoundsResource.h
|
||||||
VFSEntry.h
|
VFSEntry.h
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,292 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderPreset.h"
|
||||||
|
|
||||||
|
#include "addons/addoninfo/AddonInfo.h"
|
||||||
|
#include "addons/addoninfo/AddonType.h"
|
||||||
|
#include "addons/binary-addons/BinaryAddonBase.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderPreset.h"
|
||||||
|
#include "filesystem/SpecialProtocol.h"
|
||||||
|
#include "utils/StringUtils.h"
|
||||||
|
#include "utils/URIUtils.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace ADDON;
|
||||||
|
|
||||||
|
// --- CShaderPreset -----------------------------------------------------------
|
||||||
|
|
||||||
|
CShaderPreset::CShaderPreset(preset_file file, AddonInstance_ShaderPreset& instanceStruct)
|
||||||
|
: m_file(file), m_struct(instanceStruct)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderPreset::~CShaderPreset()
|
||||||
|
{
|
||||||
|
m_struct.toAddon->PresetFileFree(&m_struct, m_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::ReadShaderPreset(video_shader& shader)
|
||||||
|
{
|
||||||
|
return m_struct.toAddon->VideoShaderRead(&m_struct, m_file, &shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::WriteShaderPreset(const video_shader& shader)
|
||||||
|
{
|
||||||
|
return m_struct.toAddon->VideoShaderWrite(&m_struct, m_file, &shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::ResolveParameters(video_shader& shader)
|
||||||
|
{
|
||||||
|
return m_struct.toAddon->VideoShaderResolveParameters(&m_struct, m_file, &shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::FreeShaderPreset(video_shader& shader)
|
||||||
|
{
|
||||||
|
m_struct.toAddon->VideoShaderFree(&m_struct, &shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CShaderPresetAddon ------------------------------------------------------
|
||||||
|
|
||||||
|
CShaderPresetAddon::CShaderPresetAddon(const AddonInfoPtr& addonInfo)
|
||||||
|
: IAddonInstanceHandler(ADDON_INSTANCE_SHADERPRESET, addonInfo)
|
||||||
|
{
|
||||||
|
// Create "C" interface structures, used to prevent API problems on update
|
||||||
|
m_ifc.shaderpreset = new AddonInstance_ShaderPreset;
|
||||||
|
m_ifc.shaderpreset->props = new AddonProps_ShaderPreset();
|
||||||
|
m_ifc.shaderpreset->toAddon = new KodiToAddonFuncTable_ShaderPreset();
|
||||||
|
m_ifc.shaderpreset->toKodi = new AddonToKodiFuncTable_ShaderPreset();
|
||||||
|
|
||||||
|
ResetProperties();
|
||||||
|
|
||||||
|
// Initialize properties
|
||||||
|
m_strUserPath = CSpecialProtocol::TranslatePath(Profile());
|
||||||
|
m_strClientPath = CSpecialProtocol::TranslatePath(Path());
|
||||||
|
m_extensions = StringUtils::Split(
|
||||||
|
addonInfo->Type(ADDON::AddonType::SHADERDLL)->GetValue("@extensions").asString(), "|");
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderPresetAddon::~CShaderPresetAddon(void)
|
||||||
|
{
|
||||||
|
DestroyAddon();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetAddon::CreateAddon(void)
|
||||||
|
{
|
||||||
|
std::unique_lock<CSharedSection> lock(m_dllSection);
|
||||||
|
|
||||||
|
// Reset all properties to defaults
|
||||||
|
ResetProperties();
|
||||||
|
|
||||||
|
// Initialise the add-on
|
||||||
|
CLog::Log(LOGDEBUG, "{} - creating ShaderPreset add-on instance '{}'", __FUNCTION__, Name());
|
||||||
|
|
||||||
|
if (CreateInstance() != ADDON_STATUS_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetAddon::DestroyAddon()
|
||||||
|
{
|
||||||
|
std::unique_lock<CSharedSection> lock(m_dllSection);
|
||||||
|
|
||||||
|
DestroyInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetAddon::ResetProperties(void)
|
||||||
|
{
|
||||||
|
m_ifc.shaderpreset->props->user_path = m_strUserPath.c_str();
|
||||||
|
m_ifc.shaderpreset->props->addon_path = m_strClientPath.c_str();
|
||||||
|
|
||||||
|
m_ifc.shaderpreset->toKodi->kodiInstance = this;
|
||||||
|
|
||||||
|
memset(m_ifc.shaderpreset->toAddon, 0, sizeof(KodiToAddonFuncTable_ShaderPreset));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetAddon::LoadPreset(const std::string& presetPath,
|
||||||
|
SHADER::IShaderPreset& shaderPreset)
|
||||||
|
{
|
||||||
|
bool bSuccess = false;
|
||||||
|
|
||||||
|
std::string translatedPath = CSpecialProtocol::TranslatePath(presetPath);
|
||||||
|
|
||||||
|
preset_file file =
|
||||||
|
m_ifc.shaderpreset->toAddon->PresetFileNew(m_ifc.shaderpreset, translatedPath.c_str());
|
||||||
|
|
||||||
|
if (file != nullptr)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CShaderPreset> shaderPresetAddon =
|
||||||
|
std::make_unique<CShaderPreset>(file, *m_ifc.shaderpreset);
|
||||||
|
|
||||||
|
video_shader videoShader = {};
|
||||||
|
if (shaderPresetAddon->ReadShaderPreset(videoShader))
|
||||||
|
{
|
||||||
|
if (shaderPresetAddon->ResolveParameters(videoShader))
|
||||||
|
{
|
||||||
|
TranslateShaderPreset(videoShader, shaderPreset);
|
||||||
|
bSuccess = true;
|
||||||
|
}
|
||||||
|
shaderPresetAddon->FreeShaderPreset(videoShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @todo Instead of copying every parameter to every pass and resolving them
|
||||||
|
//! later in GetShaderParameters, we should resolve which param goes to which
|
||||||
|
//! shader in the add-on
|
||||||
|
void CShaderPresetAddon::TranslateShaderPreset(const video_shader& shader,
|
||||||
|
SHADER::IShaderPreset& shaderPreset)
|
||||||
|
{
|
||||||
|
if (shader.passes != nullptr)
|
||||||
|
{
|
||||||
|
for (unsigned int passIdx = 0; passIdx < shader.pass_count; ++passIdx)
|
||||||
|
{
|
||||||
|
SHADER::ShaderPass shaderPass;
|
||||||
|
TranslateShaderPass(shader.passes[passIdx], shaderPass);
|
||||||
|
|
||||||
|
if (shader.luts != nullptr)
|
||||||
|
{
|
||||||
|
for (unsigned int lutIdx = 0; lutIdx < shader.lut_count; ++lutIdx)
|
||||||
|
{
|
||||||
|
SHADER::ShaderLut shaderLut;
|
||||||
|
TranslateShaderLut(shader.luts[lutIdx], shaderLut);
|
||||||
|
shaderPass.luts.emplace_back(std::move(shaderLut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shader.parameters != nullptr)
|
||||||
|
{
|
||||||
|
for (unsigned int parIdx = 0; parIdx < shader.parameter_count; ++parIdx)
|
||||||
|
{
|
||||||
|
SHADER::ShaderParameter shaderParam;
|
||||||
|
TranslateShaderParameter(shader.parameters[parIdx], shaderParam);
|
||||||
|
shaderPass.parameters.emplace_back(std::move(shaderParam));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shaderPreset.GetPasses().emplace_back(std::move(shaderPass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetAddon::TranslateShaderPass(const video_shader_pass& pass,
|
||||||
|
SHADER::ShaderPass& shaderPass)
|
||||||
|
{
|
||||||
|
shaderPass.sourcePath = pass.source_path ? pass.source_path : "";
|
||||||
|
shaderPass.vertexSource = pass.vertex_source ? pass.vertex_source : "";
|
||||||
|
shaderPass.fragmentSource = pass.fragment_source ? pass.fragment_source : "";
|
||||||
|
shaderPass.filterType = TranslateFilterType(pass.filter);
|
||||||
|
shaderPass.wrapType = TranslateWrapType(pass.wrap);
|
||||||
|
shaderPass.frameCountMod = pass.frame_count_mod;
|
||||||
|
|
||||||
|
const auto& fbo = pass.fbo;
|
||||||
|
auto& shaderFbo = shaderPass.fbo;
|
||||||
|
|
||||||
|
shaderFbo.scaleX.scaleType = TranslateScaleType(fbo.scale_x.type);
|
||||||
|
switch (fbo.scale_x.type)
|
||||||
|
{
|
||||||
|
case SHADER_SCALE_TYPE_ABSOLUTE:
|
||||||
|
shaderFbo.scaleX.abs = fbo.scale_x.abs;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
shaderFbo.scaleX.scale = fbo.scale_x.scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shaderFbo.scaleY.scaleType = TranslateScaleType(fbo.scale_y.type);
|
||||||
|
switch (fbo.scale_y.type)
|
||||||
|
{
|
||||||
|
case SHADER_SCALE_TYPE_ABSOLUTE:
|
||||||
|
shaderFbo.scaleY.abs = fbo.scale_y.abs;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
shaderFbo.scaleY.scale = fbo.scale_y.scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
shaderFbo.floatFramebuffer = fbo.fp_fbo;
|
||||||
|
shaderFbo.sRgbFramebuffer = fbo.srgb_fbo;
|
||||||
|
|
||||||
|
shaderPass.mipmap = pass.mipmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetAddon::TranslateShaderLut(const video_shader_lut& lut,
|
||||||
|
SHADER::ShaderLut& shaderLut)
|
||||||
|
{
|
||||||
|
shaderLut.strId = lut.id ? lut.id : "";
|
||||||
|
shaderLut.path = lut.path ? lut.path : "";
|
||||||
|
shaderLut.filterType = TranslateFilterType(lut.filter);
|
||||||
|
shaderLut.wrapType = TranslateWrapType(lut.wrap);
|
||||||
|
shaderLut.mipmap = lut.mipmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetAddon::TranslateShaderParameter(const video_shader_parameter& param,
|
||||||
|
SHADER::ShaderParameter& shaderParam)
|
||||||
|
{
|
||||||
|
shaderParam.strId = param.id ? param.id : "";
|
||||||
|
shaderParam.description = param.desc ? param.desc : "";
|
||||||
|
shaderParam.current = param.current;
|
||||||
|
shaderParam.minimum = param.minimum;
|
||||||
|
shaderParam.initial = param.initial;
|
||||||
|
shaderParam.maximum = param.maximum;
|
||||||
|
shaderParam.step = param.step;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHADER::FilterType CShaderPresetAddon::TranslateFilterType(SHADER_FILTER_TYPE type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SHADER_FILTER_TYPE_LINEAR:
|
||||||
|
return SHADER::FilterType::LINEAR;
|
||||||
|
case SHADER_FILTER_TYPE_NEAREST:
|
||||||
|
return SHADER::FilterType::NEAREST;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SHADER::FilterType::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHADER::WrapType CShaderPresetAddon::TranslateWrapType(SHADER_WRAP_TYPE type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SHADER_WRAP_TYPE_BORDER:
|
||||||
|
return SHADER::WrapType::BORDER;
|
||||||
|
case SHADER_WRAP_TYPE_EDGE:
|
||||||
|
return SHADER::WrapType::EDGE;
|
||||||
|
case SHADER_WRAP_TYPE_REPEAT:
|
||||||
|
return SHADER::WrapType::REPEAT;
|
||||||
|
case SHADER_WRAP_TYPE_MIRRORED_REPEAT:
|
||||||
|
return SHADER::WrapType::MIRRORED_REPEAT;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SHADER::WrapType::BORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHADER::ScaleType CShaderPresetAddon::TranslateScaleType(SHADER_SCALE_TYPE type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SHADER_SCALE_TYPE_INPUT:
|
||||||
|
return SHADER::ScaleType::INPUT;
|
||||||
|
case SHADER_SCALE_TYPE_ABSOLUTE:
|
||||||
|
return SHADER::ScaleType::ABSOLUTE_SCALE;
|
||||||
|
case SHADER_SCALE_TYPE_VIEWPORT:
|
||||||
|
return SHADER::ScaleType::VIEWPORT;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SHADER::ScaleType::INPUT;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "addons/binary-addons/AddonInstanceHandler.h"
|
||||||
|
#include "addons/kodi-dev-kit/include/kodi/addon-instance/ShaderPreset.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderPresetLoader.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderTypes.h"
|
||||||
|
#include "threads/SharedSection.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ADDON
|
||||||
|
{
|
||||||
|
class CAddonInfo;
|
||||||
|
typedef std::shared_ptr<CAddonInfo> AddonInfoPtr;
|
||||||
|
|
||||||
|
class CShaderPreset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CShaderPreset(preset_file file, AddonInstance_ShaderPreset& instanceStruct);
|
||||||
|
~CShaderPreset();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Loads preset file and all associated state (passes, textures,
|
||||||
|
* imports, etc)
|
||||||
|
*
|
||||||
|
* \param shader Shader passes handle
|
||||||
|
*
|
||||||
|
* \return True if successful, otherwise false
|
||||||
|
*/
|
||||||
|
bool ReadShaderPreset(video_shader& shader);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Save preset and all associated state (passes, textures, imports,
|
||||||
|
* etc) to disk
|
||||||
|
*
|
||||||
|
* \param shader Shader passes handle
|
||||||
|
*/
|
||||||
|
void WriteShaderPreset(const video_shader& shader);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Resolve all shader parameters belonging to the shader preset
|
||||||
|
*
|
||||||
|
* \param shader Shader passes handle
|
||||||
|
*
|
||||||
|
* \return True if successful, otherwise false
|
||||||
|
*/
|
||||||
|
bool ResolveParameters(video_shader& shader);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Frees a preset file and all associated resources
|
||||||
|
*
|
||||||
|
* \param shader Shader passes handle
|
||||||
|
*/
|
||||||
|
void FreeShaderPreset(video_shader& shader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
preset_file m_file;
|
||||||
|
AddonInstance_ShaderPreset& m_struct;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Wrapper class that wraps the shader presets add-on
|
||||||
|
*/
|
||||||
|
class CShaderPresetAddon : public IAddonInstanceHandler, public KODI::SHADER::IShaderPresetLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CShaderPresetAddon(const AddonInfoPtr& addonInfo);
|
||||||
|
~CShaderPresetAddon() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Initialise the instance of this add-on
|
||||||
|
*/
|
||||||
|
bool CreateAddon();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Deinitialize the instance of this add-on
|
||||||
|
*/
|
||||||
|
void DestroyAddon();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Get the shader preset extensions supported by this add-on
|
||||||
|
*/
|
||||||
|
const std::vector<std::string>& GetExtensions() const { return m_extensions; }
|
||||||
|
|
||||||
|
// Implementation of IShaderPresetLoader
|
||||||
|
bool LoadPreset(const std::string& presetPath,
|
||||||
|
KODI::SHADER::IShaderPreset& shaderPreset) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* \brief Reset all class members to their defaults. Called by the constructors
|
||||||
|
*/
|
||||||
|
void ResetProperties(void);
|
||||||
|
|
||||||
|
static void TranslateShaderPreset(const video_shader& shader,
|
||||||
|
KODI::SHADER::IShaderPreset& shaderPreset);
|
||||||
|
static void TranslateShaderPass(const video_shader_pass& pass,
|
||||||
|
KODI::SHADER::ShaderPass& shaderPass);
|
||||||
|
static void TranslateShaderLut(const video_shader_lut& lut, KODI::SHADER::ShaderLut& shaderLut);
|
||||||
|
static void TranslateShaderParameter(const video_shader_parameter& param,
|
||||||
|
KODI::SHADER::ShaderParameter& shaderParam);
|
||||||
|
static KODI::SHADER::FilterType TranslateFilterType(SHADER_FILTER_TYPE type);
|
||||||
|
static KODI::SHADER::WrapType TranslateWrapType(SHADER_WRAP_TYPE type);
|
||||||
|
static KODI::SHADER::ScaleType TranslateScaleType(SHADER_SCALE_TYPE type);
|
||||||
|
|
||||||
|
// Cache for const char* members in AddonProps_ShaderPreset
|
||||||
|
std::string m_strUserPath; /*!< \brief Translated path to the user profile */
|
||||||
|
std::string m_strClientPath; /*!< \brief Translated path to this add-on */
|
||||||
|
|
||||||
|
std::vector<std::string> m_extensions;
|
||||||
|
|
||||||
|
CSharedSection m_dllSection;
|
||||||
|
};
|
||||||
|
} // namespace ADDON
|
||||||
|
|
@ -38,7 +38,7 @@ typedef struct
|
||||||
} TypeMapping;
|
} TypeMapping;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static constexpr const std::array<TypeMapping, 40> types =
|
static constexpr const std::array<TypeMapping, 41> types =
|
||||||
{{
|
{{
|
||||||
{"unknown", "", AddonType::UNKNOWN, 0, AddonInstanceSupport::SUPPORT_NONE, "" },
|
{"unknown", "", AddonType::UNKNOWN, 0, AddonInstanceSupport::SUPPORT_NONE, "" },
|
||||||
{"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS, 24016, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonAlbumInfo.png" },
|
{"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS, 24016, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonAlbumInfo.png" },
|
||||||
|
|
@ -63,6 +63,7 @@ static constexpr const std::array<TypeMapping, 40> types =
|
||||||
{"xbmc.addon.repository", "", AddonType::REPOSITORY, 24011, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonRepository.png" },
|
{"xbmc.addon.repository", "", AddonType::REPOSITORY, 24011, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonRepository.png" },
|
||||||
{"kodi.pvrclient", "xbmc.pvrclient", AddonType::PVRDLL, 24019, AddonInstanceSupport::SUPPORT_SETTINGS, "DefaultAddonPVRClient.png" },
|
{"kodi.pvrclient", "xbmc.pvrclient", AddonType::PVRDLL, 24019, AddonInstanceSupport::SUPPORT_SETTINGS, "DefaultAddonPVRClient.png" },
|
||||||
{"kodi.gameclient", "", AddonType::GAMEDLL, 35049, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonGame.png" },
|
{"kodi.gameclient", "", AddonType::GAMEDLL, 35049, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonGame.png" },
|
||||||
|
{"kodi.shader.presets", "", AddonType::SHADERDLL, 35256, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonGame.png" },
|
||||||
{"kodi.peripheral", "", AddonType::PERIPHERALDLL, 35010, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonPeripheral.png" },
|
{"kodi.peripheral", "", AddonType::PERIPHERALDLL, 35010, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonPeripheral.png" },
|
||||||
{"xbmc.addon.video", "", AddonType::VIDEO, 1037, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonVideo.png" },
|
{"xbmc.addon.video", "", AddonType::VIDEO, 1037, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonVideo.png" },
|
||||||
{"xbmc.addon.audio", "", AddonType::AUDIO, 1038, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMusic.png" },
|
{"xbmc.addon.audio", "", AddonType::AUDIO, 1038, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMusic.png" },
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ enum class AddonType
|
||||||
PVRDLL,
|
PVRDLL,
|
||||||
INPUTSTREAM,
|
INPUTSTREAM,
|
||||||
GAMEDLL,
|
GAMEDLL,
|
||||||
|
SHADERDLL,
|
||||||
PERIPHERALDLL,
|
PERIPHERALDLL,
|
||||||
SCRIPT,
|
SCRIPT,
|
||||||
SCRIPT_WEATHER,
|
SCRIPT_WEATHER,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ set(HEADERS
|
||||||
PVR.h
|
PVR.h
|
||||||
Peripheral.h
|
Peripheral.h
|
||||||
Screensaver.h
|
Screensaver.h
|
||||||
|
ShaderPreset.h
|
||||||
VFS.h
|
VFS.h
|
||||||
VideoCodec.h
|
VideoCodec.h
|
||||||
Visualization.h
|
Visualization.h
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../AddonBase.h"
|
||||||
|
#include "../c-api/addon-instance/shaderpreset.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
namespace kodi
|
||||||
|
{
|
||||||
|
namespace addon
|
||||||
|
{
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/// @addtogroup cpp_kodi_addon_shader_preset
|
||||||
|
/// @brief @cpp_class{ kodi::addon::CInstanceShaderPreset }
|
||||||
|
/// **Shader preset add-on instance**\n
|
||||||
|
/// This class provides the basic shader preset processing system for use as
|
||||||
|
/// an add-on in Kodi.
|
||||||
|
///
|
||||||
|
/// This class is created at addon by Kodi.
|
||||||
|
///
|
||||||
|
class ATTR_DLL_LOCAL CInstanceShaderPreset : public IAddonInstance
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//============================================================================
|
||||||
|
/// @defgroup cpp_kodi_addon_game_Base 1. Basic functions
|
||||||
|
/// @ingroup cpp_kodi_addon_game
|
||||||
|
/// @brief **Functions to manage the addon and get basic information about it**
|
||||||
|
///
|
||||||
|
///@{
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Shader preset class constructor
|
||||||
|
///
|
||||||
|
/// Used by an add-on that only supports Shader Presets, and only in one
|
||||||
|
/// instance.
|
||||||
|
///
|
||||||
|
/// This class is created in the addon by Kodi.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// **Here's an example of how to use this class:**
|
||||||
|
/// ~~~~~~~~~~~~~{.cpp}
|
||||||
|
/// #include <kodi/addon-instance/ShaderPreset.h>
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// class ATTR_DLL_LOCAL CShaderPresetExample
|
||||||
|
/// : public kodi::addon::CAddonBase,
|
||||||
|
/// public kodi::addon::CInstanceShaderPreset
|
||||||
|
/// {
|
||||||
|
/// public:
|
||||||
|
/// CShaderPresetExample()
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// virtual ~CShaderPresetExample();
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// ...
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// ADDONCREATOR(CShaderPresetExample)
|
||||||
|
/// ~~~~~~~~~~~~~
|
||||||
|
///
|
||||||
|
CInstanceShaderPreset()
|
||||||
|
: IAddonInstance(IInstanceInfo(CPrivateBase::m_interface->firstKodiInstance))
|
||||||
|
{
|
||||||
|
if (CPrivateBase::m_interface->globalSingleInstance != nullptr)
|
||||||
|
throw std::logic_error("kodi::addon::CInstanceShaderPreset: Creation of "
|
||||||
|
"more than one in single instance is not allowed!");
|
||||||
|
|
||||||
|
SetAddonStruct(CPrivateBase::m_interface->firstKodiInstance);
|
||||||
|
CPrivateBase::m_interface->globalSingleInstance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Destructor
|
||||||
|
///
|
||||||
|
~CInstanceShaderPreset() override = default;
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Used to get the full path to the add-on's user profile
|
||||||
|
///
|
||||||
|
/// @return Path to the user profile
|
||||||
|
///
|
||||||
|
/// @remarks Only called from the add-on
|
||||||
|
///
|
||||||
|
std::string UserPath() const
|
||||||
|
{
|
||||||
|
if (m_instanceData->props->user_path != nullptr)
|
||||||
|
return m_instanceData->props->user_path;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Used to get the full path where the add-on is installed
|
||||||
|
///
|
||||||
|
/// @return The add-on installation path
|
||||||
|
///
|
||||||
|
/// @remarks Only called from the add-on itself
|
||||||
|
///
|
||||||
|
std::string AddonPath() const
|
||||||
|
{
|
||||||
|
if (m_instanceData->props->addon_path != nullptr)
|
||||||
|
return m_instanceData->props->addon_path;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief **Loads a preset file**
|
||||||
|
///
|
||||||
|
/// @param path The path to the preset file
|
||||||
|
///
|
||||||
|
/// @return The preset file, or NULL if file doesn't exist
|
||||||
|
///
|
||||||
|
virtual preset_file PresetFileNew(const char* path) { return nullptr; }
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief **Free a preset file**
|
||||||
|
///
|
||||||
|
virtual void PresetFileFree(preset_file file) {}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Loads preset file and all associated state (passes, textures,
|
||||||
|
/// imports, etc)
|
||||||
|
///
|
||||||
|
/// @param file Preset file to read from
|
||||||
|
/// @param shader Shader passes handle
|
||||||
|
///
|
||||||
|
/// @return True if successful, otherwise false
|
||||||
|
///
|
||||||
|
virtual bool ShaderPresetRead(preset_file file, video_shader& shader) { return false; }
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Save preset and all associated state (passes, textures, imports,
|
||||||
|
/// etc) to disk
|
||||||
|
///
|
||||||
|
/// @param file Preset file to read from
|
||||||
|
/// @param shader Shader passes handle
|
||||||
|
///
|
||||||
|
virtual void ShaderPresetWrite(preset_file file, const video_shader& shader) {}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Resolve all shader parameters belonging to the shader preset
|
||||||
|
///
|
||||||
|
/// @param file Preset file to read from
|
||||||
|
/// @param shader Shader passes handle
|
||||||
|
///
|
||||||
|
/// @return True if successful, otherwise false
|
||||||
|
///
|
||||||
|
virtual bool ShaderPresetResolveParameters(preset_file file, video_shader& shader)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/// @brief Free all state related to shader preset
|
||||||
|
///
|
||||||
|
/// @param The shader object to free
|
||||||
|
///
|
||||||
|
virtual void ShaderPresetFree(video_shader& shader) {}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetAddonStruct(KODI_ADDON_INSTANCE_STRUCT* instance)
|
||||||
|
{
|
||||||
|
instance->hdl = this;
|
||||||
|
|
||||||
|
instance->shaderpreset->toAddon->PresetFileNew = ADDON_preset_file_new;
|
||||||
|
instance->shaderpreset->toAddon->PresetFileFree = ADDON_preset_file_free;
|
||||||
|
|
||||||
|
instance->shaderpreset->toAddon->VideoShaderRead = ADDON_video_shader_read_file;
|
||||||
|
instance->shaderpreset->toAddon->VideoShaderWrite = ADDON_video_shader_write_file;
|
||||||
|
instance->shaderpreset->toAddon->VideoShaderResolveParameters =
|
||||||
|
ADDON_video_shader_resolve_parameters;
|
||||||
|
instance->shaderpreset->toAddon->VideoShaderFree = ADDON_video_shader_free;
|
||||||
|
|
||||||
|
m_instanceData = instance->shaderpreset;
|
||||||
|
m_instanceData->toAddon->addonInstance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Shader preset operations ----------------------------------------------
|
||||||
|
|
||||||
|
inline static preset_file ADDON_preset_file_new(const AddonInstance_ShaderPreset* addonInstance,
|
||||||
|
const char* path)
|
||||||
|
{
|
||||||
|
return static_cast<CInstanceShaderPreset*>(
|
||||||
|
static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance))
|
||||||
|
->PresetFileNew(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void ADDON_preset_file_free(const AddonInstance_ShaderPreset* addonInstance,
|
||||||
|
preset_file file)
|
||||||
|
{
|
||||||
|
return static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance)
|
||||||
|
->PresetFileFree(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool ADDON_video_shader_read_file(const AddonInstance_ShaderPreset* addonInstance,
|
||||||
|
preset_file file,
|
||||||
|
video_shader* shader)
|
||||||
|
{
|
||||||
|
if (shader != nullptr)
|
||||||
|
return static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance)
|
||||||
|
->ShaderPresetRead(file, *shader);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void ADDON_video_shader_write_file(const AddonInstance_ShaderPreset* addonInstance,
|
||||||
|
preset_file file,
|
||||||
|
const video_shader* shader)
|
||||||
|
{
|
||||||
|
if (shader != nullptr)
|
||||||
|
static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance)
|
||||||
|
->ShaderPresetWrite(file, *shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool ADDON_video_shader_resolve_parameters(
|
||||||
|
const AddonInstance_ShaderPreset* addonInstance, preset_file file, video_shader* shader)
|
||||||
|
{
|
||||||
|
if (shader != nullptr)
|
||||||
|
return static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance)
|
||||||
|
->ShaderPresetResolveParameters(file, *shader);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void ADDON_video_shader_free(const AddonInstance_ShaderPreset* addonInstance,
|
||||||
|
video_shader* shader)
|
||||||
|
{
|
||||||
|
if (shader != nullptr)
|
||||||
|
static_cast<CInstanceShaderPreset*>(addonInstance->toAddon->addonInstance)
|
||||||
|
->ShaderPresetFree(*shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddonInstance_ShaderPreset* m_instanceData;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace addon */
|
||||||
|
} /* namespace kodi */
|
||||||
|
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
@ -10,6 +10,7 @@ set(HEADERS
|
||||||
peripheral.h
|
peripheral.h
|
||||||
pvr.h
|
pvr.h
|
||||||
screensaver.h
|
screensaver.h
|
||||||
|
shaderpreset.h
|
||||||
vfs.h
|
vfs.h
|
||||||
video_codec.h
|
video_codec.h
|
||||||
visualization.h
|
visualization.h
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,303 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef C_API_ADDONINSTANCE_SHADER_PRESET_H
|
||||||
|
#define C_API_ADDONINSTANCE_SHADER_PRESET_H
|
||||||
|
|
||||||
|
#include "../addon_base.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
//============================================================================
|
||||||
|
/// @ingroup cpp_kodi_addon_shader_preset_Defs
|
||||||
|
/// @brief **The data system used for shader presets**
|
||||||
|
///
|
||||||
|
///@{
|
||||||
|
typedef void* preset_file;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Scale types
|
||||||
|
*
|
||||||
|
* If no scale type is specified, it is assumed that the scale type is
|
||||||
|
* relative to the input with a scaling factor of 1.0.
|
||||||
|
*
|
||||||
|
* Exceptions: If no scale type is set for the last pass, it is assumed to
|
||||||
|
* output at the full resolution rather than assuming of scale of 1.0, and
|
||||||
|
* bypasses any frame-buffer object rendering.
|
||||||
|
*/
|
||||||
|
typedef enum SHADER_SCALE_TYPE
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief Use the source size
|
||||||
|
*
|
||||||
|
* Output size of the shader pass is relative to the input size. Value is
|
||||||
|
* float.
|
||||||
|
*/
|
||||||
|
SHADER_SCALE_TYPE_INPUT,
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Use the window viewport size
|
||||||
|
*
|
||||||
|
* Output size of the shader pass is relative to the size of the window
|
||||||
|
* viewport. Value is float. This value can change over time if the user
|
||||||
|
* resizes his/her window!
|
||||||
|
*/
|
||||||
|
SHADER_SCALE_TYPE_ABSOLUTE,
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Use a statically defined size
|
||||||
|
*
|
||||||
|
* Output size is statically defined to a certain size. Useful for hi-res
|
||||||
|
* blenders or similar.
|
||||||
|
*/
|
||||||
|
SHADER_SCALE_TYPE_VIEWPORT
|
||||||
|
} SHADER_SCALE_TYPE;
|
||||||
|
|
||||||
|
typedef enum SHADER_FILTER_TYPE
|
||||||
|
{
|
||||||
|
SHADER_FILTER_TYPE_UNSPEC,
|
||||||
|
SHADER_FILTER_TYPE_LINEAR,
|
||||||
|
SHADER_FILTER_TYPE_NEAREST
|
||||||
|
} SHADER_FILTER_TYPE;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Texture wrapping mode
|
||||||
|
*/
|
||||||
|
typedef enum SHADER_WRAP_TYPE
|
||||||
|
{
|
||||||
|
SHADER_WRAP_TYPE_BORDER, /* Deprecated, will be translated to EDGE in GLES */
|
||||||
|
SHADER_WRAP_TYPE_EDGE,
|
||||||
|
SHADER_WRAP_TYPE_REPEAT,
|
||||||
|
SHADER_WRAP_TYPE_MIRRORED_REPEAT
|
||||||
|
} SHADER_WRAP_TYPE;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief FBO scaling parameters for a single axis
|
||||||
|
*/
|
||||||
|
typedef struct fbo_scale_axis
|
||||||
|
{
|
||||||
|
SHADER_SCALE_TYPE type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
float scale;
|
||||||
|
unsigned abs;
|
||||||
|
};
|
||||||
|
} fbo_scale_axis;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief FBO parameters
|
||||||
|
*/
|
||||||
|
typedef struct fbo_scale
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief sRGB framebuffer
|
||||||
|
*/
|
||||||
|
bool srgb_fbo;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Float framebuffer
|
||||||
|
*
|
||||||
|
* This parameter defines if the pass should be rendered to a 32-bit
|
||||||
|
* floating point buffer. This only takes effect if the pass is actually
|
||||||
|
* rendered to an FBO. This is useful for shaders which have to store FBO
|
||||||
|
* values outside the range [0, 1].
|
||||||
|
*/
|
||||||
|
bool fp_fbo;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Scaling parameters for X axis
|
||||||
|
*/
|
||||||
|
fbo_scale_axis scale_x;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Scaling parameters for Y axis
|
||||||
|
*/
|
||||||
|
fbo_scale_axis scale_y;
|
||||||
|
} fbo_scale;
|
||||||
|
|
||||||
|
typedef struct video_shader_parameter
|
||||||
|
{
|
||||||
|
char* id;
|
||||||
|
char* desc;
|
||||||
|
float current;
|
||||||
|
float minimum;
|
||||||
|
float initial;
|
||||||
|
float maximum;
|
||||||
|
float step;
|
||||||
|
} video_shader_parameter;
|
||||||
|
|
||||||
|
typedef struct video_shader_pass
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief Path to the shader pass source
|
||||||
|
*/
|
||||||
|
char* source_path;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The vertex shader source
|
||||||
|
*/
|
||||||
|
char* vertex_source;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The fragment shader source, if separate from the vertex source, or
|
||||||
|
* NULL otherwise
|
||||||
|
*/
|
||||||
|
char* fragment_source;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief FBO parameters
|
||||||
|
*/
|
||||||
|
fbo_scale fbo;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Defines how the result of this pass will be filtered
|
||||||
|
*
|
||||||
|
* @todo Define behavior for unspecified filter
|
||||||
|
*/
|
||||||
|
SHADER_FILTER_TYPE filter;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Wrapping mode
|
||||||
|
*/
|
||||||
|
SHADER_WRAP_TYPE wrap;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Frame count mod
|
||||||
|
*/
|
||||||
|
unsigned frame_count_mod;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Mipmapping
|
||||||
|
*/
|
||||||
|
bool mipmap;
|
||||||
|
} video_shader_pass;
|
||||||
|
|
||||||
|
typedef struct video_shader_lut
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief Name of the sampler uniform, e.g. `uniform sampler2D foo`.
|
||||||
|
*/
|
||||||
|
char* id;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Path of the texture
|
||||||
|
*/
|
||||||
|
char* path;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Filtering for the texture
|
||||||
|
*/
|
||||||
|
SHADER_FILTER_TYPE filter;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Texture wrapping mode
|
||||||
|
*/
|
||||||
|
SHADER_WRAP_TYPE wrap;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Use mipmapping for the texture
|
||||||
|
*/
|
||||||
|
bool mipmap;
|
||||||
|
} video_shader_lut;
|
||||||
|
|
||||||
|
typedef struct video_shader
|
||||||
|
{
|
||||||
|
unsigned pass_count;
|
||||||
|
video_shader_pass* passes;
|
||||||
|
|
||||||
|
unsigned lut_count;
|
||||||
|
video_shader_lut* luts;
|
||||||
|
|
||||||
|
unsigned parameter_count;
|
||||||
|
video_shader_parameter* parameters;
|
||||||
|
} video_shader;
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
//--==----==----==----==----==----==----==----==----==----==----==----==----==--
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief ShaderPreset properties
|
||||||
|
*
|
||||||
|
* Not to be used outside this header.
|
||||||
|
*/
|
||||||
|
typedef struct AddonProps_ShaderPreset
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief The path to the user profile
|
||||||
|
*/
|
||||||
|
const char* user_path;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The path to this add-on
|
||||||
|
*/
|
||||||
|
const char* addon_path;
|
||||||
|
} AddonProps_ShaderPreset;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Structure to transfer the methods from kodi_shader_preset_dll.h to
|
||||||
|
* Kodi
|
||||||
|
*/
|
||||||
|
struct AddonInstance_ShaderPreset;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief ShaderPreset callbacks
|
||||||
|
*
|
||||||
|
* Not to be used outside this header.
|
||||||
|
*/
|
||||||
|
typedef struct AddonToKodiFuncTable_ShaderPreset
|
||||||
|
{
|
||||||
|
KODI_HANDLE kodiInstance;
|
||||||
|
} AddonToKodiFuncTable_ShaderPreset;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief ShaderPreset function hooks
|
||||||
|
*
|
||||||
|
* Not to be used outside this header.
|
||||||
|
*/
|
||||||
|
typedef struct KodiToAddonFuncTable_ShaderPreset
|
||||||
|
{
|
||||||
|
KODI_HANDLE addonInstance;
|
||||||
|
|
||||||
|
preset_file(__cdecl* PresetFileNew)(const struct AddonInstance_ShaderPreset*, const char*);
|
||||||
|
void(__cdecl* PresetFileFree)(const struct AddonInstance_ShaderPreset*, preset_file);
|
||||||
|
bool(__cdecl* VideoShaderRead)(const struct AddonInstance_ShaderPreset*,
|
||||||
|
preset_file,
|
||||||
|
struct video_shader*);
|
||||||
|
void(__cdecl* VideoShaderWrite)(const struct AddonInstance_ShaderPreset*,
|
||||||
|
preset_file,
|
||||||
|
const struct video_shader*);
|
||||||
|
bool(__cdecl* VideoShaderResolveParameters)(const struct AddonInstance_ShaderPreset*,
|
||||||
|
preset_file,
|
||||||
|
struct video_shader*);
|
||||||
|
void(__cdecl* VideoShaderFree)(const struct AddonInstance_ShaderPreset*, struct video_shader*);
|
||||||
|
} KodiToAddonFuncTable_ShaderPreset;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief ShaderPreset instance
|
||||||
|
*
|
||||||
|
* Not to be used outside this header.
|
||||||
|
*/
|
||||||
|
typedef struct AddonInstance_ShaderPreset
|
||||||
|
{
|
||||||
|
struct AddonProps_ShaderPreset* props;
|
||||||
|
struct AddonToKodiFuncTable_ShaderPreset* toKodi;
|
||||||
|
struct KodiToAddonFuncTable_ShaderPreset* toAddon;
|
||||||
|
} AddonInstance_ShaderPreset;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* !C_API_ADDONINSTANCE_SHADER_PRESET_H */
|
||||||
|
|
@ -281,6 +281,7 @@ extern "C"
|
||||||
struct AddonInstance_Peripheral* peripheral;
|
struct AddonInstance_Peripheral* peripheral;
|
||||||
struct AddonInstance_PVR* pvr;
|
struct AddonInstance_PVR* pvr;
|
||||||
struct AddonInstance_Screensaver* screensaver;
|
struct AddonInstance_Screensaver* screensaver;
|
||||||
|
struct AddonInstance_ShaderPreset* shaderpreset;
|
||||||
struct AddonInstance_VFSEntry* vfs;
|
struct AddonInstance_VFSEntry* vfs;
|
||||||
struct AddonInstance_VideoCodec* videocodec;
|
struct AddonInstance_VideoCodec* videocodec;
|
||||||
struct AddonInstance_Visualization* visualization;
|
struct AddonInstance_Visualization* visualization;
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,11 @@
|
||||||
#define ADDON_INSTANCE_VERSION_SCREENSAVER_DEPENDS "c-api/addon-instance/screensaver.h" \
|
#define ADDON_INSTANCE_VERSION_SCREENSAVER_DEPENDS "c-api/addon-instance/screensaver.h" \
|
||||||
"addon-instance/Screensaver.h"
|
"addon-instance/Screensaver.h"
|
||||||
|
|
||||||
|
#define ADDON_INSTANCE_VERSION_SHADERPRESET "1.0.0"
|
||||||
|
#define ADDON_INSTANCE_VERSION_SHADERPRESET_MIN "1.0.0"
|
||||||
|
#define ADDON_INSTANCE_VERSION_SHADERPRESET_XML_ID "kodi.binary.instance.shaderpreset"
|
||||||
|
#define ADDON_INSTANCE_VERSION_SHADERPRESET_DEPENDS "addon-instance/ShaderPreset.h"
|
||||||
|
|
||||||
#define ADDON_INSTANCE_VERSION_VFS "3.0.1"
|
#define ADDON_INSTANCE_VERSION_VFS "3.0.1"
|
||||||
#define ADDON_INSTANCE_VERSION_VFS_MIN "3.0.1"
|
#define ADDON_INSTANCE_VERSION_VFS_MIN "3.0.1"
|
||||||
#define ADDON_INSTANCE_VERSION_VFS_XML_ID "kodi.binary.instance.vfs"
|
#define ADDON_INSTANCE_VERSION_VFS_XML_ID "kodi.binary.instance.vfs"
|
||||||
|
|
@ -247,6 +252,9 @@ typedef enum ADDON_TYPE
|
||||||
|
|
||||||
/// Video codec instance, see @ref cpp_kodi_addon_videocodec "kodi::addon::CInstanceVideoCodec"
|
/// Video codec instance, see @ref cpp_kodi_addon_videocodec "kodi::addon::CInstanceVideoCodec"
|
||||||
ADDON_INSTANCE_VIDEOCODEC = 112,
|
ADDON_INSTANCE_VIDEOCODEC = 112,
|
||||||
|
|
||||||
|
/// Shader preset instance, see @ref cpp_kodi_addon_shaderpreset "kodi::addon::CInstanceShaderPreset"
|
||||||
|
ADDON_INSTANCE_SHADERPRESET = 113,
|
||||||
} ADDON_TYPE;
|
} ADDON_TYPE;
|
||||||
///@}
|
///@}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -333,6 +341,10 @@ inline const char* GetTypeVersion(int type)
|
||||||
case ADDON_INSTANCE_SCREENSAVER:
|
case ADDON_INSTANCE_SCREENSAVER:
|
||||||
return ADDON_INSTANCE_VERSION_SCREENSAVER;
|
return ADDON_INSTANCE_VERSION_SCREENSAVER;
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(BUILD_KODI_ADDON) || defined(ADDON_INSTANCE_VERSION_SHADERPRESET_USED)
|
||||||
|
case ADDON_INSTANCE_SHADERPRESET:
|
||||||
|
return ADDON_INSTANCE_VERSION_SHADERPRESET;
|
||||||
|
#endif
|
||||||
#if !defined(BUILD_KODI_ADDON) || defined(ADDON_INSTANCE_VERSION_VFS_USED)
|
#if !defined(BUILD_KODI_ADDON) || defined(ADDON_INSTANCE_VERSION_VFS_USED)
|
||||||
case ADDON_INSTANCE_VFS:
|
case ADDON_INSTANCE_VFS:
|
||||||
return ADDON_INSTANCE_VERSION_VFS;
|
return ADDON_INSTANCE_VERSION_VFS;
|
||||||
|
|
@ -394,6 +406,8 @@ inline const char* GetTypeMinVersion(int type)
|
||||||
return ADDON_INSTANCE_VERSION_PVR_MIN;
|
return ADDON_INSTANCE_VERSION_PVR_MIN;
|
||||||
case ADDON_INSTANCE_SCREENSAVER:
|
case ADDON_INSTANCE_SCREENSAVER:
|
||||||
return ADDON_INSTANCE_VERSION_SCREENSAVER_MIN;
|
return ADDON_INSTANCE_VERSION_SCREENSAVER_MIN;
|
||||||
|
case ADDON_INSTANCE_SHADERPRESET:
|
||||||
|
return ADDON_INSTANCE_VERSION_SHADERPRESET_MIN;
|
||||||
case ADDON_INSTANCE_VFS:
|
case ADDON_INSTANCE_VFS:
|
||||||
return ADDON_INSTANCE_VERSION_VFS_MIN;
|
return ADDON_INSTANCE_VERSION_VFS_MIN;
|
||||||
case ADDON_INSTANCE_VISUALIZATION:
|
case ADDON_INSTANCE_VISUALIZATION:
|
||||||
|
|
@ -448,6 +462,8 @@ inline const char* GetTypeName(int type)
|
||||||
return "PVR";
|
return "PVR";
|
||||||
case ADDON_INSTANCE_SCREENSAVER:
|
case ADDON_INSTANCE_SCREENSAVER:
|
||||||
return "ScreenSaver";
|
return "ScreenSaver";
|
||||||
|
case ADDON_INSTANCE_SHADERPRESET:
|
||||||
|
return "ShaderPreset";
|
||||||
case ADDON_INSTANCE_VISUALIZATION:
|
case ADDON_INSTANCE_VISUALIZATION:
|
||||||
return "Visualization";
|
return "Visualization";
|
||||||
case ADDON_INSTANCE_VIDEOCODEC:
|
case ADDON_INSTANCE_VIDEOCODEC:
|
||||||
|
|
@ -499,6 +515,8 @@ inline int GetTypeId(const char* name)
|
||||||
return ADDON_INSTANCE_PVR;
|
return ADDON_INSTANCE_PVR;
|
||||||
else if (strcmp(name, "screensaver") == 0)
|
else if (strcmp(name, "screensaver") == 0)
|
||||||
return ADDON_INSTANCE_SCREENSAVER;
|
return ADDON_INSTANCE_SCREENSAVER;
|
||||||
|
else if (strcmp(name, "shaderpreset") == 0)
|
||||||
|
return ADDON_INSTANCE_SHADERPRESET;
|
||||||
else if (strcmp(name, "vfs") == 0)
|
else if (strcmp(name, "vfs") == 0)
|
||||||
return ADDON_INSTANCE_VFS;
|
return ADDON_INSTANCE_VFS;
|
||||||
else if (strcmp(name, "visualization") == 0)
|
else if (strcmp(name, "visualization") == 0)
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,7 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRendererForSettings(
|
||||||
renderer->SetScalingMethod(effectiveRenderSettings.VideoSettings().GetScalingMethod());
|
renderer->SetScalingMethod(effectiveRenderSettings.VideoSettings().GetScalingMethod());
|
||||||
renderer->SetStretchMode(effectiveRenderSettings.VideoSettings().GetRenderStretchMode());
|
renderer->SetStretchMode(effectiveRenderSettings.VideoSettings().GetRenderStretchMode());
|
||||||
renderer->SetRenderRotation(effectiveRenderSettings.VideoSettings().GetRenderRotation());
|
renderer->SetRenderRotation(effectiveRenderSettings.VideoSettings().GetRenderRotation());
|
||||||
|
renderer->SetShaderPreset(effectiveRenderSettings.VideoSettings().GetShaderPreset());
|
||||||
renderer->SetPixels(effectiveRenderSettings.VideoSettings().GetPixels());
|
renderer->SetPixels(effectiveRenderSettings.VideoSettings().GetPixels());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -589,10 +590,16 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRendererForPool(
|
||||||
// If buffer pool has no compatible renderers, create one now
|
// If buffer pool has no compatible renderers, create one now
|
||||||
if (!renderer)
|
if (!renderer)
|
||||||
{
|
{
|
||||||
|
const std::string& shaderPreset = renderSettings.VideoSettings().GetShaderPreset();
|
||||||
|
|
||||||
CLog::Log(LOGDEBUG, "RetroPlayer[RENDER]: Creating renderer for {}",
|
CLog::Log(LOGDEBUG, "RetroPlayer[RENDER]: Creating renderer for {}",
|
||||||
m_processInfo.GetRenderSystemName(bufferPool));
|
m_processInfo.GetRenderSystemName(bufferPool));
|
||||||
|
|
||||||
|
// Try to create a renderer now, unless the shader preset has failed already
|
||||||
|
if (shaderPreset.empty() ||
|
||||||
|
m_failedShaderPresets.find(shaderPreset) == m_failedShaderPresets.end())
|
||||||
renderer.reset(m_processInfo.CreateRenderer(bufferPool, renderSettings));
|
renderer.reset(m_processInfo.CreateRenderer(bufferPool, renderSettings));
|
||||||
|
|
||||||
if (renderer && renderer->Configure(m_format))
|
if (renderer && renderer->Configure(m_format))
|
||||||
{
|
{
|
||||||
// Ensure we have a render buffer for this renderer
|
// Ensure we have a render buffer for this renderer
|
||||||
|
|
@ -602,6 +609,10 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRendererForPool(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
renderer.reset();
|
renderer.reset();
|
||||||
|
|
||||||
|
// If we failed to create a renderer, blocklist the shader preset
|
||||||
|
if (!renderer && !shaderPreset.empty())
|
||||||
|
m_failedShaderPresets.insert(shaderPreset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderer;
|
return renderer;
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,39 @@
|
||||||
|
|
||||||
#include "RenderVideoSettings.h"
|
#include "RenderVideoSettings.h"
|
||||||
|
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
using namespace KODI;
|
using namespace KODI;
|
||||||
using namespace RETRO;
|
using namespace RETRO;
|
||||||
|
|
||||||
#define VIDEO_FILTER_NEAREST "nearest"
|
#define VIDEO_FILTER_NEAREST "nearest"
|
||||||
#define VIDEO_FILTER_LINEAR "linear"
|
#define VIDEO_FILTER_LINEAR "linear"
|
||||||
|
|
||||||
|
#define VIDEO_FILTER_DEFAULT VIDEO_FILTER_NEAREST
|
||||||
|
|
||||||
void CRenderVideoSettings::Reset()
|
void CRenderVideoSettings::Reset()
|
||||||
{
|
{
|
||||||
m_scalingMethod = SCALINGMETHOD::AUTO;
|
m_scalingMethod = SCALINGMETHOD::AUTO;
|
||||||
m_stretchMode = STRETCHMODE::Normal;
|
m_stretchMode = STRETCHMODE::Normal;
|
||||||
m_rotationDegCCW = 0;
|
m_rotationDegCCW = 0;
|
||||||
|
m_shaderPreset.clear();
|
||||||
m_pixelPath.clear();
|
m_pixelPath.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRenderVideoSettings::operator==(const CRenderVideoSettings& rhs) const
|
bool CRenderVideoSettings::operator==(const CRenderVideoSettings& rhs) const
|
||||||
{
|
{
|
||||||
return m_scalingMethod == rhs.m_scalingMethod && m_stretchMode == rhs.m_stretchMode &&
|
return m_scalingMethod == rhs.m_scalingMethod && m_stretchMode == rhs.m_stretchMode &&
|
||||||
m_rotationDegCCW == rhs.m_rotationDegCCW && m_pixelPath == rhs.m_pixelPath;
|
m_rotationDegCCW == rhs.m_rotationDegCCW && m_shaderPreset == rhs.m_shaderPreset &&
|
||||||
|
m_pixelPath == rhs.m_pixelPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRenderVideoSettings::operator<(const CRenderVideoSettings& rhs) const
|
bool CRenderVideoSettings::operator<(const CRenderVideoSettings& rhs) const
|
||||||
{
|
{
|
||||||
|
if (m_shaderPreset < rhs.m_shaderPreset)
|
||||||
|
return true;
|
||||||
|
if (m_shaderPreset > rhs.m_shaderPreset)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (m_scalingMethod < rhs.m_scalingMethod)
|
if (m_scalingMethod < rhs.m_scalingMethod)
|
||||||
return true;
|
return true;
|
||||||
if (m_scalingMethod > rhs.m_scalingMethod)
|
if (m_scalingMethod > rhs.m_scalingMethod)
|
||||||
|
|
@ -55,6 +66,13 @@ bool CRenderVideoSettings::operator<(const CRenderVideoSettings& rhs) const
|
||||||
|
|
||||||
std::string CRenderVideoSettings::GetVideoFilter() const
|
std::string CRenderVideoSettings::GetVideoFilter() const
|
||||||
{
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_shaderPreset.empty() && m_scalingMethod != SCALINGMETHOD::AUTO)
|
||||||
|
CLog::LogF(LOGWARNING, "Shader preset selected but scaling method is not AUTO");
|
||||||
|
|
||||||
|
if (UsesShaderPreset())
|
||||||
|
return m_shaderPreset;
|
||||||
|
|
||||||
switch (m_scalingMethod)
|
switch (m_scalingMethod)
|
||||||
{
|
{
|
||||||
case SCALINGMETHOD::NEAREST:
|
case SCALINGMETHOD::NEAREST:
|
||||||
|
|
@ -73,17 +91,33 @@ void CRenderVideoSettings::SetVideoFilter(const std::string& videoFilter)
|
||||||
if (videoFilter == VIDEO_FILTER_NEAREST)
|
if (videoFilter == VIDEO_FILTER_NEAREST)
|
||||||
{
|
{
|
||||||
m_scalingMethod = SCALINGMETHOD::NEAREST;
|
m_scalingMethod = SCALINGMETHOD::NEAREST;
|
||||||
|
ResetShaderPreset();
|
||||||
}
|
}
|
||||||
else if (videoFilter == VIDEO_FILTER_LINEAR)
|
else if (videoFilter == VIDEO_FILTER_LINEAR)
|
||||||
{
|
{
|
||||||
m_scalingMethod = SCALINGMETHOD::LINEAR;
|
m_scalingMethod = SCALINGMETHOD::LINEAR;
|
||||||
|
ResetShaderPreset();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_scalingMethod = SCALINGMETHOD::AUTO;
|
m_scalingMethod = SCALINGMETHOD::AUTO;
|
||||||
|
|
||||||
|
// Not a known video filter, assume it's a shader preset path
|
||||||
|
SetShaderPreset(videoFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRenderVideoSettings::ResetShaderPreset()
|
||||||
|
{
|
||||||
|
m_shaderPreset.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void CRenderVideoSettings::ResetPixels()
|
void CRenderVideoSettings::ResetPixels()
|
||||||
{
|
{
|
||||||
m_pixelPath.clear();
|
m_pixelPath.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CRenderVideoSettings::UsesShaderPreset() const
|
||||||
|
{
|
||||||
|
return !m_shaderPreset.empty() && m_scalingMethod == SCALINGMETHOD::AUTO;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ public:
|
||||||
STRETCHMODE GetRenderStretchMode() const { return m_stretchMode; }
|
STRETCHMODE GetRenderStretchMode() const { return m_stretchMode; }
|
||||||
void SetRenderStretchMode(STRETCHMODE mode) { m_stretchMode = mode; }
|
void SetRenderStretchMode(STRETCHMODE mode) { m_stretchMode = mode; }
|
||||||
|
|
||||||
|
const std::string& GetShaderPreset() const { return m_shaderPreset; }
|
||||||
|
void SetShaderPreset(const std::string& shaderPreset) { m_shaderPreset = shaderPreset; }
|
||||||
|
void ResetShaderPreset();
|
||||||
|
|
||||||
unsigned int GetRenderRotation() const { return m_rotationDegCCW; }
|
unsigned int GetRenderRotation() const { return m_rotationDegCCW; }
|
||||||
void SetRenderRotation(unsigned int rotationDegCCW) { m_rotationDegCCW = rotationDegCCW; }
|
void SetRenderRotation(unsigned int rotationDegCCW) { m_rotationDegCCW = rotationDegCCW; }
|
||||||
|
|
||||||
|
|
@ -51,9 +55,12 @@ public:
|
||||||
void ResetPixels();
|
void ResetPixels();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool UsesShaderPreset() const;
|
||||||
|
|
||||||
SCALINGMETHOD m_scalingMethod;
|
SCALINGMETHOD m_scalingMethod;
|
||||||
STRETCHMODE m_stretchMode;
|
STRETCHMODE m_stretchMode;
|
||||||
unsigned int m_rotationDegCCW;
|
unsigned int m_rotationDegCCW;
|
||||||
|
std::string m_shaderPreset;
|
||||||
std::string m_pixelPath;
|
std::string m_pixelPath;
|
||||||
};
|
};
|
||||||
} // namespace RETRO
|
} // namespace RETRO
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "cores/RetroPlayer/buffers/IRenderBufferPool.h"
|
#include "cores/RetroPlayer/buffers/IRenderBufferPool.h"
|
||||||
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
#include "cores/RetroPlayer/rendering/RenderUtils.h"
|
#include "cores/RetroPlayer/rendering/RenderUtils.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderPreset.h"
|
||||||
#include "utils/log.h"
|
#include "utils/log.h"
|
||||||
|
|
||||||
using namespace KODI;
|
using namespace KODI;
|
||||||
|
|
@ -23,7 +24,11 @@ using namespace RETRO;
|
||||||
CRPBaseRenderer::CRPBaseRenderer(const CRenderSettings& renderSettings,
|
CRPBaseRenderer::CRPBaseRenderer(const CRenderSettings& renderSettings,
|
||||||
CRenderContext& context,
|
CRenderContext& context,
|
||||||
std::shared_ptr<IRenderBufferPool> bufferPool)
|
std::shared_ptr<IRenderBufferPool> bufferPool)
|
||||||
: m_context(context), m_bufferPool(std::move(bufferPool)), m_renderSettings(renderSettings)
|
: m_context(context),
|
||||||
|
m_bufferPool(std::move(bufferPool)),
|
||||||
|
m_renderSettings(renderSettings),
|
||||||
|
m_bShadersNeedUpdate(true),
|
||||||
|
m_bUseShaderPreset(false)
|
||||||
{
|
{
|
||||||
m_bufferPool->RegisterRenderer(this);
|
m_bufferPool->RegisterRenderer(this);
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +42,22 @@ CRPBaseRenderer::~CRPBaseRenderer()
|
||||||
|
|
||||||
bool CRPBaseRenderer::IsCompatible(const CRenderVideoSettings& settings) const
|
bool CRPBaseRenderer::IsCompatible(const CRenderVideoSettings& settings) const
|
||||||
{
|
{
|
||||||
return m_bufferPool->IsCompatible(settings);
|
if (!m_bufferPool->IsCompatible(settings))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Shader preset must match
|
||||||
|
std::string shaderPreset;
|
||||||
|
if (m_shaderPreset)
|
||||||
|
shaderPreset = m_shaderPreset->GetShaderPreset();
|
||||||
|
|
||||||
|
// Shader preset might not be initialized yet
|
||||||
|
if (!shaderPreset.empty())
|
||||||
|
{
|
||||||
|
if (settings.GetShaderPreset() != shaderPreset)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRPBaseRenderer::Configure(AVPixelFormat format)
|
bool CRPBaseRenderer::Configure(AVPixelFormat format)
|
||||||
|
|
@ -132,6 +152,15 @@ void CRPBaseRenderer::SetRenderRotation(unsigned int rotationDegCCW)
|
||||||
m_renderSettings.VideoSettings().SetRenderRotation(rotationDegCCW);
|
m_renderSettings.VideoSettings().SetRenderRotation(rotationDegCCW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRPBaseRenderer::SetShaderPreset(const std::string& presetPath)
|
||||||
|
{
|
||||||
|
if (presetPath != m_renderSettings.VideoSettings().GetShaderPreset())
|
||||||
|
{
|
||||||
|
m_renderSettings.VideoSettings().SetShaderPreset(presetPath);
|
||||||
|
m_bShadersNeedUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CRPBaseRenderer::SetPixels(const std::string& pixelPath)
|
void CRPBaseRenderer::SetPixels(const std::string& pixelPath)
|
||||||
{
|
{
|
||||||
m_renderSettings.VideoSettings().SetPixels(pixelPath);
|
m_renderSettings.VideoSettings().SetPixels(pixelPath);
|
||||||
|
|
@ -203,6 +232,10 @@ void CRPBaseRenderer::ManageRenderArea(const IRenderBuffer& renderBuffer)
|
||||||
|
|
||||||
// Adapt the drawing rect points if we have to rotate
|
// Adapt the drawing rect points if we have to rotate
|
||||||
m_rotatedDestCoords = CRenderUtils::ReorderDrawPoints(destRect, rotationDegCCW);
|
m_rotatedDestCoords = CRenderUtils::ReorderDrawPoints(destRect, rotationDegCCW);
|
||||||
|
|
||||||
|
// Update video shader source size
|
||||||
|
if (m_shaderPreset)
|
||||||
|
m_shaderPreset->SetVideoSize(sourceWidth, sourceHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRPBaseRenderer::MarkDirty()
|
void CRPBaseRenderer::MarkDirty()
|
||||||
|
|
@ -210,6 +243,21 @@ void CRPBaseRenderer::MarkDirty()
|
||||||
// CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(m_dimensions); //! @todo
|
// CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(m_dimensions); //! @todo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Updates everything needed for video shaders (shader presets)
|
||||||
|
* Needs to be called after m_renderBuffer has been set
|
||||||
|
*/
|
||||||
|
void CRPBaseRenderer::Updateshaders()
|
||||||
|
{
|
||||||
|
if (m_bShadersNeedUpdate)
|
||||||
|
{
|
||||||
|
if (m_shaderPreset)
|
||||||
|
m_bUseShaderPreset =
|
||||||
|
m_shaderPreset->SetShaderPreset(m_renderSettings.VideoSettings().GetShaderPreset());
|
||||||
|
m_bShadersNeedUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CRPBaseRenderer::PreRender(bool clear)
|
void CRPBaseRenderer::PreRender(bool clear)
|
||||||
{
|
{
|
||||||
if (!m_bConfigured)
|
if (!m_bConfigured)
|
||||||
|
|
@ -219,6 +267,8 @@ void CRPBaseRenderer::PreRender(bool clear)
|
||||||
if (clear)
|
if (clear)
|
||||||
m_context.Clear(m_context.UseLimitedColor() ? UTILS::COLOR::LIMITED_BLACK
|
m_context.Clear(m_context.UseLimitedColor() ? UTILS::COLOR::LIMITED_BLACK
|
||||||
: UTILS::COLOR::BLACK);
|
: UTILS::COLOR::BLACK);
|
||||||
|
|
||||||
|
// ManageRenderArea(*m_renderBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRPBaseRenderer::PostRender()
|
void CRPBaseRenderer::PostRender()
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ extern "C"
|
||||||
|
|
||||||
namespace KODI
|
namespace KODI
|
||||||
{
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderPreset;
|
||||||
|
}
|
||||||
|
|
||||||
namespace RETRO
|
namespace RETRO
|
||||||
{
|
{
|
||||||
class CRenderContext;
|
class CRenderContext;
|
||||||
|
|
@ -67,6 +72,7 @@ public:
|
||||||
void SetScalingMethod(SCALINGMETHOD method);
|
void SetScalingMethod(SCALINGMETHOD method);
|
||||||
void SetStretchMode(STRETCHMODE stretchMode);
|
void SetStretchMode(STRETCHMODE stretchMode);
|
||||||
void SetRenderRotation(unsigned int rotationDegCCW);
|
void SetRenderRotation(unsigned int rotationDegCCW);
|
||||||
|
void SetShaderPreset(const std::string& presetPath);
|
||||||
void SetPixels(const std::string& pixelPath);
|
void SetPixels(const std::string& pixelPath);
|
||||||
|
|
||||||
// Rendering properties
|
// Rendering properties
|
||||||
|
|
@ -95,6 +101,13 @@ protected:
|
||||||
CRect m_sourceRect;
|
CRect m_sourceRect;
|
||||||
std::array<CPoint, 4> m_rotatedDestCoords{};
|
std::array<CPoint, 4> m_rotatedDestCoords{};
|
||||||
|
|
||||||
|
// Video shaders
|
||||||
|
void Updateshaders();
|
||||||
|
std::unique_ptr<SHADER::IShaderPreset> m_shaderPreset;
|
||||||
|
|
||||||
|
bool m_bShadersNeedUpdate;
|
||||||
|
bool m_bUseShaderPreset;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
* \brief Calculate driven dimensions
|
* \brief Calculate driven dimensions
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,14 @@ CRenderBufferPoolGuiTexture::CRenderBufferPoolGuiTexture(SCALINGMETHOD scalingMe
|
||||||
|
|
||||||
bool CRenderBufferPoolGuiTexture::IsCompatible(const CRenderVideoSettings& renderSettings) const
|
bool CRenderBufferPoolGuiTexture::IsCompatible(const CRenderVideoSettings& renderSettings) const
|
||||||
{
|
{
|
||||||
return renderSettings.GetScalingMethod() == m_scalingMethod;
|
if (renderSettings.GetScalingMethod() != m_scalingMethod)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Shaders not supported
|
||||||
|
if (!renderSettings.GetShaderPreset().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
IRenderBuffer* CRenderBufferPoolGuiTexture::CreateRenderBuffer(void* header /* = nullptr */)
|
IRenderBuffer* CRenderBufferPoolGuiTexture::CreateRenderBuffer(void* header /* = nullptr */)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@
|
||||||
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
#include "cores/RetroPlayer/rendering/RenderTranslator.h"
|
#include "cores/RetroPlayer/rendering/RenderTranslator.h"
|
||||||
#include "cores/RetroPlayer/rendering/RenderVideoSettings.h"
|
#include "cores/RetroPlayer/rendering/RenderVideoSettings.h"
|
||||||
#include "cores/RetroPlayer/rendering/VideoShaders/windows/RPWinOutputShader.h"
|
#include "cores/RetroPlayer/shaders/windows/RPWinOutputShader.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderPresetDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderTextureDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderTextureDXRef.h"
|
||||||
#include "guilib/D3DResource.h"
|
#include "guilib/D3DResource.h"
|
||||||
#include "rendering/dx/RenderSystemDX.h"
|
#include "rendering/dx/RenderSystemDX.h"
|
||||||
#include "utils/log.h"
|
#include "utils/log.h"
|
||||||
|
|
@ -60,7 +63,8 @@ CWinRenderBuffer::~CWinRenderBuffer()
|
||||||
|
|
||||||
bool CWinRenderBuffer::CreateTexture()
|
bool CWinRenderBuffer::CreateTexture()
|
||||||
{
|
{
|
||||||
if (!m_intermediateTarget->Create(m_width, m_height, 1, D3D11_USAGE_DYNAMIC, m_targetDxFormat))
|
if (!m_intermediateTarget->GetTexture().Create(m_width, m_height, 1, D3D11_USAGE_DYNAMIC,
|
||||||
|
m_targetDxFormat))
|
||||||
{
|
{
|
||||||
CLog::Log(LOGERROR, "WinRenderer: Intermediate render target creation failed");
|
CLog::Log(LOGERROR, "WinRenderer: Intermediate render target creation failed");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -73,7 +77,8 @@ bool CWinRenderBuffer::GetTexture(uint8_t*& data, unsigned int& stride)
|
||||||
{
|
{
|
||||||
// Scale and upload texture
|
// Scale and upload texture
|
||||||
D3D11_MAPPED_SUBRESOURCE destlr;
|
D3D11_MAPPED_SUBRESOURCE destlr;
|
||||||
if (!m_intermediateTarget->LockRect(0, &destlr, D3D11_MAP_WRITE_DISCARD))
|
|
||||||
|
if (!m_intermediateTarget->GetTexture().LockRect(0, &destlr, D3D11_MAP_WRITE_DISCARD))
|
||||||
{
|
{
|
||||||
CLog::Log(LOGERROR, "WinRenderer: Failed to lock swtarget texture into memory");
|
CLog::Log(LOGERROR, "WinRenderer: Failed to lock swtarget texture into memory");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -87,7 +92,7 @@ bool CWinRenderBuffer::GetTexture(uint8_t*& data, unsigned int& stride)
|
||||||
|
|
||||||
bool CWinRenderBuffer::ReleaseTexture()
|
bool CWinRenderBuffer::ReleaseTexture()
|
||||||
{
|
{
|
||||||
if (!m_intermediateTarget->UnlockRect(0))
|
if (!m_intermediateTarget->GetTexture().UnlockRect(0))
|
||||||
{
|
{
|
||||||
CLog::Log(LOGERROR, "WinRenderer: Failed to unlock swtarget texture");
|
CLog::Log(LOGERROR, "WinRenderer: Failed to unlock swtarget texture");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -110,7 +115,8 @@ bool CWinRenderBuffer::UploadTexture()
|
||||||
// Create intermediate texture
|
// Create intermediate texture
|
||||||
if (!m_intermediateTarget)
|
if (!m_intermediateTarget)
|
||||||
{
|
{
|
||||||
m_intermediateTarget.reset(new CD3DTexture);
|
m_intermediateTarget =
|
||||||
|
std::make_unique<SHADER::CShaderTextureDX>(std::make_shared<CD3DTexture>());
|
||||||
if (!CreateTexture())
|
if (!CreateTexture())
|
||||||
{
|
{
|
||||||
m_intermediateTarget.reset();
|
m_intermediateTarget.reset();
|
||||||
|
|
@ -176,6 +182,13 @@ CWinRenderBufferPool::CWinRenderBufferPool()
|
||||||
|
|
||||||
bool CWinRenderBufferPool::IsCompatible(const CRenderVideoSettings& renderSettings) const
|
bool CWinRenderBufferPool::IsCompatible(const CRenderVideoSettings& renderSettings) const
|
||||||
{
|
{
|
||||||
|
//! @todo Move this logic to generic class
|
||||||
|
|
||||||
|
// Shader presets are compatible
|
||||||
|
if (!renderSettings.GetShaderPreset().empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If no shader preset is specified, scaling methods must match
|
||||||
return GetShader(renderSettings.GetScalingMethod()) != nullptr;
|
return GetShader(renderSettings.GetScalingMethod()) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +210,7 @@ bool CWinRenderBufferPool::ConfigureDX()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRPWinOutputShader* CWinRenderBufferPool::GetShader(SCALINGMETHOD scalingMethod) const
|
SHADER::CRPWinOutputShader* CWinRenderBufferPool::GetShader(SCALINGMETHOD scalingMethod) const
|
||||||
{
|
{
|
||||||
auto it = m_outputShaders.find(scalingMethod);
|
auto it = m_outputShaders.find(scalingMethod);
|
||||||
|
|
||||||
|
|
@ -221,7 +234,9 @@ void CWinRenderBufferPool::CompileOutputShaders()
|
||||||
{
|
{
|
||||||
for (auto scalingMethod : GetScalingMethods())
|
for (auto scalingMethod : GetScalingMethods())
|
||||||
{
|
{
|
||||||
std::unique_ptr<CRPWinOutputShader> outputShader(new CRPWinOutputShader);
|
std::unique_ptr<SHADER::CRPWinOutputShader> outputShader =
|
||||||
|
std::make_unique<SHADER::CRPWinOutputShader>();
|
||||||
|
|
||||||
if (outputShader->Create(scalingMethod))
|
if (outputShader->Create(scalingMethod))
|
||||||
m_outputShaders[scalingMethod] = std::move(outputShader);
|
m_outputShaders[scalingMethod] = std::move(outputShader);
|
||||||
else
|
else
|
||||||
|
|
@ -237,6 +252,8 @@ CRPWinRenderer::CRPWinRenderer(const CRenderSettings& renderSettings,
|
||||||
std::shared_ptr<IRenderBufferPool> bufferPool)
|
std::shared_ptr<IRenderBufferPool> bufferPool)
|
||||||
: CRPBaseRenderer(renderSettings, context, std::move(bufferPool))
|
: CRPBaseRenderer(renderSettings, context, std::move(bufferPool))
|
||||||
{
|
{
|
||||||
|
// Initialize CRPBaseRenderer fields
|
||||||
|
m_shaderPreset = std::make_unique<SHADER::CShaderPresetDX>(m_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRPWinRenderer::ConfigureInternal()
|
bool CRPWinRenderer::ConfigureInternal()
|
||||||
|
|
@ -280,11 +297,51 @@ void CRPWinRenderer::Render(CD3DTexture& target)
|
||||||
const CPoint destPoints[4] = {m_rotatedDestCoords[0], m_rotatedDestCoords[1],
|
const CPoint destPoints[4] = {m_rotatedDestCoords[0], m_rotatedDestCoords[1],
|
||||||
m_rotatedDestCoords[2], m_rotatedDestCoords[3]};
|
m_rotatedDestCoords[2], m_rotatedDestCoords[3]};
|
||||||
|
|
||||||
if (m_renderBuffer != nullptr)
|
CWinRenderBuffer* renderBuffer = static_cast<CWinRenderBuffer*>(m_renderBuffer);
|
||||||
|
if (renderBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SHADER::CShaderTextureDX* renderBufferTarget = renderBuffer->GetTarget();
|
||||||
|
if (renderBufferTarget == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Updateshaders();
|
||||||
|
|
||||||
|
// Are we using video shaders?
|
||||||
|
if (m_bUseShaderPreset)
|
||||||
{
|
{
|
||||||
CD3DTexture* intermediateTarget = static_cast<CWinRenderBuffer*>(m_renderBuffer)->GetTarget();
|
//! @todo Orientation?
|
||||||
if (intermediateTarget != nullptr)
|
/*
|
||||||
|
CPoint destPoints[4];
|
||||||
|
// select destination rectangle
|
||||||
|
if (m_renderOrientation)
|
||||||
{
|
{
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
destPoints[i] = m_rotatedDestCoords[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRect destRect = m_context.StereoCorrection(m_renderSettings.Geometry().Dimensions());
|
||||||
|
destPoints[0] = { destRect.x1, destRect.y1 };
|
||||||
|
destPoints[1] = { destRect.x2, destRect.y1 };
|
||||||
|
destPoints[2] = { destRect.x2, destRect.y2 };
|
||||||
|
destPoints[3] = { destRect.x1, destRect.y2 };
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
SHADER::CShaderTextureDXRef targetTexture{target};
|
||||||
|
|
||||||
|
// Render shaders and ouput to display
|
||||||
|
if (!m_shaderPreset->RenderUpdate(destPoints, *renderBufferTarget, targetTexture))
|
||||||
|
{
|
||||||
|
m_bShadersNeedUpdate = false;
|
||||||
|
m_bUseShaderPreset = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Not using video shaders, output using output shader
|
||||||
|
{
|
||||||
|
CD3DTexture& intermediateTarget = renderBufferTarget->GetTexture();
|
||||||
|
|
||||||
CRect viewPort;
|
CRect viewPort;
|
||||||
m_context.GetViewPort(viewPort);
|
m_context.GetViewPort(viewPort);
|
||||||
|
|
||||||
|
|
@ -292,14 +349,13 @@ void CRPWinRenderer::Render(CD3DTexture& target)
|
||||||
SCALINGMETHOD scalingMethod = m_renderSettings.VideoSettings().GetScalingMethod();
|
SCALINGMETHOD scalingMethod = m_renderSettings.VideoSettings().GetScalingMethod();
|
||||||
|
|
||||||
CWinRenderBufferPool* bufferPool = static_cast<CWinRenderBufferPool*>(m_bufferPool.get());
|
CWinRenderBufferPool* bufferPool = static_cast<CWinRenderBufferPool*>(m_bufferPool.get());
|
||||||
CRPWinOutputShader* outputShader = bufferPool->GetShader(scalingMethod);
|
SHADER::CRPWinOutputShader* outputShader = bufferPool->GetShader(scalingMethod);
|
||||||
|
|
||||||
// Use the picked output shader to render to the target
|
// Use the picked output shader to render to the target
|
||||||
if (outputShader != nullptr)
|
if (outputShader != nullptr)
|
||||||
{
|
{
|
||||||
outputShader->Render(*intermediateTarget, m_sourceRect, destPoints, viewPort, &target,
|
outputShader->Render(intermediateTarget, m_sourceRect, destPoints, viewPort, target,
|
||||||
m_context.UseLimitedColor() ? 1 : 0);
|
m_context.UseLimitedColor() ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@
|
||||||
#include "cores/RetroPlayer/buffers/BaseRenderBufferPool.h"
|
#include "cores/RetroPlayer/buffers/BaseRenderBufferPool.h"
|
||||||
#include "cores/RetroPlayer/buffers/video/RenderBufferSysMem.h"
|
#include "cores/RetroPlayer/buffers/video/RenderBufferSysMem.h"
|
||||||
#include "cores/RetroPlayer/process/RPProcessInfo.h"
|
#include "cores/RetroPlayer/process/RPProcessInfo.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/RPWinOutputShader.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -24,6 +26,11 @@ struct SwsContext;
|
||||||
|
|
||||||
namespace KODI
|
namespace KODI
|
||||||
{
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class CShaderTextureDX;
|
||||||
|
} // namespace SHADER
|
||||||
|
|
||||||
namespace RETRO
|
namespace RETRO
|
||||||
{
|
{
|
||||||
class CRenderContext;
|
class CRenderContext;
|
||||||
|
|
@ -51,7 +58,7 @@ public:
|
||||||
// implementation of IRenderBuffer via CRenderBufferSysMem
|
// implementation of IRenderBuffer via CRenderBufferSysMem
|
||||||
bool UploadTexture() override;
|
bool UploadTexture() override;
|
||||||
|
|
||||||
CD3DTexture* GetTarget() { return m_intermediateTarget.get(); }
|
SHADER::CShaderTextureDX* GetTarget() { return m_intermediateTarget.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CreateTexture();
|
bool CreateTexture();
|
||||||
|
|
@ -71,7 +78,7 @@ private:
|
||||||
const DXGI_FORMAT m_targetDxFormat;
|
const DXGI_FORMAT m_targetDxFormat;
|
||||||
|
|
||||||
AVPixelFormat m_targetPixFormat;
|
AVPixelFormat m_targetPixFormat;
|
||||||
std::unique_ptr<CD3DTexture> m_intermediateTarget;
|
std::unique_ptr<SHADER::CShaderTextureDX> m_intermediateTarget;
|
||||||
|
|
||||||
SwsContext* m_swsContext = nullptr;
|
SwsContext* m_swsContext = nullptr;
|
||||||
};
|
};
|
||||||
|
|
@ -90,7 +97,7 @@ public:
|
||||||
|
|
||||||
// DirectX interface
|
// DirectX interface
|
||||||
bool ConfigureDX();
|
bool ConfigureDX();
|
||||||
CRPWinOutputShader* GetShader(SCALINGMETHOD scalingMethod) const;
|
SHADER::CRPWinOutputShader* GetShader(SCALINGMETHOD scalingMethod) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::vector<SCALINGMETHOD>& GetScalingMethods();
|
static const std::vector<SCALINGMETHOD>& GetScalingMethods();
|
||||||
|
|
@ -98,7 +105,7 @@ private:
|
||||||
void CompileOutputShaders();
|
void CompileOutputShaders();
|
||||||
|
|
||||||
DXGI_FORMAT m_targetDxFormat = DXGI_FORMAT_UNKNOWN;
|
DXGI_FORMAT m_targetDxFormat = DXGI_FORMAT_UNKNOWN;
|
||||||
std::map<SCALINGMETHOD, std::unique_ptr<CRPWinOutputShader>> m_outputShaders;
|
std::map<SCALINGMETHOD, std::unique_ptr<SHADER::CRPWinOutputShader>> m_outputShaders;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CRPWinRenderer : public CRPBaseRenderer
|
class CRPWinRenderer : public CRPBaseRenderer
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
if(NOT ENABLE_STATIC_LIBS)
|
|
||||||
core_add_library(rp-videoshaders)
|
|
||||||
endif()
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
set(SOURCES RPWinOutputShader.cpp)
|
|
||||||
|
|
||||||
set(HEADERS RPWinOutputShader.h)
|
|
||||||
|
|
||||||
core_add_library(rp-videoshaders-windows)
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2017-2018 Team Kodi
|
|
||||||
* This file is part of Kodi - https://kodi.tv
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
* See LICENSES/README.md for more information.
|
|
||||||
*/
|
|
||||||
#include "RPWinOutputShader.h"
|
|
||||||
|
|
||||||
#include "utils/log.h"
|
|
||||||
|
|
||||||
using namespace KODI;
|
|
||||||
using namespace RETRO;
|
|
||||||
|
|
||||||
bool CRPWinOutputShader::Create(SCALINGMETHOD scalingMethod)
|
|
||||||
{
|
|
||||||
CWinShader::CreateVertexBuffer(4, sizeof(CUSTOMVERTEX));
|
|
||||||
|
|
||||||
DefinesMap defines;
|
|
||||||
switch (scalingMethod)
|
|
||||||
{
|
|
||||||
case SCALINGMETHOD::NEAREST:
|
|
||||||
defines["SAMP_NEAREST"] = "";
|
|
||||||
break;
|
|
||||||
case SCALINGMETHOD::LINEAR:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string effectPath("special://xbmc/system/shaders/rp_output_d3d.fx");
|
|
||||||
|
|
||||||
if (!LoadEffect(effectPath, &defines))
|
|
||||||
{
|
|
||||||
CLog::LogF(LOGERROR, "Failed to load shader {}.", effectPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create input layout
|
|
||||||
D3D11_INPUT_ELEMENT_DESC layout[] = {
|
|
||||||
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
|
||||||
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
|
||||||
};
|
|
||||||
return CWinShader::CreateInputLayout(layout, ARRAYSIZE(layout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CRPWinOutputShader::Render(CD3DTexture& sourceTexture,
|
|
||||||
CRect sourceRect,
|
|
||||||
const CPoint points[4],
|
|
||||||
CRect& viewPort,
|
|
||||||
CD3DTexture* target,
|
|
||||||
unsigned range)
|
|
||||||
{
|
|
||||||
PrepareParameters(sourceTexture.GetWidth(), sourceTexture.GetHeight(), sourceRect, points);
|
|
||||||
SetShaderParameters(sourceTexture, range, viewPort);
|
|
||||||
Execute({target}, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CRPWinOutputShader::PrepareParameters(unsigned sourceWidth,
|
|
||||||
unsigned sourceHeight,
|
|
||||||
CRect sourceRect,
|
|
||||||
const CPoint points[4])
|
|
||||||
{
|
|
||||||
bool changed = false;
|
|
||||||
for (int i = 0; i < 4 && !changed; ++i)
|
|
||||||
changed = points[i] != m_destPoints[i];
|
|
||||||
|
|
||||||
if (m_sourceWidth != sourceWidth || m_sourceHeight != sourceHeight ||
|
|
||||||
m_sourceRect != sourceRect || changed)
|
|
||||||
{
|
|
||||||
m_sourceWidth = sourceWidth;
|
|
||||||
m_sourceHeight = sourceHeight;
|
|
||||||
m_sourceRect = sourceRect;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
m_destPoints[i] = points[i];
|
|
||||||
|
|
||||||
CUSTOMVERTEX* v = nullptr;
|
|
||||||
CWinShader::LockVertexBuffer(static_cast<void**>(static_cast<void*>(&v)));
|
|
||||||
|
|
||||||
v[0].x = m_destPoints[0].x;
|
|
||||||
v[0].y = m_destPoints[0].y;
|
|
||||||
v[0].z = 0.0f;
|
|
||||||
v[0].tu = m_sourceRect.x1 / m_sourceWidth;
|
|
||||||
v[0].tv = m_sourceRect.y1 / m_sourceHeight;
|
|
||||||
|
|
||||||
v[1].x = m_destPoints[1].x;
|
|
||||||
v[1].y = m_destPoints[1].y;
|
|
||||||
v[1].z = 0.0f;
|
|
||||||
v[1].tu = m_sourceRect.x2 / m_sourceWidth;
|
|
||||||
v[1].tv = m_sourceRect.y1 / m_sourceHeight;
|
|
||||||
|
|
||||||
v[2].x = m_destPoints[2].x;
|
|
||||||
v[2].y = m_destPoints[2].y;
|
|
||||||
v[2].z = 0.0f;
|
|
||||||
v[2].tu = m_sourceRect.x2 / m_sourceWidth;
|
|
||||||
v[2].tv = m_sourceRect.y2 / m_sourceHeight;
|
|
||||||
|
|
||||||
v[3].x = m_destPoints[3].x;
|
|
||||||
v[3].y = m_destPoints[3].y;
|
|
||||||
v[3].z = 0.0f;
|
|
||||||
v[3].tu = m_sourceRect.x1 / m_sourceWidth;
|
|
||||||
v[3].tv = m_sourceRect.y2 / m_sourceHeight;
|
|
||||||
|
|
||||||
CWinShader::UnlockVertexBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CRPWinOutputShader::SetShaderParameters(CD3DTexture& sourceTexture,
|
|
||||||
unsigned range,
|
|
||||||
CRect& viewPort)
|
|
||||||
{
|
|
||||||
m_effect.SetTechnique("OUTPUT_T");
|
|
||||||
m_effect.SetResources("g_Texture", sourceTexture.GetAddressOfSRV(), 1);
|
|
||||||
|
|
||||||
float viewPortArray[2] = {viewPort.Width(), viewPort.Height()};
|
|
||||||
m_effect.SetFloatArray("g_viewPort", viewPortArray, 2);
|
|
||||||
|
|
||||||
float params[3] = {static_cast<float>(range)};
|
|
||||||
m_effect.SetFloatArray("m_params", params, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2017-2018 Team Kodi
|
|
||||||
* This file is part of Kodi - https://kodi.tv
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
* See LICENSES/README.md for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cores/GameSettings.h"
|
|
||||||
#include "cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h"
|
|
||||||
|
|
||||||
namespace KODI
|
|
||||||
{
|
|
||||||
namespace RETRO
|
|
||||||
{
|
|
||||||
|
|
||||||
class CRPWinOutputShader : public CWinShader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
~CRPWinOutputShader() = default;
|
|
||||||
|
|
||||||
bool Create(SCALINGMETHOD scalingMethod);
|
|
||||||
void Render(CD3DTexture& sourceTexture,
|
|
||||||
CRect sourceRect,
|
|
||||||
const CPoint points[4],
|
|
||||||
CRect& viewPort,
|
|
||||||
CD3DTexture* target,
|
|
||||||
unsigned range = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void PrepareParameters(unsigned sourceWidth,
|
|
||||||
unsigned sourceHeight,
|
|
||||||
CRect sourceRect,
|
|
||||||
const CPoint points[4]);
|
|
||||||
void SetShaderParameters(CD3DTexture& sourceTexture, unsigned range, CRect& viewPort);
|
|
||||||
|
|
||||||
unsigned m_sourceWidth{0};
|
|
||||||
unsigned m_sourceHeight{0};
|
|
||||||
CRect m_sourceRect{0.f, 0.f, 0.f, 0.f};
|
|
||||||
CPoint m_destPoints[4] = {
|
|
||||||
{0.f, 0.f},
|
|
||||||
{0.f, 0.f},
|
|
||||||
{0.f, 0.f},
|
|
||||||
{0.f, 0.f},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CUSTOMVERTEX
|
|
||||||
{
|
|
||||||
FLOAT x;
|
|
||||||
FLOAT y;
|
|
||||||
FLOAT z;
|
|
||||||
|
|
||||||
FLOAT tu;
|
|
||||||
FLOAT tv;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace RETRO
|
|
||||||
} // namespace KODI
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
set(SOURCES ShaderPreset.cpp
|
||||||
|
ShaderPresetFactory.cpp
|
||||||
|
ShaderUtils.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADERS IShader.h
|
||||||
|
IShaderLut.h
|
||||||
|
IShaderPreset.h
|
||||||
|
IShaderPresetLoader.h
|
||||||
|
IShaderSampler.h
|
||||||
|
IShaderTexture.h
|
||||||
|
ShaderPreset.h
|
||||||
|
ShaderPresetFactory.h
|
||||||
|
ShaderTypes.h
|
||||||
|
ShaderUtils.h
|
||||||
|
)
|
||||||
|
|
||||||
|
core_add_library(rp-shaders)
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
#include "utils/Geometry.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderLut;
|
||||||
|
class IShaderTexture;
|
||||||
|
|
||||||
|
class IShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShader() = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Construct the video shader instance
|
||||||
|
*
|
||||||
|
* \param shaderSource Source code of the shader (both vertex and pixel/fragment)
|
||||||
|
* \param shaderPath Full path to the shader file
|
||||||
|
* \param shaderParameters Struct with all parameters pertaining to the shader
|
||||||
|
* \param luts Look-up textures pertaining to the shader
|
||||||
|
* \param viewPortSize Size of the window/viewport
|
||||||
|
* \param passIdx Index of the video shader pass
|
||||||
|
* \param frameCountMod Modulo applied to the frame count before sendign it to the shader
|
||||||
|
*
|
||||||
|
* \return Returns false if creating the shader failed, true otherwise
|
||||||
|
*/
|
||||||
|
virtual bool Create(std::string shaderSource,
|
||||||
|
std::string shaderPath,
|
||||||
|
ShaderParameterMap shaderParameters,
|
||||||
|
std::vector<std::shared_ptr<IShaderLut>> luts,
|
||||||
|
float2 viewPortSize,
|
||||||
|
unsigned int passIdx,
|
||||||
|
unsigned int frameCountMod = 0) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Renders the video shader to the target texture
|
||||||
|
*
|
||||||
|
* \param source Source texture to pass to the shader as input
|
||||||
|
* \param target Target texture to render the shader to
|
||||||
|
*/
|
||||||
|
virtual void Render(IShaderTexture& source, IShaderTexture& target) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Sets the input and output sizes in pixels
|
||||||
|
*
|
||||||
|
* \param prevSize Input image size of the shader in pixels
|
||||||
|
* \param prevTextureSize Power-of-two input texture size in pixels
|
||||||
|
* \param nextSize Output image size of the shader in pixels
|
||||||
|
*/
|
||||||
|
virtual void SetSizes(const float2& prevSize,
|
||||||
|
const float2& prevTextureSize,
|
||||||
|
const float2& nextSize) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Called before rendering
|
||||||
|
*
|
||||||
|
* Updates any internal state needed to ensure that correct data is passed to
|
||||||
|
* the shader when rendering.
|
||||||
|
*
|
||||||
|
* \param dest Coordinates of the 4 corners of the output viewport/window
|
||||||
|
* \param sourceTexture Source texture of the first shader pass
|
||||||
|
* \param pShaderTextures Intermediate textures used for all shader passes
|
||||||
|
* \param pShaders All shader passes
|
||||||
|
* \param frameCount Number of frames that have passed
|
||||||
|
*/
|
||||||
|
virtual void PrepareParameters(
|
||||||
|
CPoint dest[4],
|
||||||
|
IShaderTexture& sourceTexture,
|
||||||
|
const std::vector<std::unique_ptr<IShaderTexture>>& pShaderTextures,
|
||||||
|
const std::vector<std::unique_ptr<IShader>>& pShaders,
|
||||||
|
uint64_t frameCount) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Updates the model view projection matrix
|
||||||
|
*
|
||||||
|
* Should usually only be called when the viewport/window size changes.
|
||||||
|
*/
|
||||||
|
virtual void UpdateMVP() = 0;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CTexture;
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace RETRO
|
||||||
|
{
|
||||||
|
class CRenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief A lookup table to apply color transforms in a shader
|
||||||
|
*/
|
||||||
|
class IShaderLut
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IShaderLut(std::string id, std::string path) : m_id(std::move(id)), m_path(std::move(path)) {}
|
||||||
|
virtual ~IShaderLut() = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Create the LUT and allocate resources
|
||||||
|
*
|
||||||
|
* \param context The render context
|
||||||
|
* \param lut The LUT information structure
|
||||||
|
*
|
||||||
|
* \return Returns true if successful and the LUT can be used, false otherwise
|
||||||
|
*/
|
||||||
|
virtual bool Create(RETRO::CRenderContext& context, const ShaderLut& lut) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets ID of LUT
|
||||||
|
*
|
||||||
|
* \return Returns unique name (ID) of look up texture
|
||||||
|
*/
|
||||||
|
const std::string& GetID() const { return m_id; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets full path of LUT
|
||||||
|
*
|
||||||
|
* \return Returns full path of look up texture
|
||||||
|
*/
|
||||||
|
const std::string& GetPath() const { return m_path; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets texture of LUT
|
||||||
|
*
|
||||||
|
* \return Returns pointer to the texture where the LUT data is stored in
|
||||||
|
*/
|
||||||
|
virtual CTexture* GetTexture() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_id;
|
||||||
|
std::string m_path;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
#include "utils/Geometry.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderTexture;
|
||||||
|
|
||||||
|
class IShaderPreset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShaderPreset() = default;
|
||||||
|
|
||||||
|
//! @todo Implement once and for all
|
||||||
|
/*!
|
||||||
|
* \brief Reads/parses a shader preset file and loads its state to the object
|
||||||
|
*
|
||||||
|
* The state is implementation specific.
|
||||||
|
*
|
||||||
|
* \param presetPath Full path of the preset file
|
||||||
|
*
|
||||||
|
* \return Returns true on successful parsing, false on failed
|
||||||
|
*/
|
||||||
|
virtual bool ReadPresetFile(const std::string& presetPath) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Updates state if needed and renderes the preset to the target texture
|
||||||
|
*
|
||||||
|
* \param dest Coordinates of the 4 corners of the output viewport/window
|
||||||
|
* \param source The source of the video frame, in its original resolution (unscaled)
|
||||||
|
* \param target The target texture that the final result will be rendered to
|
||||||
|
*
|
||||||
|
* \return Returns false if updating or rendering failed, true if both succeeded
|
||||||
|
*/
|
||||||
|
virtual bool RenderUpdate(const CPoint dest[],
|
||||||
|
IShaderTexture& source,
|
||||||
|
IShaderTexture& target) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Informs about the speed of playback
|
||||||
|
*
|
||||||
|
* \param speed Commonly 1.0 for normal playback, and 0.0 if paused
|
||||||
|
*/
|
||||||
|
virtual void SetSpeed(double speed) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Size of the input/source frame in pixels
|
||||||
|
*
|
||||||
|
* \param videoWidth Height of the source frame in pixels
|
||||||
|
* \param videoHeight Height of the source frame in pixels
|
||||||
|
*/
|
||||||
|
virtual void SetVideoSize(unsigned int videoWidth, unsigned int videoHeight) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set the preset to be rendered on the next frame
|
||||||
|
*
|
||||||
|
* \param shaderPresetPath Full path to the preset file to be loaded
|
||||||
|
*
|
||||||
|
* \return Returns false if loading the preset failed, true otherwise
|
||||||
|
*/
|
||||||
|
virtual bool SetShaderPreset(const std::string& shaderPresetPath) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the full path to the shader preset
|
||||||
|
*
|
||||||
|
* \return The full path to the currently loaded preset file
|
||||||
|
*/
|
||||||
|
virtual const std::string& GetShaderPreset() const = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the passes of the loaded preset
|
||||||
|
*
|
||||||
|
* \return All the video shader passes of the currently loaded preset
|
||||||
|
*/
|
||||||
|
virtual std::vector<ShaderPass>& GetPasses() = 0;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderPreset;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief API for a class that can load shader presets
|
||||||
|
*/
|
||||||
|
class IShaderPresetLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShaderPresetLoader() = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Load a shader preset from the given path
|
||||||
|
*
|
||||||
|
* \param presetPath The path to the shader preset
|
||||||
|
* \param shaderPreset The loaded shader preset, or untouched if false is returned
|
||||||
|
*
|
||||||
|
* \return True if the preset was loaded, false otherwise
|
||||||
|
*/
|
||||||
|
virtual bool LoadPreset(const std::string& presetPath, IShaderPreset& shaderPreset) = 0;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderSampler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShaderSampler() = default;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief Interface for a shader texture
|
||||||
|
*
|
||||||
|
* The interface is left relatively generic. The implementation should be able
|
||||||
|
* to manage any resources required by the texture.
|
||||||
|
*/
|
||||||
|
class IShaderTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShaderTexture() = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return width of texture
|
||||||
|
*
|
||||||
|
* \return Width of the texture in texels
|
||||||
|
*/
|
||||||
|
virtual float GetWidth() const = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return height of texture
|
||||||
|
*
|
||||||
|
* \return Height of the texture in texels
|
||||||
|
*/
|
||||||
|
virtual float GetHeight() const = 0;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderPreset.h"
|
||||||
|
|
||||||
|
#include "IShader.h"
|
||||||
|
#include "IShaderTexture.h"
|
||||||
|
#include "ServiceBroker.h"
|
||||||
|
#include "ShaderPresetFactory.h"
|
||||||
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
|
#include "games/GameServices.h"
|
||||||
|
#include "utils/URIUtils.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderPreset::CShaderPreset(RETRO::CRenderContext& context,
|
||||||
|
unsigned videoWidth,
|
||||||
|
unsigned videoHeight)
|
||||||
|
: m_context(context), m_videoSize(videoWidth, videoHeight)
|
||||||
|
{
|
||||||
|
CRect viewPort;
|
||||||
|
m_context.GetViewPort(viewPort);
|
||||||
|
m_outputSize = {viewPort.Width(), viewPort.Height()};
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderPreset::~CShaderPreset()
|
||||||
|
{
|
||||||
|
DisposeShaders();
|
||||||
|
|
||||||
|
// The GUI is going to render after this, so apply the state required
|
||||||
|
m_context.ApplyStateBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::ReadPresetFile(const std::string& presetPath)
|
||||||
|
{
|
||||||
|
return CServiceBroker::GetGameServices().VideoShaders().LoadPreset(presetPath, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::RenderUpdate(const CPoint dest[],
|
||||||
|
IShaderTexture& source,
|
||||||
|
IShaderTexture& target)
|
||||||
|
{
|
||||||
|
// Save the viewport
|
||||||
|
CRect viewPort;
|
||||||
|
m_context.GetViewPort(viewPort);
|
||||||
|
|
||||||
|
// Handle resizing of the viewport (window)
|
||||||
|
UpdateViewPort(viewPort);
|
||||||
|
|
||||||
|
// Update shaders/shader textures if required
|
||||||
|
if (!Update())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PrepareParameters(dest, source, target);
|
||||||
|
|
||||||
|
const unsigned int numPasses = static_cast<unsigned int>(m_pShaders.size());
|
||||||
|
|
||||||
|
// Apply all passes except the last one (which needs to be applied to the backbuffer)
|
||||||
|
IShaderTexture* sourceTexture = &source;
|
||||||
|
for (unsigned shaderIdx = 0; shaderIdx + 1 < numPasses; ++shaderIdx)
|
||||||
|
{
|
||||||
|
IShader& shader = *m_pShaders[shaderIdx];
|
||||||
|
IShaderTexture& texture = *m_pShaderTextures[shaderIdx];
|
||||||
|
RenderShader(shader, *sourceTexture, texture);
|
||||||
|
sourceTexture = &texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore our viewport
|
||||||
|
m_context.SetViewPort(viewPort);
|
||||||
|
m_context.SetScissors(viewPort);
|
||||||
|
|
||||||
|
// Apply the last pass and write to target (backbuffer) instead of the last texture
|
||||||
|
IShader* lastShader = m_pShaders.back().get();
|
||||||
|
lastShader->Render(*sourceTexture, target);
|
||||||
|
|
||||||
|
m_frameCount += static_cast<float>(m_speed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::SetVideoSize(unsigned int videoWidth, unsigned int videoHeight)
|
||||||
|
{
|
||||||
|
if (videoWidth != static_cast<unsigned int>(m_videoSize.x) ||
|
||||||
|
videoHeight != static_cast<unsigned int>(m_videoSize.y))
|
||||||
|
{
|
||||||
|
m_videoSize = {videoWidth, videoHeight};
|
||||||
|
m_bPresetNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::SetShaderPreset(const std::string& shaderPresetPath)
|
||||||
|
{
|
||||||
|
m_bPresetNeedsUpdate = true;
|
||||||
|
m_presetPath = shaderPresetPath;
|
||||||
|
return Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& CShaderPreset::GetShaderPreset() const
|
||||||
|
{
|
||||||
|
return m_presetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::Update()
|
||||||
|
{
|
||||||
|
auto updateFailed = [this](const std::string& msg)
|
||||||
|
{
|
||||||
|
m_failedPaths.insert(m_presetPath);
|
||||||
|
CLog::Log(LOGWARNING, "CShaderPreset::Update: {}", msg);
|
||||||
|
DisposeShaders();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_bPresetNeedsUpdate && !HasPathFailed(m_presetPath))
|
||||||
|
{
|
||||||
|
DisposeShaders();
|
||||||
|
|
||||||
|
if (m_presetPath.empty())
|
||||||
|
// No preset should load, just return false, we shouldn't add "" to the failed paths
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ReadPresetFile(m_presetPath))
|
||||||
|
{
|
||||||
|
CLog::Log(
|
||||||
|
LOGERROR,
|
||||||
|
"CShaderPreset::Update: Couldn't load shader preset {} or the shaders it references",
|
||||||
|
m_presetPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateShaders())
|
||||||
|
return updateFailed("Failed to initialize shaders");
|
||||||
|
|
||||||
|
if (!CreateLayouts())
|
||||||
|
return updateFailed("Failed to create layouts");
|
||||||
|
|
||||||
|
if (!CreateBuffers())
|
||||||
|
return updateFailed("Failed to initialize buffers");
|
||||||
|
|
||||||
|
if (!CreateShaderTextures())
|
||||||
|
return updateFailed("A shader texture failed to init");
|
||||||
|
|
||||||
|
if (!CreateSamplers())
|
||||||
|
return updateFailed("Failed to create samplers");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pShaders.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Each pass except the last one must have its own texture and the opposite is also true
|
||||||
|
if (m_pShaders.size() != m_pShaderTextures.size() + 1)
|
||||||
|
return updateFailed("A shader or texture failed to init");
|
||||||
|
|
||||||
|
m_bPresetNeedsUpdate = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::UpdateViewPort()
|
||||||
|
{
|
||||||
|
CRect viewPort;
|
||||||
|
m_context.GetViewPort(viewPort);
|
||||||
|
UpdateViewPort(viewPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::UpdateViewPort(CRect viewPort)
|
||||||
|
{
|
||||||
|
const float2 currentViewPortSize = {viewPort.Width(), viewPort.Height()};
|
||||||
|
if (currentViewPortSize != m_outputSize)
|
||||||
|
{
|
||||||
|
m_outputSize = currentViewPortSize;
|
||||||
|
m_bPresetNeedsUpdate = true;
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::UpdateMVPs()
|
||||||
|
{
|
||||||
|
for (std::unique_ptr<IShader>& videoShader : m_pShaders)
|
||||||
|
videoShader->UpdateMVP();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::PrepareParameters(const CPoint dest[],
|
||||||
|
IShaderTexture& source,
|
||||||
|
IShaderTexture& target)
|
||||||
|
{
|
||||||
|
if (m_dest[0] != dest[0] || m_dest[1] != dest[1] || m_dest[2] != dest[2] ||
|
||||||
|
m_dest[3] != dest[3] || target.GetWidth() != m_outputSize.x ||
|
||||||
|
target.GetHeight() != m_outputSize.y)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
m_dest[i] = dest[i];
|
||||||
|
|
||||||
|
m_outputSize = {target.GetWidth(), target.GetHeight()};
|
||||||
|
|
||||||
|
// Update projection matrix and update video shaders
|
||||||
|
UpdateMVPs();
|
||||||
|
UpdateViewPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int numPasses = static_cast<unsigned int>(m_pShaders.size());
|
||||||
|
|
||||||
|
// Prepare parameters for all shader passes
|
||||||
|
for (unsigned int shaderIdx = 0; shaderIdx < numPasses; ++shaderIdx)
|
||||||
|
{
|
||||||
|
std::unique_ptr<IShader>& videoShader = m_pShaders[shaderIdx];
|
||||||
|
videoShader->PrepareParameters(m_dest, source, m_pShaderTextures, m_pShaders,
|
||||||
|
static_cast<uint64_t>(m_frameCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPreset::DisposeShaders()
|
||||||
|
{
|
||||||
|
m_pShaders.clear();
|
||||||
|
m_pShaderTextures.clear();
|
||||||
|
m_passes.clear();
|
||||||
|
m_bPresetNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPreset::HasPathFailed(const std::string& path) const
|
||||||
|
{
|
||||||
|
return m_failedPaths.find(path) != m_failedPaths.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderParameterMap CShaderPreset::GetShaderParameters(
|
||||||
|
const std::vector<ShaderParameter>& parameters, const std::string& sourceStr) const
|
||||||
|
{
|
||||||
|
static const std::regex pragmaParamRegex("#pragma parameter ([a-zA-Z_][a-zA-Z0-9_]*)");
|
||||||
|
std::smatch matches;
|
||||||
|
|
||||||
|
std::vector<std::string> validParams;
|
||||||
|
std::string::const_iterator searchStart(sourceStr.cbegin());
|
||||||
|
while (regex_search(searchStart, sourceStr.cend(), matches, pragmaParamRegex))
|
||||||
|
{
|
||||||
|
validParams.push_back(matches[1].str());
|
||||||
|
searchStart += matches.position() + matches.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderParameterMap matchParams;
|
||||||
|
|
||||||
|
// For each param found in the source code
|
||||||
|
for (const std::string& match : validParams)
|
||||||
|
{
|
||||||
|
// For each param found in the preset file
|
||||||
|
for (const ShaderParameter& parameter : parameters)
|
||||||
|
{
|
||||||
|
// Check if they match
|
||||||
|
if (match == parameter.strId)
|
||||||
|
{
|
||||||
|
// The add-on has already handled parsing and overwriting default
|
||||||
|
// parameter values from the preset file. The final value we
|
||||||
|
// should use is in the 'current' field.
|
||||||
|
matchParams[match] = parameter.current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchParams;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderPreset.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderTypes.h"
|
||||||
|
#include "utils/Geometry.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace RETRO
|
||||||
|
{
|
||||||
|
class CRenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShader;
|
||||||
|
class IShaderTexture;
|
||||||
|
|
||||||
|
class CShaderPreset : public IShaderPreset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Instance of CShaderPreset
|
||||||
|
explicit CShaderPreset(RETRO::CRenderContext& context,
|
||||||
|
unsigned videoWidth = 0,
|
||||||
|
unsigned videoHeight = 0);
|
||||||
|
~CShaderPreset() override;
|
||||||
|
|
||||||
|
// Implementation of IShaderPreset
|
||||||
|
bool ReadPresetFile(const std::string& presetPath) override;
|
||||||
|
bool RenderUpdate(const CPoint dest[], IShaderTexture& source, IShaderTexture& target) override;
|
||||||
|
void SetSpeed(double speed) override { m_speed = speed; }
|
||||||
|
void SetVideoSize(unsigned int videoWidth, unsigned int videoHeight) override;
|
||||||
|
bool SetShaderPreset(const std::string& shaderPresetPath) override;
|
||||||
|
const std::string& GetShaderPreset() const override;
|
||||||
|
std::vector<ShaderPass>& GetPasses() override { return m_passes; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Shader interface
|
||||||
|
virtual bool CreateShaders() = 0;
|
||||||
|
virtual bool CreateLayouts() = 0;
|
||||||
|
virtual bool CreateBuffers() = 0;
|
||||||
|
virtual bool CreateShaderTextures() = 0;
|
||||||
|
virtual bool CreateSamplers() = 0;
|
||||||
|
virtual void RenderShader(IShader& shader, IShaderTexture& source, IShaderTexture& target) = 0;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
bool Update();
|
||||||
|
void UpdateViewPort();
|
||||||
|
void UpdateViewPort(CRect viewPort);
|
||||||
|
void UpdateMVPs();
|
||||||
|
void PrepareParameters(const CPoint dest[], IShaderTexture& source, IShaderTexture& target);
|
||||||
|
void DisposeShaders();
|
||||||
|
bool HasPathFailed(const std::string& path) const;
|
||||||
|
ShaderParameterMap GetShaderParameters(const std::vector<ShaderParameter>& parameters,
|
||||||
|
const std::string& sourceStr) const;
|
||||||
|
|
||||||
|
// Construction parameters
|
||||||
|
RETRO::CRenderContext& m_context;
|
||||||
|
|
||||||
|
// Relative path of the currently loaded shader preset
|
||||||
|
// If empty, it means that a preset is not currently loaded
|
||||||
|
std::string m_presetPath;
|
||||||
|
|
||||||
|
// Set of paths of presets that are known to not load correctly
|
||||||
|
// Should not contain "" (empty path) because this signifies that a preset is not loaded
|
||||||
|
std::set<std::string> m_failedPaths;
|
||||||
|
|
||||||
|
// All video shader passes of the currently loaded preset
|
||||||
|
std::vector<ShaderPass> m_passes;
|
||||||
|
|
||||||
|
// Video shaders for the shader passes
|
||||||
|
std::vector<std::unique_ptr<IShader>> m_pShaders;
|
||||||
|
|
||||||
|
// Intermediate textures used for pixel shader passes
|
||||||
|
std::vector<std::unique_ptr<IShaderTexture>> m_pShaderTextures;
|
||||||
|
|
||||||
|
// Was the shader preset changed during the last frame?
|
||||||
|
bool m_bPresetNeedsUpdate = true;
|
||||||
|
|
||||||
|
// Size of the viewport
|
||||||
|
float2 m_outputSize;
|
||||||
|
|
||||||
|
// Size of the actual source video data (ie. 160x144 for the Game Boy)
|
||||||
|
float2 m_videoSize;
|
||||||
|
|
||||||
|
// Array of vertices that comprise the full viewport
|
||||||
|
CPoint m_dest[4];
|
||||||
|
|
||||||
|
// Number of frames that have passed
|
||||||
|
float m_frameCount = 0.0f;
|
||||||
|
|
||||||
|
// Playback speed
|
||||||
|
double m_speed = 1.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderPresetFactory.h"
|
||||||
|
|
||||||
|
#include "IShaderPresetLoader.h"
|
||||||
|
#include "addons/AddonEvents.h"
|
||||||
|
#include "addons/AddonManager.h"
|
||||||
|
#include "addons/ShaderPreset.h"
|
||||||
|
#include "addons/addoninfo/AddonInfo.h"
|
||||||
|
#include "addons/addoninfo/AddonType.h"
|
||||||
|
#include "utils/URIUtils.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderPresetFactory::CShaderPresetFactory(ADDON::CAddonMgr& addons) : m_addons(addons)
|
||||||
|
{
|
||||||
|
UpdateAddons();
|
||||||
|
|
||||||
|
m_addons.Events().Subscribe(this, &CShaderPresetFactory::OnEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderPresetFactory::~CShaderPresetFactory()
|
||||||
|
{
|
||||||
|
m_addons.Events().Unsubscribe(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetFactory::RegisterLoader(IShaderPresetLoader* loader, const std::string& extension)
|
||||||
|
{
|
||||||
|
if (!extension.empty())
|
||||||
|
{
|
||||||
|
std::string strExtension = extension;
|
||||||
|
|
||||||
|
// Canonicalize extension with leading "."
|
||||||
|
if (extension[0] != '.')
|
||||||
|
strExtension.insert(strExtension.begin(), '.');
|
||||||
|
|
||||||
|
m_loaders.insert(std::make_pair(std::move(strExtension), loader));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetFactory::UnregisterLoader(IShaderPresetLoader* loader)
|
||||||
|
{
|
||||||
|
for (auto it = m_loaders.begin(); it != m_loaders.end();)
|
||||||
|
{
|
||||||
|
if (it->second == loader)
|
||||||
|
m_loaders.erase(it++);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetFactory::LoadPreset(const std::string& presetPath, IShaderPreset& shaderPreset)
|
||||||
|
{
|
||||||
|
bool bSuccess = false;
|
||||||
|
|
||||||
|
std::string extension = URIUtils::GetExtension(presetPath);
|
||||||
|
if (!extension.empty())
|
||||||
|
{
|
||||||
|
auto itLoader = m_loaders.find(extension);
|
||||||
|
if (itLoader != m_loaders.end())
|
||||||
|
bSuccess = itLoader->second->LoadPreset(presetPath, shaderPreset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetFactory::CanLoadPreset(const std::string& presetPath)
|
||||||
|
{
|
||||||
|
bool bSuccess = false;
|
||||||
|
|
||||||
|
std::string extension = URIUtils::GetExtension(presetPath);
|
||||||
|
if (!extension.empty())
|
||||||
|
bSuccess = (m_loaders.find(extension) != m_loaders.end());
|
||||||
|
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetFactory::OnEvent(const ADDON::AddonEvent& event)
|
||||||
|
{
|
||||||
|
if (typeid(event) == typeid(ADDON::AddonEvents::Enabled) ||
|
||||||
|
typeid(event) == typeid(ADDON::AddonEvents::Disabled) ||
|
||||||
|
typeid(event) == typeid(ADDON::AddonEvents::UnInstalled) ||
|
||||||
|
typeid(event) == typeid(ADDON::AddonEvents::ReInstalled))
|
||||||
|
{
|
||||||
|
UpdateAddons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetFactory::UpdateAddons()
|
||||||
|
{
|
||||||
|
using namespace ADDON;
|
||||||
|
|
||||||
|
std::vector<AddonInfoPtr> addonInfo;
|
||||||
|
m_addons.GetAddonInfos(addonInfo, true, AddonType::SHADERDLL);
|
||||||
|
|
||||||
|
// Look for removed/disabled add-ons
|
||||||
|
auto oldAddons = std::move(m_shaderAddons);
|
||||||
|
for (auto it = oldAddons.begin(); it != oldAddons.end(); ++it)
|
||||||
|
{
|
||||||
|
const std::string& addonId = it->first;
|
||||||
|
std::unique_ptr<ADDON::CShaderPresetAddon>& shaderAddon = it->second;
|
||||||
|
|
||||||
|
const bool bIsDisabled =
|
||||||
|
std::find_if(addonInfo.begin(), addonInfo.end(), [&addonId](const AddonInfoPtr& addon)
|
||||||
|
{ return addonId == addon->ID(); }) == addonInfo.end();
|
||||||
|
|
||||||
|
if (bIsDisabled)
|
||||||
|
UnregisterLoader(shaderAddon.get());
|
||||||
|
else
|
||||||
|
m_shaderAddons.emplace(addonId, std::move(shaderAddon));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for new add-ons
|
||||||
|
for (const AddonInfoPtr& shaderAddon : addonInfo)
|
||||||
|
{
|
||||||
|
std::string addonId = shaderAddon->ID();
|
||||||
|
|
||||||
|
const bool bIsNew = (m_shaderAddons.find(addonId) == m_shaderAddons.end());
|
||||||
|
if (!bIsNew)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool bIsFailed = (m_failedAddons.find(addonId) != m_failedAddons.end());
|
||||||
|
if (bIsFailed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::unique_ptr<CShaderPresetAddon> addonPtr =
|
||||||
|
std::make_unique<CShaderPresetAddon>(shaderAddon);
|
||||||
|
|
||||||
|
if (addonPtr->CreateAddon())
|
||||||
|
{
|
||||||
|
for (const auto& extension : addonPtr->GetExtensions())
|
||||||
|
RegisterLoader(addonPtr.get(), extension);
|
||||||
|
m_shaderAddons.emplace(std::move(addonId), std::move(addonPtr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_shaderAddons.emplace(std::move(addonId), std::move(addonPtr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IShaderPreset.h"
|
||||||
|
#include "addons/Addon.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ADDON
|
||||||
|
{
|
||||||
|
struct AddonEvent;
|
||||||
|
class CAddonMgr;
|
||||||
|
class CBinaryAddonManager;
|
||||||
|
class CShaderPresetAddon;
|
||||||
|
} // namespace ADDON
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderPresetLoader;
|
||||||
|
|
||||||
|
class CShaderPresetFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief Create the factory and register all shader preset add-ons
|
||||||
|
*/
|
||||||
|
CShaderPresetFactory(ADDON::CAddonMgr& addons);
|
||||||
|
~CShaderPresetFactory();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Register an object that can load shader presets
|
||||||
|
*
|
||||||
|
* \param loader The instance of a preset loader
|
||||||
|
* \param extension The extension for which the loader can load presets
|
||||||
|
*/
|
||||||
|
void RegisterLoader(IShaderPresetLoader* loader, const std::string& extension);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Unregister the shader preset loader
|
||||||
|
*
|
||||||
|
* \param load The loader that was passed to RegisterLoader()
|
||||||
|
*/
|
||||||
|
void UnregisterLoader(IShaderPresetLoader* loader);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Load a preset from the given path
|
||||||
|
*
|
||||||
|
* \param presetPath The path to the shader preset
|
||||||
|
* \param[out] shaderPreset The loaded shader preset
|
||||||
|
*
|
||||||
|
* \return True if the preset was loaded, false otherwise
|
||||||
|
*/
|
||||||
|
bool LoadPreset(const std::string& presetPath, IShaderPreset& shaderPreset);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check if a registered loader can load a given preset
|
||||||
|
*
|
||||||
|
* \param presetPath The path to the shader preset
|
||||||
|
*
|
||||||
|
* \return True if a loader can load the preset, false otherwise
|
||||||
|
*/
|
||||||
|
bool CanLoadPreset(const std::string& presetPath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnEvent(const ADDON::AddonEvent& event);
|
||||||
|
void UpdateAddons();
|
||||||
|
|
||||||
|
// Construction parameters
|
||||||
|
ADDON::CAddonMgr& m_addons;
|
||||||
|
|
||||||
|
std::map<std::string, IShaderPresetLoader*> m_loaders;
|
||||||
|
std::map<std::string, std::unique_ptr<ADDON::CShaderPresetAddon>> m_shaderAddons;
|
||||||
|
std::map<std::string, std::unique_ptr<ADDON::CShaderPresetAddon>> m_failedAddons;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
using ShaderParameterMap = std::map<std::string, float>;
|
||||||
|
|
||||||
|
enum class FilterType
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
LINEAR,
|
||||||
|
NEAREST
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WrapType
|
||||||
|
{
|
||||||
|
BORDER,
|
||||||
|
EDGE,
|
||||||
|
REPEAT,
|
||||||
|
MIRRORED_REPEAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ScaleType
|
||||||
|
{
|
||||||
|
INPUT,
|
||||||
|
ABSOLUTE_SCALE,
|
||||||
|
VIEWPORT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FboScaleAxis
|
||||||
|
{
|
||||||
|
ScaleType scaleType{ScaleType::INPUT};
|
||||||
|
float scale{1.0};
|
||||||
|
unsigned int abs{1};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FboScale
|
||||||
|
{
|
||||||
|
bool sRgbFramebuffer{false};
|
||||||
|
bool floatFramebuffer{false};
|
||||||
|
FboScaleAxis scaleX;
|
||||||
|
FboScaleAxis scaleY;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderLut
|
||||||
|
{
|
||||||
|
std::string strId;
|
||||||
|
std::string path;
|
||||||
|
FilterType filterType{FilterType::NONE};
|
||||||
|
WrapType wrapType{WrapType::BORDER};
|
||||||
|
bool mipmap{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderParameter
|
||||||
|
{
|
||||||
|
std::string strId;
|
||||||
|
std::string description;
|
||||||
|
float current{0.0f};
|
||||||
|
float minimum{0.0f};
|
||||||
|
float initial{0.0f};
|
||||||
|
float maximum{0.0f};
|
||||||
|
float step{0.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderPass
|
||||||
|
{
|
||||||
|
std::string sourcePath;
|
||||||
|
std::string vertexSource;
|
||||||
|
std::string fragmentSource;
|
||||||
|
FilterType filterType{FilterType::NONE};
|
||||||
|
WrapType wrapType{WrapType::BORDER};
|
||||||
|
unsigned int frameCountMod{0};
|
||||||
|
FboScale fbo;
|
||||||
|
bool mipmap{false};
|
||||||
|
|
||||||
|
std::vector<ShaderLut> luts;
|
||||||
|
std::vector<ShaderParameter> parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct float2
|
||||||
|
{
|
||||||
|
float2() : x(0.0f), y(0.0f) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
float2(T x_, T y_) : x(static_cast<float>(x_)), y(static_cast<float>(y_))
|
||||||
|
{
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const float2& rhs) const { return x == rhs.x && y == rhs.y; }
|
||||||
|
bool operator!=(const float2& rhs) const { return !(*this == rhs); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T Max()
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::max(x, y));
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
T Min()
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::min(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderUtils.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
std::string CShaderUtils::StripParameterPragmas(std::string source)
|
||||||
|
{
|
||||||
|
size_t pragmaPosition;
|
||||||
|
size_t newlinePosition;
|
||||||
|
|
||||||
|
while ((pragmaPosition = source.find("#pragma parameter")) != std::string::npos)
|
||||||
|
{
|
||||||
|
newlinePosition = source.find_first_of("\n", pragmaPosition + 17);
|
||||||
|
|
||||||
|
if (newlinePosition != std::string::npos)
|
||||||
|
source.erase(pragmaPosition, newlinePosition - pragmaPosition + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2 CShaderUtils::GetOptimalTextureSize(float2 videoSize)
|
||||||
|
{
|
||||||
|
unsigned int textureWidth = 1;
|
||||||
|
unsigned int textureHeight = 1;
|
||||||
|
|
||||||
|
// Find smallest possible power-of-two sized width that can contain the input texture
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (textureWidth >= videoSize.x)
|
||||||
|
break;
|
||||||
|
textureWidth *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find smallest possible power-of-two sized height that can contain the input texture
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (textureHeight >= videoSize.y)
|
||||||
|
break;
|
||||||
|
textureHeight *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return float2(textureWidth, textureHeight);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class CShaderUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief Strip shader parameter pragmas from the source code
|
||||||
|
*/
|
||||||
|
static std::string StripParameterPragmas(std::string source);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns smallest possible power-of-two sized texture
|
||||||
|
*/
|
||||||
|
static float2 GetOptimalTextureSize(float2 videoSize);
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
set(SOURCES RPWinOutputShader.cpp
|
||||||
|
ShaderDX.cpp
|
||||||
|
ShaderLutDX.cpp
|
||||||
|
ShaderPresetDX.cpp
|
||||||
|
ShaderSamplerDX.cpp
|
||||||
|
ShaderTextureDX.cpp
|
||||||
|
ShaderTextureDXRef.cpp
|
||||||
|
ShaderUtilsDX.cpp)
|
||||||
|
|
||||||
|
set(HEADERS RPWinOutputShader.h
|
||||||
|
ShaderDX.h
|
||||||
|
ShaderLutDX.h
|
||||||
|
ShaderPresetDX.h
|
||||||
|
ShaderSamplerDX.h
|
||||||
|
ShaderTextureDX.h
|
||||||
|
ShaderTextureDXRef.h
|
||||||
|
ShaderTypesDX.h
|
||||||
|
ShaderUtilsDX.h)
|
||||||
|
|
||||||
|
core_add_library(rp-shaders-windows)
|
||||||
|
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RPWinOutputShader.h"
|
||||||
|
|
||||||
|
#include "ShaderTypesDX.h"
|
||||||
|
#include "filesystem/File.h"
|
||||||
|
#include "rendering/dx/DeviceResources.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
bool CRPWinShader::CreateVertexBuffer(unsigned int count, unsigned int size)
|
||||||
|
{
|
||||||
|
if (!m_vb.Create(D3D11_BIND_VERTEX_BUFFER, count, size, DXGI_FORMAT_UNKNOWN, D3D11_USAGE_DYNAMIC))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint16_t id[4] = {3, 0, 2, 1};
|
||||||
|
if (!m_ib.Create(D3D11_BIND_INDEX_BUFFER, ARRAYSIZE(id), sizeof(uint16_t), DXGI_FORMAT_R16_UINT,
|
||||||
|
D3D11_USAGE_IMMUTABLE, id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_vbsize = count * size;
|
||||||
|
m_vertsize = size;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinShader::CreateInputLayout(D3D11_INPUT_ELEMENT_DESC* layout, unsigned numElements)
|
||||||
|
{
|
||||||
|
D3DX11_PASS_DESC desc = {};
|
||||||
|
if (FAILED(m_effect.Get()->GetTechniqueByIndex(0)->GetPassByIndex(0)->GetDesc(&desc)))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to get description");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice();
|
||||||
|
return SUCCEEDED(pDevice->CreateInputLayout(layout, numElements, desc.pIAInputSignature,
|
||||||
|
desc.IAInputSignatureSize, &m_inputLayout));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinShader::LockVertexBuffer(void** data)
|
||||||
|
{
|
||||||
|
if (!m_vb.Map(data))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to lock vertex buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinShader::UnlockVertexBuffer()
|
||||||
|
{
|
||||||
|
if (!m_vb.Unmap())
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to unlock vertex buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinShader::LoadEffect(const std::string& filename, DefinesMap* defines)
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGDEBUG, "Loading shader: {}", filename);
|
||||||
|
|
||||||
|
XFILE::CFileStream file;
|
||||||
|
if (!file.Open(filename))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to open file: {}", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pStrEffect;
|
||||||
|
getline(file, pStrEffect, '\0');
|
||||||
|
|
||||||
|
if (!m_effect.Create(pStrEffect, defines))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "{} failed", pStrEffect);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinShader::Execute(const std::vector<CD3DTexture*>& targets, unsigned int vertexIndexStep)
|
||||||
|
{
|
||||||
|
ID3D11DeviceContext* pContext = DX::DeviceResources::Get()->GetD3DContext();
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> oldRT;
|
||||||
|
|
||||||
|
// The render target will be overridden: save the caller's original RT
|
||||||
|
if (!targets.empty())
|
||||||
|
pContext->OMGetRenderTargets(1, &oldRT, nullptr);
|
||||||
|
|
||||||
|
unsigned int cPasses;
|
||||||
|
if (!m_effect.Begin(&cPasses, 0))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to begin D3D effect");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D11Buffer* vertexBuffer = m_vb.Get();
|
||||||
|
ID3D11Buffer* indexBuffer = m_ib.Get();
|
||||||
|
unsigned int stride = m_vb.GetStride();
|
||||||
|
unsigned int offset = 0;
|
||||||
|
pContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
|
||||||
|
pContext->IASetIndexBuffer(indexBuffer, m_ib.GetFormat(), 0);
|
||||||
|
pContext->IASetInputLayout(m_inputLayout.Get());
|
||||||
|
pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||||
|
|
||||||
|
for (unsigned int iPass = 0; iPass < cPasses; ++iPass)
|
||||||
|
{
|
||||||
|
SetTarget(targets.size() > iPass ? targets.at(iPass) : nullptr);
|
||||||
|
SetStepParams(iPass);
|
||||||
|
|
||||||
|
if (!m_effect.BeginPass(iPass))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to begin D3D effect pass");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pContext->DrawIndexed(4, 0, iPass * vertexIndexStep);
|
||||||
|
|
||||||
|
if (!m_effect.EndPass())
|
||||||
|
CLog::LogF(LOGERROR, "Failed to end D3D effect pass");
|
||||||
|
|
||||||
|
CD3DHelper::PSClearShaderResources(pContext);
|
||||||
|
}
|
||||||
|
if (!m_effect.End())
|
||||||
|
CLog::LogF(LOGERROR, "Failed to end D3D effect");
|
||||||
|
|
||||||
|
if (oldRT)
|
||||||
|
pContext->OMSetRenderTargets(1, oldRT.GetAddressOf(), nullptr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRPWinShader::SetTarget(CD3DTexture* target)
|
||||||
|
{
|
||||||
|
m_target = target;
|
||||||
|
if (m_target)
|
||||||
|
{
|
||||||
|
DX::DeviceResources::Get()->GetD3DContext()->OMSetRenderTargets(1, target->GetAddressOfRTV(),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRPWinOutputShader::Create(RETRO::SCALINGMETHOD scalingMethod)
|
||||||
|
{
|
||||||
|
CreateVertexBuffer(4, sizeof(CUSTOMVERTEX));
|
||||||
|
|
||||||
|
DefinesMap defines;
|
||||||
|
switch (scalingMethod)
|
||||||
|
{
|
||||||
|
case RETRO::SCALINGMETHOD::NEAREST:
|
||||||
|
defines["SAMP_NEAREST"] = "";
|
||||||
|
break;
|
||||||
|
case RETRO::SCALINGMETHOD::LINEAR:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string effectPath("special://xbmc/system/shaders/rp_output_d3d.fx");
|
||||||
|
|
||||||
|
if (!LoadEffect(effectPath, &defines))
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGERROR, "Failed to load shader: {}", effectPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create input layout
|
||||||
|
D3D11_INPUT_ELEMENT_DESC layout[] = {
|
||||||
|
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||||
|
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||||
|
};
|
||||||
|
return CreateInputLayout(layout, ARRAYSIZE(layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRPWinOutputShader::Render(CD3DTexture& sourceTexture,
|
||||||
|
CRect sourceRect,
|
||||||
|
const CPoint points[4],
|
||||||
|
CRect& viewPort,
|
||||||
|
CD3DTexture& target,
|
||||||
|
unsigned int range /* = 0 */)
|
||||||
|
{
|
||||||
|
PrepareParameters(sourceTexture.GetWidth(), sourceTexture.GetHeight(), sourceRect, points);
|
||||||
|
SetShaderParameters(sourceTexture, range, viewPort);
|
||||||
|
Execute({&target}, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRPWinOutputShader::PrepareParameters(unsigned int sourceWidth,
|
||||||
|
unsigned int sourceHeight,
|
||||||
|
CRect sourceRect,
|
||||||
|
const CPoint points[4])
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
for (unsigned int i = 0; i < 4 && !changed; ++i)
|
||||||
|
changed = points[i] != m_destPoints[i];
|
||||||
|
|
||||||
|
if (m_sourceWidth != sourceWidth || m_sourceHeight != sourceHeight ||
|
||||||
|
m_sourceRect != sourceRect || changed)
|
||||||
|
{
|
||||||
|
m_sourceWidth = sourceWidth;
|
||||||
|
m_sourceHeight = sourceHeight;
|
||||||
|
m_sourceRect = sourceRect;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
m_destPoints[i] = points[i];
|
||||||
|
|
||||||
|
CUSTOMVERTEX* v = nullptr;
|
||||||
|
LockVertexBuffer(static_cast<void**>(static_cast<void*>(&v)));
|
||||||
|
|
||||||
|
v[0].x = m_destPoints[0].x;
|
||||||
|
v[0].y = m_destPoints[0].y;
|
||||||
|
v[0].z = 0.0f;
|
||||||
|
v[0].tu = m_sourceRect.x1 / m_sourceWidth;
|
||||||
|
v[0].tv = m_sourceRect.y1 / m_sourceHeight;
|
||||||
|
|
||||||
|
v[1].x = m_destPoints[1].x;
|
||||||
|
v[1].y = m_destPoints[1].y;
|
||||||
|
v[1].z = 0.0f;
|
||||||
|
v[1].tu = m_sourceRect.x2 / m_sourceWidth;
|
||||||
|
v[1].tv = m_sourceRect.y1 / m_sourceHeight;
|
||||||
|
|
||||||
|
v[2].x = m_destPoints[2].x;
|
||||||
|
v[2].y = m_destPoints[2].y;
|
||||||
|
v[2].z = 0.0f;
|
||||||
|
v[2].tu = m_sourceRect.x2 / m_sourceWidth;
|
||||||
|
v[2].tv = m_sourceRect.y2 / m_sourceHeight;
|
||||||
|
|
||||||
|
v[3].x = m_destPoints[3].x;
|
||||||
|
v[3].y = m_destPoints[3].y;
|
||||||
|
v[3].z = 0.0f;
|
||||||
|
v[3].tu = m_sourceRect.x1 / m_sourceWidth;
|
||||||
|
v[3].tv = m_sourceRect.y2 / m_sourceHeight;
|
||||||
|
|
||||||
|
UnlockVertexBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRPWinOutputShader::SetShaderParameters(CD3DTexture& sourceTexture,
|
||||||
|
unsigned int range,
|
||||||
|
CRect& viewPort)
|
||||||
|
{
|
||||||
|
m_effect.SetTechnique("OUTPUT_T");
|
||||||
|
m_effect.SetResources("g_Texture", sourceTexture.GetAddressOfSRV(), 1);
|
||||||
|
|
||||||
|
const float viewPortArray[2] = {viewPort.Width(), viewPort.Height()};
|
||||||
|
m_effect.SetFloatArray("g_viewPort", viewPortArray, 2);
|
||||||
|
|
||||||
|
const float params[3] = {static_cast<float>(range)};
|
||||||
|
m_effect.SetFloatArray("m_params", params, 1);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/GameSettings.h"
|
||||||
|
#include "guilib/D3DResource.h"
|
||||||
|
#include "utils/Geometry.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
|
||||||
|
class CRPWinShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~CRPWinShader() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool CreateVertexBuffer(unsigned int vertCount, unsigned int vertSize);
|
||||||
|
virtual bool CreateInputLayout(D3D11_INPUT_ELEMENT_DESC* layout, unsigned numElements);
|
||||||
|
virtual bool LockVertexBuffer(void** data);
|
||||||
|
virtual bool UnlockVertexBuffer();
|
||||||
|
virtual bool LoadEffect(const std::string& filename, DefinesMap* defines);
|
||||||
|
virtual bool Execute(const std::vector<CD3DTexture*>& targets, unsigned int vertexIndexStep);
|
||||||
|
virtual void SetStepParams(unsigned stepIndex) {}
|
||||||
|
|
||||||
|
CD3DEffect m_effect;
|
||||||
|
CD3DTexture* m_target{nullptr};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetTarget(CD3DTexture* target);
|
||||||
|
|
||||||
|
CD3DBuffer m_vb;
|
||||||
|
CD3DBuffer m_ib;
|
||||||
|
unsigned int m_vbsize{0};
|
||||||
|
unsigned int m_vertsize{0};
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CRPWinOutputShader : protected CRPWinShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~CRPWinOutputShader() override = default;
|
||||||
|
|
||||||
|
bool Create(RETRO::SCALINGMETHOD scalingMethod);
|
||||||
|
void Render(CD3DTexture& sourceTexture,
|
||||||
|
CRect sourceRect,
|
||||||
|
const CPoint points[4],
|
||||||
|
CRect& viewPort,
|
||||||
|
CD3DTexture& target,
|
||||||
|
unsigned int range = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PrepareParameters(unsigned int sourceWidth,
|
||||||
|
unsigned int sourceHeight,
|
||||||
|
CRect sourceRect,
|
||||||
|
const CPoint points[4]);
|
||||||
|
void SetShaderParameters(CD3DTexture& sourceTexture, unsigned int range, CRect& viewPort);
|
||||||
|
|
||||||
|
unsigned int m_sourceWidth{0};
|
||||||
|
unsigned int m_sourceHeight{0};
|
||||||
|
CRect m_sourceRect{0.f, 0.f, 0.f, 0.f};
|
||||||
|
CPoint m_destPoints[4] = {
|
||||||
|
{0.f, 0.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderDX.h"
|
||||||
|
|
||||||
|
#include "ShaderTextureDX.h"
|
||||||
|
#include "ShaderTextureDXRef.h"
|
||||||
|
#include "ShaderTypesDX.h"
|
||||||
|
#include "ShaderUtilsDX.h"
|
||||||
|
#include "application/Application.h"
|
||||||
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderLut.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderUtils.h"
|
||||||
|
#include "guilib/TextureDX.h"
|
||||||
|
#include "rendering/dx/RenderSystemDX.h"
|
||||||
|
#include "utils/URIUtils.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderDX::CShaderDX(RETRO::CRenderContext& context) : m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderDX::~CShaderDX()
|
||||||
|
{
|
||||||
|
if (m_pInputBuffer != nullptr)
|
||||||
|
m_pInputBuffer->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderDX::Create(std::string shaderSource,
|
||||||
|
std::string shaderPath,
|
||||||
|
ShaderParameterMap shaderParameters,
|
||||||
|
std::vector<std::shared_ptr<IShaderLut>> luts,
|
||||||
|
float2 viewPortSize,
|
||||||
|
unsigned int passIdx,
|
||||||
|
unsigned int frameCountMod)
|
||||||
|
{
|
||||||
|
if (shaderPath.empty())
|
||||||
|
{
|
||||||
|
CLog::Log(LOGERROR, "CShaderDX::Create: Can't load empty shader path");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_shaderSource = std::move(shaderSource);
|
||||||
|
m_shaderPath = std::move(shaderPath);
|
||||||
|
m_shaderParameters = std::move(shaderParameters);
|
||||||
|
m_luts = std::move(luts);
|
||||||
|
m_viewportSize = viewPortSize;
|
||||||
|
m_passIdx = passIdx;
|
||||||
|
m_frameCountMod = frameCountMod;
|
||||||
|
//m_pSampler = reinterpret_cast<ID3D11SamplerState*>(sampler);
|
||||||
|
|
||||||
|
DefinesMap defines;
|
||||||
|
|
||||||
|
defines["HLSL_4"] = ""; // Using Shader Model 4
|
||||||
|
defines["HLSL_FX"] = ""; // And the FX11 framework
|
||||||
|
|
||||||
|
// We implement runtime shader parameters ("#pragma parameter")
|
||||||
|
// @note Runtime shader parameters allow convenient experimentation with real-time
|
||||||
|
// feedback, as well as override-ability by presets, but sometimes they are
|
||||||
|
// much slower because they prevent static evaluation of a lot of math.
|
||||||
|
// Disabling them drastically speeds up shaders that use them heavily.
|
||||||
|
defines["PARAMETER_UNIFORM"] = "";
|
||||||
|
|
||||||
|
m_effect.AddIncludePath(URIUtils::GetBasePath(m_shaderPath));
|
||||||
|
|
||||||
|
if (!m_effect.Create(m_shaderSource, &defines))
|
||||||
|
{
|
||||||
|
CLog::Log(LOGERROR, "CShaderDX::Create: Failed to load video shader: {}", m_shaderPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::Render(IShaderTexture& source, IShaderTexture& target)
|
||||||
|
{
|
||||||
|
CShaderTextureDX& sourceDX = static_cast<CShaderTextureDX&>(source);
|
||||||
|
|
||||||
|
// Get source texture object
|
||||||
|
const CD3DTexture& sourceTexture = sourceDX.GetTexture();
|
||||||
|
|
||||||
|
//! @todo Handle ref textures better
|
||||||
|
CShaderTextureDX* targetDX = dynamic_cast<CShaderTextureDX*>(&target);
|
||||||
|
CShaderTextureDXRef* targetDXRef = dynamic_cast<CShaderTextureDXRef*>(&target);
|
||||||
|
|
||||||
|
CD3DTexture& targetTexture =
|
||||||
|
targetDX != nullptr ? targetDX->GetTexture() : targetDXRef->GetTexture();
|
||||||
|
|
||||||
|
//! @todo Doesn't work. Another PSSetSamplers gets called by FX11 right before rendering
|
||||||
|
// overriding this.
|
||||||
|
/*
|
||||||
|
CRenderSystemDX *renderingDX = static_cast<CRenderSystemDX*>(m_context.Rendering());
|
||||||
|
renderingDX->Get3D11Context()->PSSetSamplers(2, 1, &m_pSampler);
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! @todo Check for nullptr
|
||||||
|
SetShaderParameters(sourceTexture);
|
||||||
|
Execute({&targetTexture}, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::SetSizes(const float2& prevSize,
|
||||||
|
const float2& prevTextureSize,
|
||||||
|
const float2& nextSize)
|
||||||
|
{
|
||||||
|
m_inputSize = prevSize;
|
||||||
|
m_inputTextureSize = prevTextureSize;
|
||||||
|
m_outputSize = nextSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::PrepareParameters(
|
||||||
|
CPoint dest[4],
|
||||||
|
IShaderTexture& sourceTexture,
|
||||||
|
const std::vector<std::unique_ptr<IShaderTexture>>& pShaderTextures,
|
||||||
|
const std::vector<std::unique_ptr<IShader>>& pShaders,
|
||||||
|
uint64_t frameCount)
|
||||||
|
{
|
||||||
|
CUSTOMVERTEX* v;
|
||||||
|
LockVertexBuffer(reinterpret_cast<void**>(&v));
|
||||||
|
|
||||||
|
if (m_passIdx + 1 != static_cast<unsigned int>(pShaders.size())) // Not last pass
|
||||||
|
{
|
||||||
|
// top left
|
||||||
|
v[0].x = -m_outputSize.x / 2;
|
||||||
|
v[0].y = -m_outputSize.y / 2;
|
||||||
|
// top right
|
||||||
|
v[1].x = m_outputSize.x / 2;
|
||||||
|
v[1].y = -m_outputSize.y / 2;
|
||||||
|
// bottom right
|
||||||
|
v[2].x = m_outputSize.x / 2;
|
||||||
|
v[2].y = m_outputSize.y / 2;
|
||||||
|
// bottom left
|
||||||
|
v[3].x = -m_outputSize.x / 2;
|
||||||
|
v[3].y = m_outputSize.y / 2;
|
||||||
|
|
||||||
|
// Set destination rectangle size
|
||||||
|
m_destSize = m_outputSize;
|
||||||
|
}
|
||||||
|
else // Last pass
|
||||||
|
{
|
||||||
|
// top left
|
||||||
|
v[0].x = dest[0].x - m_outputSize.x / 2;
|
||||||
|
v[0].y = dest[0].y - m_outputSize.y / 2;
|
||||||
|
// top right
|
||||||
|
v[1].x = dest[1].x - m_outputSize.x / 2;
|
||||||
|
v[1].y = dest[1].y - m_outputSize.y / 2;
|
||||||
|
// bottom right
|
||||||
|
v[2].x = dest[2].x - m_outputSize.x / 2;
|
||||||
|
v[2].y = dest[2].y - m_outputSize.y / 2;
|
||||||
|
// bottom left
|
||||||
|
v[3].x = dest[3].x - m_outputSize.x / 2;
|
||||||
|
v[3].y = dest[3].y - m_outputSize.y / 2;
|
||||||
|
|
||||||
|
// Set destination rectangle size for the last pass
|
||||||
|
m_destSize = {dest[2].x - dest[0].x, dest[2].y - dest[0].y};
|
||||||
|
}
|
||||||
|
|
||||||
|
// top left
|
||||||
|
v[0].z = 0;
|
||||||
|
v[0].tu = 0;
|
||||||
|
v[0].tv = 0;
|
||||||
|
// top right
|
||||||
|
v[1].z = 0;
|
||||||
|
v[1].tu = 1;
|
||||||
|
v[1].tv = 0;
|
||||||
|
// bottom right
|
||||||
|
v[2].z = 0;
|
||||||
|
v[2].tu = 1;
|
||||||
|
v[2].tv = 1;
|
||||||
|
// bottom left
|
||||||
|
v[3].z = 0;
|
||||||
|
v[3].tu = 0;
|
||||||
|
v[3].tv = 1;
|
||||||
|
|
||||||
|
UnlockVertexBuffer();
|
||||||
|
UpdateInputBuffer(frameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::UpdateMVP()
|
||||||
|
{
|
||||||
|
const float xScale = 1.0f / m_outputSize.x * 2.0f;
|
||||||
|
const float yScale = -1.0f / m_outputSize.y * 2.0f;
|
||||||
|
|
||||||
|
// Update projection matrix
|
||||||
|
m_MVP = DirectX::XMFLOAT4X4(xScale, 0, 0, 0, 0, yScale, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderDX::CreateVertexBuffer(unsigned int vertCount, unsigned int vertSize)
|
||||||
|
{
|
||||||
|
return CRPWinShader::CreateVertexBuffer(vertCount, vertSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderDX::CreateInputLayout(D3D11_INPUT_ELEMENT_DESC* layout, unsigned int numElements)
|
||||||
|
{
|
||||||
|
return CRPWinShader::CreateInputLayout(layout, numElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderDX::CreateInputBuffer()
|
||||||
|
{
|
||||||
|
ID3D11Device1* pDevice = DX::DeviceResources::Get()->GetD3DDevice();
|
||||||
|
cbInput inputInitData = GetInputData();
|
||||||
|
UINT inputBufSize = static_cast<UINT>((sizeof(cbInput) + 15) & ~15);
|
||||||
|
CD3D11_BUFFER_DESC cbInputDesc(inputBufSize, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC,
|
||||||
|
D3D11_CPU_ACCESS_WRITE);
|
||||||
|
D3D11_SUBRESOURCE_DATA initInputSubresource = {&inputInitData, 0, 0};
|
||||||
|
|
||||||
|
if (FAILED(pDevice->CreateBuffer(&cbInputDesc, &initInputSubresource, &m_pInputBuffer)))
|
||||||
|
{
|
||||||
|
CLog::Log(LOGERROR, "CShaderDX::CreateInputBuffer: Failed to create constant buffer for video "
|
||||||
|
"shader input data");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::UpdateInputBuffer(uint64_t frameCount)
|
||||||
|
{
|
||||||
|
ID3D11DeviceContext1* pContext = DX::DeviceResources::Get()->GetD3DContext();
|
||||||
|
cbInput input = GetInputData(frameCount);
|
||||||
|
cbInput* pData;
|
||||||
|
void** ppData = reinterpret_cast<void**>(&pData);
|
||||||
|
D3D11_MAPPED_SUBRESOURCE resource;
|
||||||
|
|
||||||
|
if (SUCCEEDED(pContext->Map(m_pInputBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource)))
|
||||||
|
{
|
||||||
|
*ppData = resource.pData;
|
||||||
|
memcpy(*ppData, &input, sizeof(cbInput));
|
||||||
|
pContext->Unmap(m_pInputBuffer, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderDX::cbInput CShaderDX::GetInputData(uint64_t frameCount) const
|
||||||
|
{
|
||||||
|
if (m_frameCountMod != 0)
|
||||||
|
frameCount %= m_frameCountMod;
|
||||||
|
|
||||||
|
cbInput input = {
|
||||||
|
{CShaderUtilsDX::ToDXVector(m_inputSize)}, // video_size
|
||||||
|
{CShaderUtilsDX::ToDXVector(m_inputTextureSize)}, // texture_size
|
||||||
|
{CShaderUtilsDX::ToDXVector(m_destSize)}, // output_size
|
||||||
|
// Current frame count that can be modulo'ed
|
||||||
|
static_cast<float>(frameCount), // frame_count
|
||||||
|
// Time always flows forward
|
||||||
|
1.0f // frame_direction
|
||||||
|
};
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderDX::SetShaderParameters(const CD3DTexture& sourceTexture)
|
||||||
|
{
|
||||||
|
m_effect.SetTechnique("TEQ");
|
||||||
|
m_effect.SetResources("decal", {const_cast<CD3DTexture&>(sourceTexture).GetAddressOfSRV()}, 1);
|
||||||
|
m_effect.SetMatrix("modelViewProj", reinterpret_cast<const float*>(&m_MVP));
|
||||||
|
//! @todo(optimization) Add frame_count to separate cbuffer
|
||||||
|
m_effect.SetConstantBuffer("input", m_pInputBuffer);
|
||||||
|
|
||||||
|
for (const auto& paramIt : m_shaderParameters)
|
||||||
|
m_effect.SetFloatArray(paramIt.first.c_str(), ¶mIt.second, 1);
|
||||||
|
|
||||||
|
for (const std::shared_ptr<IShaderLut>& lut : m_luts)
|
||||||
|
{
|
||||||
|
CDXTexture* texture = dynamic_cast<CDXTexture*>(lut->GetTexture());
|
||||||
|
if (texture != nullptr)
|
||||||
|
m_effect.SetTexture(lut->GetID().c_str(), texture->GetShaderResource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTextureDX.h"
|
||||||
|
#include "ShaderTypesDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShader.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/RPWinOutputShader.h"
|
||||||
|
#include "guilib/D3DResource.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <DirectXMath.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace RETRO
|
||||||
|
{
|
||||||
|
class CRenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShaderLut;
|
||||||
|
class IShaderSampler;
|
||||||
|
|
||||||
|
class CShaderDX : protected CRPWinShader, public IShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CShaderDX(RETRO::CRenderContext& context);
|
||||||
|
~CShaderDX() override;
|
||||||
|
|
||||||
|
// Implementation of IShader
|
||||||
|
bool Create(std::string shaderSource,
|
||||||
|
std::string shaderPath,
|
||||||
|
ShaderParameterMap shaderParameters,
|
||||||
|
std::vector<std::shared_ptr<IShaderLut>> luts,
|
||||||
|
float2 viewPortSize,
|
||||||
|
unsigned int passIdx,
|
||||||
|
unsigned int frameCountMod = 0) override;
|
||||||
|
void Render(IShaderTexture& source, IShaderTexture& target) override;
|
||||||
|
void SetSizes(const float2& prevSize,
|
||||||
|
const float2& prevTextureSize,
|
||||||
|
const float2& nextSize) override;
|
||||||
|
void PrepareParameters(CPoint dest[4],
|
||||||
|
IShaderTexture& sourceTexture,
|
||||||
|
const std::vector<std::unique_ptr<IShaderTexture>>& pShaderTextures,
|
||||||
|
const std::vector<std::unique_ptr<IShader>>& pShaders,
|
||||||
|
uint64_t frameCount) override;
|
||||||
|
void UpdateMVP() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Construct the vertex buffer that will be used to render the shader
|
||||||
|
*
|
||||||
|
* \param vertCount Number of vertices to construct. Commonly 4, for rectangular screens
|
||||||
|
* \param vertSize Size of each vertex's data in bytes
|
||||||
|
*
|
||||||
|
* \return False if creating the vertex buffer failed, true otherwise
|
||||||
|
*/
|
||||||
|
bool CreateVertexBuffer(unsigned int vertCount, unsigned int vertSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Creates the data layout of the input-assembler stage
|
||||||
|
*
|
||||||
|
* \param layout Description of the inputs to the vertex shader
|
||||||
|
* \param numElements Number of inputs to the vertex shader
|
||||||
|
*
|
||||||
|
* \return False if creating the input layout failed, true otherwise.
|
||||||
|
*/
|
||||||
|
bool CreateInputLayout(D3D11_INPUT_ELEMENT_DESC* layout, unsigned int numElements);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Creates the buffer that will be used to send "input" (as per the
|
||||||
|
* spec) data to the shader
|
||||||
|
*
|
||||||
|
* \return False if creating the input buffer failed, true otherwise
|
||||||
|
*/
|
||||||
|
bool CreateInputBuffer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct cbInput
|
||||||
|
{
|
||||||
|
DirectX::XMFLOAT2 video_size;
|
||||||
|
DirectX::XMFLOAT2 texture_size;
|
||||||
|
DirectX::XMFLOAT2 output_size;
|
||||||
|
float frame_count;
|
||||||
|
float frame_direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
void UpdateInputBuffer(uint64_t frameCount);
|
||||||
|
cbInput GetInputData(uint64_t frameCount = 0) const;
|
||||||
|
void SetShaderParameters(const CD3DTexture& sourceTexture);
|
||||||
|
|
||||||
|
// Construction parameters
|
||||||
|
RETRO::CRenderContext& m_context;
|
||||||
|
|
||||||
|
// Currently loaded shader's source code
|
||||||
|
std::string m_shaderSource;
|
||||||
|
|
||||||
|
// Currently loaded shader's relative path
|
||||||
|
std::string m_shaderPath;
|
||||||
|
|
||||||
|
// Struct with all parameters pertaining to the shader
|
||||||
|
ShaderParameterMap m_shaderParameters;
|
||||||
|
|
||||||
|
// Look-up textures pertaining to the shader
|
||||||
|
std::vector<std::shared_ptr<IShaderLut>> m_luts; //! @todo Back to DX maybe
|
||||||
|
|
||||||
|
// Resolution of the input of the shader
|
||||||
|
float2 m_inputSize;
|
||||||
|
|
||||||
|
// Resolution of the texture that holds the input
|
||||||
|
float2 m_inputTextureSize;
|
||||||
|
|
||||||
|
// Resolution of the output viewport of the shader
|
||||||
|
float2 m_outputSize;
|
||||||
|
|
||||||
|
// Resolution of the destination rectangle of the shader
|
||||||
|
float2 m_destSize;
|
||||||
|
|
||||||
|
// Resolution of the viewport/window
|
||||||
|
float2 m_viewportSize;
|
||||||
|
|
||||||
|
// Projection matrix
|
||||||
|
DirectX::XMFLOAT4X4 m_MVP{};
|
||||||
|
|
||||||
|
// Index of the video shader pass
|
||||||
|
unsigned int m_passIdx{0};
|
||||||
|
|
||||||
|
// Value to modulo (%) frame count with
|
||||||
|
// Unused if 0
|
||||||
|
unsigned int m_frameCountMod{0};
|
||||||
|
|
||||||
|
// Holds the data bound to the input cbuffer (cbInput here)
|
||||||
|
ID3D11Buffer* m_pInputBuffer{nullptr};
|
||||||
|
|
||||||
|
// Sampler state
|
||||||
|
//ID3D11SamplerState* m_pSampler{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderLutDX.h"
|
||||||
|
|
||||||
|
#include "ShaderUtilsDX.h"
|
||||||
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderPreset.h"
|
||||||
|
#include "guilib/TextureDX.h"
|
||||||
|
#include "rendering/dx/RenderSystemDX.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderLutDX::CShaderLutDX(std::string id, std::string path)
|
||||||
|
: IShaderLut(std::move(id), std::move(path))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderLutDX::~CShaderLutDX() = default;
|
||||||
|
|
||||||
|
bool CShaderLutDX::Create(RETRO::CRenderContext& context, const ShaderLut& lut)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CTexture> lutTexture{CreateLUTexture(lut)};
|
||||||
|
if (!lutTexture)
|
||||||
|
{
|
||||||
|
CLog::LogF(LOGWARNING, "CShaderLutDX::Create: Couldn't create texture for LUT: {}", lut.strId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_texture = std::move(lutTexture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CTexture> CShaderLutDX::CreateLUTexture(const ShaderLut& lut)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CTexture> texture = CTexture::LoadFromFile(lut.path);
|
||||||
|
CDXTexture* textureDX = static_cast<CDXTexture*>(texture.get());
|
||||||
|
|
||||||
|
if (textureDX == nullptr)
|
||||||
|
{
|
||||||
|
CLog::Log(LOGERROR, "CShaderLutDX::CreateLUTexture: Couldn't open LUT: {}", lut.path);
|
||||||
|
return std::unique_ptr<CTexture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lut.mipmap)
|
||||||
|
textureDX->SetMipmapping();
|
||||||
|
|
||||||
|
textureDX->SetScalingMethod(lut.filterType == FilterType::LINEAR ? TEXTURE_SCALING::LINEAR
|
||||||
|
: TEXTURE_SCALING::NEAREST);
|
||||||
|
textureDX->LoadToGPU();
|
||||||
|
|
||||||
|
//! @todo Set LUT wrap type
|
||||||
|
//D3D11_TEXTURE_ADDRESS_MODE wrapType = CShaderUtilsDX::TranslateWrapType(lut.wrapType);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderLut.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace RETRO
|
||||||
|
{
|
||||||
|
class CRenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class CTextureBase;
|
||||||
|
struct ShaderLut;
|
||||||
|
|
||||||
|
class CShaderLutDX : public IShaderLut
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CShaderLutDX() = default;
|
||||||
|
CShaderLutDX(std::string id, std::string path);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~CShaderLutDX() override;
|
||||||
|
|
||||||
|
// Implementation of IShaderLut
|
||||||
|
bool Create(RETRO::CRenderContext& context, const ShaderLut& lut) override;
|
||||||
|
CTexture* GetTexture() override { return m_texture.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<CTexture> CreateLUTexture(const ShaderLut& lut);
|
||||||
|
|
||||||
|
std::unique_ptr<CTexture> m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderPresetDX.h"
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderLutDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderTextureDX.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/windows/ShaderTypesDX.h"
|
||||||
|
#include "rendering/dx/RenderSystemDX.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderPresetDX::CShaderPresetDX(RETRO::CRenderContext& context,
|
||||||
|
unsigned videoWidth,
|
||||||
|
unsigned videoHeight)
|
||||||
|
: CShaderPreset(context, videoWidth, videoHeight)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetDX::CreateShaders()
|
||||||
|
{
|
||||||
|
const unsigned int numPasses = static_cast<unsigned int>(m_passes.size());
|
||||||
|
|
||||||
|
//! @todo Is this pass specific?
|
||||||
|
for (unsigned int shaderIdx = 0; shaderIdx < numPasses; ++shaderIdx)
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<IShaderLut>> passLUTsDX;
|
||||||
|
|
||||||
|
const ShaderPass& pass = m_passes[shaderIdx];
|
||||||
|
const unsigned int numPassLuts = static_cast<unsigned int>(pass.luts.size());
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < numPassLuts; ++i)
|
||||||
|
{
|
||||||
|
const ShaderLut& lutStruct = pass.luts[i];
|
||||||
|
|
||||||
|
std::shared_ptr<CShaderLutDX> passLut =
|
||||||
|
std::make_shared<CShaderLutDX>(lutStruct.strId, lutStruct.path);
|
||||||
|
if (passLut->Create(m_context, lutStruct))
|
||||||
|
passLUTsDX.emplace_back(std::move(passLut));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the shader
|
||||||
|
std::unique_ptr<CShaderDX> videoShader = std::make_unique<CShaderDX>(m_context);
|
||||||
|
|
||||||
|
const std::string& shaderSource = pass.vertexSource; // Also contains fragment source
|
||||||
|
const std::string& shaderPath = pass.sourcePath;
|
||||||
|
|
||||||
|
// Get only the parameters belonging to this specific shader
|
||||||
|
ShaderParameterMap passParameters = GetShaderParameters(pass.parameters, pass.vertexSource);
|
||||||
|
|
||||||
|
if (!videoShader->Create(shaderSource, shaderPath, std::move(passParameters),
|
||||||
|
std::move(passLUTsDX), m_outputSize, shaderIdx, pass.frameCountMod))
|
||||||
|
{
|
||||||
|
CLog::Log(LOGERROR, "CShaderPresetDX::CreateShaders: Couldn't create a video shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_pShaders.push_back(std::move(videoShader));
|
||||||
|
|
||||||
|
/*
|
||||||
|
IShaderSampler* passSampler = reinterpret_cast<IShaderSampler*>(
|
||||||
|
pass.filter == FILTER_TYPE_LINEAR
|
||||||
|
? m_pSampLinear
|
||||||
|
: m_pSampNearest); //! @todo Wrap in CShaderSamplerDX instead of reinterpret_cast
|
||||||
|
|
||||||
|
//! @todo Set passSampler to m_pSampler in the videoShader
|
||||||
|
videoShader->SetSampler(passSampler);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetDX::CreateLayouts()
|
||||||
|
{
|
||||||
|
for (std::unique_ptr<IShader>& videoShader : m_pShaders)
|
||||||
|
{
|
||||||
|
CShaderDX* videoShaderDX = static_cast<CShaderDX*>(videoShader.get());
|
||||||
|
videoShaderDX->CreateVertexBuffer(4, sizeof(CUSTOMVERTEX));
|
||||||
|
|
||||||
|
// Create input layout
|
||||||
|
D3D11_INPUT_ELEMENT_DESC layout[] = {
|
||||||
|
{"SV_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||||
|
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||||
|
{"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}};
|
||||||
|
|
||||||
|
if (!videoShaderDX->CreateInputLayout(layout, ARRAYSIZE(layout)))
|
||||||
|
{
|
||||||
|
CLog::Log(
|
||||||
|
LOGERROR,
|
||||||
|
"CShaderPresetDX::CreateLayouts: Failed to create input layout for Input Assembler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetDX::CreateBuffers()
|
||||||
|
{
|
||||||
|
for (std::unique_ptr<IShader>& videoShader : m_pShaders)
|
||||||
|
{
|
||||||
|
CShaderDX* videoShaderDX = static_cast<CShaderDX*>(videoShader.get());
|
||||||
|
videoShaderDX->CreateInputBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetDX::CreateShaderTextures()
|
||||||
|
{
|
||||||
|
m_pShaderTextures.clear();
|
||||||
|
|
||||||
|
float2 prevSize = m_videoSize;
|
||||||
|
float2 prevTextureSize = m_videoSize;
|
||||||
|
|
||||||
|
const unsigned int numPasses = static_cast<unsigned int>(m_passes.size());
|
||||||
|
|
||||||
|
for (unsigned int shaderIdx = 0; shaderIdx < numPasses; ++shaderIdx)
|
||||||
|
{
|
||||||
|
const ShaderPass& pass = m_passes[shaderIdx];
|
||||||
|
|
||||||
|
// Resolve final texture resolution, taking scale type and scale multiplier into account
|
||||||
|
float2 scaledSize;
|
||||||
|
float2 textureSize;
|
||||||
|
switch (pass.fbo.scaleX.scaleType)
|
||||||
|
{
|
||||||
|
case ScaleType::ABSOLUTE_SCALE:
|
||||||
|
scaledSize.x = static_cast<float>(pass.fbo.scaleX.abs);
|
||||||
|
break;
|
||||||
|
case ScaleType::VIEWPORT:
|
||||||
|
scaledSize.x =
|
||||||
|
pass.fbo.scaleX.scale ? pass.fbo.scaleX.scale * m_outputSize.x : m_outputSize.x;
|
||||||
|
break;
|
||||||
|
case ScaleType::INPUT:
|
||||||
|
default:
|
||||||
|
scaledSize.x = pass.fbo.scaleX.scale ? pass.fbo.scaleX.scale * prevSize.x : prevSize.x;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (pass.fbo.scaleY.scaleType)
|
||||||
|
{
|
||||||
|
case ScaleType::ABSOLUTE_SCALE:
|
||||||
|
scaledSize.y = static_cast<float>(pass.fbo.scaleY.abs);
|
||||||
|
break;
|
||||||
|
case ScaleType::VIEWPORT:
|
||||||
|
scaledSize.y =
|
||||||
|
pass.fbo.scaleY.scale ? pass.fbo.scaleY.scale * m_outputSize.y : m_outputSize.y;
|
||||||
|
break;
|
||||||
|
case ScaleType::INPUT:
|
||||||
|
default:
|
||||||
|
scaledSize.y = pass.fbo.scaleY.scale ? pass.fbo.scaleY.scale * prevSize.y : prevSize.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shaderIdx + 1 == numPasses)
|
||||||
|
{
|
||||||
|
// We're supposed to output at full (viewport) resolution
|
||||||
|
scaledSize.x = m_outputSize.x;
|
||||||
|
scaledSize.y = m_outputSize.y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Determine the framebuffer data format
|
||||||
|
DXGI_FORMAT textureFormat;
|
||||||
|
if (pass.fbo.floatFramebuffer)
|
||||||
|
{
|
||||||
|
// Give priority to float framebuffer parameter (we can't use both float and sRGB)
|
||||||
|
textureFormat = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pass.fbo.sRgbFramebuffer)
|
||||||
|
textureFormat = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
|
||||||
|
else
|
||||||
|
textureFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @todo Enable usage of optimal texture sizes once multi-pass preset
|
||||||
|
// geometry and LUT rendering are fixed.
|
||||||
|
//
|
||||||
|
// Current issues:
|
||||||
|
// - Enabling optimal texture sizes breaks geometry for many multi-pass
|
||||||
|
// presets
|
||||||
|
// - LUTs render incorrectly due to missing per-pass and per-LUT
|
||||||
|
// TexCoord attributes.
|
||||||
|
//
|
||||||
|
// Planned solution:
|
||||||
|
// - Implement additional TexCoord attributes for each pass and LUT,
|
||||||
|
// setting coordinates to `xamt` and `yamt` instead of 1
|
||||||
|
//
|
||||||
|
// Reference implementation in RetroArch:
|
||||||
|
// https://github.com/libretro/RetroArch/blob/09a59edd6b415b7bd124b03bda68ccc4d60b0ea8/gfx/drivers/gl2.c#L3018
|
||||||
|
//
|
||||||
|
textureSize = scaledSize; // CShaderUtils::GetOptimalTextureSize(scaledSize)
|
||||||
|
|
||||||
|
std::shared_ptr<CD3DTexture> textureDX = std::make_shared<CD3DTexture>();
|
||||||
|
|
||||||
|
if (!textureDX->Create(static_cast<UINT>(textureSize.x), static_cast<UINT>(textureSize.y), 1,
|
||||||
|
D3D11_USAGE_DEFAULT, textureFormat, nullptr, 0))
|
||||||
|
{
|
||||||
|
CLog::Log(
|
||||||
|
LOGERROR,
|
||||||
|
"CShaderPresetDX::CreateShaderTextures: Couldn't create texture for video shader: {}",
|
||||||
|
pass.sourcePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pShaderTextures.emplace_back(std::make_unique<CShaderTextureDX>(std::move(textureDX)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify shader of its source and dest size
|
||||||
|
m_pShaders[shaderIdx]->SetSizes(prevSize, prevTextureSize, scaledSize);
|
||||||
|
|
||||||
|
prevSize = scaledSize;
|
||||||
|
prevTextureSize = textureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateMVPs();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CShaderPresetDX::CreateSamplers()
|
||||||
|
{
|
||||||
|
// Describe the Sampler States
|
||||||
|
// As specified in the common-shaders spec
|
||||||
|
D3D11_SAMPLER_DESC sampDesc;
|
||||||
|
ZeroMemory(&sampDesc, sizeof(D3D11_SAMPLER_DESC));
|
||||||
|
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
||||||
|
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
|
||||||
|
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
|
||||||
|
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
|
||||||
|
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||||
|
sampDesc.MinLOD = 0;
|
||||||
|
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||||
|
FLOAT blackBorder[4] = {1, 0, 0, 1}; //! @todo Turn this back to black
|
||||||
|
memcpy(sampDesc.BorderColor, &blackBorder, 4 * sizeof(FLOAT));
|
||||||
|
|
||||||
|
ID3D11Device1* pDevice = DX::DeviceResources::Get()->GetD3DDevice();
|
||||||
|
|
||||||
|
if (FAILED(pDevice->CreateSamplerState(&sampDesc, &m_pSampNearest)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
D3D11_SAMPLER_DESC sampDescLinear = sampDesc;
|
||||||
|
sampDescLinear.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||||
|
if (FAILED(pDevice->CreateSamplerState(&sampDescLinear, &m_pSampLinear)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShaderPresetDX::RenderShader(IShader& shader, IShaderTexture& source, IShaderTexture& target)
|
||||||
|
{
|
||||||
|
const CRect newViewPort(0.f, 0.f, target.GetWidth(), target.GetHeight());
|
||||||
|
m_context.SetViewPort(newViewPort);
|
||||||
|
m_context.SetScissors(newViewPort);
|
||||||
|
|
||||||
|
shader.Render(source, target);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderPreset.h"
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace RETRO
|
||||||
|
{
|
||||||
|
class CRenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class IShader;
|
||||||
|
class IShaderTexture;
|
||||||
|
|
||||||
|
class CShaderPresetDX : public CShaderPreset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Instance of CShaderPreset
|
||||||
|
explicit CShaderPresetDX(RETRO::CRenderContext& context,
|
||||||
|
unsigned videoWidth = 0,
|
||||||
|
unsigned videoHeight = 0);
|
||||||
|
~CShaderPresetDX() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Implementation of CShaderPreset
|
||||||
|
bool CreateShaders() override;
|
||||||
|
bool CreateLayouts() override;
|
||||||
|
bool CreateBuffers() override;
|
||||||
|
bool CreateShaderTextures() override;
|
||||||
|
bool CreateSamplers() override;
|
||||||
|
void RenderShader(IShader& shader, IShaderTexture& source, IShaderTexture& target) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Point/nearest neighbor sampler
|
||||||
|
ID3D11SamplerState* m_pSampNearest = nullptr;
|
||||||
|
|
||||||
|
// Linear sampler
|
||||||
|
ID3D11SamplerState* m_pSampLinear = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderSamplerDX.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderSamplerDX::CShaderSamplerDX(ID3D11SamplerState* sampler) : m_sampler(sampler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CShaderSamplerDX::~CShaderSamplerDX()
|
||||||
|
{
|
||||||
|
if (m_sampler != nullptr)
|
||||||
|
m_sampler->Release();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderSampler.h"
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
|
||||||
|
class CShaderSamplerDX : public IShaderSampler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CShaderSamplerDX(ID3D11SamplerState* sampler);
|
||||||
|
~CShaderSamplerDX() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ID3D11SamplerState* const m_sampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderTextureDX.h"
|
||||||
|
|
||||||
|
#include "guilib/D3DResource.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderTextureDX::CShaderTextureDX(std::shared_ptr<CD3DTexture> texture)
|
||||||
|
: m_texture(std::move(texture))
|
||||||
|
{
|
||||||
|
assert(m_texture.get() != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
float CShaderTextureDX::GetWidth() const
|
||||||
|
{
|
||||||
|
return static_cast<float>(m_texture->GetWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
float CShaderTextureDX::GetHeight() const
|
||||||
|
{
|
||||||
|
return static_cast<float>(m_texture->GetHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D11ShaderResourceView* CShaderTextureDX::GetShaderResource() const
|
||||||
|
{
|
||||||
|
return m_texture->GetShaderResource();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderTexture.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
class CD3DTexture;
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Shader texture that manages the lifetime of its texture object
|
||||||
|
*/
|
||||||
|
class CShaderTextureDX : public IShaderTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CShaderTextureDX(std::shared_ptr<CD3DTexture> texture);
|
||||||
|
~CShaderTextureDX() override = default;
|
||||||
|
|
||||||
|
// Implementation of IShaderTexture
|
||||||
|
float GetWidth() const override;
|
||||||
|
float GetHeight() const override;
|
||||||
|
|
||||||
|
// DirectX interface
|
||||||
|
CD3DTexture& GetTexture() { return *m_texture; }
|
||||||
|
const CD3DTexture& GetTexture() const { return *m_texture; }
|
||||||
|
ID3D11ShaderResourceView* GetShaderResource() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Construction parameter
|
||||||
|
const std::shared_ptr<CD3DTexture> m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPD3D-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderTextureDXRef.h"
|
||||||
|
|
||||||
|
#include "guilib/D3DResource.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
CShaderTextureDXRef::CShaderTextureDXRef(CD3DTexture& texture) : m_texture(texture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float CShaderTextureDXRef::GetWidth() const
|
||||||
|
{
|
||||||
|
return static_cast<float>(m_texture.GetWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
float CShaderTextureDXRef::GetHeight() const
|
||||||
|
{
|
||||||
|
return static_cast<float>(m_texture.GetHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D11ShaderResourceView* CShaderTextureDXRef::GetShaderResource() const
|
||||||
|
{
|
||||||
|
return m_texture.GetShaderResource();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/IShaderTexture.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
class CD3DTexture;
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Shader texture that wraps an external texture object
|
||||||
|
*
|
||||||
|
* NOTE: The lifetime of the external texture object must outlast this class.
|
||||||
|
*/
|
||||||
|
class CShaderTextureDXRef : public IShaderTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CShaderTextureDXRef(CD3DTexture& texture);
|
||||||
|
~CShaderTextureDXRef() override = default;
|
||||||
|
|
||||||
|
// Implementation of IShaderTexture
|
||||||
|
float GetWidth() const override;
|
||||||
|
float GetHeight() const override;
|
||||||
|
|
||||||
|
// DirectX interface
|
||||||
|
CD3DTexture& GetTexture() { return m_texture; }
|
||||||
|
const CD3DTexture& GetTexture() const { return m_texture; }
|
||||||
|
ID3D11ShaderResourceView* GetShaderResource() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Construction parameter
|
||||||
|
CD3DTexture& m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <minwindef.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
struct CUSTOMVERTEX
|
||||||
|
{
|
||||||
|
FLOAT x;
|
||||||
|
FLOAT y;
|
||||||
|
FLOAT z;
|
||||||
|
|
||||||
|
FLOAT tu;
|
||||||
|
FLOAT tv;
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ShaderUtilsDX.h"
|
||||||
|
|
||||||
|
using namespace KODI;
|
||||||
|
using namespace SHADER;
|
||||||
|
|
||||||
|
D3D11_TEXTURE_ADDRESS_MODE CShaderUtilsDX::TranslateWrapType(WrapType wrapType)
|
||||||
|
{
|
||||||
|
D3D11_TEXTURE_ADDRESS_MODE dxWrap;
|
||||||
|
switch (wrapType)
|
||||||
|
{
|
||||||
|
case WrapType::EDGE:
|
||||||
|
dxWrap = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||||
|
break;
|
||||||
|
case WrapType::REPEAT:
|
||||||
|
dxWrap = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
break;
|
||||||
|
case WrapType::MIRRORED_REPEAT:
|
||||||
|
dxWrap = D3D11_TEXTURE_ADDRESS_MIRROR;
|
||||||
|
break;
|
||||||
|
case WrapType::BORDER:
|
||||||
|
default:
|
||||||
|
dxWrap = D3D11_TEXTURE_ADDRESS_BORDER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return dxWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectX::XMFLOAT2 CShaderUtilsDX::ToDXVector(const float2& vec)
|
||||||
|
{
|
||||||
|
return DirectX::XMFLOAT2(static_cast<float>(vec.x), static_cast<float>(vec.y));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2025 Team Kodi
|
||||||
|
* This file is part of Kodi - https://kodi.tv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* See LICENSES/README.md for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <DirectXMath.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
namespace KODI
|
||||||
|
{
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class CShaderUtilsDX
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static D3D11_TEXTURE_ADDRESS_MODE TranslateWrapType(WrapType wrapType);
|
||||||
|
static DirectX::XMFLOAT2 ToDXVector(const float2& vec);
|
||||||
|
};
|
||||||
|
} // namespace SHADER
|
||||||
|
} // namespace KODI
|
||||||
|
|
@ -62,6 +62,7 @@ const std::set<AddonType> infoProviderTypes = {
|
||||||
AddonType::SCRAPER_MUSICVIDEOS, AddonType::SCRAPER_TVSHOWS,
|
AddonType::SCRAPER_MUSICVIDEOS, AddonType::SCRAPER_TVSHOWS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const std::set<AddonType> lookAndFeelTypes = {
|
const std::set<AddonType> lookAndFeelTypes = {
|
||||||
AddonType::SKIN,
|
AddonType::SKIN,
|
||||||
AddonType::SCREENSAVER,
|
AddonType::SCREENSAVER,
|
||||||
|
|
@ -75,9 +76,11 @@ const std::set<AddonType> lookAndFeelTypes = {
|
||||||
const std::set<AddonType> gameTypes = {
|
const std::set<AddonType> gameTypes = {
|
||||||
AddonType::GAME_CONTROLLER,
|
AddonType::GAME_CONTROLLER,
|
||||||
AddonType::GAMEDLL,
|
AddonType::GAMEDLL,
|
||||||
|
AddonType::SHADERDLL,
|
||||||
AddonType::GAME,
|
AddonType::GAME,
|
||||||
AddonType::RESOURCE_GAMES,
|
AddonType::RESOURCE_GAMES,
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
static bool IsInfoProviderType(AddonType type)
|
static bool IsInfoProviderType(AddonType type)
|
||||||
{
|
{
|
||||||
|
|
@ -127,9 +130,15 @@ static bool IsGameResource(const AddonPtr& addon)
|
||||||
|
|
||||||
static bool IsGameSupportAddon(const AddonPtr& addon)
|
static bool IsGameSupportAddon(const AddonPtr& addon)
|
||||||
{
|
{
|
||||||
return addon->Type() == AddonType::GAMEDLL &&
|
if (addon->Type() == AddonType::GAMEDLL &&
|
||||||
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
|
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
|
||||||
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
|
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addon->Type() == AddonType::SHADERDLL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsGameAddon(const AddonPtr& addon)
|
static bool IsGameAddon(const AddonPtr& addon)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "controllers/Controller.h"
|
#include "controllers/Controller.h"
|
||||||
#include "controllers/ControllerManager.h"
|
#include "controllers/ControllerManager.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderPresetFactory.h"
|
||||||
#include "games/GameSettings.h"
|
#include "games/GameSettings.h"
|
||||||
#include "games/GameUtils.h"
|
#include "games/GameUtils.h"
|
||||||
#include "games/agents/input/AgentInput.h"
|
#include "games/agents/input/AgentInput.h"
|
||||||
|
|
@ -22,12 +23,14 @@ CGameServices::CGameServices(CControllerManager& controllerManager,
|
||||||
RETRO::CGUIGameRenderManager& renderManager,
|
RETRO::CGUIGameRenderManager& renderManager,
|
||||||
PERIPHERALS::CPeripherals& peripheralManager,
|
PERIPHERALS::CPeripherals& peripheralManager,
|
||||||
const CProfileManager& profileManager,
|
const CProfileManager& profileManager,
|
||||||
CInputManager& inputManager)
|
CInputManager& inputManager,
|
||||||
|
ADDON::CAddonMgr& addons)
|
||||||
: m_controllerManager(controllerManager),
|
: m_controllerManager(controllerManager),
|
||||||
m_gameRenderManager(renderManager),
|
m_gameRenderManager(renderManager),
|
||||||
m_profileManager(profileManager),
|
m_profileManager(profileManager),
|
||||||
m_gameSettings(new CGameSettings()),
|
m_gameSettings(new CGameSettings()),
|
||||||
m_agentInput(std::make_unique<CAgentInput>(peripheralManager, inputManager))
|
m_agentInput(std::make_unique<CAgentInput>(peripheralManager, inputManager)),
|
||||||
|
m_videoShaders(new SHADER::CShaderPresetFactory(addons))
|
||||||
{
|
{
|
||||||
// Load the add-ons from the database asynchronously
|
// Load the add-ons from the database asynchronously
|
||||||
m_initializationTask =
|
m_initializationTask =
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
class CInputManager;
|
class CInputManager;
|
||||||
class CProfileManager;
|
class CProfileManager;
|
||||||
|
|
||||||
|
namespace ADDON
|
||||||
|
{
|
||||||
|
class CAddonMgr;
|
||||||
|
} // namespace ADDON
|
||||||
|
|
||||||
namespace PERIPHERALS
|
namespace PERIPHERALS
|
||||||
{
|
{
|
||||||
class CPeripherals;
|
class CPeripherals;
|
||||||
|
|
@ -29,6 +34,11 @@ namespace RETRO
|
||||||
class CGUIGameRenderManager;
|
class CGUIGameRenderManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace SHADER
|
||||||
|
{
|
||||||
|
class CShaderPresetFactory;
|
||||||
|
}
|
||||||
|
|
||||||
namespace GAME
|
namespace GAME
|
||||||
{
|
{
|
||||||
class CAgentInput;
|
class CAgentInput;
|
||||||
|
|
@ -45,7 +55,8 @@ public:
|
||||||
RETRO::CGUIGameRenderManager& renderManager,
|
RETRO::CGUIGameRenderManager& renderManager,
|
||||||
PERIPHERALS::CPeripherals& peripheralManager,
|
PERIPHERALS::CPeripherals& peripheralManager,
|
||||||
const CProfileManager& profileManager,
|
const CProfileManager& profileManager,
|
||||||
CInputManager& inputManager);
|
CInputManager& inputManager,
|
||||||
|
ADDON::CAddonMgr& addons);
|
||||||
~CGameServices();
|
~CGameServices();
|
||||||
|
|
||||||
ControllerPtr GetController(const std::string& controllerId);
|
ControllerPtr GetController(const std::string& controllerId);
|
||||||
|
|
@ -73,6 +84,8 @@ public:
|
||||||
|
|
||||||
CAgentInput& AgentInput() { return *m_agentInput; }
|
CAgentInput& AgentInput() { return *m_agentInput; }
|
||||||
|
|
||||||
|
SHADER::CShaderPresetFactory& VideoShaders() { return *m_videoShaders; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Called when an add-on repo is installed
|
* \brief Called when an add-on repo is installed
|
||||||
*
|
*
|
||||||
|
|
@ -90,6 +103,7 @@ private:
|
||||||
// Game services
|
// Game services
|
||||||
std::unique_ptr<CGameSettings> m_gameSettings;
|
std::unique_ptr<CGameSettings> m_gameSettings;
|
||||||
std::unique_ptr<CAgentInput> m_agentInput;
|
std::unique_ptr<CAgentInput> m_agentInput;
|
||||||
|
std::unique_ptr<SHADER::CShaderPresetFactory> m_videoShaders;
|
||||||
|
|
||||||
// Game threads
|
// Game threads
|
||||||
std::future<void> m_initializationTask;
|
std::future<void> m_initializationTask;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ constexpr auto SAVESTATE_CAPTION = "savestate.caption";
|
||||||
constexpr auto SAVESTATE_GAME_CLIENT = "savestate.gameclient";
|
constexpr auto SAVESTATE_GAME_CLIENT = "savestate.gameclient";
|
||||||
constexpr auto SAVESTATE_GAME_CLIENT_VERSION = "savestate.gameclientversion";
|
constexpr auto SAVESTATE_GAME_CLIENT_VERSION = "savestate.gameclientversion";
|
||||||
|
|
||||||
|
// String of list item property "game.videofilter" when no filter is set
|
||||||
|
constexpr auto PROPERTY_NO_VIDEO_FILTER = "";
|
||||||
|
|
||||||
// Control IDs for game dialogs
|
// Control IDs for game dialogs
|
||||||
constexpr unsigned int CONTROL_VIDEO_HEADING = 10810;
|
constexpr unsigned int CONTROL_VIDEO_HEADING = 10810;
|
||||||
constexpr unsigned int CONTROL_VIDEO_THUMBS = 10811;
|
constexpr unsigned int CONTROL_VIDEO_THUMBS = 10811;
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,32 @@
|
||||||
|
|
||||||
#include "DialogGameVideoFilter.h"
|
#include "DialogGameVideoFilter.h"
|
||||||
|
|
||||||
|
#include "ServiceBroker.h"
|
||||||
|
#include "URL.h"
|
||||||
#include "cores/RetroPlayer/guibridge/GUIGameVideoHandle.h"
|
#include "cores/RetroPlayer/guibridge/GUIGameVideoHandle.h"
|
||||||
#include "cores/RetroPlayer/rendering/RenderVideoSettings.h"
|
#include "cores/RetroPlayer/rendering/RenderVideoSettings.h"
|
||||||
|
#include "cores/RetroPlayer/shaders/ShaderPresetFactory.h"
|
||||||
|
#include "filesystem/File.h"
|
||||||
|
#include "filesystem/SpecialProtocol.h"
|
||||||
|
#include "games/GameServices.h"
|
||||||
|
#include "games/dialogs/DialogGameDefines.h"
|
||||||
#include "guilib/LocalizeStrings.h"
|
#include "guilib/LocalizeStrings.h"
|
||||||
#include "guilib/WindowIDs.h"
|
#include "guilib/WindowIDs.h"
|
||||||
#include "settings/GameSettings.h"
|
#include "settings/GameSettings.h"
|
||||||
#include "settings/MediaSettings.h"
|
#include "settings/MediaSettings.h"
|
||||||
#include "utils/StringUtils.h"
|
#include "utils/StringUtils.h"
|
||||||
|
#include "utils/URIUtils.h"
|
||||||
#include "utils/Variant.h"
|
#include "utils/Variant.h"
|
||||||
|
#include "utils/XBMCTinyXML.h"
|
||||||
|
#include "utils/log.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
using namespace KODI;
|
using namespace KODI;
|
||||||
using namespace GAME;
|
using namespace GAME;
|
||||||
|
|
||||||
|
#define PRESETS_ADDON_NAME "game.shader.presets"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct ScalingMethodProperties
|
struct ScalingMethodProperties
|
||||||
|
|
@ -50,6 +64,7 @@ void CDialogGameVideoFilter::PreInit()
|
||||||
{
|
{
|
||||||
m_items.Clear();
|
m_items.Clear();
|
||||||
|
|
||||||
|
InitScalingMethods();
|
||||||
InitVideoFilters();
|
InitVideoFilters();
|
||||||
|
|
||||||
if (m_items.Size() == 0)
|
if (m_items.Size() == 0)
|
||||||
|
|
@ -61,7 +76,7 @@ void CDialogGameVideoFilter::PreInit()
|
||||||
m_bHasDescription = false;
|
m_bHasDescription = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDialogGameVideoFilter::InitVideoFilters()
|
void CDialogGameVideoFilter::InitScalingMethods()
|
||||||
{
|
{
|
||||||
if (m_gameVideoHandle)
|
if (m_gameVideoHandle)
|
||||||
{
|
{
|
||||||
|
|
@ -84,6 +99,97 @@ void CDialogGameVideoFilter::InitVideoFilters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDialogGameVideoFilter::InitVideoFilters()
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<VideoFilterProperties> videoFilters;
|
||||||
|
|
||||||
|
std::string xmlPath;
|
||||||
|
std::unique_ptr<CXBMCTinyXML> xml;
|
||||||
|
|
||||||
|
// TODO: Have the add-on give us the xml as a string (or parse it)
|
||||||
|
std::string xmlFilename;
|
||||||
|
#ifdef TARGET_WINDOWS
|
||||||
|
xmlFilename = "ShaderPresetsHLSLP.xml";
|
||||||
|
#else
|
||||||
|
xmlFilename = "ShaderPresetsGLSLP.xml";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const std::string homeAddonPath = CSpecialProtocol::TranslatePath(
|
||||||
|
URIUtils::AddFileToFolder("special://home", "addons", PRESETS_ADDON_NAME));
|
||||||
|
const std::string systemAddonPath = CSpecialProtocol::TranslatePath(
|
||||||
|
URIUtils::AddFileToFolder("special://xbmc", "addons", PRESETS_ADDON_NAME));
|
||||||
|
const std::string binAddonPath = CSpecialProtocol::TranslatePath(
|
||||||
|
URIUtils::AddFileToFolder("special://xbmcbinaddons", PRESETS_ADDON_NAME));
|
||||||
|
|
||||||
|
for (const auto& basePath : {homeAddonPath, systemAddonPath, binAddonPath})
|
||||||
|
{
|
||||||
|
xmlPath = URIUtils::AddFileToFolder(basePath, "resources", xmlFilename);
|
||||||
|
|
||||||
|
CLog::LogF(LOGERROR, "Looking for shader preset XML at {}", CURL::GetRedacted(xmlPath));
|
||||||
|
|
||||||
|
if (XFILE::CFile::Exists(xmlPath))
|
||||||
|
{
|
||||||
|
xml = std::make_unique<CXBMCTinyXML>(xmlPath);
|
||||||
|
if (xml->LoadFile())
|
||||||
|
break;
|
||||||
|
|
||||||
|
CLog::LogF(LOGERROR, "Couldn't load shader presets from XML, {}", CURL::GetRedacted(xmlPath));
|
||||||
|
xml.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xml)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto root = xml->RootElement();
|
||||||
|
TiXmlNode* child = nullptr;
|
||||||
|
|
||||||
|
while ((child = root->IterateChildren(child)))
|
||||||
|
{
|
||||||
|
VideoFilterProperties videoFilter;
|
||||||
|
|
||||||
|
if (child->FirstChild() == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TiXmlNode* pathNode;
|
||||||
|
if ((pathNode = child->FirstChild("path")))
|
||||||
|
if ((pathNode = pathNode->FirstChild()))
|
||||||
|
videoFilter.path =
|
||||||
|
URIUtils::AddFileToFolder(URIUtils::GetBasePath(xmlPath), pathNode->Value());
|
||||||
|
|
||||||
|
TiXmlNode* nameNode;
|
||||||
|
if ((nameNode = child->FirstChild("name")))
|
||||||
|
if ((nameNode = nameNode->FirstChild()))
|
||||||
|
videoFilter.name = nameNode->Value();
|
||||||
|
|
||||||
|
TiXmlNode* folderNode;
|
||||||
|
if ((folderNode = child->FirstChild("folder")))
|
||||||
|
if ((folderNode = folderNode->FirstChild()))
|
||||||
|
videoFilter.folder = folderNode->Value();
|
||||||
|
|
||||||
|
videoFilters.emplace_back(videoFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLog::Log(LOGDEBUG, "Loaded {} shader presets from default XML, {}", videoFilters.size(),
|
||||||
|
CURL::GetRedacted(xmlPath));
|
||||||
|
|
||||||
|
for (const auto& videoFilter : videoFilters)
|
||||||
|
{
|
||||||
|
bool canLoadPreset =
|
||||||
|
CServiceBroker::GetGameServices().VideoShaders().CanLoadPreset(videoFilter.path);
|
||||||
|
|
||||||
|
if (!canLoadPreset)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CFileItem item{videoFilter.name};
|
||||||
|
item.SetLabel2(videoFilter.folder);
|
||||||
|
item.SetProperty("game.videofilter", CVariant{videoFilter.path});
|
||||||
|
|
||||||
|
m_items.Add(std::move(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CDialogGameVideoFilter::GetItems(CFileItemList& items)
|
void CDialogGameVideoFilter::GetItems(CFileItemList& items)
|
||||||
{
|
{
|
||||||
for (const auto& item : m_items)
|
for (const auto& item : m_items)
|
||||||
|
|
@ -100,7 +206,7 @@ void CDialogGameVideoFilter::OnItemFocus(unsigned int index)
|
||||||
std::string description;
|
std::string description;
|
||||||
GetProperties(*item, videoFilter, description);
|
GetProperties(*item, videoFilter, description);
|
||||||
|
|
||||||
CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
::CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||||
|
|
||||||
if (gameSettings.VideoFilter() != videoFilter)
|
if (gameSettings.VideoFilter() != videoFilter)
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +226,7 @@ void CDialogGameVideoFilter::OnItemFocus(unsigned int index)
|
||||||
|
|
||||||
unsigned int CDialogGameVideoFilter::GetFocusedItem() const
|
unsigned int CDialogGameVideoFilter::GetFocusedItem() const
|
||||||
{
|
{
|
||||||
CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
::CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||||
|
|
||||||
for (int i = 0; i < m_items.Size(); i++)
|
for (int i = 0; i < m_items.Size(); i++)
|
||||||
{
|
{
|
||||||
|
|
@ -148,6 +254,11 @@ bool CDialogGameVideoFilter::OnClickAction()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CDialogGameVideoFilter::GetLocalizedString(uint32_t code)
|
||||||
|
{
|
||||||
|
return g_localizeStrings.GetAddonString(PRESETS_ADDON_NAME, code);
|
||||||
|
}
|
||||||
|
|
||||||
void CDialogGameVideoFilter::GetProperties(const CFileItem& item,
|
void CDialogGameVideoFilter::GetProperties(const CFileItem& item,
|
||||||
std::string& videoFilter,
|
std::string& videoFilter,
|
||||||
std::string& description)
|
std::string& description)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ protected:
|
||||||
bool OnClickAction() override;
|
bool OnClickAction() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void InitScalingMethods();
|
||||||
void InitVideoFilters();
|
void InitVideoFilters();
|
||||||
|
|
||||||
static void GetProperties(const CFileItem& item,
|
static void GetProperties(const CFileItem& item,
|
||||||
|
|
@ -44,6 +45,15 @@ private:
|
||||||
|
|
||||||
CFileItemList m_items;
|
CFileItemList m_items;
|
||||||
|
|
||||||
|
static std::string GetLocalizedString(uint32_t code);
|
||||||
|
|
||||||
|
struct VideoFilterProperties
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
std::string name;
|
||||||
|
std::string folder;
|
||||||
|
};
|
||||||
|
|
||||||
//! \brief Set to true when a description has first been set
|
//! \brief Set to true when a description has first been set
|
||||||
bool m_bHasDescription = false;
|
bool m_bHasDescription = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue