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.peripheral/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.vfs/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/rendering cores/RetroPlayer/rendering
|
||||
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/shaders cores/RetroPlayer/shaders
|
||||
xbmc/cores/RetroPlayer/streams cores/RetroPlayer/streams
|
||||
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/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/VideoRenderers/windows cores/VideoPlayer/VideoRenderers/windows
|
||||
xbmc/input/touch input/touch
|
||||
|
|
|
|||
|
|
@ -40,6 +40,16 @@ IF EXIST BUILD_WIN32\addons\game.libretro.* (
|
|||
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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
<addon>kodi.binary.instance.peripheral</addon>
|
||||
<addon>kodi.binary.instance.pvr</addon>
|
||||
<addon>kodi.binary.instance.screensaver</addon>
|
||||
<addon>kodi.binary.instance.shaderpreset</addon>
|
||||
<addon>kodi.binary.instance.vfs</addon>
|
||||
<addon>kodi.binary.instance.videocodec</addon>
|
||||
<addon>kodi.binary.instance.visualization</addon>
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
<addon>xbmc.metadata</addon>
|
||||
<addon>xbmc.python</addon>
|
||||
<addon>xbmc.webinterface</addon>
|
||||
<addon optional="true">game.shader.presets</addon>
|
||||
<addon optional="true">inputstream.adaptive</addon>
|
||||
<addon optional="true">peripheral.joystick</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
|
||||
m_peripherals->Initialise();
|
||||
|
||||
m_gameServices =
|
||||
std::make_unique<GAME::CGameServices>(*m_gameControllerManager, *m_gameRenderManager,
|
||||
*m_peripherals, *profileManager, *m_inputManager);
|
||||
m_gameServices = std::make_unique<GAME::CGameServices>(
|
||||
*m_gameControllerManager, *m_gameRenderManager, *m_peripherals, *profileManager,
|
||||
*m_inputManager, *m_addonMgr);
|
||||
|
||||
m_contextMenuManager->Init();
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ AddonPtr CAddonBuilder::Generate(const AddonInfoPtr& info, AddonType type)
|
|||
return std::make_shared<CAddonDll>(info, type);
|
||||
case AddonType::GAMEDLL:
|
||||
return std::make_shared<GAME::CGameClient>(info);
|
||||
case AddonType::SHADERDLL:
|
||||
return std::make_shared<CAddonDll>(info, type);
|
||||
case AddonType::PLUGIN:
|
||||
case AddonType::SCRIPT:
|
||||
return std::make_shared<CPluginSource>(info, type);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
namespace ADDON
|
||||
{
|
||||
|
||||
const std::vector<AddonType> ADDONS_TO_CACHE = {AddonType::GAMEDLL};
|
||||
const std::vector<AddonType> ADDONS_TO_CACHE = {AddonType::GAMEDLL, AddonType::SHADERDLL};
|
||||
|
||||
CBinaryAddonCache::~CBinaryAddonCache()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ set(SOURCES Addon.cpp
|
|||
Scraper.cpp
|
||||
ScreenSaver.cpp
|
||||
Service.cpp
|
||||
ShaderPreset.cpp
|
||||
Skin.cpp
|
||||
UISoundsResource.cpp
|
||||
VFSEntry.cpp
|
||||
|
|
@ -65,6 +66,7 @@ set(HEADERS Addon.h
|
|||
Scraper.h
|
||||
ScreenSaver.h
|
||||
Service.h
|
||||
ShaderPreset.h
|
||||
Skin.h
|
||||
UISoundsResource.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;
|
||||
|
||||
// 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, "" },
|
||||
{"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" },
|
||||
{"kodi.pvrclient", "xbmc.pvrclient", AddonType::PVRDLL, 24019, AddonInstanceSupport::SUPPORT_SETTINGS, "DefaultAddonPVRClient.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" },
|
||||
{"xbmc.addon.video", "", AddonType::VIDEO, 1037, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonVideo.png" },
|
||||
{"xbmc.addon.audio", "", AddonType::AUDIO, 1038, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMusic.png" },
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ enum class AddonType
|
|||
PVRDLL,
|
||||
INPUTSTREAM,
|
||||
GAMEDLL,
|
||||
SHADERDLL,
|
||||
PERIPHERALDLL,
|
||||
SCRIPT,
|
||||
SCRIPT_WEATHER,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ set(HEADERS
|
|||
PVR.h
|
||||
Peripheral.h
|
||||
Screensaver.h
|
||||
ShaderPreset.h
|
||||
VFS.h
|
||||
VideoCodec.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
|
||||
pvr.h
|
||||
screensaver.h
|
||||
shaderpreset.h
|
||||
vfs.h
|
||||
video_codec.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_PVR* pvr;
|
||||
struct AddonInstance_Screensaver* screensaver;
|
||||
struct AddonInstance_ShaderPreset* shaderpreset;
|
||||
struct AddonInstance_VFSEntry* vfs;
|
||||
struct AddonInstance_VideoCodec* videocodec;
|
||||
struct AddonInstance_Visualization* visualization;
|
||||
|
|
|
|||
|
|
@ -163,6 +163,11 @@
|
|||
#define ADDON_INSTANCE_VERSION_SCREENSAVER_DEPENDS "c-api/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_MIN "3.0.1"
|
||||
#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"
|
||||
ADDON_INSTANCE_VIDEOCODEC = 112,
|
||||
|
||||
/// Shader preset instance, see @ref cpp_kodi_addon_shaderpreset "kodi::addon::CInstanceShaderPreset"
|
||||
ADDON_INSTANCE_SHADERPRESET = 113,
|
||||
} ADDON_TYPE;
|
||||
///@}
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -333,6 +341,10 @@ inline const char* GetTypeVersion(int type)
|
|||
case ADDON_INSTANCE_SCREENSAVER:
|
||||
return ADDON_INSTANCE_VERSION_SCREENSAVER;
|
||||
#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)
|
||||
case ADDON_INSTANCE_VFS:
|
||||
return ADDON_INSTANCE_VERSION_VFS;
|
||||
|
|
@ -394,6 +406,8 @@ inline const char* GetTypeMinVersion(int type)
|
|||
return ADDON_INSTANCE_VERSION_PVR_MIN;
|
||||
case ADDON_INSTANCE_SCREENSAVER:
|
||||
return ADDON_INSTANCE_VERSION_SCREENSAVER_MIN;
|
||||
case ADDON_INSTANCE_SHADERPRESET:
|
||||
return ADDON_INSTANCE_VERSION_SHADERPRESET_MIN;
|
||||
case ADDON_INSTANCE_VFS:
|
||||
return ADDON_INSTANCE_VERSION_VFS_MIN;
|
||||
case ADDON_INSTANCE_VISUALIZATION:
|
||||
|
|
@ -448,6 +462,8 @@ inline const char* GetTypeName(int type)
|
|||
return "PVR";
|
||||
case ADDON_INSTANCE_SCREENSAVER:
|
||||
return "ScreenSaver";
|
||||
case ADDON_INSTANCE_SHADERPRESET:
|
||||
return "ShaderPreset";
|
||||
case ADDON_INSTANCE_VISUALIZATION:
|
||||
return "Visualization";
|
||||
case ADDON_INSTANCE_VIDEOCODEC:
|
||||
|
|
@ -499,6 +515,8 @@ inline int GetTypeId(const char* name)
|
|||
return ADDON_INSTANCE_PVR;
|
||||
else if (strcmp(name, "screensaver") == 0)
|
||||
return ADDON_INSTANCE_SCREENSAVER;
|
||||
else if (strcmp(name, "shaderpreset") == 0)
|
||||
return ADDON_INSTANCE_SHADERPRESET;
|
||||
else if (strcmp(name, "vfs") == 0)
|
||||
return ADDON_INSTANCE_VFS;
|
||||
else if (strcmp(name, "visualization") == 0)
|
||||
|
|
|
|||
|
|
@ -556,6 +556,7 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRendererForSettings(
|
|||
renderer->SetScalingMethod(effectiveRenderSettings.VideoSettings().GetScalingMethod());
|
||||
renderer->SetStretchMode(effectiveRenderSettings.VideoSettings().GetRenderStretchMode());
|
||||
renderer->SetRenderRotation(effectiveRenderSettings.VideoSettings().GetRenderRotation());
|
||||
renderer->SetShaderPreset(effectiveRenderSettings.VideoSettings().GetShaderPreset());
|
||||
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 (!renderer)
|
||||
{
|
||||
const std::string& shaderPreset = renderSettings.VideoSettings().GetShaderPreset();
|
||||
|
||||
CLog::Log(LOGDEBUG, "RetroPlayer[RENDER]: Creating renderer for {}",
|
||||
m_processInfo.GetRenderSystemName(bufferPool));
|
||||
|
||||
renderer.reset(m_processInfo.CreateRenderer(bufferPool, renderSettings));
|
||||
// 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));
|
||||
|
||||
if (renderer && renderer->Configure(m_format))
|
||||
{
|
||||
// Ensure we have a render buffer for this renderer
|
||||
|
|
@ -602,6 +609,10 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRendererForPool(
|
|||
}
|
||||
else
|
||||
renderer.reset();
|
||||
|
||||
// If we failed to create a renderer, blocklist the shader preset
|
||||
if (!renderer && !shaderPreset.empty())
|
||||
m_failedShaderPresets.insert(shaderPreset);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
|
|
|
|||
|
|
@ -8,28 +8,39 @@
|
|||
|
||||
#include "RenderVideoSettings.h"
|
||||
|
||||
#include "utils/log.h"
|
||||
|
||||
using namespace KODI;
|
||||
using namespace RETRO;
|
||||
|
||||
#define VIDEO_FILTER_NEAREST "nearest"
|
||||
#define VIDEO_FILTER_LINEAR "linear"
|
||||
|
||||
#define VIDEO_FILTER_DEFAULT VIDEO_FILTER_NEAREST
|
||||
|
||||
void CRenderVideoSettings::Reset()
|
||||
{
|
||||
m_scalingMethod = SCALINGMETHOD::AUTO;
|
||||
m_stretchMode = STRETCHMODE::Normal;
|
||||
m_rotationDegCCW = 0;
|
||||
m_shaderPreset.clear();
|
||||
m_pixelPath.clear();
|
||||
}
|
||||
|
||||
bool CRenderVideoSettings::operator==(const CRenderVideoSettings& rhs) const
|
||||
{
|
||||
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
|
||||
{
|
||||
if (m_shaderPreset < rhs.m_shaderPreset)
|
||||
return true;
|
||||
if (m_shaderPreset > rhs.m_shaderPreset)
|
||||
return false;
|
||||
|
||||
if (m_scalingMethod < rhs.m_scalingMethod)
|
||||
return true;
|
||||
if (m_scalingMethod > rhs.m_scalingMethod)
|
||||
|
|
@ -55,6 +66,13 @@ bool CRenderVideoSettings::operator<(const CRenderVideoSettings& rhs) 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)
|
||||
{
|
||||
case SCALINGMETHOD::NEAREST:
|
||||
|
|
@ -73,17 +91,33 @@ void CRenderVideoSettings::SetVideoFilter(const std::string& videoFilter)
|
|||
if (videoFilter == VIDEO_FILTER_NEAREST)
|
||||
{
|
||||
m_scalingMethod = SCALINGMETHOD::NEAREST;
|
||||
ResetShaderPreset();
|
||||
}
|
||||
else if (videoFilter == VIDEO_FILTER_LINEAR)
|
||||
{
|
||||
m_scalingMethod = SCALINGMETHOD::LINEAR;
|
||||
ResetShaderPreset();
|
||||
}
|
||||
else
|
||||
{
|
||||
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()
|
||||
{
|
||||
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; }
|
||||
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; }
|
||||
void SetRenderRotation(unsigned int rotationDegCCW) { m_rotationDegCCW = rotationDegCCW; }
|
||||
|
||||
|
|
@ -51,9 +55,12 @@ public:
|
|||
void ResetPixels();
|
||||
|
||||
private:
|
||||
bool UsesShaderPreset() const;
|
||||
|
||||
SCALINGMETHOD m_scalingMethod;
|
||||
STRETCHMODE m_stretchMode;
|
||||
unsigned int m_rotationDegCCW;
|
||||
std::string m_shaderPreset;
|
||||
std::string m_pixelPath;
|
||||
};
|
||||
} // namespace RETRO
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "cores/RetroPlayer/buffers/IRenderBufferPool.h"
|
||||
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||
#include "cores/RetroPlayer/rendering/RenderUtils.h"
|
||||
#include "cores/RetroPlayer/shaders/IShaderPreset.h"
|
||||
#include "utils/log.h"
|
||||
|
||||
using namespace KODI;
|
||||
|
|
@ -23,7 +24,11 @@ using namespace RETRO;
|
|||
CRPBaseRenderer::CRPBaseRenderer(const CRenderSettings& renderSettings,
|
||||
CRenderContext& context,
|
||||
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);
|
||||
}
|
||||
|
|
@ -37,7 +42,22 @@ CRPBaseRenderer::~CRPBaseRenderer()
|
|||
|
||||
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)
|
||||
|
|
@ -132,6 +152,15 @@ void CRPBaseRenderer::SetRenderRotation(unsigned int 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)
|
||||
{
|
||||
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
|
||||
m_rotatedDestCoords = CRenderUtils::ReorderDrawPoints(destRect, rotationDegCCW);
|
||||
|
||||
// Update video shader source size
|
||||
if (m_shaderPreset)
|
||||
m_shaderPreset->SetVideoSize(sourceWidth, sourceHeight);
|
||||
}
|
||||
|
||||
void CRPBaseRenderer::MarkDirty()
|
||||
|
|
@ -210,6 +243,21 @@ void CRPBaseRenderer::MarkDirty()
|
|||
// 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)
|
||||
{
|
||||
if (!m_bConfigured)
|
||||
|
|
@ -219,6 +267,8 @@ void CRPBaseRenderer::PreRender(bool clear)
|
|||
if (clear)
|
||||
m_context.Clear(m_context.UseLimitedColor() ? UTILS::COLOR::LIMITED_BLACK
|
||||
: UTILS::COLOR::BLACK);
|
||||
|
||||
// ManageRenderArea(*m_renderBuffer);
|
||||
}
|
||||
|
||||
void CRPBaseRenderer::PostRender()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ extern "C"
|
|||
|
||||
namespace KODI
|
||||
{
|
||||
namespace SHADER
|
||||
{
|
||||
class IShaderPreset;
|
||||
}
|
||||
|
||||
namespace RETRO
|
||||
{
|
||||
class CRenderContext;
|
||||
|
|
@ -67,6 +72,7 @@ public:
|
|||
void SetScalingMethod(SCALINGMETHOD method);
|
||||
void SetStretchMode(STRETCHMODE stretchMode);
|
||||
void SetRenderRotation(unsigned int rotationDegCCW);
|
||||
void SetShaderPreset(const std::string& presetPath);
|
||||
void SetPixels(const std::string& pixelPath);
|
||||
|
||||
// Rendering properties
|
||||
|
|
@ -95,6 +101,13 @@ protected:
|
|||
CRect m_sourceRect;
|
||||
std::array<CPoint, 4> m_rotatedDestCoords{};
|
||||
|
||||
// Video shaders
|
||||
void Updateshaders();
|
||||
std::unique_ptr<SHADER::IShaderPreset> m_shaderPreset;
|
||||
|
||||
bool m_bShadersNeedUpdate;
|
||||
bool m_bUseShaderPreset;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Calculate driven dimensions
|
||||
|
|
|
|||
|
|
@ -66,7 +66,14 @@ CRenderBufferPoolGuiTexture::CRenderBufferPoolGuiTexture(SCALINGMETHOD scalingMe
|
|||
|
||||
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 */)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@
|
|||
#include "cores/RetroPlayer/rendering/RenderContext.h"
|
||||
#include "cores/RetroPlayer/rendering/RenderTranslator.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 "rendering/dx/RenderSystemDX.h"
|
||||
#include "utils/log.h"
|
||||
|
|
@ -60,7 +63,8 @@ CWinRenderBuffer::~CWinRenderBuffer()
|
|||
|
||||
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");
|
||||
return false;
|
||||
|
|
@ -73,7 +77,8 @@ bool CWinRenderBuffer::GetTexture(uint8_t*& data, unsigned int& stride)
|
|||
{
|
||||
// Scale and upload texture
|
||||
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");
|
||||
return false;
|
||||
|
|
@ -87,7 +92,7 @@ bool CWinRenderBuffer::GetTexture(uint8_t*& data, unsigned int& stride)
|
|||
|
||||
bool CWinRenderBuffer::ReleaseTexture()
|
||||
{
|
||||
if (!m_intermediateTarget->UnlockRect(0))
|
||||
if (!m_intermediateTarget->GetTexture().UnlockRect(0))
|
||||
{
|
||||
CLog::Log(LOGERROR, "WinRenderer: Failed to unlock swtarget texture");
|
||||
return false;
|
||||
|
|
@ -110,7 +115,8 @@ bool CWinRenderBuffer::UploadTexture()
|
|||
// Create intermediate texture
|
||||
if (!m_intermediateTarget)
|
||||
{
|
||||
m_intermediateTarget.reset(new CD3DTexture);
|
||||
m_intermediateTarget =
|
||||
std::make_unique<SHADER::CShaderTextureDX>(std::make_shared<CD3DTexture>());
|
||||
if (!CreateTexture())
|
||||
{
|
||||
m_intermediateTarget.reset();
|
||||
|
|
@ -176,6 +182,13 @@ CWinRenderBufferPool::CWinRenderBufferPool()
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +210,7 @@ bool CWinRenderBufferPool::ConfigureDX()
|
|||
return true;
|
||||
}
|
||||
|
||||
CRPWinOutputShader* CWinRenderBufferPool::GetShader(SCALINGMETHOD scalingMethod) const
|
||||
SHADER::CRPWinOutputShader* CWinRenderBufferPool::GetShader(SCALINGMETHOD scalingMethod) const
|
||||
{
|
||||
auto it = m_outputShaders.find(scalingMethod);
|
||||
|
||||
|
|
@ -221,7 +234,9 @@ void CWinRenderBufferPool::CompileOutputShaders()
|
|||
{
|
||||
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))
|
||||
m_outputShaders[scalingMethod] = std::move(outputShader);
|
||||
else
|
||||
|
|
@ -237,6 +252,8 @@ CRPWinRenderer::CRPWinRenderer(const CRenderSettings& renderSettings,
|
|||
std::shared_ptr<IRenderBufferPool> bufferPool)
|
||||
: CRPBaseRenderer(renderSettings, context, std::move(bufferPool))
|
||||
{
|
||||
// Initialize CRPBaseRenderer fields
|
||||
m_shaderPreset = std::make_unique<SHADER::CShaderPresetDX>(m_context);
|
||||
}
|
||||
|
||||
bool CRPWinRenderer::ConfigureInternal()
|
||||
|
|
@ -280,26 +297,65 @@ void CRPWinRenderer::Render(CD3DTexture& target)
|
|||
const CPoint destPoints[4] = {m_rotatedDestCoords[0], m_rotatedDestCoords[1],
|
||||
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();
|
||||
if (intermediateTarget != nullptr)
|
||||
//! @todo Orientation?
|
||||
/*
|
||||
CPoint destPoints[4];
|
||||
// select destination rectangle
|
||||
if (m_renderOrientation)
|
||||
{
|
||||
CRect viewPort;
|
||||
m_context.GetViewPort(viewPort);
|
||||
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 };
|
||||
}
|
||||
*/
|
||||
|
||||
// Pick appropriate output shader depending on the scaling method of the renderer
|
||||
SCALINGMETHOD scalingMethod = m_renderSettings.VideoSettings().GetScalingMethod();
|
||||
SHADER::CShaderTextureDXRef targetTexture{target};
|
||||
|
||||
CWinRenderBufferPool* bufferPool = static_cast<CWinRenderBufferPool*>(m_bufferPool.get());
|
||||
CRPWinOutputShader* outputShader = bufferPool->GetShader(scalingMethod);
|
||||
// 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();
|
||||
|
||||
// Use the picked output shader to render to the target
|
||||
if (outputShader != nullptr)
|
||||
{
|
||||
outputShader->Render(*intermediateTarget, m_sourceRect, destPoints, viewPort, &target,
|
||||
m_context.UseLimitedColor() ? 1 : 0);
|
||||
}
|
||||
CRect viewPort;
|
||||
m_context.GetViewPort(viewPort);
|
||||
|
||||
// Pick appropriate output shader depending on the scaling method of the renderer
|
||||
SCALINGMETHOD scalingMethod = m_renderSettings.VideoSettings().GetScalingMethod();
|
||||
|
||||
CWinRenderBufferPool* bufferPool = static_cast<CWinRenderBufferPool*>(m_bufferPool.get());
|
||||
SHADER::CRPWinOutputShader* outputShader = bufferPool->GetShader(scalingMethod);
|
||||
|
||||
// Use the picked output shader to render to the target
|
||||
if (outputShader != nullptr)
|
||||
{
|
||||
outputShader->Render(intermediateTarget, m_sourceRect, destPoints, viewPort, target,
|
||||
m_context.UseLimitedColor() ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
#include "cores/RetroPlayer/buffers/BaseRenderBufferPool.h"
|
||||
#include "cores/RetroPlayer/buffers/video/RenderBufferSysMem.h"
|
||||
#include "cores/RetroPlayer/process/RPProcessInfo.h"
|
||||
#include "cores/RetroPlayer/shaders/windows/RPWinOutputShader.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
|
@ -24,6 +26,11 @@ struct SwsContext;
|
|||
|
||||
namespace KODI
|
||||
{
|
||||
namespace SHADER
|
||||
{
|
||||
class CShaderTextureDX;
|
||||
} // namespace SHADER
|
||||
|
||||
namespace RETRO
|
||||
{
|
||||
class CRenderContext;
|
||||
|
|
@ -51,7 +58,7 @@ public:
|
|||
// implementation of IRenderBuffer via CRenderBufferSysMem
|
||||
bool UploadTexture() override;
|
||||
|
||||
CD3DTexture* GetTarget() { return m_intermediateTarget.get(); }
|
||||
SHADER::CShaderTextureDX* GetTarget() { return m_intermediateTarget.get(); }
|
||||
|
||||
private:
|
||||
bool CreateTexture();
|
||||
|
|
@ -71,7 +78,7 @@ private:
|
|||
const DXGI_FORMAT m_targetDxFormat;
|
||||
|
||||
AVPixelFormat m_targetPixFormat;
|
||||
std::unique_ptr<CD3DTexture> m_intermediateTarget;
|
||||
std::unique_ptr<SHADER::CShaderTextureDX> m_intermediateTarget;
|
||||
|
||||
SwsContext* m_swsContext = nullptr;
|
||||
};
|
||||
|
|
@ -90,7 +97,7 @@ public:
|
|||
|
||||
// DirectX interface
|
||||
bool ConfigureDX();
|
||||
CRPWinOutputShader* GetShader(SCALINGMETHOD scalingMethod) const;
|
||||
SHADER::CRPWinOutputShader* GetShader(SCALINGMETHOD scalingMethod) const;
|
||||
|
||||
private:
|
||||
static const std::vector<SCALINGMETHOD>& GetScalingMethods();
|
||||
|
|
@ -98,7 +105,7 @@ private:
|
|||
void CompileOutputShaders();
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
const std::set<AddonType> lookAndFeelTypes = {
|
||||
AddonType::SKIN,
|
||||
AddonType::SCREENSAVER,
|
||||
|
|
@ -75,9 +76,11 @@ const std::set<AddonType> lookAndFeelTypes = {
|
|||
const std::set<AddonType> gameTypes = {
|
||||
AddonType::GAME_CONTROLLER,
|
||||
AddonType::GAMEDLL,
|
||||
AddonType::SHADERDLL,
|
||||
AddonType::GAME,
|
||||
AddonType::RESOURCE_GAMES,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static bool IsInfoProviderType(AddonType type)
|
||||
{
|
||||
|
|
@ -127,9 +130,15 @@ static bool IsGameResource(const AddonPtr& addon)
|
|||
|
||||
static bool IsGameSupportAddon(const AddonPtr& addon)
|
||||
{
|
||||
return addon->Type() == AddonType::GAMEDLL &&
|
||||
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
|
||||
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
|
||||
if (addon->Type() == AddonType::GAMEDLL &&
|
||||
!std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
|
||||
!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)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "controllers/Controller.h"
|
||||
#include "controllers/ControllerManager.h"
|
||||
#include "cores/RetroPlayer/shaders/ShaderPresetFactory.h"
|
||||
#include "games/GameSettings.h"
|
||||
#include "games/GameUtils.h"
|
||||
#include "games/agents/input/AgentInput.h"
|
||||
|
|
@ -22,12 +23,14 @@ CGameServices::CGameServices(CControllerManager& controllerManager,
|
|||
RETRO::CGUIGameRenderManager& renderManager,
|
||||
PERIPHERALS::CPeripherals& peripheralManager,
|
||||
const CProfileManager& profileManager,
|
||||
CInputManager& inputManager)
|
||||
CInputManager& inputManager,
|
||||
ADDON::CAddonMgr& addons)
|
||||
: m_controllerManager(controllerManager),
|
||||
m_gameRenderManager(renderManager),
|
||||
m_profileManager(profileManager),
|
||||
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
|
||||
m_initializationTask =
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
class CInputManager;
|
||||
class CProfileManager;
|
||||
|
||||
namespace ADDON
|
||||
{
|
||||
class CAddonMgr;
|
||||
} // namespace ADDON
|
||||
|
||||
namespace PERIPHERALS
|
||||
{
|
||||
class CPeripherals;
|
||||
|
|
@ -29,6 +34,11 @@ namespace RETRO
|
|||
class CGUIGameRenderManager;
|
||||
}
|
||||
|
||||
namespace SHADER
|
||||
{
|
||||
class CShaderPresetFactory;
|
||||
}
|
||||
|
||||
namespace GAME
|
||||
{
|
||||
class CAgentInput;
|
||||
|
|
@ -45,7 +55,8 @@ public:
|
|||
RETRO::CGUIGameRenderManager& renderManager,
|
||||
PERIPHERALS::CPeripherals& peripheralManager,
|
||||
const CProfileManager& profileManager,
|
||||
CInputManager& inputManager);
|
||||
CInputManager& inputManager,
|
||||
ADDON::CAddonMgr& addons);
|
||||
~CGameServices();
|
||||
|
||||
ControllerPtr GetController(const std::string& controllerId);
|
||||
|
|
@ -73,6 +84,8 @@ public:
|
|||
|
||||
CAgentInput& AgentInput() { return *m_agentInput; }
|
||||
|
||||
SHADER::CShaderPresetFactory& VideoShaders() { return *m_videoShaders; }
|
||||
|
||||
/*!
|
||||
* \brief Called when an add-on repo is installed
|
||||
*
|
||||
|
|
@ -90,6 +103,7 @@ private:
|
|||
// Game services
|
||||
std::unique_ptr<CGameSettings> m_gameSettings;
|
||||
std::unique_ptr<CAgentInput> m_agentInput;
|
||||
std::unique_ptr<SHADER::CShaderPresetFactory> m_videoShaders;
|
||||
|
||||
// Game threads
|
||||
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_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
|
||||
constexpr unsigned int CONTROL_VIDEO_HEADING = 10810;
|
||||
constexpr unsigned int CONTROL_VIDEO_THUMBS = 10811;
|
||||
|
|
|
|||
|
|
@ -8,18 +8,32 @@
|
|||
|
||||
#include "DialogGameVideoFilter.h"
|
||||
|
||||
#include "ServiceBroker.h"
|
||||
#include "URL.h"
|
||||
#include "cores/RetroPlayer/guibridge/GUIGameVideoHandle.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/WindowIDs.h"
|
||||
#include "settings/GameSettings.h"
|
||||
#include "settings/MediaSettings.h"
|
||||
#include "utils/StringUtils.h"
|
||||
#include "utils/URIUtils.h"
|
||||
#include "utils/Variant.h"
|
||||
#include "utils/XBMCTinyXML.h"
|
||||
#include "utils/log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace KODI;
|
||||
using namespace GAME;
|
||||
|
||||
#define PRESETS_ADDON_NAME "game.shader.presets"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ScalingMethodProperties
|
||||
|
|
@ -50,6 +64,7 @@ void CDialogGameVideoFilter::PreInit()
|
|||
{
|
||||
m_items.Clear();
|
||||
|
||||
InitScalingMethods();
|
||||
InitVideoFilters();
|
||||
|
||||
if (m_items.Size() == 0)
|
||||
|
|
@ -61,7 +76,7 @@ void CDialogGameVideoFilter::PreInit()
|
|||
m_bHasDescription = false;
|
||||
}
|
||||
|
||||
void CDialogGameVideoFilter::InitVideoFilters()
|
||||
void CDialogGameVideoFilter::InitScalingMethods()
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (const auto& item : m_items)
|
||||
|
|
@ -100,7 +206,7 @@ void CDialogGameVideoFilter::OnItemFocus(unsigned int index)
|
|||
std::string description;
|
||||
GetProperties(*item, videoFilter, description);
|
||||
|
||||
CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||
::CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||
|
||||
if (gameSettings.VideoFilter() != videoFilter)
|
||||
{
|
||||
|
|
@ -120,7 +226,7 @@ void CDialogGameVideoFilter::OnItemFocus(unsigned int index)
|
|||
|
||||
unsigned int CDialogGameVideoFilter::GetFocusedItem() const
|
||||
{
|
||||
CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||
::CGameSettings& gameSettings = CMediaSettings::GetInstance().GetCurrentGameSettings();
|
||||
|
||||
for (int i = 0; i < m_items.Size(); i++)
|
||||
{
|
||||
|
|
@ -148,6 +254,11 @@ bool CDialogGameVideoFilter::OnClickAction()
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string CDialogGameVideoFilter::GetLocalizedString(uint32_t code)
|
||||
{
|
||||
return g_localizeStrings.GetAddonString(PRESETS_ADDON_NAME, code);
|
||||
}
|
||||
|
||||
void CDialogGameVideoFilter::GetProperties(const CFileItem& item,
|
||||
std::string& videoFilter,
|
||||
std::string& description)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ protected:
|
|||
bool OnClickAction() override;
|
||||
|
||||
private:
|
||||
void InitScalingMethods();
|
||||
void InitVideoFilters();
|
||||
|
||||
static void GetProperties(const CFileItem& item,
|
||||
|
|
@ -44,6 +45,15 @@ private:
|
|||
|
||||
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
|
||||
bool m_bHasDescription = false;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue