mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 04:35:56 +00:00
FidelityFX FSR implementation (#2624)
* host_shaders: support for includes * video_core: add a simpler vulkan asserts * video_core: refactored post processing pipeline to another file * renderer_vulkan: add define param to compile shader utility * video_core: fsr implementation * devtools: show resolution & fsr state
This commit is contained in:
parent
ba1eb298de
commit
f663176a5d
26 changed files with 4984 additions and 324 deletions
|
@ -584,6 +584,7 @@ set(COMMON src/common/logging/backend.cpp
|
||||||
src/common/spin_lock.h
|
src/common/spin_lock.h
|
||||||
src/common/stb.cpp
|
src/common/stb.cpp
|
||||||
src/common/stb.h
|
src/common/stb.h
|
||||||
|
src/common/string_literal.h
|
||||||
src/common/string_util.cpp
|
src/common/string_util.cpp
|
||||||
src/common/string_util.h
|
src/common/string_util.h
|
||||||
src/common/thread.cpp
|
src/common/thread.cpp
|
||||||
|
@ -845,6 +846,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||||
src/video_core/renderer_vulkan/vk_shader_util.h
|
src/video_core/renderer_vulkan/vk_shader_util.h
|
||||||
src/video_core/renderer_vulkan/vk_swapchain.cpp
|
src/video_core/renderer_vulkan/vk_swapchain.cpp
|
||||||
src/video_core/renderer_vulkan/vk_swapchain.h
|
src/video_core/renderer_vulkan/vk_swapchain.h
|
||||||
|
src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp
|
||||||
|
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
|
||||||
|
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
|
||||||
|
src/video_core/renderer_vulkan/host_passes/pp_pass.h
|
||||||
src/video_core/texture_cache/image.cpp
|
src/video_core/texture_cache/image.cpp
|
||||||
src/video_core/texture_cache/image.h
|
src/video_core/texture_cache/image.h
|
||||||
src/video_core/texture_cache/image_info.cpp
|
src/video_core/texture_cache/image_info.cpp
|
||||||
|
|
|
@ -112,3 +112,8 @@ SPDX-License-Identifier = "CC0-1.0"
|
||||||
path = "cmake/CMakeRC.cmake"
|
path = "cmake/CMakeRC.cmake"
|
||||||
SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.com>"
|
SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.com>"
|
||||||
SPDX-License-Identifier = "MIT"
|
SPDX-License-Identifier = "MIT"
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
path = "src/video_core/host_shaders/fsr/*"
|
||||||
|
SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
|
||||||
|
SPDX-License-Identifier = "MIT"
|
||||||
|
|
15
src/common/string_literal.h
Normal file
15
src/common/string_literal.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
template <size_t N, typename C = char>
|
||||||
|
struct StringLiteral {
|
||||||
|
static constexpr size_t len = N;
|
||||||
|
|
||||||
|
constexpr StringLiteral(const C (&str)[N]) {
|
||||||
|
std::copy_n(str, N, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
C value[N]{};
|
||||||
|
};
|
|
@ -158,6 +158,10 @@ public:
|
||||||
float Framerate = 1.0f / 60.0f;
|
float Framerate = 1.0f / 60.0f;
|
||||||
float FrameDeltaTime;
|
float FrameDeltaTime;
|
||||||
|
|
||||||
|
std::pair<u32, u32> game_resolution{};
|
||||||
|
std::pair<u32, u32> output_resolution{};
|
||||||
|
bool is_using_fsr{};
|
||||||
|
|
||||||
void ShowDebugMessage(std::string message) {
|
void ShowDebugMessage(std::string message) {
|
||||||
if (message.empty()) {
|
if (message.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -81,8 +81,24 @@ void L::DrawMenuBar() {
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (BeginMenu("Display")) {
|
if (BeginMenu("Display")) {
|
||||||
|
auto& pp_settings = presenter->GetPPSettingsRef();
|
||||||
if (BeginMenu("Brightness")) {
|
if (BeginMenu("Brightness")) {
|
||||||
SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f);
|
SliderFloat("Gamma", &pp_settings.gamma, 0.1f, 2.0f);
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (BeginMenu("FSR")) {
|
||||||
|
auto& fsr = presenter->GetFsrSettingsRef();
|
||||||
|
Checkbox("FSR Enabled", &fsr.enable);
|
||||||
|
BeginDisabled(!fsr.enable);
|
||||||
|
{
|
||||||
|
Checkbox("RCAS", &fsr.use_rcas);
|
||||||
|
BeginDisabled(!fsr.use_rcas);
|
||||||
|
{
|
||||||
|
SliderFloat("RCAS Attenuation", &fsr.rcas_attenuation, 0.0, 3.0);
|
||||||
|
}
|
||||||
|
EndDisabled();
|
||||||
|
}
|
||||||
|
EndDisabled();
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
|
|
|
@ -74,7 +74,7 @@ void FrameGraph::Draw() {
|
||||||
if (!is_open) {
|
if (!is_open) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetNextWindowSize({340.0, 185.0f}, ImGuiCond_FirstUseEver);
|
SetNextWindowSize({308.0, 270.0f}, ImGuiCond_FirstUseEver);
|
||||||
if (Begin("Video debug info", &is_open)) {
|
if (Begin("Video debug info", &is_open)) {
|
||||||
const auto& ctx = *GImGui;
|
const auto& ctx = *GImGui;
|
||||||
const auto& io = ctx.IO;
|
const auto& io = ctx.IO;
|
||||||
|
@ -88,13 +88,20 @@ void FrameGraph::Draw() {
|
||||||
frameRate = 1000.0f / deltaTime;
|
frameRate = 1000.0f / deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SeparatorText("Frame graph");
|
||||||
|
DrawFrameGraph();
|
||||||
|
|
||||||
|
SeparatorText("Renderer info");
|
||||||
|
|
||||||
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
|
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
|
||||||
Text("Presenter time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, 1.0f / io.DeltaTime);
|
Text("Presenter time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, 1.0f / io.DeltaTime);
|
||||||
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
|
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
|
||||||
DebugState.gnm_frame_count.load());
|
DebugState.gnm_frame_count.load());
|
||||||
|
Text("Game Res: %dx%d", DebugState.game_resolution.first,
|
||||||
SeparatorText("Frame graph");
|
DebugState.game_resolution.second);
|
||||||
DrawFrameGraph();
|
Text("Output Res: %dx%d", DebugState.output_resolution.first,
|
||||||
|
DebugState.output_resolution.second);
|
||||||
|
Text("FSR: %s", DebugState.is_using_fsr ? "on" : "off");
|
||||||
}
|
}
|
||||||
End();
|
End();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include "common/string_literal.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/kernel/orbis_error.h"
|
#include "core/libraries/kernel/orbis_error.h"
|
||||||
|
|
||||||
|
@ -18,15 +19,6 @@ void ErrSceToPosix(int result);
|
||||||
int ErrnoToSceKernelError(int e);
|
int ErrnoToSceKernelError(int e);
|
||||||
void SetPosixErrno(int e);
|
void SetPosixErrno(int e);
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
struct StringLiteral {
|
|
||||||
constexpr StringLiteral(const char (&str)[N]) {
|
|
||||||
std::copy_n(str, N, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
char value[N];
|
|
||||||
};
|
|
||||||
|
|
||||||
template <StringLiteral name, class F, F f>
|
template <StringLiteral name, class F, F f>
|
||||||
struct WrapperImpl;
|
struct WrapperImpl;
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettin
|
||||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
presenter->GetGammaRef() = settings->gamma;
|
presenter->GetPPSettingsRef().gamma = settings->gamma;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ set(SHADER_FILES
|
||||||
detilers/micro_64bpp.comp
|
detilers/micro_64bpp.comp
|
||||||
detilers/micro_8bpp.comp
|
detilers/micro_8bpp.comp
|
||||||
fs_tri.vert
|
fs_tri.vert
|
||||||
|
fsr.comp
|
||||||
post_process.frag
|
post_process.frag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,28 +9,31 @@ get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
|
||||||
string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
|
string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
|
||||||
string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
|
string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
|
||||||
|
|
||||||
FILE(READ ${SOURCE_FILE} line_contents)
|
# Function to recursively parse #include directives and replace them with file contents
|
||||||
|
function(parse_includes file_path output_content)
|
||||||
|
file(READ ${file_path} file_content)
|
||||||
|
# This regex includes \n at the begin to (hackish) avoid including comments
|
||||||
|
string(REGEX MATCHALL "\n#include +\"[^\"]+\"" includes "${file_content}")
|
||||||
|
|
||||||
# Replace double quotes with single quotes,
|
set(parsed_content "${file_content}")
|
||||||
# as double quotes will be used to wrap the lines
|
foreach (include_match ${includes})
|
||||||
STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}")
|
string(REGEX MATCH "\"([^\"]+)\"" _ "${include_match}")
|
||||||
|
set(include_file ${CMAKE_MATCH_1})
|
||||||
|
get_filename_component(include_full_path "${file_path}" DIRECTORY)
|
||||||
|
set(include_full_path "${include_full_path}/${include_file}")
|
||||||
|
|
||||||
# CMake separates list elements with semicolons, but semicolons
|
if (NOT EXISTS "${include_full_path}")
|
||||||
# are used extensively in the shader code.
|
message(FATAL_ERROR "Included file not found: ${include_full_path} from ${file_path}")
|
||||||
# Replace with a temporary marker, to be reverted later.
|
endif ()
|
||||||
STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}")
|
|
||||||
|
|
||||||
# Make every line an individual element in the CMake list.
|
parse_includes("${include_full_path}" sub_content)
|
||||||
STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}")
|
string(REPLACE "${include_match}" "\n${sub_content}" parsed_content "${parsed_content}")
|
||||||
|
endforeach ()
|
||||||
|
set(${output_content} "${parsed_content}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
# Build the shader string, wrapping each line in double quotes.
|
parse_includes("${SOURCE_FILE}" CONTENTS)
|
||||||
foreach(line IN LISTS line_contents)
|
|
||||||
string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n)
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Revert the original semicolons in the source.
|
|
||||||
STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}")
|
|
||||||
|
|
||||||
get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
|
get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
|
||||||
make_directory(${OUTPUT_DIR})
|
file(MAKE_DIRECTORY ${OUTPUT_DIR})
|
||||||
configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
|
configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
|
||||||
|
|
91
src/video_core/host_shaders/fsr.comp
Normal file
91
src/video_core/host_shaders/fsr.comp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects: enable
|
||||||
|
#extension GL_ARB_shading_language_420pack: enable
|
||||||
|
|
||||||
|
// FidelityFX Super Resolution Sample
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files(the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions :
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
layout (push_constant) uniform const_buffer
|
||||||
|
{
|
||||||
|
uvec4 Const0;
|
||||||
|
uvec4 Const1;
|
||||||
|
uvec4 Const2;
|
||||||
|
uvec4 Const3;
|
||||||
|
uvec4 Sample;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define A_GPU 1
|
||||||
|
#define A_GLSL 1
|
||||||
|
|
||||||
|
#define A_HALF
|
||||||
|
#include "fsr/ffx_a.h"
|
||||||
|
|
||||||
|
layout (set = 0, binding = 0) uniform texture2D InputTexture;
|
||||||
|
layout (set = 0, binding = 1, rgba16f) uniform image2D OutputTexture;
|
||||||
|
layout (set = 0, binding = 2) uniform sampler InputSampler;
|
||||||
|
|
||||||
|
#if SAMPLE_EASU
|
||||||
|
#define FSR_EASU_H 1
|
||||||
|
AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 0)); return res; }
|
||||||
|
AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 1)); return res; }
|
||||||
|
AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 2)); return res; }
|
||||||
|
#endif// SAMPLE_EASU
|
||||||
|
|
||||||
|
#if SAMPLE_RCAS
|
||||||
|
#define FSR_RCAS_H
|
||||||
|
AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(sampler2D(InputTexture, InputSampler), ASU2(p), 0)); }
|
||||||
|
void FsrRcasInputH(inout AH1 r, inout AH1 g, inout AH1 b) { }
|
||||||
|
#endif// SAMPLE_RCAS
|
||||||
|
|
||||||
|
#include "fsr/ffx_fsr1.h"
|
||||||
|
|
||||||
|
void CurrFilter(AU2 pos)
|
||||||
|
{
|
||||||
|
#if SAMPLE_EASU
|
||||||
|
AH3 c;
|
||||||
|
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
|
||||||
|
if (Sample.x == 1)
|
||||||
|
c *= c;
|
||||||
|
imageStore(OutputTexture, ASU2(pos), AH4(c, 1));
|
||||||
|
#endif// SAMPLE_EASU
|
||||||
|
#if SAMPLE_RCAS
|
||||||
|
AH3 c;
|
||||||
|
FsrRcasH(c.r, c.g, c.b, pos, Const0);
|
||||||
|
if (Sample.x == 1)
|
||||||
|
c *= c;
|
||||||
|
imageStore(OutputTexture, ASU2(pos), AH4(c, 1));
|
||||||
|
#endif// SAMPLE_RCAS
|
||||||
|
}
|
||||||
|
|
||||||
|
layout (local_size_x = 64) in;
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
|
||||||
|
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.y += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x -= 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
}
|
2657
src/video_core/host_shaders/fsr/ffx_a.h
Normal file
2657
src/video_core/host_shaders/fsr/ffx_a.h
Normal file
File diff suppressed because it is too large
Load diff
1200
src/video_core/host_shaders/fsr/ffx_fsr1.h
Normal file
1200
src/video_core/host_shaders/fsr/ffx_fsr1.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ layout (location = 0) out vec4 color;
|
||||||
|
|
||||||
layout (binding = 0) uniform sampler2D texSampler;
|
layout (binding = 0) uniform sampler2D texSampler;
|
||||||
|
|
||||||
layout(push_constant) uniform settings {
|
layout (push_constant) uniform settings {
|
||||||
float gamma;
|
float gamma;
|
||||||
bool hdr;
|
bool hdr;
|
||||||
} pp;
|
} pp;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
namespace HostShaders {
|
namespace HostShaders {
|
||||||
|
|
||||||
constexpr std::string_view @CONTENTS_NAME@ = {
|
constexpr std::string_view @CONTENTS_NAME@ = R"shader_src(
|
||||||
@CONTENTS@
|
@CONTENTS@
|
||||||
};
|
)shader_src";
|
||||||
|
|
||||||
} // namespace HostShaders
|
} // namespace HostShaders
|
||||||
|
|
445
src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp
Normal file
445
src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "fsr_pass.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "video_core/host_shaders/fsr_comp.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
|
|
||||||
|
#define A_CPU
|
||||||
|
#include "core/debug_state.h"
|
||||||
|
#include "video_core/host_shaders/fsr/ffx_a.h"
|
||||||
|
#include "video_core/host_shaders/fsr/ffx_fsr1.h"
|
||||||
|
|
||||||
|
typedef u32 uvec4[4];
|
||||||
|
|
||||||
|
struct FSRConstants {
|
||||||
|
uvec4 Const0;
|
||||||
|
uvec4 Const1;
|
||||||
|
uvec4 Const2;
|
||||||
|
uvec4 Const3;
|
||||||
|
uvec4 Sample;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Vulkan::HostPasses {
|
||||||
|
|
||||||
|
void FsrPass::Create(vk::Device device, VmaAllocator allocator, u32 num_images) {
|
||||||
|
this->device = device;
|
||||||
|
this->num_images = num_images;
|
||||||
|
|
||||||
|
sampler = Check<"create upscaling sampler">(device.createSamplerUnique(vk::SamplerCreateInfo{
|
||||||
|
.magFilter = vk::Filter::eLinear,
|
||||||
|
.minFilter = vk::Filter::eLinear,
|
||||||
|
.mipmapMode = vk::SamplerMipmapMode::eNearest,
|
||||||
|
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.addressModeW = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.maxAnisotropy = 1.0f,
|
||||||
|
.minLod = -1000.0f,
|
||||||
|
.maxLod = 1000.0f,
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::array<vk::DescriptorSetLayoutBinding, 3> layoutBindings{{
|
||||||
|
{
|
||||||
|
.binding = 0,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.binding = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.binding = 2,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampler,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
.pImmutableSamplers = &sampler.get(),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
descriptor_set_layout =
|
||||||
|
Check<"create fsr descriptor set layout">(device.createDescriptorSetLayoutUnique({
|
||||||
|
.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptor,
|
||||||
|
.bindingCount = layoutBindings.size(),
|
||||||
|
.pBindings = layoutBindings.data(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const vk::PushConstantRange push_constants{
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
.offset = 0,
|
||||||
|
.size = sizeof(FSRConstants),
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& cs_easu_module =
|
||||||
|
Compile(HostShaders::FSR_COMP, vk::ShaderStageFlagBits::eCompute, device,
|
||||||
|
{
|
||||||
|
"SAMPLE_EASU=1",
|
||||||
|
});
|
||||||
|
ASSERT(cs_easu_module);
|
||||||
|
SetObjectName(device, cs_easu_module, "fsr.comp [EASU]");
|
||||||
|
|
||||||
|
const auto& cs_rcas_module =
|
||||||
|
Compile(HostShaders::FSR_COMP, vk::ShaderStageFlagBits::eCompute, device,
|
||||||
|
{
|
||||||
|
"SAMPLE_RCAS=1",
|
||||||
|
});
|
||||||
|
ASSERT(cs_rcas_module);
|
||||||
|
SetObjectName(device, cs_rcas_module, "fsr.comp [RCAS]");
|
||||||
|
|
||||||
|
pipeline_layout = Check<"fsp pipeline layout">(device.createPipelineLayoutUnique({
|
||||||
|
.setLayoutCount = 1,
|
||||||
|
.pSetLayouts = &descriptor_set_layout.get(),
|
||||||
|
.pushConstantRangeCount = 1,
|
||||||
|
.pPushConstantRanges = &push_constants,
|
||||||
|
}));
|
||||||
|
SetObjectName(device, pipeline_layout.get(), "fsr pipeline layout");
|
||||||
|
|
||||||
|
const vk::ComputePipelineCreateInfo easu_pinfo{
|
||||||
|
.stage{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
.module = cs_easu_module,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
.layout = pipeline_layout.get(),
|
||||||
|
};
|
||||||
|
easu_pipeline =
|
||||||
|
Check<"fsp easu compute pipelines">(device.createComputePipelineUnique({}, easu_pinfo));
|
||||||
|
SetObjectName(device, easu_pipeline.get(), "fsr easu pipeline");
|
||||||
|
|
||||||
|
const vk::ComputePipelineCreateInfo rcas_pinfo{
|
||||||
|
.stage{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
.module = cs_rcas_module,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
.layout = pipeline_layout.get(),
|
||||||
|
};
|
||||||
|
rcas_pipeline =
|
||||||
|
Check<"fsp rcas compute pipelines">(device.createComputePipelineUnique({}, rcas_pinfo));
|
||||||
|
SetObjectName(device, rcas_pipeline.get(), "fsr rcas pipeline");
|
||||||
|
|
||||||
|
device.destroyShaderModule(cs_easu_module);
|
||||||
|
device.destroyShaderModule(cs_rcas_module);
|
||||||
|
|
||||||
|
available_imgs.resize(num_images);
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
auto& img = available_imgs[i];
|
||||||
|
img.id = i;
|
||||||
|
img.intermediary_image = VideoCore::UniqueImage(device, allocator);
|
||||||
|
img.output_image = VideoCore::UniqueImage(device, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageView FsrPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||||
|
vk::Extent2D input_size, vk::Extent2D output_size, Settings settings,
|
||||||
|
bool hdr) {
|
||||||
|
if (!settings.enable) {
|
||||||
|
DebugState.is_using_fsr = false;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
if (input_size.width >= output_size.width && input_size.height >= output_size.height) {
|
||||||
|
DebugState.is_using_fsr = false;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugState.is_using_fsr = true;
|
||||||
|
|
||||||
|
if (output_size != cur_size) {
|
||||||
|
ResizeAndInvalidate(output_size.width, output_size.height);
|
||||||
|
}
|
||||||
|
auto [width, height] = cur_size;
|
||||||
|
|
||||||
|
auto& img = available_imgs[cur_image];
|
||||||
|
if (++cur_image >= available_imgs.size()) {
|
||||||
|
cur_image = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.dirty) {
|
||||||
|
CreateImages(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int thread_group_work_region_dim = 16;
|
||||||
|
int dispatch_x = (width + (thread_group_work_region_dim - 1)) / thread_group_work_region_dim;
|
||||||
|
int dispatch_y = (height + (thread_group_work_region_dim - 1)) / thread_group_work_region_dim;
|
||||||
|
|
||||||
|
constexpr vk::ImageSubresourceRange simple_subresource = {
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.levelCount = 1,
|
||||||
|
.layerCount = 1,
|
||||||
|
};
|
||||||
|
const std::array enter_barrier{
|
||||||
|
vk::ImageMemoryBarrier2{
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eShaderWrite,
|
||||||
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.newLayout = vk::ImageLayout::eGeneral,
|
||||||
|
.image = img.intermediary_image,
|
||||||
|
.subresourceRange = simple_subresource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.pipelineBarrier2({
|
||||||
|
.imageMemoryBarrierCount = enter_barrier.size(),
|
||||||
|
.pImageMemoryBarriers = enter_barrier.data(),
|
||||||
|
});
|
||||||
|
|
||||||
|
FSRConstants consts{};
|
||||||
|
FsrEasuCon(reinterpret_cast<AU1*>(&consts.Const0), reinterpret_cast<AU1*>(&consts.Const1),
|
||||||
|
reinterpret_cast<AU1*>(&consts.Const2), reinterpret_cast<AU1*>(&consts.Const3),
|
||||||
|
static_cast<AF1>(input_size.width), static_cast<AF1>(input_size.height),
|
||||||
|
static_cast<AF1>(input_size.width), static_cast<AF1>(input_size.height), (AF1)width,
|
||||||
|
(AF1)height);
|
||||||
|
consts.Sample[0] = hdr && !settings.use_rcas ? 1 : 0;
|
||||||
|
|
||||||
|
if (settings.use_rcas) {
|
||||||
|
|
||||||
|
{ // easu
|
||||||
|
std::array<vk::DescriptorImageInfo, 3> img_info{{
|
||||||
|
{
|
||||||
|
.imageView = input,
|
||||||
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.imageView = img.intermediary_image_view.get(),
|
||||||
|
.imageLayout = vk::ImageLayout::eGeneral,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.sampler = sampler.get(),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<vk::WriteDescriptorSet, 3> set_writes{{
|
||||||
|
{
|
||||||
|
.dstBinding = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||||
|
.pImageInfo = &img_info[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 1,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.pImageInfo = &img_info[1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 2,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampler,
|
||||||
|
.pImageInfo = &img_info[2],
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, easu_pipeline.get());
|
||||||
|
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0,
|
||||||
|
set_writes);
|
||||||
|
cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0,
|
||||||
|
sizeof(FSRConstants), &consts);
|
||||||
|
cmdbuf.dispatch(dispatch_x, dispatch_y, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array img_barrier{
|
||||||
|
vk::ImageMemoryBarrier2{
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||||
|
.oldLayout = vk::ImageLayout::eGeneral,
|
||||||
|
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
.image = img.intermediary_image,
|
||||||
|
.subresourceRange = simple_subresource,
|
||||||
|
},
|
||||||
|
vk::ImageMemoryBarrier2{
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
|
||||||
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.newLayout = vk::ImageLayout::eGeneral,
|
||||||
|
.image = img.output_image,
|
||||||
|
.subresourceRange = simple_subresource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||||
|
.imageMemoryBarrierCount = img_barrier.size(),
|
||||||
|
.pImageMemoryBarriers = img_barrier.data(),
|
||||||
|
});
|
||||||
|
|
||||||
|
{ // rcas
|
||||||
|
consts = {};
|
||||||
|
FsrRcasCon(reinterpret_cast<AU1*>(&consts.Const0), settings.rcas_attenuation);
|
||||||
|
consts.Sample[0] = hdr ? 1 : 0;
|
||||||
|
|
||||||
|
std::array<vk::DescriptorImageInfo, 3> img_info{{
|
||||||
|
{
|
||||||
|
.imageView = img.intermediary_image_view.get(),
|
||||||
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.imageView = img.output_image_view.get(),
|
||||||
|
.imageLayout = vk::ImageLayout::eGeneral,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.sampler = sampler.get(),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<vk::WriteDescriptorSet, 3> set_writes{{
|
||||||
|
{
|
||||||
|
.dstBinding = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||||
|
.pImageInfo = &img_info[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 1,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.pImageInfo = &img_info[1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 2,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampler,
|
||||||
|
.pImageInfo = &img_info[2],
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, rcas_pipeline.get());
|
||||||
|
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0,
|
||||||
|
set_writes);
|
||||||
|
cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0,
|
||||||
|
sizeof(FSRConstants), &consts);
|
||||||
|
cmdbuf.dispatch(dispatch_x, dispatch_y, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// only easu
|
||||||
|
std::array<vk::DescriptorImageInfo, 3> img_info{{
|
||||||
|
{
|
||||||
|
.imageView = input,
|
||||||
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.imageView = img.output_image_view.get(),
|
||||||
|
.imageLayout = vk::ImageLayout::eGeneral,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.sampler = sampler.get(),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<vk::WriteDescriptorSet, 3> set_writes{{
|
||||||
|
{
|
||||||
|
.dstBinding = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||||
|
.pImageInfo = &img_info[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 1,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.pImageInfo = &img_info[1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = 2,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eSampler,
|
||||||
|
.pImageInfo = &img_info[2],
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, easu_pipeline.get());
|
||||||
|
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0,
|
||||||
|
set_writes);
|
||||||
|
cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0,
|
||||||
|
sizeof(FSRConstants), &consts);
|
||||||
|
cmdbuf.dispatch(dispatch_x, dispatch_y, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array return_barrier{
|
||||||
|
vk::ImageMemoryBarrier2{
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||||
|
.oldLayout = vk::ImageLayout::eGeneral,
|
||||||
|
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
.image = img.output_image,
|
||||||
|
.subresourceRange = simple_subresource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.pipelineBarrier2({
|
||||||
|
.imageMemoryBarrierCount = return_barrier.size(),
|
||||||
|
.pImageMemoryBarriers = return_barrier.data(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return img.output_image_view.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FsrPass::ResizeAndInvalidate(u32 width, u32 height) {
|
||||||
|
this->cur_size = vk::Extent2D{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
available_imgs[i].dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FsrPass::CreateImages(Img& img) const {
|
||||||
|
img.dirty = false;
|
||||||
|
|
||||||
|
vk::ImageCreateInfo image_create_info{
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = vk::Format::eR16G16B16A16Sfloat,
|
||||||
|
.extent{
|
||||||
|
.width = cur_size.width,
|
||||||
|
.height = cur_size.height,
|
||||||
|
.depth = 1,
|
||||||
|
},
|
||||||
|
.mipLevels = 1,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
.usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage,
|
||||||
|
.initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
};
|
||||||
|
img.intermediary_image.Create(image_create_info);
|
||||||
|
SetObjectName(device, static_cast<vk::Image>(img.intermediary_image),
|
||||||
|
"FSR Intermediary Image #{}", img.id);
|
||||||
|
image_create_info.usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||||
|
vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage |
|
||||||
|
vk::ImageUsageFlagBits::eColorAttachment;
|
||||||
|
img.output_image.Create(image_create_info);
|
||||||
|
SetObjectName(device, static_cast<vk::Image>(img.output_image), "FSR Output Image #{}", img.id);
|
||||||
|
|
||||||
|
vk::ImageViewCreateInfo image_view_create_info{
|
||||||
|
.image = img.intermediary_image,
|
||||||
|
.viewType = vk::ImageViewType::e2D,
|
||||||
|
.format = vk::Format::eR16G16B16A16Sfloat,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.levelCount = 1,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
img.intermediary_image_view = Check<"create fsr intermediary image view">(
|
||||||
|
device.createImageViewUnique(image_view_create_info));
|
||||||
|
SetObjectName(device, img.intermediary_image_view.get(), "FSR Intermediary ImageView #{}",
|
||||||
|
img.id);
|
||||||
|
|
||||||
|
image_view_create_info.image = img.output_image;
|
||||||
|
img.output_image_view =
|
||||||
|
Check<"create fsr output image view">(device.createImageViewUnique(image_view_create_info));
|
||||||
|
SetObjectName(device, img.output_image_view.get(), "FSR Output ImageView #{}", img.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan::HostPasses
|
56
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
Normal file
56
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_common.h"
|
||||||
|
#include "video_core/texture_cache/image.h"
|
||||||
|
|
||||||
|
namespace Vulkan::HostPasses {
|
||||||
|
|
||||||
|
class FsrPass {
|
||||||
|
public:
|
||||||
|
struct Settings {
|
||||||
|
bool enable{true};
|
||||||
|
bool use_rcas{true};
|
||||||
|
float rcas_attenuation{0.25f};
|
||||||
|
};
|
||||||
|
|
||||||
|
void Create(vk::Device device, VmaAllocator allocator, u32 num_images);
|
||||||
|
|
||||||
|
vk::ImageView Render(vk::CommandBuffer cmdbuf, vk::ImageView input, vk::Extent2D input_size,
|
||||||
|
vk::Extent2D output_size, Settings settings, bool hdr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Img {
|
||||||
|
u8 id{};
|
||||||
|
bool dirty{true};
|
||||||
|
|
||||||
|
VideoCore::UniqueImage intermediary_image;
|
||||||
|
vk::UniqueImageView intermediary_image_view;
|
||||||
|
|
||||||
|
VideoCore::UniqueImage output_image;
|
||||||
|
vk::UniqueImageView output_image_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResizeAndInvalidate(u32 width, u32 height);
|
||||||
|
void CreateImages(Img& img) const;
|
||||||
|
|
||||||
|
vk::Device device{};
|
||||||
|
u32 num_images{};
|
||||||
|
|
||||||
|
vk::UniqueDescriptorSetLayout descriptor_set_layout{};
|
||||||
|
vk::UniqueDescriptorSet easu_descriptor_set{};
|
||||||
|
vk::UniqueDescriptorSet rcas_descriptor_set{};
|
||||||
|
vk::UniqueSampler sampler{};
|
||||||
|
vk::UniquePipelineLayout pipeline_layout{};
|
||||||
|
vk::UniquePipeline easu_pipeline{};
|
||||||
|
vk::UniquePipeline rcas_pipeline{};
|
||||||
|
|
||||||
|
vk::Extent2D cur_size{};
|
||||||
|
u32 cur_image{};
|
||||||
|
std::vector<Img> available_imgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan::HostPasses
|
255
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
Normal file
255
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "pp_pass.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "video_core/host_shaders/fs_tri_vert.h"
|
||||||
|
#include "video_core/host_shaders/post_process_frag.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_presenter.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
|
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
|
||||||
|
namespace Vulkan::HostPasses {
|
||||||
|
|
||||||
|
void PostProcessingPass::Create(vk::Device device) {
|
||||||
|
static const std::array pp_shaders{
|
||||||
|
HostShaders::FS_TRI_VERT,
|
||||||
|
HostShaders::POST_PROCESS_FRAG,
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::container::static_vector<vk::DescriptorSetLayoutBinding, 2> bindings{
|
||||||
|
{
|
||||||
|
.binding = 0,
|
||||||
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::DescriptorSetLayoutCreateInfo desc_layout_ci{
|
||||||
|
.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR,
|
||||||
|
.bindingCount = static_cast<u32>(bindings.size()),
|
||||||
|
.pBindings = bindings.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
desc_set_layout = Check<"create pp descriptor set layout">(
|
||||||
|
device.createDescriptorSetLayoutUnique(desc_layout_ci));
|
||||||
|
|
||||||
|
const vk::PushConstantRange push_constants{
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.offset = 0,
|
||||||
|
.size = sizeof(Settings),
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& vs_module = Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, device);
|
||||||
|
ASSERT(vs_module);
|
||||||
|
SetObjectName(device, vs_module, "fs_tri.vert");
|
||||||
|
|
||||||
|
const auto& fs_module = Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, device);
|
||||||
|
ASSERT(fs_module);
|
||||||
|
SetObjectName(device, fs_module, "post_process.frag");
|
||||||
|
|
||||||
|
const std::array shaders_ci{
|
||||||
|
vk::PipelineShaderStageCreateInfo{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vs_module,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
vk::PipelineShaderStageCreateInfo{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fs_module,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineLayoutCreateInfo layout_info{
|
||||||
|
.setLayoutCount = 1U,
|
||||||
|
.pSetLayouts = &*desc_set_layout,
|
||||||
|
.pushConstantRangeCount = 1,
|
||||||
|
.pPushConstantRanges = &push_constants,
|
||||||
|
};
|
||||||
|
|
||||||
|
pipeline_layout =
|
||||||
|
Check<"create pp pipeline layout">(device.createPipelineLayoutUnique(layout_info));
|
||||||
|
|
||||||
|
const std::array pp_color_formats{
|
||||||
|
vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format,
|
||||||
|
};
|
||||||
|
const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci{
|
||||||
|
.colorAttachmentCount = pp_color_formats.size(),
|
||||||
|
.pColorAttachmentFormats = pp_color_formats.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineVertexInputStateCreateInfo vertex_input_info{
|
||||||
|
.vertexBindingDescriptionCount = 0u,
|
||||||
|
.vertexAttributeDescriptionCount = 0u,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineInputAssemblyStateCreateInfo input_assembly{
|
||||||
|
.topology = vk::PrimitiveTopology::eTriangleList,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::Viewport viewport{
|
||||||
|
.x = 0.0f,
|
||||||
|
.y = 0.0f,
|
||||||
|
.width = 1.0f,
|
||||||
|
.height = 1.0f,
|
||||||
|
.minDepth = 0.0f,
|
||||||
|
.maxDepth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::Rect2D scissor = {
|
||||||
|
.offset = {0, 0},
|
||||||
|
.extent = {1, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineViewportStateCreateInfo viewport_info{
|
||||||
|
.viewportCount = 1,
|
||||||
|
.pViewports = &viewport,
|
||||||
|
.scissorCount = 1,
|
||||||
|
.pScissors = &scissor,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineRasterizationStateCreateInfo raster_state{
|
||||||
|
.depthClampEnable = false,
|
||||||
|
.rasterizerDiscardEnable = false,
|
||||||
|
.polygonMode = vk::PolygonMode::eFill,
|
||||||
|
.cullMode = vk::CullModeFlagBits::eBack,
|
||||||
|
.frontFace = vk::FrontFace::eClockwise,
|
||||||
|
.depthBiasEnable = false,
|
||||||
|
.lineWidth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineMultisampleStateCreateInfo multisampling{
|
||||||
|
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array attachments{
|
||||||
|
vk::PipelineColorBlendAttachmentState{
|
||||||
|
.blendEnable = false,
|
||||||
|
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||||
|
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineColorBlendStateCreateInfo color_blending{
|
||||||
|
.logicOpEnable = false,
|
||||||
|
.logicOp = vk::LogicOp::eCopy,
|
||||||
|
.attachmentCount = attachments.size(),
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array dynamic_states{
|
||||||
|
vk::DynamicState::eViewport,
|
||||||
|
vk::DynamicState::eScissor,
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PipelineDynamicStateCreateInfo dynamic_info{
|
||||||
|
.dynamicStateCount = dynamic_states.size(),
|
||||||
|
.pDynamicStates = dynamic_states.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::GraphicsPipelineCreateInfo pipeline_info{
|
||||||
|
.pNext = &pipeline_rendering_ci,
|
||||||
|
.stageCount = shaders_ci.size(),
|
||||||
|
.pStages = shaders_ci.data(),
|
||||||
|
.pVertexInputState = &vertex_input_info,
|
||||||
|
.pInputAssemblyState = &input_assembly,
|
||||||
|
.pViewportState = &viewport_info,
|
||||||
|
.pRasterizationState = &raster_state,
|
||||||
|
.pMultisampleState = &multisampling,
|
||||||
|
.pColorBlendState = &color_blending,
|
||||||
|
.pDynamicState = &dynamic_info,
|
||||||
|
.layout = *pipeline_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
pipeline = Check<"create post process pipeline">(device.createGraphicsPipelineUnique(
|
||||||
|
/*pipeline_cache*/ {}, pipeline_info));
|
||||||
|
|
||||||
|
// Once pipeline is compiled, we don't need the shader module anymore
|
||||||
|
device.destroyShaderModule(vs_module);
|
||||||
|
device.destroyShaderModule(fs_module);
|
||||||
|
|
||||||
|
// Create sampler resource
|
||||||
|
const vk::SamplerCreateInfo sampler_ci{
|
||||||
|
.magFilter = vk::Filter::eLinear,
|
||||||
|
.minFilter = vk::Filter::eLinear,
|
||||||
|
.mipmapMode = vk::SamplerMipmapMode::eNearest,
|
||||||
|
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
};
|
||||||
|
sampler = Check<"create pp sampler">(device.createSamplerUnique(sampler_ci));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||||
|
vk::Extent2D input_size, Frame& frame, Settings settings) {
|
||||||
|
const std::array<vk::RenderingAttachmentInfo, 1> attachments{{
|
||||||
|
{
|
||||||
|
.imageView = frame.image_view,
|
||||||
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
const vk::RenderingInfo rendering_info{
|
||||||
|
.renderArea{
|
||||||
|
.extent{
|
||||||
|
.width = frame.width,
|
||||||
|
.height = frame.height,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.layerCount = 1,
|
||||||
|
.colorAttachmentCount = attachments.size(),
|
||||||
|
.pColorAttachments = attachments.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorImageInfo image_info{
|
||||||
|
.sampler = *sampler,
|
||||||
|
.imageView = input,
|
||||||
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array set_writes{
|
||||||
|
vk::WriteDescriptorSet{
|
||||||
|
.dstSet = VK_NULL_HANDLE,
|
||||||
|
.dstBinding = 0,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.pImageInfo = &image_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pipeline);
|
||||||
|
|
||||||
|
const std::array viewports = {
|
||||||
|
vk::Viewport{
|
||||||
|
.width = static_cast<float>(frame.width),
|
||||||
|
.height = static_cast<float>(frame.height),
|
||||||
|
.minDepth = 0.0f,
|
||||||
|
.maxDepth = 1.0f,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cmdbuf.setViewport(0, viewports);
|
||||||
|
cmdbuf.setScissor(0, vk::Rect2D{
|
||||||
|
.extent{
|
||||||
|
.width = frame.width,
|
||||||
|
.height = frame.height,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, set_writes);
|
||||||
|
cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(Settings),
|
||||||
|
&settings);
|
||||||
|
|
||||||
|
cmdbuf.beginRendering(rendering_info);
|
||||||
|
cmdbuf.draw(3, 1, 0, 0);
|
||||||
|
cmdbuf.endRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan::HostPasses
|
34
src/video_core/renderer_vulkan/host_passes/pp_pass.h
Normal file
34
src/video_core/renderer_vulkan/host_passes/pp_pass.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_common.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
class Frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Vulkan::HostPasses {
|
||||||
|
|
||||||
|
class PostProcessingPass {
|
||||||
|
public:
|
||||||
|
struct Settings {
|
||||||
|
float gamma = 1.0f;
|
||||||
|
u32 hdr = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Create(vk::Device device);
|
||||||
|
|
||||||
|
void Render(vk::CommandBuffer cmdbuf, vk::ImageView input, vk::Extent2D input_size,
|
||||||
|
Frame& output, Settings settings);
|
||||||
|
|
||||||
|
private:
|
||||||
|
vk::UniquePipeline pipeline{};
|
||||||
|
vk::UniquePipelineLayout pipeline_layout{};
|
||||||
|
vk::UniqueDescriptorSetLayout desc_set_layout{};
|
||||||
|
vk::UniqueSampler sampler{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan::HostPasses
|
|
@ -5,7 +5,9 @@
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/string_literal.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/renderer_vulkan/vk_common.h"
|
#include "video_core/renderer_vulkan/vk_common.h"
|
||||||
|
|
||||||
|
@ -48,4 +50,25 @@ void SetObjectName(vk::Device device, const HandleType& handle, const char* form
|
||||||
SetObjectName(device, handle, debug_name);
|
SetObjectName(device, handle, debug_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <StringLiteral msg = "">
|
||||||
|
static void Check(vk::Result r) {
|
||||||
|
if constexpr (msg.len <= 1) {
|
||||||
|
ASSERT_MSG(r == vk::Result::eSuccess, "vk::Result={}", vk::to_string(r));
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(r == vk::Result::eSuccess, "Failed to {}: vk::Result={}", msg.value,
|
||||||
|
vk::to_string(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <StringLiteral msg = "", typename T>
|
||||||
|
static T Check(vk::ResultValue<T> r) {
|
||||||
|
if constexpr (msg.len <= 1) {
|
||||||
|
ASSERT_MSG(r.result == vk::Result::eSuccess, "vk::Result={}", vk::to_string(r.result));
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(r.result == vk::Result::eSuccess, "Failed to {}: vk::Result={}", msg.value,
|
||||||
|
vk::to_string(r.result));
|
||||||
|
}
|
||||||
|
return std::move(r.value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
#include "core/libraries/system/systemservice.h"
|
#include "core/libraries/system/systemservice.h"
|
||||||
#include "imgui/renderer/imgui_core.h"
|
#include "imgui/renderer/imgui_core.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||||
#include "video_core/renderer_vulkan/vk_presenter.h"
|
#include "video_core/renderer_vulkan/vk_presenter.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
#include "video_core/texture_cache/image.h"
|
#include "video_core/texture_cache/image.h"
|
||||||
|
|
||||||
#include "video_core/host_shaders/fs_tri_vert.h"
|
#include "video_core/host_shaders/fs_tri_vert.h"
|
||||||
#include "video_core/host_shaders/post_process_frag.h"
|
|
||||||
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
|
@ -106,191 +106,6 @@ static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_widt
|
||||||
dst_rect.offset.x, dst_rect.offset.y);
|
dst_rect.offset.x, dst_rect.offset.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Presenter::CreatePostProcessPipeline() {
|
|
||||||
static const std::array pp_shaders{
|
|
||||||
HostShaders::FS_TRI_VERT,
|
|
||||||
HostShaders::POST_PROCESS_FRAG,
|
|
||||||
};
|
|
||||||
|
|
||||||
boost::container::static_vector<vk::DescriptorSetLayoutBinding, 2> bindings{
|
|
||||||
{
|
|
||||||
.binding = 0,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = {
|
|
||||||
.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR,
|
|
||||||
.bindingCount = static_cast<u32>(bindings.size()),
|
|
||||||
.pBindings = bindings.data(),
|
|
||||||
};
|
|
||||||
auto desc_layout_result = instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci);
|
|
||||||
ASSERT_MSG(desc_layout_result.result == vk::Result::eSuccess,
|
|
||||||
"Failed to create descriptor set layout: {}",
|
|
||||||
vk::to_string(desc_layout_result.result));
|
|
||||||
pp_desc_set_layout = std::move(desc_layout_result.value);
|
|
||||||
|
|
||||||
const vk::PushConstantRange push_constants = {
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
.offset = 0,
|
|
||||||
.size = sizeof(PostProcessSettings),
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto& vs_module =
|
|
||||||
Vulkan::Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, instance.GetDevice());
|
|
||||||
ASSERT(vs_module);
|
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), vs_module, "fs_tri.vert");
|
|
||||||
|
|
||||||
const auto& fs_module =
|
|
||||||
Vulkan::Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, instance.GetDevice());
|
|
||||||
ASSERT(fs_module);
|
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), fs_module, "post_process.frag");
|
|
||||||
|
|
||||||
const std::array shaders_ci{
|
|
||||||
vk::PipelineShaderStageCreateInfo{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
|
||||||
.module = vs_module,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
vk::PipelineShaderStageCreateInfo{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
.module = fs_module,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::DescriptorSetLayout set_layout = *pp_desc_set_layout;
|
|
||||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
|
||||||
.setLayoutCount = 1U,
|
|
||||||
.pSetLayouts = &set_layout,
|
|
||||||
.pushConstantRangeCount = 1,
|
|
||||||
.pPushConstantRanges = &push_constants,
|
|
||||||
};
|
|
||||||
auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info);
|
|
||||||
ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}",
|
|
||||||
vk::to_string(layout_result));
|
|
||||||
pp_pipeline_layout = std::move(layout);
|
|
||||||
|
|
||||||
const std::array pp_color_formats{
|
|
||||||
vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format,
|
|
||||||
};
|
|
||||||
const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = {
|
|
||||||
.colorAttachmentCount = 1u,
|
|
||||||
.pColorAttachmentFormats = pp_color_formats.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
|
|
||||||
.vertexBindingDescriptionCount = 0u,
|
|
||||||
.vertexAttributeDescriptionCount = 0u,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
|
||||||
.topology = vk::PrimitiveTopology::eTriangleList,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::Viewport viewport = {
|
|
||||||
.x = 0.0f,
|
|
||||||
.y = 0.0f,
|
|
||||||
.width = 1.0f,
|
|
||||||
.height = 1.0f,
|
|
||||||
.minDepth = 0.0f,
|
|
||||||
.maxDepth = 1.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::Rect2D scissor = {
|
|
||||||
.offset = {0, 0},
|
|
||||||
.extent = {1, 1},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineViewportStateCreateInfo viewport_info = {
|
|
||||||
.viewportCount = 1,
|
|
||||||
.pViewports = &viewport,
|
|
||||||
.scissorCount = 1,
|
|
||||||
.pScissors = &scissor,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
|
||||||
.depthClampEnable = false,
|
|
||||||
.rasterizerDiscardEnable = false,
|
|
||||||
.polygonMode = vk::PolygonMode::eFill,
|
|
||||||
.cullMode = vk::CullModeFlagBits::eBack,
|
|
||||||
.frontFace = vk::FrontFace::eClockwise,
|
|
||||||
.depthBiasEnable = false,
|
|
||||||
.lineWidth = 1.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineMultisampleStateCreateInfo multisampling = {
|
|
||||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::array attachments{
|
|
||||||
vk::PipelineColorBlendAttachmentState{
|
|
||||||
.blendEnable = false,
|
|
||||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
|
||||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineColorBlendStateCreateInfo color_blending = {
|
|
||||||
.logicOpEnable = false,
|
|
||||||
.logicOp = vk::LogicOp::eCopy,
|
|
||||||
.attachmentCount = attachments.size(),
|
|
||||||
.pAttachments = attachments.data(),
|
|
||||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::array dynamic_states = {
|
|
||||||
vk::DynamicState::eViewport,
|
|
||||||
vk::DynamicState::eScissor,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::PipelineDynamicStateCreateInfo dynamic_info = {
|
|
||||||
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
|
|
||||||
.pDynamicStates = dynamic_states.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::GraphicsPipelineCreateInfo pipeline_info = {
|
|
||||||
.pNext = &pipeline_rendering_ci,
|
|
||||||
.stageCount = static_cast<u32>(shaders_ci.size()),
|
|
||||||
.pStages = shaders_ci.data(),
|
|
||||||
.pVertexInputState = &vertex_input_info,
|
|
||||||
.pInputAssemblyState = &input_assembly,
|
|
||||||
.pViewportState = &viewport_info,
|
|
||||||
.pRasterizationState = &raster_state,
|
|
||||||
.pMultisampleState = &multisampling,
|
|
||||||
.pColorBlendState = &color_blending,
|
|
||||||
.pDynamicState = &dynamic_info,
|
|
||||||
.layout = *pp_pipeline_layout,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = instance.GetDevice().createGraphicsPipelineUnique(
|
|
||||||
/*pipeline_cache*/ {}, pipeline_info);
|
|
||||||
if (result.result == vk::Result::eSuccess) {
|
|
||||||
pp_pipeline = std::move(result.value);
|
|
||||||
} else {
|
|
||||||
UNREACHABLE_MSG("Post process pipeline creation failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once pipeline is compiled, we don't need the shader module anymore
|
|
||||||
instance.GetDevice().destroyShaderModule(vs_module);
|
|
||||||
instance.GetDevice().destroyShaderModule(fs_module);
|
|
||||||
|
|
||||||
// Create sampler resource
|
|
||||||
const vk::SamplerCreateInfo sampler_ci = {
|
|
||||||
.magFilter = vk::Filter::eLinear,
|
|
||||||
.minFilter = vk::Filter::eLinear,
|
|
||||||
.mipmapMode = vk::SamplerMipmapMode::eNearest,
|
|
||||||
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
|
|
||||||
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
|
|
||||||
};
|
|
||||||
auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci);
|
|
||||||
ASSERT_MSG(sampler_result == vk::Result::eSuccess, "Failed to create sampler: {}",
|
|
||||||
vk::to_string(sampler_result));
|
|
||||||
pp_sampler = std::move(smplr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_)
|
Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_)
|
||||||
: window{window_}, liverpool{liverpool_},
|
: window{window_}, liverpool{liverpool_},
|
||||||
instance{window, Config::getGpuId(), Config::vkValidationEnabled(),
|
instance{window, Config::getGpuId(), Config::vkValidationEnabled(),
|
||||||
|
@ -306,15 +121,15 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_
|
||||||
present_frames.resize(num_images);
|
present_frames.resize(num_images);
|
||||||
for (u32 i = 0; i < num_images; i++) {
|
for (u32 i = 0; i < num_images; i++) {
|
||||||
Frame& frame = present_frames[i];
|
Frame& frame = present_frames[i];
|
||||||
auto [fence_result, fence] =
|
frame.id = i;
|
||||||
device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled});
|
auto fence = Check<"create present done fence">(
|
||||||
ASSERT_MSG(fence_result == vk::Result::eSuccess, "Failed to create present done fence: {}",
|
device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled}));
|
||||||
vk::to_string(fence_result));
|
|
||||||
frame.present_done = fence;
|
frame.present_done = fence;
|
||||||
free_queue.push(&frame);
|
free_queue.push(&frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatePostProcessPipeline();
|
fsr_pass.Create(device, instance.GetAllocator(), num_images);
|
||||||
|
pp_pass.Create(device);
|
||||||
|
|
||||||
ImGui::Layer::AddLayer(Common::Singleton<Core::Devtools::Layer>::Instance());
|
ImGui::Layer::AddLayer(Common::Singleton<Core::Devtools::Layer>::Instance());
|
||||||
}
|
}
|
||||||
|
@ -376,6 +191,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
frame->image = vk::Image{unsafe_image};
|
frame->image = vk::Image{unsafe_image};
|
||||||
|
SetObjectName(device, frame->image, "Frame image #{}", frame->id);
|
||||||
|
|
||||||
const vk::ImageViewCreateInfo view_info = {
|
const vk::ImageViewCreateInfo view_info = {
|
||||||
.image = frame->image,
|
.image = frame->image,
|
||||||
|
@ -389,9 +205,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) {
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
auto [view_result, view] = device.createImageView(view_info);
|
auto view = Check<"create frame image view">(device.createImageView(view_info));
|
||||||
ASSERT_MSG(view_result == vk::Result::eSuccess, "Failed to create frame image view: {}",
|
|
||||||
vk::to_string(view_result));
|
|
||||||
frame->image_view = view;
|
frame->image_view = view;
|
||||||
frame->width = width;
|
frame->width = width;
|
||||||
frame->height = height;
|
frame->height = height;
|
||||||
|
@ -560,13 +374,9 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
||||||
// Request a free presentation frame.
|
// Request a free presentation frame.
|
||||||
Frame* frame = GetRenderFrame();
|
Frame* frame = GetRenderFrame();
|
||||||
|
|
||||||
if (image_id != VideoCore::NULL_IMAGE_ID) {
|
if (frame->width != expected_frame_width || frame->height != expected_frame_height ||
|
||||||
const auto& image = texture_cache.GetImage(image_id);
|
|
||||||
const auto extent = image.info.size;
|
|
||||||
if (frame->width != extent.width || frame->height != extent.height ||
|
|
||||||
frame->is_hdr != swapchain.GetHDR()) {
|
frame->is_hdr != swapchain.GetHDR()) {
|
||||||
RecreateFrame(frame, extent.width, extent.height);
|
RecreateFrame(frame, expected_frame_width, expected_frame_height);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOP flips are triggered from GPU thread so use the drawing scheduler to record
|
// EOP flips are triggered from GPU thread so use the drawing scheduler to record
|
||||||
|
@ -575,7 +385,9 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
||||||
auto& scheduler = is_eop ? draw_scheduler : flip_scheduler;
|
auto& scheduler = is_eop ? draw_scheduler : flip_scheduler;
|
||||||
scheduler.EndRendering();
|
scheduler.EndRendering();
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
if (Config::getVkHostMarkersEnabled()) {
|
|
||||||
|
bool vk_host_markers_enabled = Config::getVkHostMarkersEnabled();
|
||||||
|
if (vk_host_markers_enabled) {
|
||||||
const auto label = fmt::format("PrepareFrameInternal:{}", image_id.index);
|
const auto label = fmt::format("PrepareFrameInternal:{}", image_id.index);
|
||||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||||
.pLabelName = label.c_str(),
|
.pLabelName = label.c_str(),
|
||||||
|
@ -605,33 +417,12 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
||||||
.pImageMemoryBarriers = &pre_barrier,
|
.pImageMemoryBarriers = &pre_barrier,
|
||||||
});
|
});
|
||||||
|
|
||||||
const std::array attachments = {vk::RenderingAttachmentInfo{
|
|
||||||
.imageView = frame->image_view,
|
|
||||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
||||||
.loadOp = vk::AttachmentLoadOp::eClear,
|
|
||||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
|
||||||
}};
|
|
||||||
const vk::RenderingInfo rendering_info{
|
|
||||||
.renderArea =
|
|
||||||
vk::Rect2D{
|
|
||||||
.offset = {0, 0},
|
|
||||||
.extent = {frame->width, frame->height},
|
|
||||||
},
|
|
||||||
.layerCount = 1,
|
|
||||||
.colorAttachmentCount = attachments.size(),
|
|
||||||
.pColorAttachments = attachments.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (image_id != VideoCore::NULL_IMAGE_ID) {
|
if (image_id != VideoCore::NULL_IMAGE_ID) {
|
||||||
auto& image = texture_cache.GetImage(image_id);
|
auto& image = texture_cache.GetImage(image_id);
|
||||||
|
vk::Extent2D image_size = {image.info.size.width, image.info.size.height};
|
||||||
image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {},
|
image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {},
|
||||||
cmdbuf);
|
cmdbuf);
|
||||||
|
|
||||||
static vk::DescriptorImageInfo image_info{
|
|
||||||
.sampler = *pp_sampler,
|
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
};
|
|
||||||
|
|
||||||
VideoCore::ImageViewInfo info{};
|
VideoCore::ImageViewInfo info{};
|
||||||
info.format = image.info.pixel_format;
|
info.format = image.info.pixel_format;
|
||||||
// Exclude alpha from output frame to avoid blending with UI.
|
// Exclude alpha from output frame to avoid blending with UI.
|
||||||
|
@ -641,52 +432,53 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
||||||
.b = vk::ComponentSwizzle::eIdentity,
|
.b = vk::ComponentSwizzle::eIdentity,
|
||||||
.a = vk::ComponentSwizzle::eOne,
|
.a = vk::ComponentSwizzle::eOne,
|
||||||
};
|
};
|
||||||
|
vk::ImageView imageView;
|
||||||
if (auto view = image.FindView(info)) {
|
if (auto view = image.FindView(info)) {
|
||||||
image_info.imageView = *texture_cache.GetImageView(view).image_view;
|
imageView = *texture_cache.GetImageView(view).image_view;
|
||||||
} else {
|
} else {
|
||||||
image_info.imageView = *texture_cache.RegisterImageView(image_id, info).image_view;
|
imageView = *texture_cache.RegisterImageView(image_id, info).image_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::array set_writes{
|
if (vk_host_markers_enabled) {
|
||||||
vk::WriteDescriptorSet{
|
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||||
.dstSet = VK_NULL_HANDLE,
|
.pLabelName = "Host/FSR",
|
||||||
.dstBinding = 0,
|
});
|
||||||
.dstArrayElement = 0,
|
}
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.pImageInfo = &image_info,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pp_pipeline);
|
imageView = fsr_pass.Render(cmdbuf, imageView, image_size, {frame->width, frame->height},
|
||||||
|
fsr_settings, frame->is_hdr);
|
||||||
|
|
||||||
const auto& dst_rect =
|
if (vk_host_markers_enabled) {
|
||||||
FitImage(image.info.size.width, image.info.size.height, frame->width, frame->height);
|
cmdbuf.endDebugUtilsLabelEXT();
|
||||||
|
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||||
|
.pLabelName = "Host/Post processing",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pp_pass.Render(cmdbuf, imageView, image_size, *frame, pp_settings);
|
||||||
|
if (vk_host_markers_enabled) {
|
||||||
|
cmdbuf.endDebugUtilsLabelEXT();
|
||||||
|
}
|
||||||
|
|
||||||
const std::array viewports = {
|
DebugState.game_resolution = {image_size.width, image_size.height};
|
||||||
vk::Viewport{
|
DebugState.output_resolution = {frame->width, frame->height};
|
||||||
.x = 1.0f * dst_rect.offset.x,
|
|
||||||
.y = 1.0f * dst_rect.offset.y,
|
|
||||||
.width = 1.0f * dst_rect.extent.width,
|
|
||||||
.height = 1.0f * dst_rect.extent.height,
|
|
||||||
.minDepth = 0.0f,
|
|
||||||
.maxDepth = 1.0f,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
cmdbuf.setViewport(0, viewports);
|
|
||||||
cmdbuf.setScissor(0, {dst_rect});
|
|
||||||
|
|
||||||
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pp_pipeline_layout, 0,
|
|
||||||
set_writes);
|
|
||||||
cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0,
|
|
||||||
sizeof(PostProcessSettings), &pp_settings);
|
|
||||||
|
|
||||||
cmdbuf.beginRendering(rendering_info);
|
|
||||||
cmdbuf.draw(3, 1, 0, 0);
|
|
||||||
cmdbuf.endRendering();
|
|
||||||
} else {
|
} else {
|
||||||
// Fix display of garbage images on startup on some drivers
|
// Fix display of garbage images on startup on some drivers
|
||||||
|
const std::array<vk::RenderingAttachmentInfo, 1> attachments = {{
|
||||||
|
{
|
||||||
|
.imageView = frame->image_view,
|
||||||
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
const vk::RenderingInfo rendering_info{
|
||||||
|
.renderArea{
|
||||||
|
.extent{frame->width, frame->height},
|
||||||
|
},
|
||||||
|
.layerCount = 1,
|
||||||
|
.colorAttachmentCount = attachments.size(),
|
||||||
|
.pColorAttachments = attachments.data(),
|
||||||
|
};
|
||||||
cmdbuf.beginRendering(rendering_info);
|
cmdbuf.beginRendering(rendering_info);
|
||||||
cmdbuf.endRendering();
|
cmdbuf.endRendering();
|
||||||
}
|
}
|
||||||
|
@ -706,7 +498,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
||||||
.pImageMemoryBarriers = &post_barrier,
|
.pImageMemoryBarriers = &post_barrier,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Config::getVkHostMarkersEnabled()) {
|
if (vk_host_markers_enabled) {
|
||||||
cmdbuf.endDebugUtilsLabelEXT();
|
cmdbuf.endDebugUtilsLabelEXT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,6 +628,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) {
|
||||||
ImVec2 contentArea = ImGui::GetContentRegionAvail();
|
ImVec2 contentArea = ImGui::GetContentRegionAvail();
|
||||||
const vk::Rect2D imgRect =
|
const vk::Rect2D imgRect =
|
||||||
FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y);
|
FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y);
|
||||||
|
SetExpectedGameSize((s32)contentArea.x, (s32)contentArea.y);
|
||||||
ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{
|
ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{
|
||||||
(float)imgRect.offset.x,
|
(float)imgRect.offset.x,
|
||||||
(float)imgRect.offset.y,
|
(float)imgRect.offset.y,
|
||||||
|
@ -914,12 +707,20 @@ Frame* Presenter::GetRenderFrame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize default frame image
|
|
||||||
if (frame->width == 0 || frame->height == 0 || frame->is_hdr != swapchain.GetHDR()) {
|
|
||||||
RecreateFrame(frame, Config::getScreenWidth(), Config::getScreenHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Presenter::SetExpectedGameSize(s32 width, s32 height) {
|
||||||
|
constexpr float expectedRatio = 1920.0 / 1080.0f;
|
||||||
|
const float ratio = (float)width / (float)height;
|
||||||
|
|
||||||
|
expected_frame_height = height;
|
||||||
|
expected_frame_width = width;
|
||||||
|
if (ratio > expectedRatio) {
|
||||||
|
expected_frame_width = static_cast<s32>(height * expectedRatio);
|
||||||
|
} else {
|
||||||
|
expected_frame_height = static_cast<s32>(width / expectedRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "imgui/imgui_config.h"
|
#include "imgui/imgui_config.h"
|
||||||
#include "video_core/amdgpu/liverpool.h"
|
#include "video_core/amdgpu/liverpool.h"
|
||||||
|
#include "video_core/renderer_vulkan/host_passes/fsr_pass.h"
|
||||||
|
#include "video_core/renderer_vulkan/host_passes/pp_pass.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
|
@ -32,6 +34,7 @@ struct Frame {
|
||||||
vk::Semaphore ready_semaphore;
|
vk::Semaphore ready_semaphore;
|
||||||
u64 ready_tick;
|
u64 ready_tick;
|
||||||
bool is_hdr{false};
|
bool is_hdr{false};
|
||||||
|
u8 id{};
|
||||||
|
|
||||||
ImTextureID imgui_texture;
|
ImTextureID imgui_texture;
|
||||||
};
|
};
|
||||||
|
@ -45,17 +48,16 @@ enum SchedulerType {
|
||||||
class Rasterizer;
|
class Rasterizer;
|
||||||
|
|
||||||
class Presenter {
|
class Presenter {
|
||||||
struct PostProcessSettings {
|
|
||||||
float gamma = 1.0f;
|
|
||||||
u32 hdr = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Presenter(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool);
|
Presenter(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool);
|
||||||
~Presenter();
|
~Presenter();
|
||||||
|
|
||||||
float& GetGammaRef() {
|
HostPasses::PostProcessingPass::Settings& GetPPSettingsRef() {
|
||||||
return pp_settings.gamma;
|
return pp_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostPasses::FsrPass::Settings& GetFsrSettingsRef() {
|
||||||
|
return fsr_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
Frontend::WindowSDL& GetWindow() const {
|
Frontend::WindowSDL& GetWindow() const {
|
||||||
|
@ -117,16 +119,19 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreatePostProcessPipeline();
|
|
||||||
Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true);
|
Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true);
|
||||||
Frame* GetRenderFrame();
|
Frame* GetRenderFrame();
|
||||||
|
|
||||||
|
void SetExpectedGameSize(s32 width, s32 height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PostProcessSettings pp_settings{};
|
u32 expected_frame_width{1920};
|
||||||
vk::UniquePipeline pp_pipeline{};
|
u32 expected_frame_height{1080};
|
||||||
vk::UniquePipelineLayout pp_pipeline_layout{};
|
|
||||||
vk::UniqueDescriptorSetLayout pp_desc_set_layout{};
|
HostPasses::FsrPass fsr_pass;
|
||||||
vk::UniqueSampler pp_sampler{};
|
HostPasses::FsrPass::Settings fsr_settings{};
|
||||||
|
HostPasses::PostProcessingPass::Settings pp_settings{};
|
||||||
|
HostPasses::PostProcessingPass pp_pass;
|
||||||
Frontend::WindowSDL& window;
|
Frontend::WindowSDL& window;
|
||||||
AmdGpu::Liverpool* liverpool;
|
AmdGpu::Liverpool* liverpool;
|
||||||
Instance instance;
|
Instance instance;
|
||||||
|
|
|
@ -159,7 +159,8 @@ bool InitializeCompiler() {
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device) {
|
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device,
|
||||||
|
std::vector<std::string> defines) {
|
||||||
if (!InitializeCompiler()) {
|
if (!InitializeCompiler()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -178,12 +179,46 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||||
glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
|
glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
|
||||||
shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
|
shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
|
||||||
|
|
||||||
|
std::string preambleString;
|
||||||
|
std::vector<std::string> processes;
|
||||||
|
|
||||||
|
for (auto& def : defines) {
|
||||||
|
processes.push_back("define-macro ");
|
||||||
|
processes.back().append(def);
|
||||||
|
|
||||||
|
preambleString.append("#define ");
|
||||||
|
if (const size_t equal = def.find_first_of("="); equal != def.npos) {
|
||||||
|
def[equal] = ' ';
|
||||||
|
}
|
||||||
|
preambleString.append(def);
|
||||||
|
preambleString.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
shader->setPreamble(preambleString.c_str());
|
||||||
|
shader->addProcesses(processes);
|
||||||
|
|
||||||
glslang::TShader::ForbidIncluder includer;
|
glslang::TShader::ForbidIncluder includer;
|
||||||
|
|
||||||
|
std::string preprocessedStr;
|
||||||
|
if (!shader->preprocess(&DefaultTBuiltInResource, default_version, profile, false, true,
|
||||||
|
messages, &preprocessedStr, includer)) [[unlikely]] {
|
||||||
|
LOG_ERROR(Render_Vulkan,
|
||||||
|
"Shader preprocess error\n"
|
||||||
|
"Shader Info Log:\n"
|
||||||
|
"{}\n{}",
|
||||||
|
shader->getInfoLog(), shader->getInfoDebugLog());
|
||||||
|
LOG_ERROR(Render_Vulkan, "Shader Source:\n{}", code);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages,
|
if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages,
|
||||||
includer)) [[unlikely]] {
|
includer)) [[unlikely]] {
|
||||||
LOG_INFO(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(),
|
LOG_ERROR(Render_Vulkan,
|
||||||
shader->getInfoDebugLog());
|
"Shader parse error\n"
|
||||||
LOG_INFO(Render_Vulkan, "Shader Source:\n{}", code);
|
"Shader Info Log:\n"
|
||||||
|
"{}\n{}",
|
||||||
|
shader->getInfoLog(), shader->getInfoDebugLog());
|
||||||
|
LOG_ERROR(Render_Vulkan, "Shader Source:\n{}", code);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +226,11 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||||
auto program = std::make_unique<glslang::TProgram>();
|
auto program = std::make_unique<glslang::TProgram>();
|
||||||
program->addShader(shader.get());
|
program->addShader(shader.get());
|
||||||
if (!program->link(messages)) {
|
if (!program->link(messages)) {
|
||||||
LOG_INFO(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(),
|
LOG_ERROR(Render_Vulkan,
|
||||||
program->getInfoDebugLog());
|
"Shader link error\n"
|
||||||
|
"Program Info Log:\n"
|
||||||
|
"{}\n{}",
|
||||||
|
program->getInfoLog(), program->getInfoDebugLog());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ namespace Vulkan {
|
||||||
* @param stage The pipeline stage the shader will be used in.
|
* @param stage The pipeline stage the shader will be used in.
|
||||||
* @param device The vulkan device handle.
|
* @param device The vulkan device handle.
|
||||||
*/
|
*/
|
||||||
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device);
|
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device,
|
||||||
|
std::vector<std::string> defines = {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a vulkan shader module from SPIR-V bytecode.
|
* @brief Creates a vulkan shader module from SPIR-V bytecode.
|
||||||
|
|
|
@ -113,6 +113,8 @@ static vk::FormatFeatureFlags2 FormatFeatureFlags(const vk::ImageUsageFlags usag
|
||||||
return feature_flags;
|
return feature_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniqueImage::UniqueImage() {}
|
||||||
|
|
||||||
UniqueImage::UniqueImage(vk::Device device_, VmaAllocator allocator_)
|
UniqueImage::UniqueImage(vk::Device device_, VmaAllocator allocator_)
|
||||||
: device{device_}, allocator{allocator_} {}
|
: device{device_}, allocator{allocator_} {}
|
||||||
|
|
||||||
|
@ -123,6 +125,9 @@ UniqueImage::~UniqueImage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) {
|
void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) {
|
||||||
|
if (image) {
|
||||||
|
vmaDestroyImage(allocator, image, allocation);
|
||||||
|
}
|
||||||
const VmaAllocationCreateInfo alloc_info = {
|
const VmaAllocationCreateInfo alloc_info = {
|
||||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
|
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
||||||
|
|
|
@ -35,6 +35,7 @@ enum ImageFlagBits : u32 {
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
|
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
|
||||||
|
|
||||||
struct UniqueImage {
|
struct UniqueImage {
|
||||||
|
explicit UniqueImage();
|
||||||
explicit UniqueImage(vk::Device device, VmaAllocator allocator);
|
explicit UniqueImage(vk::Device device, VmaAllocator allocator);
|
||||||
~UniqueImage();
|
~UniqueImage();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue