FullscreenUI: Add offscreen-based screen fade
This commit is contained in:
parent
4e2872f248
commit
e6e6313219
|
@ -8,6 +8,7 @@
|
|||
#include "controller.h"
|
||||
#include "game_list.h"
|
||||
#include "gpu.h"
|
||||
#include "gpu_backend.h"
|
||||
#include "gpu_presenter.h"
|
||||
#include "gpu_thread.h"
|
||||
#include "gte_types.h"
|
||||
|
@ -237,6 +238,8 @@ static void CopyTextToClipboard(std::string title, std::string_view text);
|
|||
static void DrawAboutWindow();
|
||||
static void FixStateIfPaused();
|
||||
static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
|
||||
static bool CompileTransitionPipelines();
|
||||
static void UpdateTransitionState();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Backgrounds
|
||||
|
@ -528,6 +531,7 @@ private:
|
|||
struct ALIGN_TO_CACHE_LINE UIState
|
||||
{
|
||||
// Main
|
||||
TransitionState transition_state = TransitionState::Inactive;
|
||||
MainWindowType current_main_window = MainWindowType::None;
|
||||
PauseSubMenu current_pause_submenu = PauseSubMenu::None;
|
||||
bool initialized = false;
|
||||
|
@ -552,6 +556,14 @@ struct ALIGN_TO_CACHE_LINE UIState
|
|||
std::unique_ptr<GPUPipeline> app_background_shader;
|
||||
Timer::Value app_background_load_time = 0;
|
||||
|
||||
// Transition Resources
|
||||
TransitionStartCallback transition_start_callback;
|
||||
std::unique_ptr<GPUTexture> transition_prev_texture;
|
||||
std::unique_ptr<GPUTexture> transition_current_texture;
|
||||
std::unique_ptr<GPUPipeline> transition_blend_pipeline;
|
||||
float transition_total_time = 0.0f;
|
||||
float transition_remaining_time = 0.0f;
|
||||
|
||||
// Settings
|
||||
float settings_last_bg_alpha = 1.0f;
|
||||
SettingsPage settings_page = SettingsPage::Interface;
|
||||
|
@ -764,7 +776,8 @@ bool FullscreenUI::IsInitialized()
|
|||
|
||||
bool FullscreenUI::HasActiveWindow()
|
||||
{
|
||||
return s_state.initialized && (s_state.current_main_window != MainWindowType::None || AreAnyDialogsOpen());
|
||||
return s_state.initialized && (s_state.current_main_window != MainWindowType::None ||
|
||||
s_state.transition_state != TransitionState::Inactive || AreAnyDialogsOpen());
|
||||
}
|
||||
|
||||
bool FullscreenUI::AreAnyDialogsOpen()
|
||||
|
@ -786,6 +799,169 @@ void FullscreenUI::UpdateRunIdleState()
|
|||
GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, new_run_idle);
|
||||
}
|
||||
|
||||
void FullscreenUI::BeginTransition(TransitionStartCallback func, float time)
|
||||
{
|
||||
if (s_state.transition_state == TransitionState::Starting)
|
||||
{
|
||||
WARNING_LOG("More than one transition started");
|
||||
if (s_state.transition_start_callback)
|
||||
std::move(s_state.transition_start_callback)();
|
||||
}
|
||||
|
||||
s_state.transition_state = TransitionState::Starting;
|
||||
s_state.transition_total_time = time;
|
||||
s_state.transition_remaining_time = time;
|
||||
s_state.transition_start_callback = func;
|
||||
UpdateRunIdleState();
|
||||
}
|
||||
|
||||
void FullscreenUI::CancelTransition()
|
||||
{
|
||||
if (s_state.transition_state != TransitionState::Active)
|
||||
return;
|
||||
|
||||
if (s_state.transition_state == TransitionState::Starting && s_state.transition_start_callback)
|
||||
std::move(s_state.transition_start_callback)();
|
||||
|
||||
s_state.transition_state = TransitionState::Inactive;
|
||||
s_state.transition_start_callback = {};
|
||||
s_state.transition_remaining_time = 0.0f;
|
||||
}
|
||||
|
||||
void FullscreenUI::BeginTransition(float time, TransitionStartCallback func)
|
||||
{
|
||||
BeginTransition(std::move(func), time);
|
||||
}
|
||||
|
||||
bool FullscreenUI::IsTransitionActive()
|
||||
{
|
||||
return (s_state.transition_state != TransitionState::Inactive);
|
||||
}
|
||||
|
||||
FullscreenUI::TransitionState FullscreenUI::GetTransitionState()
|
||||
{
|
||||
return s_state.transition_state;
|
||||
}
|
||||
|
||||
GPUTexture* FullscreenUI::GetTransitionRenderTexture(GPUSwapChain* swap_chain)
|
||||
{
|
||||
if (!g_gpu_device->ResizeTexture(&s_state.transition_current_texture, swap_chain->GetWidth(), swap_chain->GetHeight(),
|
||||
GPUTexture::Type::RenderTarget, swap_chain->GetFormat(), GPUTexture::Flags::None,
|
||||
false))
|
||||
{
|
||||
ERROR_LOG("Failed to allocate {}x{} texture for transition, cancelling.", swap_chain->GetWidth(),
|
||||
swap_chain->GetHeight());
|
||||
s_state.transition_state = TransitionState::Inactive;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return s_state.transition_current_texture.get();
|
||||
}
|
||||
|
||||
bool FullscreenUI::CompileTransitionPipelines()
|
||||
{
|
||||
const RenderAPI render_api = g_gpu_device->GetRenderAPI();
|
||||
const ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), false, false);
|
||||
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
|
||||
Error error;
|
||||
std::unique_ptr<GPUShader> vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
|
||||
shadergen.GeneratePassthroughVertexShader(), &error);
|
||||
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
|
||||
shadergen.GenerateFadeFragmentShader(), &error);
|
||||
if (!vs || !fs)
|
||||
{
|
||||
ERROR_LOG("Failed to compile transition shaders: {}", error.GetDescription());
|
||||
return false;
|
||||
}
|
||||
GL_OBJECT_NAME(vs, "Transition Vertex Shader");
|
||||
GL_OBJECT_NAME(fs, "Transition Fragment Shader");
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig;
|
||||
GPUBackend::SetScreenQuadInputLayout(plconfig);
|
||||
plconfig.layout = GPUPipeline::Layout::MultiTextureAndPushConstants;
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
||||
plconfig.SetTargetFormats(swap_chain ? swap_chain->GetFormat() : GPUTexture::Format::RGBA8);
|
||||
plconfig.samples = 1;
|
||||
plconfig.per_sample_shading = false;
|
||||
plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||
plconfig.vertex_shader = vs.get();
|
||||
plconfig.geometry_shader = nullptr;
|
||||
plconfig.fragment_shader = fs.get();
|
||||
|
||||
s_state.transition_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, &error);
|
||||
if (!s_state.transition_blend_pipeline)
|
||||
{
|
||||
ERROR_LOG("Failed to create transition blend pipeline: {}", error.GetDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FullscreenUI::RenderTransitionBlend(GPUSwapChain* swap_chain)
|
||||
{
|
||||
GPUTexture* const curr = s_state.transition_current_texture.get();
|
||||
DebugAssert(curr);
|
||||
|
||||
if (s_state.transition_state == TransitionState::Starting)
|
||||
{
|
||||
// copy current frame
|
||||
if (!g_gpu_device->ResizeTexture(&s_state.transition_prev_texture, curr->GetWidth(), curr->GetHeight(),
|
||||
GPUTexture::Type::RenderTarget, curr->GetFormat(), GPUTexture::Flags::None, false))
|
||||
{
|
||||
ERROR_LOG("Failed to allocate {}x{} texture for transition, cancelling.", curr->GetWidth(), curr->GetHeight());
|
||||
s_state.transition_state = TransitionState::Inactive;
|
||||
return;
|
||||
}
|
||||
|
||||
g_gpu_device->CopyTextureRegion(s_state.transition_prev_texture.get(), 0, 0, 0, 0, curr, 0, 0, 0, 0,
|
||||
curr->GetWidth(), curr->GetHeight());
|
||||
|
||||
s_state.transition_state = TransitionState::Active;
|
||||
}
|
||||
|
||||
const float transition_alpha = s_state.transition_remaining_time / s_state.transition_total_time;
|
||||
const float uniforms[2] = {1.0f - transition_alpha, transition_alpha};
|
||||
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||
g_gpu_device->SetPipeline(s_state.transition_blend_pipeline.get());
|
||||
g_gpu_device->SetViewportAndScissor(0, 0, swap_chain->GetPostRotatedWidth(), swap_chain->GetPostRotatedHeight());
|
||||
g_gpu_device->SetTextureSampler(0, curr, g_gpu_device->GetNearestSampler());
|
||||
g_gpu_device->SetTextureSampler(1, s_state.transition_prev_texture.get(), g_gpu_device->GetNearestSampler());
|
||||
|
||||
const GSVector2i size = swap_chain->GetSizeVec();
|
||||
const GSVector2i postrotated_size = swap_chain->GetPostRotatedSizeVec();
|
||||
const GSVector4 uv_rect = g_gpu_device->UsesLowerLeftOrigin() ? GSVector4::cxpr(0.0f, 1.0f, 1.0f, 0.0f) :
|
||||
GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
GPUPresenter::DrawScreenQuad(GSVector4i::loadh(size), uv_rect, size, postrotated_size, DisplayRotation::Normal,
|
||||
swap_chain->GetPreRotation());
|
||||
}
|
||||
|
||||
void FullscreenUI::UpdateTransitionState()
|
||||
{
|
||||
if (s_state.transition_state == TransitionState::Inactive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (s_state.transition_state == TransitionState::Starting)
|
||||
{
|
||||
// starting is cleared in render
|
||||
if (s_state.transition_start_callback)
|
||||
std::move(s_state.transition_start_callback)();
|
||||
}
|
||||
|
||||
s_state.transition_remaining_time -= ImGui::GetIO().DeltaTime;
|
||||
if (s_state.transition_remaining_time <= 0.0f)
|
||||
{
|
||||
// At 1080p we're only talking 2MB of VRAM, 16MB at 4K.. saves reallocating it on the next transition.
|
||||
// g_gpu_device->RecycleTexture(std::move(s_state.transition_current_texture));
|
||||
// g_gpu_device->RecycleTexture(std::move(s_state.transition_prev_texture));
|
||||
s_state.transition_state = TransitionState::Inactive;
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::OnSystemStarting()
|
||||
{
|
||||
// NOTE: Called on CPU thread.
|
||||
|
@ -1116,6 +1292,7 @@ void FullscreenUI::Render()
|
|||
}
|
||||
|
||||
ImGuiFullscreen::ResetCloseMenuIfNeeded();
|
||||
UpdateTransitionState();
|
||||
}
|
||||
|
||||
void FullscreenUI::InvalidateCoverCache()
|
||||
|
@ -1175,11 +1352,20 @@ bool FullscreenUI::LoadResources()
|
|||
s_state.fallback_exe_texture = LoadTexture("fullscreenui/exe-file.png");
|
||||
s_state.fallback_psf_texture = LoadTexture("fullscreenui/psf-file.png");
|
||||
s_state.fallback_playlist_texture = LoadTexture("fullscreenui/playlist-file.png");
|
||||
|
||||
if (!CompileTransitionPipelines())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FullscreenUI::DestroyResources()
|
||||
{
|
||||
s_state.transition_blend_pipeline.reset();
|
||||
g_gpu_device->RecycleTexture(std::move(s_state.transition_prev_texture));
|
||||
g_gpu_device->RecycleTexture(std::move(s_state.transition_current_texture));
|
||||
s_state.transition_state = TransitionState::Inactive;
|
||||
s_state.transition_start_callback = {};
|
||||
s_state.fallback_playlist_texture.reset();
|
||||
s_state.fallback_psf_texture.reset();
|
||||
s_state.fallback_exe_texture.reset();
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
class SmallStringBase;
|
||||
|
||||
class GPUSwapChain;
|
||||
class GPUTexture;
|
||||
|
||||
struct GPUSettings;
|
||||
|
||||
namespace FullscreenUI {
|
||||
|
@ -46,6 +49,25 @@ void SetTheme();
|
|||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
static constexpr float SHORT_TRANSITION_TIME = 0.08f;
|
||||
static constexpr float DEFAULT_TRANSITION_TIME = 0.15f;
|
||||
|
||||
enum class TransitionState : u8
|
||||
{
|
||||
Inactive,
|
||||
Starting,
|
||||
Active,
|
||||
};
|
||||
|
||||
using TransitionStartCallback = std::function<void()>;
|
||||
void BeginTransition(TransitionStartCallback func, float time = DEFAULT_TRANSITION_TIME);
|
||||
void BeginTransition(float time, TransitionStartCallback func);
|
||||
void CancelTransition();
|
||||
bool IsTransitionActive();
|
||||
TransitionState GetTransitionState();
|
||||
GPUTexture* GetTransitionRenderTexture(GPUSwapChain* swap_chain);
|
||||
void RenderTransitionBlend(GPUSwapChain* swap_chain);
|
||||
|
||||
std::vector<std::string_view> GetThemeNames();
|
||||
std::span<const char* const> GetThemeConfigNames();
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
|
|||
plconfig.SetTargetFormats(m_present_format);
|
||||
|
||||
std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
|
||||
shadergen.GenerateDisplayVertexShader(), error);
|
||||
shadergen.GeneratePassthroughVertexShader(), error);
|
||||
if (!vso)
|
||||
return false;
|
||||
GL_OBJECT_NAME(vso, "Display Vertex Shader");
|
||||
|
@ -1047,19 +1047,39 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
|||
ImGuiManager::RenderSoftwareCursors();
|
||||
|
||||
ImGuiManager::RenderDebugWindows();
|
||||
|
||||
// render offscreen for transitions
|
||||
if (FullscreenUI::IsTransitionActive())
|
||||
{
|
||||
GPUTexture* const rtex = FullscreenUI::GetTransitionRenderTexture(g_gpu_device->GetMainSwapChain());
|
||||
if (rtex)
|
||||
{
|
||||
if (presenter)
|
||||
presenter->RenderDisplay(rtex, rtex->GetSizeVec(), true, true);
|
||||
else
|
||||
g_gpu_device->ClearRenderTarget(rtex, GPUDevice::DEFAULT_CLEAR_COLOR);
|
||||
|
||||
g_gpu_device->SetRenderTarget(rtex);
|
||||
g_gpu_device->RenderImGui(rtex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
const GPUDevice::PresentResult pres =
|
||||
skip_present ? GPUDevice::PresentResult::SkipPresent :
|
||||
(presenter ? presenter->RenderDisplay(nullptr, swap_chain->GetSizeVec(), true, true) :
|
||||
g_gpu_device->BeginPresent(swap_chain));
|
||||
const GPUDevice::PresentResult pres = skip_present ?
|
||||
GPUDevice::PresentResult::SkipPresent :
|
||||
((presenter && !FullscreenUI::IsTransitionActive()) ?
|
||||
presenter->RenderDisplay(nullptr, swap_chain->GetSizeVec(), true, true) :
|
||||
g_gpu_device->BeginPresent(swap_chain));
|
||||
if (pres == GPUDevice::PresentResult::OK)
|
||||
{
|
||||
if (presenter)
|
||||
presenter->m_skipped_present_count = 0;
|
||||
|
||||
g_gpu_device->RenderImGui(swap_chain);
|
||||
if (FullscreenUI::IsTransitionActive())
|
||||
FullscreenUI::RenderTransitionBlend(swap_chain);
|
||||
else
|
||||
g_gpu_device->RenderImGui(swap_chain);
|
||||
|
||||
const GPUDevice::Features features = g_gpu_device->GetFeatures();
|
||||
const bool scheduled_present = (present_time != 0);
|
||||
|
|
|
@ -89,6 +89,11 @@ public:
|
|||
/// Reloads post-processing settings. Only callable from the CPU thread.
|
||||
static void ReloadPostProcessingSettings(bool display, bool internal, bool reload_shaders);
|
||||
|
||||
// Draws the specified bounding box with display rotation and pre-rotation.
|
||||
static void DrawScreenQuad(const GSVector4i rect, const GSVector4 uv_rect, const GSVector2i target_size,
|
||||
const GSVector2i final_target_size, DisplayRotation uv_rotation,
|
||||
WindowInfo::PreRotation prerotation);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
|
@ -109,9 +114,6 @@ private:
|
|||
bool dst_alpha_blend, DisplayRotation rotation, WindowInfo::PreRotation prerotation);
|
||||
GPUDevice::PresentResult ApplyDisplayPostProcess(GPUTexture* target, GPUTexture* input,
|
||||
const GSVector4i display_rect);
|
||||
void DrawScreenQuad(const GSVector4i rect, const GSVector4 uv_rect, const GSVector2i target_size,
|
||||
const GSVector2i final_target_size, DisplayRotation uv_rotation,
|
||||
WindowInfo::PreRotation prerotation);
|
||||
|
||||
bool DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve);
|
||||
void DestroyDeinterlaceTextures();
|
||||
|
|
|
@ -20,27 +20,6 @@ float2 ClampUV(float2 uv) {
|
|||
})";
|
||||
}
|
||||
|
||||
std::string GPUShaderGen::GenerateDisplayVertexShader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteDisplayUniformBuffer(ss);
|
||||
DeclareVertexEntryPoint(ss, {"float2 a_pos", "float2 a_tex0"}, 0, 1, {}, false, "", false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
v_pos = float4(a_pos, 0.0f, 1.0f);
|
||||
v_tex0 = a_tex0;
|
||||
|
||||
// NDC space Y flip in Vulkan.
|
||||
#if API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
std::string GPUShaderGen::GenerateDisplayFragmentShader(bool clamp_uv, bool nearest) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -804,6 +804,65 @@ void GPUDevice::RenderImGui(GPUSwapChain* swap_chain)
|
|||
}
|
||||
}
|
||||
|
||||
void GPUDevice::RenderImGui(GPUTexture* texture)
|
||||
{
|
||||
GL_SCOPE("RenderImGui");
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
SetViewport(0, 0, texture->GetWidth(), texture->GetHeight());
|
||||
|
||||
const GSMatrix4x4 mproj = GSMatrix4x4::OffCenterOrthographicProjection(
|
||||
0.0f, 0.0f, static_cast<float>(texture->GetWidth()), static_cast<float>(texture->GetHeight()), 0.0f, 1.0f);
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
|
||||
// Render command lists
|
||||
const bool flip = UsesLowerLeftOrigin();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
static_assert(sizeof(ImDrawIdx) == sizeof(DrawIndex));
|
||||
|
||||
u32 base_vertex, base_index;
|
||||
UploadVertexBuffer(cmd_list->VtxBuffer.Data, sizeof(ImDrawVert), cmd_list->VtxBuffer.Size, &base_vertex);
|
||||
UploadIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size, &base_index);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
|
||||
if ((pcmd->ElemCount == 0 && !pcmd->UserCallback) || pcmd->ClipRect.z <= pcmd->ClipRect.x ||
|
||||
pcmd->ClipRect.w <= pcmd->ClipRect.y)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i clip = GSVector4i(GSVector4::load<false>(&pcmd->ClipRect.x));
|
||||
if (flip)
|
||||
clip = FlipToLowerLeft(clip, texture->GetHeight());
|
||||
|
||||
SetScissor(clip);
|
||||
SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->TextureId), m_linear_sampler);
|
||||
|
||||
if (pcmd->UserCallback) [[unlikely]]
|
||||
{
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawIndexed(pcmd->ElemCount, base_index + pcmd->IdxOffset, base_vertex + pcmd->VtxOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex)
|
||||
{
|
||||
void* map;
|
||||
|
|
|
@ -861,6 +861,7 @@ public:
|
|||
|
||||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderImGui(GPUSwapChain* swap_chain);
|
||||
void RenderImGui(GPUTexture* texture);
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
|
|
|
@ -793,6 +793,26 @@ void ShaderGen::DeclareFragmentEntryPoint(
|
|||
}
|
||||
}
|
||||
|
||||
std::string ShaderGen::GeneratePassthroughVertexShader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareVertexEntryPoint(ss, {"float2 a_pos", "float2 a_tex0"}, 0, 1, {}, false, "", false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
v_pos = float4(a_pos, 0.0f, 1.0f);
|
||||
v_tex0 = a_tex0;
|
||||
|
||||
// NDC space Y flip in Vulkan.
|
||||
#if API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateScreenQuadVertexShader(float z /* = 0.0f */) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -809,26 +829,6 @@ std::string ShaderGen::GenerateScreenQuadVertexShader(float z /* = 0.0f */) cons
|
|||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateUVQuadVertexShader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareUniformBuffer(ss, {"float2 u_uv_min", "float2 u_uv_max"}, true);
|
||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
|
||||
ss << R"(
|
||||
{
|
||||
v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||
v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||
v_tex0 = u_uv_min + (u_uv_max - u_uv_min) * v_tex0;
|
||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateFillFragmentShader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -925,3 +925,23 @@ std::string ShaderGen::GenerateImGuiFragmentShader() const
|
|||
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateFadeFragmentShader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareUniformBuffer(ss, {"float u_tex0_weight", "float u_tex1_weight"}, true);
|
||||
DeclareTexture(ss, "samp0", 0);
|
||||
DeclareTexture(ss, "samp1", 1);
|
||||
DeclareFragmentEntryPoint(ss, 0, 1);
|
||||
|
||||
ss << R"(
|
||||
{
|
||||
o_col0 = SAMPLE_TEXTURE(samp0, v_tex0) * u_tex0_weight;
|
||||
o_col0 += SAMPLE_TEXTURE(samp1, v_tex0) * u_tex1_weight;
|
||||
o_col0.a = 1.0f;
|
||||
}
|
||||
)";
|
||||
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
|
|
@ -27,14 +27,15 @@ public:
|
|||
ALWAYS_INLINE bool IsVulkan() const { return (m_render_api == RenderAPI::Vulkan); }
|
||||
ALWAYS_INLINE bool IsMetal() const { return (m_render_api == RenderAPI::Metal); }
|
||||
|
||||
std::string GeneratePassthroughVertexShader() const;
|
||||
std::string GenerateScreenQuadVertexShader(float z = 0.0f) const;
|
||||
std::string GenerateUVQuadVertexShader() const;
|
||||
std::string GenerateFillFragmentShader() const;
|
||||
std::string GenerateFillFragmentShader(const GSVector4 fixed_color) const;
|
||||
std::string GenerateCopyFragmentShader(bool offset = true) const;
|
||||
|
||||
std::string GenerateImGuiVertexShader() const;
|
||||
std::string GenerateImGuiFragmentShader() const;
|
||||
std::string GenerateFadeFragmentShader() const;
|
||||
|
||||
const char* GetInterpolationQualifier(bool interface_block, bool centroid_interpolation, bool sample_interpolation,
|
||||
bool is_out) const;
|
||||
|
|
Loading…
Reference in New Issue