gamescope/src/reshade_effect_manager.cpp
2023-09-24 08:25:57 +01:00

1841 lines
72 KiB
C++

#include <cstring>
#include <variant>
#include "reshade_effect_manager.hpp"
#include "log.hpp"
#include "steamcompmgr.hpp"
#include "effect_parser.hpp"
#include "effect_codegen.hpp"
#include "effect_preprocessor.hpp"
#include "reshade_api_format.hpp"
#include <stb_image.h>
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
const char *homedir;
static std::string GetLocalUsrDir()
{
const char *homedir = nullptr;
if ((homedir = getenv("HOME")) == nullptr)
homedir = getpwuid(getuid())->pw_dir;
return std::string(homedir) + "/.local";
}
static std::string GetUsrDir()
{
return "/usr";
}
static LogScope reshade_log("gamescope_reshade");
///////////////
// Uniforms
///////////////
class ReshadeUniform
{
public:
ReshadeUniform(const reshadefx::uniform_info& info);
virtual ~ReshadeUniform() {};
virtual void update(void* mappedBuffer) = 0;
protected:
void copy(void* mappedBuffer, const void* data, size_t size);
template <typename T>
void copy(void* mappedBuffer, const T* thing);
reshadefx::uniform_info m_info;
};
class FrameTimeUniform : public ReshadeUniform
{
public:
FrameTimeUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~FrameTimeUniform();
private:
std::chrono::time_point<std::chrono::high_resolution_clock> lastFrame;
};
class FrameCountUniform : public ReshadeUniform
{
public:
FrameCountUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~FrameCountUniform();
private:
int32_t count = 0;
};
class DateUniform : public ReshadeUniform
{
public:
DateUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~DateUniform();
};
class TimerUniform : public ReshadeUniform
{
public:
TimerUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~TimerUniform();
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start;
};
class PingPongUniform : public ReshadeUniform
{
public:
PingPongUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~PingPongUniform();
private:
std::chrono::time_point<std::chrono::high_resolution_clock> lastFrame;
float min = 0.0f;
float max = 0.0f;
float stepMin = 0.0f;
float stepMax = 0.0f;
float smoothing = 0.0f;
float currentValue[2] = {0.0f, 1.0f};
};
class RandomUniform : public ReshadeUniform
{
public:
RandomUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~RandomUniform();
private:
int max = 0;
int min = 0;
};
class KeyUniform : public ReshadeUniform
{
public:
KeyUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~KeyUniform();
};
class MouseButtonUniform : public ReshadeUniform
{
public:
MouseButtonUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~MouseButtonUniform();
};
class MousePointUniform : public ReshadeUniform
{
public:
MousePointUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~MousePointUniform();
};
class MouseDeltaUniform : public ReshadeUniform
{
public:
MouseDeltaUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~MouseDeltaUniform();
};
class DepthUniform : public ReshadeUniform
{
public:
DepthUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~DepthUniform();
};
class DataUniform : public ReshadeUniform
{
public:
DataUniform(reshadefx::uniform_info uniformInfo);
virtual void update(void* mappedBuffer) override;
virtual ~DataUniform();
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
ReshadeUniform::ReshadeUniform(const reshadefx::uniform_info& info)
: m_info(info)
{
}
void ReshadeUniform::copy(void* mappedBuffer, const void* data, size_t size)
{
assert(size <= m_info.size);
std::memcpy(((uint8_t*)mappedBuffer) + m_info.offset, data, size);
}
template <typename T>
void ReshadeUniform::copy(void* mappedBuffer, const T* thing)
{
assert(m_info.type.array_length == 0 || m_info.type.array_length == 1);
uint32_t zero_data[16] = {};
switch (m_info.type.base)
{
case reshadefx::type::t_bool:
if (thing)
{
VkBool32 VkBool32_stuff[16];
for (uint32_t i = 0; i < m_info.type.components(); i++)
VkBool32_stuff[i] = !!thing[i];
copy(mappedBuffer, VkBool32_stuff, sizeof(VkBool32) * m_info.type.components());
}
else
{
if (m_info.has_initializer_value)
copy(mappedBuffer, (const void*)&m_info.initializer_value.as_uint[0], sizeof(VkBool32) * m_info.type.components() );
else
copy(mappedBuffer, (const void*)zero_data, sizeof(VkBool32) * m_info.type.components() );
}
break;
case reshadefx::type::t_int:
if (thing)
{
int32_t int32_t_stuff[16];
for (uint32_t i = 0; i < m_info.type.components(); i++)
int32_t_stuff[i] = thing[i];
copy(mappedBuffer, int32_t_stuff, sizeof(int32_t) * m_info.type.components());
}
else
{
if (m_info.has_initializer_value)
copy(mappedBuffer, (const void*)&m_info.initializer_value.as_int[0], sizeof(int32_t) * m_info.type.components() );
else
copy(mappedBuffer, (const void*)zero_data, sizeof(int32_t) * m_info.type.components() );
}
break;
case reshadefx::type::t_uint:
if (thing)
{
uint32_t uint32_t_stuff[16];
for (uint32_t i = 0; i < m_info.type.components(); i++)
uint32_t_stuff[i] = thing[i];
copy(mappedBuffer, uint32_t_stuff, sizeof(uint32_t) * m_info.type.components());
}
else
{
if (m_info.has_initializer_value)
copy(mappedBuffer, (const void*)&m_info.initializer_value.as_uint[0], sizeof(uint32_t) * m_info.type.components() );
else
copy(mappedBuffer, (const void*)zero_data, sizeof(uint32_t) * m_info.type.components() );
}
break;
case reshadefx::type::t_float:
if (thing)
{
float float_stuff[16];
for (uint32_t i = 0; i < m_info.type.components(); i++)
float_stuff[i] = thing[i];
copy(mappedBuffer, float_stuff, sizeof(float) * m_info.type.components());
}
else
{
if (m_info.has_initializer_value)
copy(mappedBuffer, (const void*)&m_info.initializer_value.as_float[0], sizeof(float) * m_info.type.components() );
else
copy(mappedBuffer, (const void*)zero_data, sizeof(float) * m_info.type.components() );
}
break;
default:
reshade_log.errorf("Unknown uniform type!");
break;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
FrameTimeUniform::FrameTimeUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
lastFrame = std::chrono::high_resolution_clock::now();
}
void FrameTimeUniform::update(void* mappedBuffer)
{
auto currentFrame = std::chrono::high_resolution_clock::now();
std::chrono::duration<float, std::milli> duration = currentFrame - lastFrame;
lastFrame = currentFrame;
float frametime = duration.count();
copy(mappedBuffer, &frametime);
}
FrameTimeUniform::~FrameTimeUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
FrameCountUniform::FrameCountUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void FrameCountUniform::update(void* mappedBuffer)
{
copy(mappedBuffer, &count);
count++;
}
FrameCountUniform::~FrameCountUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
DateUniform::DateUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void DateUniform::update(void* mappedBuffer)
{
auto now = std::chrono::system_clock::now();
std::time_t nowC = std::chrono::system_clock::to_time_t(now);
struct tm* currentTime = std::localtime(&nowC);
float year = 1900.0f + static_cast<float>(currentTime->tm_year);
float month = 1.0f + static_cast<float>(currentTime->tm_mon);
float day = static_cast<float>(currentTime->tm_mday);
float seconds = static_cast<float>((currentTime->tm_hour * 60 + currentTime->tm_min) * 60 + currentTime->tm_sec);
float date[] = {year, month, day, seconds};
copy(mappedBuffer, date);
}
DateUniform::~DateUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
TimerUniform::TimerUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
start = std::chrono::high_resolution_clock::now();
}
void TimerUniform::update(void* mappedBuffer)
{
auto currentFrame = std::chrono::high_resolution_clock::now();
std::chrono::duration<float, std::milli> duration = currentFrame - start;
float timer = duration.count();
copy(mappedBuffer, &timer);
}
TimerUniform::~TimerUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
PingPongUniform::PingPongUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
if (auto minAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "min"; });
minAnnotation != uniformInfo.annotations.end())
{
min = minAnnotation->type.is_floating_point() ? minAnnotation->value.as_float[0] : static_cast<float>(minAnnotation->value.as_int[0]);
}
if (auto maxAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "max"; });
maxAnnotation != uniformInfo.annotations.end())
{
max = maxAnnotation->type.is_floating_point() ? maxAnnotation->value.as_float[0] : static_cast<float>(maxAnnotation->value.as_int[0]);
}
if (auto smoothingAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "smoothing"; });
smoothingAnnotation != uniformInfo.annotations.end())
{
smoothing = smoothingAnnotation->type.is_floating_point() ? smoothingAnnotation->value.as_float[0]
: static_cast<float>(smoothingAnnotation->value.as_int[0]);
}
if (auto stepAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "step"; });
stepAnnotation != uniformInfo.annotations.end())
{
stepMin =
stepAnnotation->type.is_floating_point() ? stepAnnotation->value.as_float[0] : static_cast<float>(stepAnnotation->value.as_int[0]);
stepMax =
stepAnnotation->type.is_floating_point() ? stepAnnotation->value.as_float[1] : static_cast<float>(stepAnnotation->value.as_int[1]);
}
lastFrame = std::chrono::high_resolution_clock::now();
}
void PingPongUniform::update(void* mappedBuffer)
{
auto currentFrame = std::chrono::high_resolution_clock::now();
std::chrono::duration<float, std::ratio<1>> frameTime = currentFrame - lastFrame;
float increment = stepMax == 0 ? stepMin : (stepMin + std::fmod(static_cast<float>(std::rand()), stepMax - stepMin + 1.0f));
if (currentValue[1] >= 0)
{
increment = std::max(increment - std::max(0.0f, smoothing - (max - currentValue[0])), 0.05f);
increment *= frameTime.count();
if ((currentValue[0] += increment) >= max)
{
currentValue[0] = max, currentValue[1] = -1.0f;
}
}
else
{
increment = std::max(increment - std::max(0.0f, smoothing - (currentValue[0] - min)), 0.05f);
increment *= frameTime.count();
if ((currentValue[0] -= increment) <= min)
{
currentValue[0] = min, currentValue[1] = 1.0f;
}
}
copy(mappedBuffer, currentValue);
}
PingPongUniform::~PingPongUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
RandomUniform::RandomUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
if (auto minAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "min"; });
minAnnotation != uniformInfo.annotations.end())
{
min = minAnnotation->type.is_integral() ? minAnnotation->value.as_int[0] : static_cast<int>(minAnnotation->value.as_float[0]);
}
if (auto maxAnnotation =
std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "max"; });
maxAnnotation != uniformInfo.annotations.end())
{
max = maxAnnotation->type.is_integral() ? maxAnnotation->value.as_int[0] : static_cast<int>(maxAnnotation->value.as_float[0]);
}
}
void RandomUniform::update(void* mappedBuffer)
{
int32_t value = min + (std::rand() % (max - min + 1));
copy(mappedBuffer, &value);
}
RandomUniform::~RandomUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
KeyUniform::KeyUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void KeyUniform::update(void* mappedBuffer)
{
VkBool32 keyDown = VK_FALSE; // TODO
copy(mappedBuffer, &keyDown);
}
KeyUniform::~KeyUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
MouseButtonUniform::MouseButtonUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void MouseButtonUniform::update(void* mappedBuffer)
{
VkBool32 keyDown = VK_FALSE; // TODO
copy(mappedBuffer, &keyDown);
}
MouseButtonUniform::~MouseButtonUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
MousePointUniform::MousePointUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void MousePointUniform::update(void* mappedBuffer)
{
MouseCursor *cursor = steamcompmgr_get_current_cursor();
int32_t point[2] = {0, 0};
if (cursor)
{
point[0] = cursor->x();
point[1] = cursor->y();
}
copy(mappedBuffer, point);
}
MousePointUniform::~MousePointUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
MouseDeltaUniform::MouseDeltaUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void MouseDeltaUniform::update(void* mappedBuffer)
{
float delta[2] = {0.0f, 0.0f}; // TODO
copy(mappedBuffer, delta);
}
MouseDeltaUniform::~MouseDeltaUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
DepthUniform::DepthUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void DepthUniform::update(void* mappedBuffer)
{
VkBool32 hasDepth = VK_FALSE;
copy(mappedBuffer, &hasDepth);
}
DepthUniform::~DepthUniform()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
DataUniform::DataUniform(reshadefx::uniform_info uniformInfo)
: ReshadeUniform(uniformInfo)
{
}
void DataUniform::update(void* mappedBuffer)
{
copy<uint32_t>(mappedBuffer, nullptr);
}
DataUniform::~DataUniform()
{
}
static std::vector<std::shared_ptr<ReshadeUniform>> createReshadeUniforms(const reshadefx::module& module)
{
std::vector<std::shared_ptr<ReshadeUniform>> uniforms;
for (auto& uniform : module.uniforms)
{
auto sourceAnnotation = std::find_if(uniform.annotations.begin(), uniform.annotations.end(), [](const auto& a) { return a.name == "source"; });
if (sourceAnnotation == uniform.annotations.end())
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new DataUniform(uniform)));
continue;
}
else
{
auto& source = sourceAnnotation->value.string_data;
if (source == "frametime")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new FrameTimeUniform(uniform)));
}
else if (source == "framecount")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new FrameCountUniform(uniform)));
}
else if (source == "date")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new DateUniform(uniform)));
}
else if (source == "timer")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new TimerUniform(uniform)));
}
else if (source == "pingpong")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new PingPongUniform(uniform)));
}
else if (source == "random")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new RandomUniform(uniform)));
}
else if (source == "key")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new KeyUniform(uniform)));
}
else if (source == "mousebutton")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new MouseButtonUniform(uniform)));
}
else if (source == "mousepoint")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new MousePointUniform(uniform)));
}
else if (source == "mousedelta")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new MouseDeltaUniform(uniform)));
}
else if (source == "bufready_depth")
{
uniforms.push_back(std::shared_ptr<ReshadeUniform>(new DepthUniform(uniform)));
}
else
{
reshade_log.errorf("Unknown uniform source: %s", source.c_str());
}
}
}
return uniforms;
}
//
static reshade::api::color_space ConvertToReshadeColorSpace(GamescopeAppTextureColorspace colorspace)
{
switch (colorspace)
{
default:
return reshade::api::color_space::unknown;
case GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR: /* Actually SRGB -> Linear... I should change this cause its confusing */
case GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB:
return reshade::api::color_space::srgb_nonlinear;
case GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB:
return reshade::api::color_space::extended_srgb_linear;
case GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ:
return reshade::api::color_space::hdr10_st2084;
}
}
static VkFormat ConvertReshadeFormat(reshadefx::texture_format texFormat)
{
switch (texFormat)
{
case reshadefx::texture_format::r8: return VK_FORMAT_R8_UNORM;
case reshadefx::texture_format::r16f: return VK_FORMAT_R16_SFLOAT;
case reshadefx::texture_format::r32f: return VK_FORMAT_R32_SFLOAT;
case reshadefx::texture_format::rg8: return VK_FORMAT_R8G8_UNORM;
case reshadefx::texture_format::rg16: return VK_FORMAT_R16G16_UNORM;
case reshadefx::texture_format::rg16f: return VK_FORMAT_R16G16_SFLOAT;
case reshadefx::texture_format::rg32f: return VK_FORMAT_R32G32_SFLOAT;
case reshadefx::texture_format::rgba8: return VK_FORMAT_R8G8B8A8_UNORM;
case reshadefx::texture_format::rgba16: return VK_FORMAT_R16G16B16A16_UNORM;
case reshadefx::texture_format::rgba16f: return VK_FORMAT_R16G16B16A16_SFLOAT;
case reshadefx::texture_format::rgba32f: return VK_FORMAT_R32G32B32A32_SFLOAT;
case reshadefx::texture_format::rgb10a2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
default:
reshade_log.errorf("Couldn't convert texture format: %d\n", static_cast<int>(texFormat));
return VK_FORMAT_UNDEFINED;
}
}
#if 0
static VkCompareOp ConvertReshadeCompareOp(reshadefx::pass_stencil_func compareOp)
{
switch (compareOp)
{
case reshadefx::pass_stencil_func::never: return VK_COMPARE_OP_NEVER;
case reshadefx::pass_stencil_func::less: return VK_COMPARE_OP_LESS;
case reshadefx::pass_stencil_func::equal: return VK_COMPARE_OP_EQUAL;
case reshadefx::pass_stencil_func::less_equal: return VK_COMPARE_OP_LESS_OR_EQUAL;
case reshadefx::pass_stencil_func::greater: return VK_COMPARE_OP_GREATER;
case reshadefx::pass_stencil_func::not_equal: return VK_COMPARE_OP_NOT_EQUAL;
case reshadefx::pass_stencil_func::greater_equal: return VK_COMPARE_OP_GREATER_OR_EQUAL;
case reshadefx::pass_stencil_func::always: return VK_COMPARE_OP_ALWAYS;
default: return VK_COMPARE_OP_ALWAYS;
}
}
static VkStencilOp ConvertReshadeStencilOp(reshadefx::pass_stencil_op stencilOp)
{
switch (stencilOp)
{
case reshadefx::pass_stencil_op::zero: return VK_STENCIL_OP_ZERO;
case reshadefx::pass_stencil_op::keep: return VK_STENCIL_OP_KEEP;
case reshadefx::pass_stencil_op::replace: return VK_STENCIL_OP_REPLACE;
case reshadefx::pass_stencil_op::increment_saturate: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case reshadefx::pass_stencil_op::decrement_saturate: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case reshadefx::pass_stencil_op::invert: return VK_STENCIL_OP_INVERT;
case reshadefx::pass_stencil_op::increment: return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case reshadefx::pass_stencil_op::decrement: return VK_STENCIL_OP_DECREMENT_AND_WRAP;
default: return VK_STENCIL_OP_KEEP;
}
}
#endif
static VkBlendOp ConvertReshadeBlendOp(reshadefx::pass_blend_op blendOp)
{
switch (blendOp)
{
case reshadefx::pass_blend_op::add: return VK_BLEND_OP_ADD;
case reshadefx::pass_blend_op::subtract: return VK_BLEND_OP_SUBTRACT;
case reshadefx::pass_blend_op::reverse_subtract: return VK_BLEND_OP_REVERSE_SUBTRACT;
case reshadefx::pass_blend_op::min: return VK_BLEND_OP_MIN;
case reshadefx::pass_blend_op::max: return VK_BLEND_OP_MAX;
default: return VK_BLEND_OP_ADD;
}
}
static VkBlendFactor ConvertReshadeBlendFactor(reshadefx::pass_blend_factor blendFactor)
{
switch (blendFactor)
{
case reshadefx::pass_blend_factor::zero: return VK_BLEND_FACTOR_ZERO;
case reshadefx::pass_blend_factor::one: return VK_BLEND_FACTOR_ONE;
case reshadefx::pass_blend_factor::source_color: return VK_BLEND_FACTOR_SRC_COLOR;
case reshadefx::pass_blend_factor::source_alpha: return VK_BLEND_FACTOR_SRC_ALPHA;
case reshadefx::pass_blend_factor::one_minus_source_color: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case reshadefx::pass_blend_factor::one_minus_source_alpha: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case reshadefx::pass_blend_factor::dest_alpha: return VK_BLEND_FACTOR_DST_ALPHA;
case reshadefx::pass_blend_factor::one_minus_dest_alpha: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case reshadefx::pass_blend_factor::dest_color: return VK_BLEND_FACTOR_DST_COLOR;
case reshadefx::pass_blend_factor::one_minus_dest_color: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
default: return VK_BLEND_FACTOR_ZERO;
}
}
static VkSamplerAddressMode ConvertReshadeAddressMode(reshadefx::texture_address_mode addressMode)
{
switch (addressMode)
{
case reshadefx::texture_address_mode::wrap: return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case reshadefx::texture_address_mode::mirror: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case reshadefx::texture_address_mode::clamp: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case reshadefx::texture_address_mode::border: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
}
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
static void ConvertReshadeFilter(const reshadefx::filter_mode& textureFilter, VkFilter& minFilter, VkFilter& magFilter, VkSamplerMipmapMode& mipmapMode)
{
switch (textureFilter)
{
case reshadefx::filter_mode::min_mag_mip_point:
minFilter = VK_FILTER_NEAREST;
magFilter = VK_FILTER_NEAREST;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
return;
case reshadefx::filter_mode::min_mag_point_mip_linear:
minFilter = VK_FILTER_NEAREST;
magFilter = VK_FILTER_NEAREST;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
return;
case reshadefx::filter_mode::min_point_mag_linear_mip_point:
minFilter = VK_FILTER_NEAREST;
magFilter = VK_FILTER_LINEAR;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
return;
case reshadefx::filter_mode::min_point_mag_mip_linear:
minFilter = VK_FILTER_NEAREST;
magFilter = VK_FILTER_LINEAR;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
return;
case reshadefx::filter_mode::min_linear_mag_mip_point:
minFilter = VK_FILTER_LINEAR;
magFilter = VK_FILTER_NEAREST;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
return;
case reshadefx::filter_mode::min_linear_mag_point_mip_linear:
minFilter = VK_FILTER_LINEAR;
magFilter = VK_FILTER_NEAREST;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
return;
case reshadefx::filter_mode::min_mag_linear_mip_point:
minFilter = VK_FILTER_LINEAR;
magFilter = VK_FILTER_LINEAR;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
return;
default:
case reshadefx::filter_mode::min_mag_mip_linear:
minFilter = VK_FILTER_LINEAR;
magFilter = VK_FILTER_LINEAR;
mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
return;
}
}
static uint32_t GetFormatBitDepth(VkFormat format)
{
switch (format)
{
default:
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SRGB:
return 8;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
return 6;
case VK_FORMAT_R16G16B16A16_SFLOAT:
case VK_FORMAT_R16G16B16A16_UNORM:
return 16;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
return 10;
}
}
ReshadeEffectPipeline::ReshadeEffectPipeline()
{
}
ReshadeEffectPipeline::~ReshadeEffectPipeline()
{
m_device->waitIdle();
for (auto& pipeline : m_pipelines)
m_device->vk.DestroyPipeline(m_device->device(), pipeline, nullptr);
m_pipelines.clear();
for (auto& sampler : m_samplers)
m_device->vk.DestroySampler(m_device->device(), sampler.sampler, nullptr);
m_samplers.clear();
m_uniforms.clear();
m_textures.clear();
m_rt = nullptr;
m_cmdBuffer = std::nullopt;
m_device->vk.DestroyBuffer(m_device->device(), m_buffer, nullptr);
m_device->vk.FreeMemory(m_device->device(), m_bufferMemory, nullptr);
m_mappedPtr = nullptr;
for (uint32_t i = 0; i < GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT; i++)
{
m_device->vk.FreeDescriptorSets(m_device->device(), m_descriptorPool, 1, &m_descriptorSets[i]);
m_device->vk.DestroyDescriptorSetLayout(m_device->device(), m_descriptorSetLayouts[i], nullptr);
}
m_device->vk.DestroyDescriptorPool(m_device->device(), m_descriptorPool, nullptr);
m_device->vk.DestroyPipelineLayout(m_device->device(), m_pipelineLayout, nullptr);
}
bool ReshadeEffectPipeline::init(CVulkanDevice *device, const ReshadeEffectKey &key)
{
m_key = key;
m_device = device;
VkPhysicalDeviceProperties deviceProperties;
device->vk.GetPhysicalDeviceProperties(device->physDev(), &deviceProperties);
reshadefx::preprocessor pp;
pp.add_macro_definition("__RESHADE__", std::to_string(INT_MAX));
pp.add_macro_definition("__RESHADE_PERFORMANCE_MODE__", "0");
pp.add_macro_definition("__VENDOR__", std::to_string(deviceProperties.vendorID));
pp.add_macro_definition("__DEVICE__", std::to_string(deviceProperties.deviceID));
pp.add_macro_definition("__RENDERER__", std::to_string(0x20000));
pp.add_macro_definition("__APPLICATION__", std::to_string(0x0));
pp.add_macro_definition("BUFFER_WIDTH", std::to_string(key.bufferWidth));
pp.add_macro_definition("BUFFER_HEIGHT", std::to_string(key.bufferHeight));
pp.add_macro_definition("BUFFER_RCP_WIDTH", "(1.0 / BUFFER_WIDTH)");
pp.add_macro_definition("BUFFER_RCP_HEIGHT", "(1.0 / BUFFER_HEIGHT)");
pp.add_macro_definition("BUFFER_COLOR_SPACE", std::to_string(static_cast<uint32_t>(ConvertToReshadeColorSpace(key.bufferColorSpace))));
pp.add_macro_definition("BUFFER_COLOR_BIT_DEPTH", std::to_string(GetFormatBitDepth(key.bufferFormat)));
pp.add_macro_definition("GAMESCOPE", "1");
pp.add_macro_definition("GAMESCOPE_SDR_ON_HDR_NITS", std::to_string(g_ColorMgmt.pending.flSDROnHDRBrightness));
std::string gamescope_reshade_share_path = "/share/gamescope/reshade";
std::string local_reshade_path = GetLocalUsrDir() + gamescope_reshade_share_path;
std::string global_reshade_path = GetUsrDir() + gamescope_reshade_share_path;
pp.add_include_path(local_reshade_path + "/Shaders");
pp.add_include_path(global_reshade_path + "/Shaders");
std::string local_shader_file_path = local_reshade_path + "/Shaders/" + key.path;
std::string global_shader_file_path = global_reshade_path + "/Shaders/" + key.path;
if (!pp.append_file(local_shader_file_path))
{
if (!pp.append_file(global_shader_file_path))
{
reshade_log.errorf("Failed to load reshade fx file: %s (%s or %s) - %s", key.path.c_str(), local_shader_file_path.c_str(), global_shader_file_path.c_str(), pp.errors().c_str());
return false;
}
}
std::string errors = pp.errors();
if (!errors.empty())
{
reshade_log.errorf("Failed to parse reshade fx shader module: %s", errors.c_str());
return false;
}
std::unique_ptr<reshadefx::codegen> codegen(reshadefx::create_codegen_spirv(
true /* vulkan semantics */, true /* debug info */, false /* uniforms to spec constants */, false /*flip vertex shader*/));
reshadefx::parser parser;
parser.parse(pp.output(), codegen.get());
errors = parser.errors();
if (!errors.empty())
{
reshade_log.errorf("Failed to parse reshade fx shader module: %s", errors.c_str());
return false;
}
m_module = std::make_unique<reshadefx::module>();
codegen->write_result(*m_module);
#if 0
FILE *f = fopen("test.spv", "wb");
fwrite(m_module->code.data(), 1, m_module->code.size(), f);
fclose(f);
#endif
if (m_module->techniques.size() <= key.techniqueIdx)
{
reshade_log.errorf("Invalid technique index");
return false;
}
auto& technique = m_module->techniques[key.techniqueIdx];
reshade_log.infof("Using technique: %s\n", technique.name.c_str());
// Allocate command buffers
{
VkCommandBufferAllocateInfo commandBufferAllocateInfo =
{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = device->generalCommandPool(),
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1
};
VkCommandBuffer cmdBuffer = VK_NULL_HANDLE;
VkResult result = device->vk.AllocateCommandBuffers(device->device(), &commandBufferAllocateInfo, &cmdBuffer);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkAllocateCommandBuffers failed");
return false;
}
m_cmdBuffer.emplace(device, cmdBuffer, device->generalQueue(), device->generalQueueFamily());
}
// Create Uniform Buffer
{
VkBufferCreateInfo bufferCreateInfo =
{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = m_module->total_uniform_size,
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
};
VkResult result = device->vk.CreateBuffer(device->device(), &bufferCreateInfo, nullptr, &m_buffer);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkCreateBuffer failed");
return false;
}
VkMemoryRequirements memRequirements;
device->vk.GetBufferMemoryRequirements(device->device(), m_buffer, &memRequirements);
uint32_t memTypeIndex = device->findMemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, memRequirements.memoryTypeBits);
assert(memTypeIndex != ~0u);
VkMemoryAllocateInfo allocInfo =
{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = memTypeIndex,
};
result = device->vk.AllocateMemory(device->device(), &allocInfo, nullptr, &m_bufferMemory);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkAllocateMemory failed");
return false;
}
device->vk.BindBufferMemory(device->device(), m_buffer, m_bufferMemory, 0);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkBindBufferMemory failed");
return false;
}
result = device->vk.MapMemory(device->device(), m_bufferMemory, 0, VK_WHOLE_SIZE, 0, &m_mappedPtr);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkMapMemory failed");
return false;
}
}
// Create Uniforms
m_uniforms = createReshadeUniforms(*m_module);
// Create Textures
{
m_rt = std::make_shared<CVulkanTexture>();
CVulkanTexture::createFlags flags;
flags.bSampled = true;
flags.bStorage = true;
flags.bColorAttachment = true;
bool ret = m_rt->BInit(m_key.bufferWidth, m_key.bufferHeight, 1, VulkanFormatToDRM(m_key.bufferFormat), flags, nullptr);
assert(ret);
}
for (const auto& tex : m_module->textures)
{
std::shared_ptr<CVulkanTexture> texture;
if (tex.semantic.empty())
{
texture = std::make_shared<CVulkanTexture>();
CVulkanTexture::createFlags flags;
flags.bSampled = true;
// Always need storage.
flags.bStorage = true;
if (tex.render_target)
flags.bColorAttachment = true;
// Not supported rn.
assert(tex.levels == 1);
assert(tex.type == reshadefx::texture_type::texture_2d);
bool ret = texture->BInit(tex.width, tex.height, tex.depth, VulkanFormatToDRM(ConvertReshadeFormat(tex.format)), flags, nullptr);
assert(ret);
}
if (const auto source = std::find_if(
tex.annotations.begin(), tex.annotations.end(), [](const auto& a) { return a.name == "source"; });
source != tex.annotations.end())
{
std::string filePath = local_reshade_path + "/Textures/" + source->value.string_data;
int w, h, channels;
unsigned char *data = stbi_load(filePath.c_str(), &w, &h, &channels, STBI_rgb_alpha);
if (!data)
{
filePath = global_reshade_path + "/Textures/" + source->value.string_data;
data = stbi_load(filePath.c_str(), &w, &h, &channels, STBI_rgb_alpha);
}
if (data)
{
uint8_t *pixels = data;
std::vector<uint8_t> resized_data;
if (w != (int)texture->width() || h != (int)texture->height())
{
resized_data.resize(texture->width() * texture->height() * 4);
stbir_resize_uint8(data, w, h, 0, resized_data.data(), texture->width(), texture->height(), 0, STBI_rgb_alpha);
w = texture->width();
h = texture->height();
pixels = resized_data.data();
}
size_t size = w * h * 4;
VkBufferCreateInfo bufferCreateInfo =
{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
};
VkBuffer scratchBuffer = VK_NULL_HANDLE;
VkResult result = device->vk.CreateBuffer(device->device(), &bufferCreateInfo, nullptr, &scratchBuffer);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create scratch buffer");
return false;
}
VkMemoryRequirements memRequirements;
device->vk.GetBufferMemoryRequirements(device->device(), scratchBuffer, &memRequirements);
uint32_t memTypeIndex = device->findMemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, memRequirements.memoryTypeBits);
assert(memTypeIndex != ~0u);
VkMemoryAllocateInfo allocInfo =
{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = memTypeIndex,
};
VkDeviceMemory scratchMemory = VK_NULL_HANDLE;
result = device->vk.AllocateMemory(device->device(), &allocInfo, nullptr, &scratchMemory);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkAllocateMemory failed");
return false;
}
device->vk.BindBufferMemory(device->device(), scratchBuffer, scratchMemory, 0);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkBindBufferMemory failed");
return false;
}
void *scratchPtr = nullptr;
result = device->vk.MapMemory(device->device(), scratchMemory, 0, VK_WHOLE_SIZE, 0, &scratchPtr);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkMapMemory failed");
return false;
}
memcpy(scratchPtr, pixels, size);
m_cmdBuffer->reset();
m_cmdBuffer->begin();
m_cmdBuffer->copyBufferToImage(scratchBuffer, 0, 0, texture);
device->submitInternal(&*m_cmdBuffer);
device->waitIdle(false);
free(data);
device->vk.DestroyBuffer(device->device(), scratchBuffer, nullptr);
device->vk.FreeMemory(device->device(), scratchMemory, nullptr);
}
}
else if (texture)
{
m_cmdBuffer->reset();
m_cmdBuffer->begin();
VkClearColorValue clearColor{};
VkImageSubresourceRange range =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
m_cmdBuffer->prepareDestImage(texture.get());
m_cmdBuffer->insertBarrier();
device->vk.CmdClearColorImage(m_cmdBuffer->rawBuffer(), texture->vkImage(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range);
m_cmdBuffer->markDirty(texture.get());
device->submitInternal(&*m_cmdBuffer);
device->waitIdle(false);
}
m_textures.emplace_back(std::move(texture));
}
// Create Samplers
{
for (const auto& sampler : m_module->samplers)
{
std::shared_ptr<CVulkanTexture> tex;
tex = findTexture(sampler.texture_name);
if (!tex)
{
reshade_log.errorf("Couldn't find texture with name: %s", sampler.texture_name.c_str());
}
VkFilter minFilter;
VkFilter magFilter;
VkSamplerMipmapMode mipmapMode;
ConvertReshadeFilter(sampler.filter, minFilter, magFilter, mipmapMode);
VkSamplerCreateInfo samplerCreateInfo;
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCreateInfo.pNext = nullptr;
samplerCreateInfo.flags = 0;
samplerCreateInfo.magFilter = magFilter;
samplerCreateInfo.minFilter = minFilter;
samplerCreateInfo.mipmapMode = mipmapMode;
samplerCreateInfo.addressModeU = ConvertReshadeAddressMode(sampler.address_u);
samplerCreateInfo.addressModeV = ConvertReshadeAddressMode(sampler.address_v);
samplerCreateInfo.addressModeW = ConvertReshadeAddressMode(sampler.address_w);
samplerCreateInfo.mipLodBias = sampler.lod_bias;
samplerCreateInfo.anisotropyEnable = VK_FALSE;
samplerCreateInfo.maxAnisotropy = 0;
samplerCreateInfo.compareEnable = VK_FALSE;
samplerCreateInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerCreateInfo.minLod = sampler.min_lod;
samplerCreateInfo.maxLod = sampler.max_lod;
samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerCreateInfo.unnormalizedCoordinates = VK_FALSE;
VkSampler vkSampler;
VkResult result = device->vk.CreateSampler(device->device(), &samplerCreateInfo, nullptr, &vkSampler);
if (result != VK_SUCCESS)
{
reshade_log.errorf("vkCreateSampler failed");
return false;
}
m_samplers.emplace_back(vkSampler, std::move(tex));
}
}
// Create Descriptor Set Layouts
{
VkDescriptorSetLayoutBinding layoutBinding;
layoutBinding.binding = 0;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
layoutBinding.pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo layoutCreateInfo;
layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutCreateInfo.pNext = nullptr;
layoutCreateInfo.flags = 0;
layoutCreateInfo.bindingCount = 1;
layoutCreateInfo.pBindings = &layoutBinding;
VkResult result = device->vk.CreateDescriptorSetLayout(device->device(), &layoutCreateInfo, nullptr, &m_descriptorSetLayouts[GAMESCOPE_RESHADE_DESCRIPTOR_SET_UBO]);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create descriptor set layout.");
return false;
}
}
{
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
for (uint32_t i = 0; i < m_module->samplers.size(); i++)
{
VkDescriptorSetLayoutBinding layoutBinding;
layoutBinding.binding = i;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
layoutBinding.pImmutableSamplers = nullptr;
layoutBindings.push_back(layoutBinding);
}
VkDescriptorSetLayoutCreateInfo layoutCreateInfo;
layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutCreateInfo.pNext = nullptr;
layoutCreateInfo.flags = 0;
layoutCreateInfo.bindingCount = layoutBindings.size();
layoutCreateInfo.pBindings = layoutBindings.data();
VkResult result = device->vk.CreateDescriptorSetLayout(device->device(), &layoutCreateInfo, nullptr, &m_descriptorSetLayouts[GAMESCOPE_RESHADE_DESCRIPTOR_SET_SAMPLED_IMAGES]);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create descriptor set layout.");
return false;
}
}
{
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
for (uint32_t i = 0; i < m_module->samplers.size(); i++)
{
VkDescriptorSetLayoutBinding layoutBinding;
layoutBinding.binding = i;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
layoutBinding.pImmutableSamplers = nullptr;
layoutBindings.push_back(layoutBinding);
}
VkDescriptorSetLayoutCreateInfo layoutCreateInfo;
layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutCreateInfo.pNext = nullptr;
layoutCreateInfo.flags = 0;
layoutCreateInfo.bindingCount = layoutBindings.size();
layoutCreateInfo.pBindings = layoutBindings.data();
VkResult result = device->vk.CreateDescriptorSetLayout(device->device(), &layoutCreateInfo, nullptr, &m_descriptorSetLayouts[GAMESCOPE_RESHADE_DESCRIPTOR_SET_STORAGE_IMAGES]);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create descriptor set layout.");
return false;
}
}
{
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.setLayoutCount = uint32_t(std::size(m_descriptorSetLayouts)),
.pSetLayouts = m_descriptorSetLayouts,
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
};
VkResult result = device->vk.CreatePipelineLayout(device->device(), &pipelineLayoutCreateInfo, nullptr, &m_pipelineLayout);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create pipeline layout.");
return false;
}
}
{
VkDescriptorPoolSize descriptorPoolSizes[] =
{
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT * 1u) },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT * m_module->samplers.size()) },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, uint32_t(GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT * m_module->storages.size()) },
};
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo;
descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolCreateInfo.pNext = nullptr;
descriptorPoolCreateInfo.flags = 0;
descriptorPoolCreateInfo.maxSets = GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT;
descriptorPoolCreateInfo.poolSizeCount = std::size(descriptorPoolSizes);
descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes;
VkResult result = device->vk.CreateDescriptorPool(device->device(), &descriptorPoolCreateInfo, nullptr, &m_descriptorPool);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to create descriptor pool.");
return false;
}
}
for (uint32_t i = 0; i < GAMESCOPE_RESHADE_DESCRIPTOR_SET_COUNT; i++)
{
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo;
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocateInfo.pNext = nullptr;
descriptorSetAllocateInfo.descriptorPool = m_descriptorPool;
descriptorSetAllocateInfo.descriptorSetCount = 1;
descriptorSetAllocateInfo.pSetLayouts = &m_descriptorSetLayouts[i];
VkResult result = device->vk.AllocateDescriptorSets(device->device(), &descriptorSetAllocateInfo, &m_descriptorSets[i]);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to allocate descriptor set.");
return false;
}
}
// Create Pipelines
for (const auto& pass : technique.passes)
{
reshade_log.infof("Compiling pass: %s", pass.name.c_str());
VkShaderModuleCreateInfo shaderModuleInfo =
{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = m_module->code.size(),
.pCode = reinterpret_cast<uint32_t*>(m_module->code.data()),
};
if (!pass.cs_entry_point.empty())
{
VkPipelineShaderStageCreateInfo shaderStageCreateInfoCompute =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = &shaderModuleInfo,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.pName = pass.cs_entry_point.c_str(),
};
VkComputePipelineCreateInfo pipelineInfo =
{
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.stage = shaderStageCreateInfoCompute,
.layout = m_pipelineLayout,
};
VkPipeline pipeline = VK_NULL_HANDLE;
VkResult result = device->vk.CreateComputePipelines(device->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to CreateComputePipelines");
return false;
}
m_pipelines.push_back(pipeline);
}
else
{
std::vector<VkPipelineColorBlendAttachmentState> attachmentBlendStates;
std::vector<VkFormat> colorFormats;
uint32_t maxRenderWidth = 0;
uint32_t maxRenderHeight = 0;
for (int i = 0; i < 8; i++)
{
std::shared_ptr<CVulkanTexture> rt;
if (i == 0 && pass.render_target_names[0].empty())
rt = m_rt;
else if (pass.render_target_names[i].empty())
break;
else
rt = findTexture(pass.render_target_names[i]);
if (rt == nullptr)
continue;
maxRenderWidth = std::max<uint32_t>(maxRenderWidth, rt->width());
maxRenderHeight = std::max<uint32_t>(maxRenderHeight, rt->height());
colorFormats.push_back(rt->format());
VkPipelineColorBlendAttachmentState colorBlendAttachment;
colorBlendAttachment.blendEnable = pass.blend_enable[i];
colorBlendAttachment.srcColorBlendFactor = ConvertReshadeBlendFactor(pass.src_blend[i]);
colorBlendAttachment.dstColorBlendFactor = ConvertReshadeBlendFactor(pass.dest_blend[i]);
colorBlendAttachment.colorBlendOp = ConvertReshadeBlendOp(pass.blend_op[i]);
colorBlendAttachment.srcAlphaBlendFactor = ConvertReshadeBlendFactor(pass.src_blend_alpha[i]);
colorBlendAttachment.dstAlphaBlendFactor = ConvertReshadeBlendFactor(pass.dest_blend_alpha[i]);
colorBlendAttachment.alphaBlendOp = ConvertReshadeBlendOp(pass.blend_op_alpha[i]);
colorBlendAttachment.colorWriteMask = pass.color_write_mask[i];
attachmentBlendStates.push_back(colorBlendAttachment);
}
VkPipelineRenderingCreateInfo renderingCreateInfo;
renderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
renderingCreateInfo.pNext = nullptr;
renderingCreateInfo.viewMask = 0;
renderingCreateInfo.colorAttachmentCount = colorFormats.size();
renderingCreateInfo.pColorAttachmentFormats = colorFormats.data();
renderingCreateInfo.depthAttachmentFormat = VK_FORMAT_UNDEFINED;
renderingCreateInfo.stencilAttachmentFormat = VK_FORMAT_UNDEFINED;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent.width = pass.viewport_width ? pass.viewport_width : maxRenderWidth;
scissor.extent.height = pass.viewport_height ? pass.viewport_height : maxRenderHeight;
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = static_cast<float>(scissor.extent.height);
viewport.width = static_cast<float>(scissor.extent.width);
viewport.height = -static_cast<float>(scissor.extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkPipelineShaderStageCreateInfo shaderStageCreateInfoVert;
shaderStageCreateInfoVert.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageCreateInfoVert.pNext = &shaderModuleInfo;
shaderStageCreateInfoVert.flags = 0;
shaderStageCreateInfoVert.stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStageCreateInfoVert.module = VK_NULL_HANDLE;
shaderStageCreateInfoVert.pName = pass.vs_entry_point.c_str();
shaderStageCreateInfoVert.pSpecializationInfo = nullptr;
VkPipelineShaderStageCreateInfo shaderStageCreateInfoFrag;
shaderStageCreateInfoFrag.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageCreateInfoFrag.pNext = &shaderModuleInfo;
shaderStageCreateInfoFrag.flags = 0;
shaderStageCreateInfoFrag.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStageCreateInfoFrag.module = VK_NULL_HANDLE;
shaderStageCreateInfoFrag.pName = pass.ps_entry_point.c_str();
shaderStageCreateInfoFrag.pSpecializationInfo = nullptr;
VkPipelineShaderStageCreateInfo shaderStages[] = {shaderStageCreateInfoVert, shaderStageCreateInfoFrag};
VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo;
vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputCreateInfo.pNext = nullptr;
vertexInputCreateInfo.flags = 0;
vertexInputCreateInfo.vertexBindingDescriptionCount = 0;
vertexInputCreateInfo.pVertexBindingDescriptions = nullptr;
vertexInputCreateInfo.vertexAttributeDescriptionCount = 0;
vertexInputCreateInfo.pVertexAttributeDescriptions = nullptr;
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
switch (pass.topology)
{
case reshadefx::primitive_topology::point_list: topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; break;
case reshadefx::primitive_topology::line_list: topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; break;
case reshadefx::primitive_topology::line_strip: topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; break;
case reshadefx::primitive_topology::triangle_list: topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; break;
case reshadefx::primitive_topology::triangle_strip: topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; break;
default: reshade_log.errorf("Unsupported primitive type: %d", (uint32_t) pass.topology); break;
}
VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo;
inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyCreateInfo.pNext = nullptr;
inputAssemblyCreateInfo.flags = 0;
inputAssemblyCreateInfo.topology = topology;
inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo viewportStateCreateInfo;
viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCreateInfo.pNext = nullptr;
viewportStateCreateInfo.flags = 0;
viewportStateCreateInfo.viewportCount = 1;
viewportStateCreateInfo.pViewports = &viewport;
viewportStateCreateInfo.scissorCount = 1;
viewportStateCreateInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizationCreateInfo;
rasterizationCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationCreateInfo.pNext = nullptr;
rasterizationCreateInfo.flags = 0;
rasterizationCreateInfo.depthClampEnable = VK_FALSE;
rasterizationCreateInfo.rasterizerDiscardEnable = VK_FALSE;
rasterizationCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationCreateInfo.cullMode = VK_CULL_MODE_NONE;
rasterizationCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationCreateInfo.depthBiasEnable = VK_FALSE;
rasterizationCreateInfo.depthBiasConstantFactor = 0.0f;
rasterizationCreateInfo.depthBiasClamp = 0.0f;
rasterizationCreateInfo.depthBiasSlopeFactor = 0.0f;
rasterizationCreateInfo.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisampleCreateInfo;
multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleCreateInfo.pNext = nullptr;
multisampleCreateInfo.flags = 0;
multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampleCreateInfo.sampleShadingEnable = VK_FALSE;
multisampleCreateInfo.minSampleShading = 1.0f;
multisampleCreateInfo.pSampleMask = nullptr;
multisampleCreateInfo.alphaToCoverageEnable = VK_FALSE;
multisampleCreateInfo.alphaToOneEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendCreateInfo;
colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendCreateInfo.pNext = nullptr;
colorBlendCreateInfo.flags = 0;
colorBlendCreateInfo.logicOpEnable = VK_FALSE;
colorBlendCreateInfo.logicOp = VK_LOGIC_OP_NO_OP;
colorBlendCreateInfo.attachmentCount = attachmentBlendStates.size();
colorBlendCreateInfo.pAttachments = attachmentBlendStates.data();
colorBlendCreateInfo.blendConstants[0] = 0.0f;
colorBlendCreateInfo.blendConstants[1] = 0.0f;
colorBlendCreateInfo.blendConstants[2] = 0.0f;
colorBlendCreateInfo.blendConstants[3] = 0.0f;
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo;
dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateCreateInfo.pNext = nullptr;
dynamicStateCreateInfo.flags = 0;
dynamicStateCreateInfo.dynamicStateCount = 0;
dynamicStateCreateInfo.pDynamicStates = nullptr;
#if 0
VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {};
depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilStateCreateInfo.pNext = nullptr;
depthStencilStateCreateInfo.depthTestEnable = VK_FALSE;
depthStencilStateCreateInfo.depthWriteEnable = VK_FALSE;
depthStencilStateCreateInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
depthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
depthStencilStateCreateInfo.stencilTestEnable = pass.stencil_enable;
depthStencilStateCreateInfo.front.failOp = convertReshadeStencilOp(pass.stencil_op_fail);
depthStencilStateCreateInfo.front.passOp = convertReshadeStencilOp(pass.stencil_op_pass);
depthStencilStateCreateInfo.front.depthFailOp = convertReshadeStencilOp(pass.stencil_op_depth_fail);
depthStencilStateCreateInfo.front.compareOp = convertReshadeCompareOp(pass.stencil_comparison_func);
depthStencilStateCreateInfo.front.compareMask = pass.stencil_read_mask;
depthStencilStateCreateInfo.front.writeMask = pass.stencil_write_mask;
depthStencilStateCreateInfo.front.reference = pass.stencil_reference_value;
depthStencilStateCreateInfo.back = depthStencilStateCreateInfo.front;
depthStencilStateCreateInfo.minDepthBounds = 0.0f;
depthStencilStateCreateInfo.maxDepthBounds = 1.0f;
#endif
VkGraphicsPipelineCreateInfo pipelineCreateInfo;
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.pNext = &renderingCreateInfo;
pipelineCreateInfo.flags = 0;
pipelineCreateInfo.stageCount = 2;
pipelineCreateInfo.pStages = shaderStages;
pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo;
pipelineCreateInfo.pTessellationState = nullptr;
pipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
pipelineCreateInfo.pRasterizationState = &rasterizationCreateInfo;
pipelineCreateInfo.pMultisampleState = &multisampleCreateInfo;
// pipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo;
pipelineCreateInfo.pDepthStencilState = nullptr;
pipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo;
pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
pipelineCreateInfo.layout = m_pipelineLayout;
pipelineCreateInfo.renderPass = VK_NULL_HANDLE;
pipelineCreateInfo.subpass = 0;
pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineCreateInfo.basePipelineIndex = -1;
VkPipeline pipeline = VK_NULL_HANDLE;
VkResult result = device->vk.CreateGraphicsPipelines(device->device(), VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline);
if (result != VK_SUCCESS)
{
reshade_log.errorf("Failed to vkCreateGraphicsPipelines");
return false;
}
m_pipelines.push_back(pipeline);
}
}
return true;
}
void ReshadeEffectPipeline::update()
{
for (auto& uniform : m_uniforms)
uniform->update(m_mappedPtr);
}
uint64_t ReshadeEffectPipeline::execute(std::shared_ptr<CVulkanTexture> inImage, std::shared_ptr<CVulkanTexture> *outImage)
{
CVulkanDevice *device = m_device;
this->update();
// Update descriptor sets.
{
VkDescriptorBufferInfo bufferInfo =
{
.buffer = m_buffer,
.range = VK_WHOLE_SIZE,
};
VkWriteDescriptorSet writeDescriptorSet =
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = m_descriptorSets[GAMESCOPE_RESHADE_DESCRIPTOR_SET_UBO],
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &bufferInfo,
};
device->vk.UpdateDescriptorSets(device->device(), 1, &writeDescriptorSet, 0, nullptr);
}
//device->vk.UpdateDescriptorSets(cmd, )
for (size_t i = 0; i < m_samplers.size(); i++)
{
bool srgb = m_module->samplers[i].srgb;
VkDescriptorImageInfo imageInfo =
{
.sampler = m_samplers[i].sampler,
.imageView = m_samplers[i].texture ? m_samplers[i].texture->view(srgb) : inImage->view(srgb),
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
};
VkWriteDescriptorSet writeDescriptorSet =
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = m_descriptorSets[GAMESCOPE_RESHADE_DESCRIPTOR_SET_SAMPLED_IMAGES],
.dstBinding = uint32_t(i),
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &imageInfo,
};
device->vk.UpdateDescriptorSets(device->device(), 1, &writeDescriptorSet, 0, nullptr);
}
for (size_t i = 0; i < m_module->storages.size(); i++)
{
// TODO: Cache
auto tex = findTexture(m_module->storages[i].texture_name);
VkDescriptorImageInfo imageInfo =
{
.imageView = tex ? tex->srgbView() : VK_NULL_HANDLE,
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
};
VkWriteDescriptorSet writeDescriptorSet =
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = m_descriptorSets[GAMESCOPE_RESHADE_DESCRIPTOR_SET_STORAGE_IMAGES],
.dstBinding = uint32_t(i),
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.pImageInfo = &imageInfo,
};
device->vk.UpdateDescriptorSets(device->device(), 1, &writeDescriptorSet, 0, nullptr);
}
// Draw and compute time!
m_cmdBuffer->reset();
m_cmdBuffer->begin();
VkCommandBuffer cmd = m_cmdBuffer->rawBuffer();
device->vk.CmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, std::size(m_descriptorSets), m_descriptorSets, 0, nullptr);
device->vk.CmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelineLayout, 0, std::size(m_descriptorSets), m_descriptorSets, 0, nullptr);
for (size_t i = 0; i < m_textures.size(); i++)
{
auto& tex = m_textures[i];
auto& texInfo = m_module->textures[i];
if (tex && (texInfo.storage_access || texInfo.render_target))
m_cmdBuffer->discardImage(tex.get());
}
if (m_rt)
m_cmdBuffer->discardImage(m_rt.get());
std::shared_ptr<CVulkanTexture> lastRT;
auto& technique = m_module->techniques[m_key.techniqueIdx];
uint32_t passIdx = 0;
for (auto& pass : technique.passes)
{
for (size_t i = 0; i < m_textures.size(); i++)
{
auto& tex = m_textures[i];
auto& texInfo = m_module->textures[i];
if (tex && texInfo.storage_access)
m_cmdBuffer->prepareDestImage(tex.get());
else
m_cmdBuffer->prepareSrcImage(tex != nullptr ? tex.get() : inImage.get());
}
m_cmdBuffer->insertBarrier();
std::array<std::shared_ptr<CVulkanTexture>, 8> rts{};
if (!pass.cs_entry_point.empty())
{
device->vk.CmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelines[passIdx]);
device->vk.CmdDispatch(cmd,
pass.viewport_width ? pass.viewport_width : m_key.bufferWidth,
pass.viewport_height ? pass.viewport_height : m_key.bufferHeight,
pass.viewport_dispatch_z);
}
else
{
for (int i = 0; i < 8; i++)
{
if (i == 0 && pass.render_target_names[0].empty())
rts[i] = m_rt;
else if (pass.render_target_names[i].empty())
break;
else
rts[i] = findTexture(pass.render_target_names[i]);
}
for (int i = 0; i < 8; i++)
{
if (rts[i])
m_cmdBuffer->prepareDestImage(rts[i].get());
}
device->vk.CmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines[passIdx]);
std::vector<VkRenderingAttachmentInfo> colorAttachmentInfos;
uint32_t maxRenderWidth = 0;
uint32_t maxRenderHeight = 0;
for (int i = 0; i < 8; i++)
{
if (rts[i])
{
maxRenderWidth = std::max<uint32_t>(maxRenderWidth, rts[i]->width());
maxRenderHeight = std::max<uint32_t>(maxRenderHeight, rts[i]->height());
const VkRenderingAttachmentInfo colorAttachmentInfo
{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = rts[i]->view(pass.srgb_write_enable),
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.loadOp = pass.clear_render_targets ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
colorAttachmentInfos.push_back(colorAttachmentInfo);
}
}
const VkRenderingInfo renderInfo
{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea = { { 0, 0, }, { maxRenderWidth, maxRenderHeight }},
.layerCount = 1,
.colorAttachmentCount = uint32_t(colorAttachmentInfos.size()),
.pColorAttachments = colorAttachmentInfos.data(),
};
device->vk.CmdBeginRendering(cmd, &renderInfo);
device->vk.CmdDraw(cmd, pass.num_vertices, 1, 0, 0);
device->vk.CmdEndRendering(cmd);
}
for (int i = 0; i < 8; i++)
{
if (rts[i])
m_cmdBuffer->markDirty(rts[i].get());
}
// Insert a stupidly huge fat barrier.
VkMemoryBarrier memBarrier = {};
memBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
memBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
device->vk.CmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memBarrier, 0, NULL, 0, NULL);
if (rts[0])
lastRT = rts[0];
passIdx++;
}
if (lastRT)
*outImage = lastRT;
return device->submitInternal(&*m_cmdBuffer);
}
std::shared_ptr<CVulkanTexture> ReshadeEffectPipeline::findTexture(std::string_view name)
{
for (size_t i = 0; i < m_module->textures.size(); i++)
{
if (m_module->textures[i].unique_name == name)
return m_textures[i];
}
return nullptr;
}
////////////////////////////////
// ReshadeEffectManager
////////////////////////////////
ReshadeEffectManager::ReshadeEffectManager()
{
}
void ReshadeEffectManager::init(CVulkanDevice *device)
{
m_device = device;
}
void ReshadeEffectManager::clear()
{
m_lastKey = ReshadeEffectKey{};
m_lastPipeline = nullptr;
}
ReshadeEffectPipeline* ReshadeEffectManager::pipeline(const ReshadeEffectKey &key)
{
if (m_lastKey == key)
return m_lastPipeline.get();
m_lastKey = key;
m_lastPipeline = nullptr;
auto pipeline = std::make_unique<ReshadeEffectPipeline>();
if (!pipeline->init(m_device, key))
return nullptr;
m_lastPipeline = std::move(pipeline);
return m_lastPipeline.get();
}
ReshadeEffectManager g_reshadeManager;