PostProcessing/FX: Support multiple render targets

This commit is contained in:
Stenzek 2023-12-08 16:38:11 +10:00
parent fdd3edf73a
commit ff37afef55
No known key found for this signature in database
2 changed files with 55 additions and 28 deletions

View File

@ -317,7 +317,6 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
if (!temp_module.techniques.empty()) if (!temp_module.techniques.empty())
{ {
u32 max_rt = 0;
bool has_passes = false; bool has_passes = false;
for (const reshadefx::technique_info& tech : temp_module.techniques) for (const reshadefx::technique_info& tech : temp_module.techniques)
{ {
@ -325,6 +324,7 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
{ {
has_passes = true; has_passes = true;
u32 max_rt = 0;
for (u32 i = 0; i < std::size(pi.render_target_names); i++) for (u32 i = 0; i < std::size(pi.render_target_names); i++)
{ {
if (pi.render_target_names[i].empty()) if (pi.render_target_names[i].empty())
@ -333,6 +333,13 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
max_rt = std::max(max_rt, i); max_rt = std::max(max_rt, i);
} }
if (max_rt > GPUDevice::MAX_RENDER_TARGETS)
{
Error::SetString(error, fmt::format("Too many render targets ({}) in pass {}, only {} are supported.", max_rt,
pi.name, GPUDevice::MAX_RENDER_TARGETS));
return false;
}
if (pi.samplers.size() > GPUDevice::MAX_TEXTURE_SAMPLERS) if (pi.samplers.size() > GPUDevice::MAX_TEXTURE_SAMPLERS)
{ {
Error::SetString(error, fmt::format("Too many samplers ({}) in pass {}, only {} are supported.", Error::SetString(error, fmt::format("Too many samplers ({}) in pass {}, only {} are supported.",
@ -346,11 +353,6 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
Error::SetString(error, "No passes defined in file."); Error::SetString(error, "No passes defined in file.");
return false; return false;
} }
else if (max_rt > 0)
{
Error::SetString(error, "Shaders with multiple render targets are currently not supported.");
return false;
}
} }
// Might go invalid when creating pipelines. // Might go invalid when creating pipelines.
@ -967,32 +969,40 @@ bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer
if (is_final) if (is_final)
{ {
pass.render_target = OUTPUT_COLOR_TEXTURE; pass.render_targets.push_back(OUTPUT_COLOR_TEXTURE);
} }
else if (!pi.render_target_names[0].empty()) else if (!pi.render_target_names[0].empty())
{ {
pass.render_target = static_cast<TextureID>(m_textures.size()); for (const std::string& rtname : pi.render_target_names)
{
if (rtname.empty())
break;
TextureID rt = static_cast<TextureID>(m_textures.size());
for (u32 i = 0; i < static_cast<u32>(m_textures.size()); i++) for (u32 i = 0; i < static_cast<u32>(m_textures.size()); i++)
{ {
if (m_textures[i].reshade_name == pi.render_target_names[0]) if (m_textures[i].reshade_name == rtname)
{ {
pass.render_target = static_cast<TextureID>(i); rt = static_cast<TextureID>(i);
break; break;
} }
} }
if (pass.render_target == static_cast<TextureID>(m_textures.size())) if (rt == static_cast<TextureID>(m_textures.size()))
{ {
Error::SetString(error, fmt::format("Unknown texture '{}' used as render target in pass '{}'", Error::SetString(error,
pi.render_target_names[0], pi.name)); fmt::format("Unknown texture '{}' used as render target in pass '{}'", rtname, pi.name));
return false; return false;
} }
pass.render_targets.push_back(rt);
}
} }
else else
{ {
Texture new_rt; Texture new_rt;
new_rt.rt_scale = 1.0f; new_rt.rt_scale = 1.0f;
new_rt.format = backbuffer_format; new_rt.format = backbuffer_format;
pass.render_target = static_cast<TextureID>(m_textures.size()); pass.render_targets.push_back(static_cast<TextureID>(m_textures.size()));
m_textures.push_back(std::move(new_rt)); m_textures.push_back(std::move(new_rt));
} }
@ -1151,7 +1161,7 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
const std::string_view code(mod.code.data(), mod.code.size()); const std::string_view code(mod.code.data(), mod.code.size());
auto get_shader = [api, needs_main_defn, &code](const std::string& name, const std::vector<Sampler>& samplers, auto get_shader = [api, needs_main_defn, &code](const std::string& name, const std::span<Sampler> samplers,
GPUShaderStage stage) { GPUShaderStage stage) {
std::string real_code; std::string real_code;
if (needs_main_defn) if (needs_main_defn)
@ -1222,7 +1232,15 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
if (!vs || !fs) if (!vs || !fs)
return false; return false;
plconfig.SetTargetFormats((pass.render_target >= 0) ? m_textures[pass.render_target].format : format); for (size_t i = 0; i < pass.render_targets.size(); i++)
{
plconfig.color_formats[i] =
((pass.render_targets[i] >= 0) ? m_textures[pass.render_targets[i]].format : format);
}
for (size_t i = pass.render_targets.size(); i < GPUDevice::MAX_RENDER_TARGETS; i++)
plconfig.color_formats[i] = GPUTexture::Format::Unknown;
plconfig.depth_format = GPUTexture::Format::Unknown;
plconfig.blend = MapBlendState(info); plconfig.blend = MapBlendState(info);
plconfig.primitive = MapPrimitive(info.topology); plconfig.primitive = MapPrimitive(info.topology);
plconfig.vertex_shader = vs.get(); plconfig.vertex_shader = vs.get();
@ -1431,12 +1449,11 @@ bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input, GPUTexture* final
for (const Pass& pass : m_passes) for (const Pass& pass : m_passes)
{ {
GL_SCOPE_FMT("Draw pass {}", pass.name.c_str()); GL_SCOPE_FMT("Draw pass {}", pass.name.c_str());
GL_INS_FMT("Render Target: ID {} [{}]", pass.render_target, GetTextureNameForID(pass.render_target)); DebugAssert(!pass.render_targets.empty());
GPUTexture* output = GetTextureByID(pass.render_target, input, final_target);
if (!output) if (pass.render_targets.size() == 1 && pass.render_targets[0] == OUTPUT_COLOR_TEXTURE && !final_target)
{ {
// Drawing to final buffer. // Special case: drawing to final buffer.
if (!g_gpu_device->BeginPresent(false)) if (!g_gpu_device->BeginPresent(false))
{ {
GL_POP(); GL_POP();
@ -1445,7 +1462,16 @@ bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input, GPUTexture* final
} }
else else
{ {
g_gpu_device->SetRenderTargets(&output, 1, nullptr); std::array<GPUTexture*, GPUDevice::MAX_RENDER_TARGETS> render_targets;
for (size_t i = 0; i < pass.render_targets.size(); i++)
{
GL_INS_FMT("Render Target {}: ID {} [{}]", i, pass.render_targets[i],
GetTextureNameForID(pass.render_targets[i]));
render_targets[i] = GetTextureByID(pass.render_targets[i], input, final_target);
DebugAssert(render_targets[i]);
}
g_gpu_device->SetRenderTargets(render_targets.data(), static_cast<u32>(pass.render_targets.size()), nullptr);
} }
g_gpu_device->SetPipeline(pass.pipeline.get()); g_gpu_device->SetPipeline(pass.pipeline.get());

View File

@ -5,6 +5,7 @@
#include "postprocessing_shader.h" #include "postprocessing_shader.h"
#include "common/thirdparty/SmallVector.h"
#include "common/timer.h" #include "common/timer.h"
// reshadefx // reshadefx
@ -103,8 +104,8 @@ private:
struct Pass struct Pass
{ {
std::unique_ptr<GPUPipeline> pipeline; std::unique_ptr<GPUPipeline> pipeline;
TextureID render_target; llvm::SmallVector<TextureID, GPUDevice::MAX_RENDER_TARGETS> render_targets;
std::vector<Sampler> samplers; llvm::SmallVector<Sampler, GPUDevice::MAX_TEXTURE_SAMPLERS> samplers;
u32 num_vertices; u32 num_vertices;
#ifdef _DEBUG #ifdef _DEBUG