Postfx
This commit is contained in:
parent
2834635f59
commit
a43b6ee9db
|
@ -38,6 +38,12 @@ static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view
|
|||
{
|
||||
// WKPDID_D3DDebugObjectName
|
||||
static constexpr GUID guid = {0x429b8c22, 0x9188, 0x4b0c, 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00};
|
||||
|
||||
UINT existing_data_size;
|
||||
HRESULT hr = obj->GetPrivateData(guid, &existing_data_size, nullptr);
|
||||
if (SUCCEEDED(hr) && existing_data_size > 0)
|
||||
return;
|
||||
|
||||
const std::wstring wname = StringUtil::UTF8StringToWideString(name);
|
||||
obj->SetPrivateData(guid, static_cast<UINT>(wname.length()) * 2u, wname.c_str());
|
||||
}
|
||||
|
@ -163,21 +169,6 @@ RenderAPI D3D11Device::GetRenderAPI() const
|
|||
return RenderAPI::D3D11;
|
||||
}
|
||||
|
||||
void* D3D11Device::GetDevice() const
|
||||
{
|
||||
return m_device.Get();
|
||||
}
|
||||
|
||||
void* D3D11Device::GetContext() const
|
||||
{
|
||||
return m_context.Get();
|
||||
}
|
||||
|
||||
bool D3D11Device::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(m_device);
|
||||
}
|
||||
|
||||
bool D3D11Device::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
|
@ -396,11 +387,12 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {
|
||||
{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
|
||||
|
||||
ComPtr<ID3D11Device> device;
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
hr =
|
||||
D3D11CreateDevice(dxgi_adapter.Get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
|
||||
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()),
|
||||
D3D11_SDK_VERSION, m_device.GetAddressOf(), nullptr, m_context.GetAddressOf());
|
||||
|
||||
D3D11_SDK_VERSION, device.GetAddressOf(), nullptr, context.GetAddressOf());
|
||||
// we re-grab these later, see below
|
||||
dxgi_adapter.Reset();
|
||||
temp_dxgi_factory.Reset();
|
||||
|
@ -410,6 +402,11 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
Log_ErrorPrintf("Failed to create D3D device: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
else if (FAILED(hr = device.As(&m_device)) || FAILED(hr = context.As(&m_context)))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to get D3D11.1 device: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_settings.gpu_use_debug_device && IsDebuggerPresent())
|
||||
{
|
||||
|
@ -603,20 +600,22 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||
return false;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC backbuffer_desc;
|
||||
backbuffer->GetDesc(&backbuffer_desc);
|
||||
|
||||
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
||||
backbuffer_desc.ArraySize);
|
||||
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
m_swap_chain_texture = std::make_unique<D3D11Texture>();
|
||||
if (!m_swap_chain_texture->Adopt(m_device.Get(), std::move(backbuffer)))
|
||||
{
|
||||
Log_ErrorPrintf("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
|
||||
m_swap_chain_texture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info.surface_width = backbuffer_desc.Width;
|
||||
m_window_info.surface_height = backbuffer_desc.Height;
|
||||
if (!(m_swap_chain_framebuffer = std::unique_ptr<D3D11Framebuffer>(
|
||||
static_cast<D3D11Framebuffer*>(CreateFramebuffer(m_swap_chain_texture.get()).release()))))
|
||||
{
|
||||
m_swap_chain_texture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info.surface_width = m_swap_chain_texture->GetWidth();
|
||||
m_window_info.surface_height = m_swap_chain_texture->GetHeight();
|
||||
Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||
|
@ -652,7 +651,8 @@ void D3D11Device::DestroySurface()
|
|||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
m_swap_chain.Reset();
|
||||
}
|
||||
|
||||
|
@ -676,7 +676,8 @@ void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
|||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
|
@ -745,7 +746,8 @@ bool D3D11Device::SetFullscreen(bool fullscreen, u32 width, u32 height, float re
|
|||
return true;
|
||||
}
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
m_swap_chain.Reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
|
@ -764,7 +766,7 @@ bool D3D11Device::CreateBuffers()
|
|||
{
|
||||
if (!m_vertex_buffer.Create(m_device.Get(), D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE) ||
|
||||
!m_index_buffer.Create(m_device.Get(), D3D11_BIND_INDEX_BUFFER, INDEX_BUFFER_SIZE) ||
|
||||
!m_uniform_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, MAX_UNIFORM_BUFFER_SIZE))
|
||||
!m_uniform_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, UNIFORM_BUFFER_SIZE))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create vertex/index/uniform buffers.");
|
||||
return false;
|
||||
|
@ -795,11 +797,19 @@ bool D3D11Device::Render(bool skip_present)
|
|||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), s_clear_color.data());
|
||||
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
||||
m_current_framebuffer = nullptr;
|
||||
ClearRenderTarget(m_swap_chain_texture.get(), 0);
|
||||
|
||||
RenderDisplay();
|
||||
if (HasDisplayTexture())
|
||||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", left, top, width, height);
|
||||
RenderDisplay(m_swap_chain_framebuffer.get(), left, top, width, height, m_display_texture, m_display_texture_view_x,
|
||||
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
||||
IsUsingLinearFiltering());
|
||||
}
|
||||
|
||||
SetFramebuffer(m_swap_chain_framebuffer.get());
|
||||
SetViewportAndScissor(0, 0, m_swap_chain_framebuffer->GetWidth(), m_swap_chain_framebuffer->GetHeight());
|
||||
|
||||
RenderImGui();
|
||||
|
||||
|
@ -816,6 +826,8 @@ bool D3D11Device::Render(bool skip_present)
|
|||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
|
||||
m_current_framebuffer = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1032,9 +1044,9 @@ D3D11Framebuffer::~D3D11Framebuffer() = default;
|
|||
void D3D11Framebuffer::SetDebugName(const std::string_view& name)
|
||||
{
|
||||
if (m_rtv)
|
||||
SetD3DDebugObjectName(m_rtv.Get(), name);
|
||||
SetD3DDebugObjectName(m_rtv.Get(), fmt::format("{} RTV", name));
|
||||
if (m_dsv)
|
||||
SetD3DDebugObjectName(m_dsv.Get(), name);
|
||||
SetD3DDebugObjectName(m_dsv.Get(), fmt::format("{} DSV", name));
|
||||
}
|
||||
|
||||
void D3D11Framebuffer::CommitClear(ID3D11DeviceContext* context)
|
||||
|
@ -1633,7 +1645,7 @@ std::unique_ptr<GPUPipeline> D3D11Device::CreatePipeline(const GPUPipeline::Grap
|
|||
return std::unique_ptr<GPUPipeline>(
|
||||
new D3D11Pipeline(std::move(rs), std::move(ds), std::move(bs), std::move(il),
|
||||
static_cast<const D3D11Shader*>(config.vertex_shader)->GetVertexShader(),
|
||||
static_cast<const D3D11Shader*>(config.pixel_shader)->GetPixelShader(),
|
||||
static_cast<const D3D11Shader*>(config.fragment_shader)->GetPixelShader(),
|
||||
primitives[static_cast<u8>(config.primitive)]));
|
||||
}
|
||||
|
||||
|
@ -2070,29 +2082,33 @@ void D3D11Device::UnmapIndexBuffer(u32 used_index_count)
|
|||
|
||||
void D3D11Device::PushUniformBuffer(const void* data, u32 data_size)
|
||||
{
|
||||
Assert(data_size <= MAX_UNIFORM_BUFFER_SIZE);
|
||||
|
||||
const auto res = m_uniform_buffer.Map(m_context.Get(), MAX_UNIFORM_BUFFER_SIZE, MAX_UNIFORM_BUFFER_SIZE);
|
||||
const u32 used_space = Common::AlignUpPow2(data_size, UNIFORM_BUFFER_ALIGNMENT);
|
||||
const auto res = m_uniform_buffer.Map(m_context.Get(), UNIFORM_BUFFER_ALIGNMENT, used_space);
|
||||
std::memcpy(res.pointer, data, data_size);
|
||||
m_uniform_buffer.Unmap(m_context.Get(), data_size);
|
||||
|
||||
m_context->VSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
|
||||
m_context->PSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
|
||||
const UINT first_constant = (res.index_aligned * UNIFORM_BUFFER_ALIGNMENT) / 16u;
|
||||
const UINT num_constants = (used_space * UNIFORM_BUFFER_ALIGNMENT) / 16u;
|
||||
m_context->VSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
|
||||
m_context->PSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
|
||||
}
|
||||
|
||||
void* D3D11Device::MapUniformBuffer(u32 size)
|
||||
{
|
||||
Assert(size <= MAX_UNIFORM_BUFFER_SIZE);
|
||||
|
||||
const auto res = m_uniform_buffer.Map(m_context.Get(), MAX_UNIFORM_BUFFER_SIZE, MAX_UNIFORM_BUFFER_SIZE);
|
||||
const u32 used_space = Common::AlignUpPow2(size, UNIFORM_BUFFER_ALIGNMENT);
|
||||
const auto res = m_uniform_buffer.Map(m_context.Get(), UNIFORM_BUFFER_ALIGNMENT, used_space);
|
||||
return res.pointer;
|
||||
}
|
||||
|
||||
void D3D11Device::UnmapUniformBuffer(u32 size)
|
||||
{
|
||||
m_uniform_buffer.Unmap(m_context.Get(), size);
|
||||
m_context->VSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
|
||||
m_context->PSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
|
||||
const u32 used_space = Common::AlignUpPow2(size, UNIFORM_BUFFER_ALIGNMENT);
|
||||
const UINT first_constant = m_uniform_buffer.GetPosition() / 16u;
|
||||
const UINT num_constants = used_space / 16u;
|
||||
|
||||
m_uniform_buffer.Unmap(m_context.Get(), used_space);
|
||||
m_context->VSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
|
||||
m_context->PSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
|
||||
}
|
||||
|
||||
void D3D11Device::SetFramebuffer(GPUFramebuffer* fb)
|
||||
|
@ -2186,231 +2202,3 @@ void D3D11Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
|
|||
PreDrawCheck();
|
||||
m_context->DrawIndexed(index_count, base_index, base_vertex);
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct PostProcessingStage
|
||||
{
|
||||
ComPtr<ID3D11VertexShader> vertex_shader;
|
||||
ComPtr<ID3D11PixelShader> pixel_shader;
|
||||
D3D11Texture output_texture;
|
||||
u32 uniforms_size;
|
||||
};
|
||||
|
||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
||||
void ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top, s32 final_width,
|
||||
s32 final_height, D3D11Texture* texture, s32 texture_view_x, s32 texture_view_y,
|
||||
s32 texture_view_width, s32 texture_view_height, u32 target_width, u32 target_height);
|
||||
FrontendCommon::PostProcessingChain m_post_processing_chain;
|
||||
D3D11Texture m_post_processing_input_texture;
|
||||
std::vector<PostProcessingStage> m_post_processing_stages;
|
||||
Common::Timer m_post_processing_timer;
|
||||
|
||||
bool D3D11Device::SetPostProcessingChain(const std::string_view& config)
|
||||
{
|
||||
if (config.empty())
|
||||
{
|
||||
m_post_processing_input_texture.Destroy();
|
||||
m_post_processing_stages.clear();
|
||||
m_post_processing_chain.ClearStages();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_post_processing_chain.CreateFromString(config))
|
||||
return false;
|
||||
|
||||
m_post_processing_stages.clear();
|
||||
|
||||
D3D11::ShaderCache shader_cache;
|
||||
shader_cache.Open(EmuFolders::Cache, m_device->GetFeatureLevel(), SHADER_CACHE_VERSION,
|
||||
g_settings.gpu_use_debug_device);
|
||||
|
||||
FrontendCommon::PostProcessingShaderGen shadergen(RenderAPI::D3D11, true);
|
||||
u32 max_ubo_size = 0;
|
||||
|
||||
for (u32 i = 0; i < m_post_processing_chain.GetStageCount(); i++)
|
||||
{
|
||||
const FrontendCommon::PostProcessingShader& shader = m_post_processing_chain.GetShaderStage(i);
|
||||
const std::string vs = shadergen.GeneratePostProcessingVertexShader(shader);
|
||||
const std::string ps = shadergen.GeneratePostProcessingFragmentShader(shader);
|
||||
|
||||
PostProcessingStage stage;
|
||||
stage.uniforms_size = shader.GetUniformsSize();
|
||||
stage.vertex_shader = shader_cache.GetVertexShader(m_device.Get(), vs);
|
||||
stage.pixel_shader = shader_cache.GetPixelShader(m_device.Get(), ps);
|
||||
if (!stage.vertex_shader || !stage.pixel_shader)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to compile one or more post-processing shaders, disabling.");
|
||||
m_post_processing_stages.clear();
|
||||
m_post_processing_chain.ClearStages();
|
||||
return false;
|
||||
}
|
||||
|
||||
max_ubo_size = std::max(max_ubo_size, stage.uniforms_size);
|
||||
m_post_processing_stages.push_back(std::move(stage));
|
||||
}
|
||||
|
||||
if (m_push_uniform_buffer.GetSize() < max_ubo_size &&
|
||||
!m_push_uniform_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, max_ubo_size))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to allocate %u byte constant buffer for postprocessing", max_ubo_size);
|
||||
m_post_processing_stages.clear();
|
||||
m_post_processing_chain.ClearStages();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_post_processing_timer.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::CheckPostProcessingRenderTargets(u32 target_width, u32 target_height)
|
||||
{
|
||||
DebugAssert(!m_post_processing_stages.empty());
|
||||
|
||||
const GPUTexture::Type type = GPUTexture::Type::RenderTarget;
|
||||
const GPUTexture::Format format = GPUTexture::Format::RGBA8;
|
||||
|
||||
if (m_post_processing_input_texture.GetWidth() != target_width ||
|
||||
m_post_processing_input_texture.GetHeight() != target_height)
|
||||
{
|
||||
if (!m_post_processing_input_texture.Create(m_device.Get(), target_width, target_height, 1, 1, 1, type, format))
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 target_count = (static_cast<u32>(m_post_processing_stages.size()) - 1);
|
||||
for (u32 i = 0; i < target_count; i++)
|
||||
{
|
||||
PostProcessingStage& pps = m_post_processing_stages[i];
|
||||
if (pps.output_texture.GetWidth() != target_width || pps.output_texture.GetHeight() != target_height)
|
||||
{
|
||||
if (!pps.output_texture.Create(m_device.Get(), target_width, target_height, 1, 1, 1, type, format))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11Device::ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top,
|
||||
s32 final_width, s32 final_height, D3D11Texture* texture, s32 texture_view_x,
|
||||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||
u32 target_width, u32 target_height)
|
||||
{
|
||||
if (!CheckPostProcessingRenderTargets(target_width, target_height))
|
||||
{
|
||||
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
|
||||
texture_view_width, texture_view_height, IsUsingLinearFiltering());
|
||||
return;
|
||||
}
|
||||
|
||||
// downsample/upsample - use same viewport for remainder
|
||||
m_context->ClearRenderTargetView(m_post_processing_input_texture.GetD3DRTV(), s_clear_color.data());
|
||||
m_context->OMSetRenderTargets(1, m_post_processing_input_texture.GetD3DRTVArray(), nullptr);
|
||||
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
|
||||
texture_view_width, texture_view_height, IsUsingLinearFiltering());
|
||||
|
||||
const s32 orig_texture_width = texture_view_width;
|
||||
const s32 orig_texture_height = texture_view_height;
|
||||
texture = &m_post_processing_input_texture;
|
||||
texture_view_x = final_left;
|
||||
texture_view_y = final_top;
|
||||
texture_view_width = final_width;
|
||||
texture_view_height = final_height;
|
||||
|
||||
const u32 final_stage = static_cast<u32>(m_post_processing_stages.size()) - 1u;
|
||||
for (u32 i = 0; i < static_cast<u32>(m_post_processing_stages.size()); i++)
|
||||
{
|
||||
PostProcessingStage& pps = m_post_processing_stages[i];
|
||||
ID3D11RenderTargetView* rtv = (i == final_stage) ? final_target : pps.output_texture.GetD3DRTV();
|
||||
m_context->ClearRenderTargetView(rtv, s_clear_color.data());
|
||||
m_context->OMSetRenderTargets(1, &rtv, nullptr);
|
||||
|
||||
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_context->VSSetShader(pps.vertex_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(pps.pixel_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShaderResources(0, 1, texture->GetD3DSRVArray());
|
||||
m_context->PSSetSamplers(0, 1, m_border_sampler.GetAddressOf());
|
||||
|
||||
const auto map = m_push_uniform_buffer.Map(m_context.Get(), m_push_uniform_buffer.GetSize(), pps.uniforms_size);
|
||||
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
|
||||
map.pointer, texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width,
|
||||
texture_view_height, GetWindowWidth(), GetWindowHeight(), orig_texture_width, orig_texture_height,
|
||||
static_cast<float>(m_post_processing_timer.GetTimeSeconds()));
|
||||
m_push_uniform_buffer.Unmap(m_context.Get(), pps.uniforms_size);
|
||||
m_context->VSSetConstantBuffers(0, 1, m_push_uniform_buffer.GetD3DBufferArray());
|
||||
m_context->PSSetConstantBuffers(0, 1, m_push_uniform_buffer.GetD3DBufferArray());
|
||||
|
||||
m_context->Draw(3, 0);
|
||||
|
||||
if (i != final_stage)
|
||||
texture = &pps.output_texture;
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* null_srv = nullptr;
|
||||
m_context->PSSetShaderResources(0, 1, &null_srv);
|
||||
}
|
||||
void D3D11Device::RenderDisplay()
|
||||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height,
|
||||
static_cast<D3D11Texture*>(m_display_texture), m_display_texture_view_x,
|
||||
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
||||
GetWindowWidth(), GetWindowHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), s_clear_color.data());
|
||||
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
||||
|
||||
if (!HasDisplayTexture())
|
||||
return;
|
||||
|
||||
RenderDisplay(left, top, width, height, static_cast<D3D11Texture*>(m_display_texture), m_display_texture_view_x,
|
||||
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
||||
IsUsingLinearFiltering());
|
||||
}
|
||||
bool D3D11Device::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
|
||||
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
|
||||
{
|
||||
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8;
|
||||
|
||||
D3D11Texture render_texture;
|
||||
if (!render_texture.Create(m_device.Get(), width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat))
|
||||
return false;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {};
|
||||
m_context->ClearRenderTargetView(render_texture.GetD3DRTV(), clear_color.data());
|
||||
m_context->OMSetRenderTargets(1, render_texture.GetD3DRTVArray(), nullptr);
|
||||
|
||||
if (HasDisplayTexture())
|
||||
{
|
||||
if (!m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(render_texture.GetD3DRTV(), draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
|
||||
draw_rect.GetHeight(), static_cast<D3D11Texture*>(m_display_texture),
|
||||
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||
m_display_texture_view_height, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
|
||||
static_cast<D3D11Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
|
||||
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
|
||||
}
|
||||
}
|
||||
|
||||
m_context->OMSetRenderTargets(0, nullptr, nullptr);
|
||||
|
||||
const u32 stride = GPUTexture::GetPixelSize(hdformat) * width;
|
||||
out_pixels->resize(width * height);
|
||||
if (!DownloadTexture(&render_texture, 0, 0, width, height, out_pixels->data(), stride))
|
||||
return false;
|
||||
|
||||
*out_stride = stride;
|
||||
*out_format = hdformat;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -264,10 +264,7 @@ public:
|
|||
~D3D11Device();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
|
@ -353,10 +350,10 @@ private:
|
|||
using InputLayoutMap =
|
||||
std::unordered_map<GPUPipeline::InputLayout, ComPtr<ID3D11InputLayout>, GPUPipeline::InputLayoutHash>;
|
||||
|
||||
// Currently we don't stream uniforms, instead just re-map the buffer every time and let the driver take care of it.
|
||||
static constexpr u32 MAX_UNIFORM_BUFFER_SIZE = 64;
|
||||
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
static constexpr u32 UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
static constexpr u32 UNIFORM_BUFFER_ALIGNMENT = 256;
|
||||
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
|
||||
|
||||
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
|
||||
|
@ -384,13 +381,14 @@ private:
|
|||
void PopTimestampQuery();
|
||||
void KickTimestampQuery();
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
ComPtr<ID3D11Device1> m_device;
|
||||
ComPtr<ID3D11DeviceContext1> m_context;
|
||||
ComPtr<ID3DUserDefinedAnnotation> m_annotation;
|
||||
|
||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||
std::unique_ptr<D3D11Texture> m_swap_chain_texture;
|
||||
std::unique_ptr<D3D11Framebuffer> m_swap_chain_framebuffer;
|
||||
|
||||
RasterizationStateMap m_rasterization_states;
|
||||
DepthStateMap m_depth_states;
|
||||
|
|
|
@ -37,21 +37,6 @@ RenderAPI D3D12GPUDevice::GetRenderAPI() const
|
|||
return RenderAPI::D3D12;
|
||||
}
|
||||
|
||||
void* D3D12GPUDevice::GetDevice() const
|
||||
{
|
||||
return g_d3d12_context->GetDevice();
|
||||
}
|
||||
|
||||
void* D3D12GPUDevice::GetContext() const
|
||||
{
|
||||
return g_d3d12_context.get();
|
||||
}
|
||||
|
||||
bool D3D12GPUDevice::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(g_d3d12_context);
|
||||
}
|
||||
|
||||
bool D3D12GPUDevice::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
|
@ -472,6 +457,7 @@ bool D3D12GPUDevice::CreateResources()
|
|||
if (!m_display_root_signature)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
rsbuilder.SetInputAssemblerFlag();
|
||||
rsbuilder.Add32BitConstants(0, FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD / sizeof(u32),
|
||||
D3D12_SHADER_VISIBILITY_ALL);
|
||||
|
@ -488,6 +474,7 @@ bool D3D12GPUDevice::CreateResources()
|
|||
m_post_processing_cb_root_signature = rsbuilder.Create();
|
||||
if (!m_post_processing_cb_root_signature)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
D3D12::GraphicsPipelineBuilder gpbuilder;
|
||||
gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
|
||||
|
@ -546,12 +533,14 @@ void D3D12GPUDevice::DestroyResources()
|
|||
{
|
||||
GPUDevice::DestroyResources();
|
||||
|
||||
#if 0
|
||||
m_post_processing_cbuffer.Destroy(false);
|
||||
m_post_processing_chain.ClearStages();
|
||||
m_post_processing_input_texture.Destroy();
|
||||
m_post_processing_stages.clear();
|
||||
m_post_processing_cb_root_signature.Reset();
|
||||
m_post_processing_root_signature.Reset();
|
||||
#endif
|
||||
|
||||
m_readback_staging_texture.Destroy(false);
|
||||
g_d3d12_context->GetSamplerHeapManager().Free(&m_border_sampler);
|
||||
|
@ -638,6 +627,7 @@ void D3D12GPUDevice::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, D3D12::Te
|
|||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
#if 0
|
||||
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(cmdlist, swap_chain_buf, left, top, width, height,
|
||||
|
@ -646,6 +636,7 @@ void D3D12GPUDevice::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, D3D12::Te
|
|||
GetWindowWidth(), GetWindowHeight());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
swap_chain_buf->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(swap_chain_buf->GetRTVOrDSVDescriptor(), s_clear_color.data(), 0, nullptr);
|
||||
|
@ -787,6 +778,7 @@ GPUDevice::AdapterAndModeList D3D12GPUDevice::GetAdapterAndModeList(IDXGIFactory
|
|||
return adapter_info;
|
||||
}
|
||||
|
||||
#if 0
|
||||
D3D12GPUDevice::PostProcessingStage::PostProcessingStage(PostProcessingStage&& move)
|
||||
: pipeline(std::move(move.pipeline)), output_texture(std::move(move.output_texture)),
|
||||
uniforms_size(move.uniforms_size)
|
||||
|
@ -798,9 +790,11 @@ D3D12GPUDevice::PostProcessingStage::~PostProcessingStage()
|
|||
{
|
||||
output_texture.Destroy(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool D3D12GPUDevice::SetPostProcessingChain(const std::string_view& config)
|
||||
{
|
||||
#if 0
|
||||
g_d3d12_context->ExecuteCommandList(true);
|
||||
|
||||
if (config.empty())
|
||||
|
@ -882,8 +876,12 @@ bool D3D12GPUDevice::SetPostProcessingChain(const std::string_view& config)
|
|||
|
||||
m_post_processing_timer.Reset();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool D3D12GPUDevice::CheckPostProcessingRenderTargets(u32 target_width, u32 target_height)
|
||||
{
|
||||
DebugAssert(!m_post_processing_stages.empty());
|
||||
|
@ -1011,3 +1009,4 @@ void D3D12GPUDevice::ApplyPostProcessingChain(ID3D12GraphicsCommandList* cmdlist
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -29,10 +29,7 @@ public:
|
|||
~D3D12GPUDevice();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
|
@ -70,17 +67,6 @@ public:
|
|||
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||
|
||||
protected:
|
||||
struct PostProcessingStage
|
||||
{
|
||||
PostProcessingStage() = default;
|
||||
PostProcessingStage(PostProcessingStage&& move);
|
||||
~PostProcessingStage();
|
||||
|
||||
ComPtr<ID3D12PipelineState> pipeline;
|
||||
D3D12::Texture output_texture;
|
||||
u32 uniforms_size = 0;
|
||||
};
|
||||
|
||||
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
|
||||
|
||||
virtual bool CreateResources() override;
|
||||
|
@ -100,12 +86,6 @@ protected:
|
|||
void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height,
|
||||
GPUTexture* texture_handle);
|
||||
|
||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
||||
void ApplyPostProcessingChain(ID3D12GraphicsCommandList* cmdlist, D3D12::Texture* final_target, s32 final_left,
|
||||
s32 final_top, s32 final_width, s32 final_height, D3D12::Texture* texture,
|
||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||
u32 target_width, u32 target_height);
|
||||
|
||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||
std::vector<D3D12::Texture> m_swap_chain_buffers;
|
||||
|
@ -121,14 +101,6 @@ protected:
|
|||
D3D12::Texture m_display_pixels_texture;
|
||||
D3D12::StagingTexture m_readback_staging_texture;
|
||||
|
||||
ComPtr<ID3D12RootSignature> m_post_processing_root_signature;
|
||||
ComPtr<ID3D12RootSignature> m_post_processing_cb_root_signature;
|
||||
FrontendCommon::PostProcessingChain m_post_processing_chain;
|
||||
D3D12::StreamBuffer m_post_processing_cbuffer;
|
||||
D3D12::Texture m_post_processing_input_texture;
|
||||
std::vector<PostProcessingStage> m_post_processing_stages;
|
||||
Common::Timer m_post_processing_timer;
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "gpu_device.h"
|
||||
#include "../settings.h"
|
||||
#include "../shadergen.h"
|
||||
#include "postprocessing_chain.h"
|
||||
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
|
@ -12,15 +14,18 @@
|
|||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "stb_image.h"
|
||||
#include "stb_image_resize.h"
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
Log_SetChannel(GPUDevice);
|
||||
|
||||
// FIXME
|
||||
|
@ -43,6 +48,28 @@ GPUSampler::GPUSampler() = default;
|
|||
|
||||
GPUSampler::~GPUSampler() = default;
|
||||
|
||||
GPUSampler::Config GPUSampler::GetNearestConfig()
|
||||
{
|
||||
Config config = {};
|
||||
config.address_u = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.address_v = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.address_w = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.min_filter = GPUSampler::Filter::Nearest;
|
||||
config.mag_filter = GPUSampler::Filter::Nearest;
|
||||
return config;
|
||||
}
|
||||
|
||||
GPUSampler::Config GPUSampler::GetLinearConfig()
|
||||
{
|
||||
Config config = {};
|
||||
config.address_u = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.address_v = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.address_w = GPUSampler::AddressMode::ClampToEdge;
|
||||
config.min_filter = GPUSampler::Filter::Linear;
|
||||
config.mag_filter = GPUSampler::Filter::Linear;
|
||||
return config;
|
||||
}
|
||||
|
||||
GPUShader::GPUShader(GPUShaderStage stage) : m_stage(stage)
|
||||
{
|
||||
}
|
||||
|
@ -188,32 +215,16 @@ bool GPUDevice::SetupDevice()
|
|||
|
||||
bool GPUDevice::CreateResources()
|
||||
{
|
||||
GPUSampler::Config spconfig = {};
|
||||
spconfig.address_u = GPUSampler::AddressMode::ClampToEdge;
|
||||
spconfig.address_v = GPUSampler::AddressMode::ClampToEdge;
|
||||
spconfig.address_w = GPUSampler::AddressMode::ClampToEdge;
|
||||
spconfig.min_filter = GPUSampler::Filter::Nearest;
|
||||
spconfig.mag_filter = GPUSampler::Filter::Nearest;
|
||||
if (!(m_point_sampler = CreateSampler(spconfig)))
|
||||
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())))
|
||||
return false;
|
||||
|
||||
spconfig.min_filter = GPUSampler::Filter::Linear;
|
||||
spconfig.mag_filter = GPUSampler::Filter::Linear;
|
||||
if (!(m_linear_sampler = CreateSampler(spconfig)))
|
||||
if (!(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig())))
|
||||
return false;
|
||||
|
||||
spconfig.mag_filter = GPUSampler::Filter::Nearest;
|
||||
spconfig.mag_filter = GPUSampler::Filter::Nearest;
|
||||
spconfig.address_u = GPUSampler::AddressMode::ClampToBorder;
|
||||
spconfig.address_v = GPUSampler::AddressMode::ClampToBorder;
|
||||
spconfig.border_color = 0xFF000000u;
|
||||
if (!(m_border_sampler = CreateSampler(spconfig)))
|
||||
return false;
|
||||
|
||||
ShaderGen shadergen(GetRenderAPI(), /*FIXME DSB*/ true);
|
||||
ShaderGen shadergen(GetRenderAPI(), m_features.dual_source_blend);
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTexture;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTexturePushConstants;
|
||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||
|
@ -235,13 +246,13 @@ bool GPUDevice::CreateResources()
|
|||
GL_OBJECT_NAME(cursor_fs, "Cursor Fragment Shader");
|
||||
|
||||
plconfig.vertex_shader = display_vs.get();
|
||||
plconfig.pixel_shader = display_fs.get();
|
||||
plconfig.fragment_shader = display_fs.get();
|
||||
if (!(m_display_pipeline = CreatePipeline(plconfig)))
|
||||
return false;
|
||||
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
|
||||
|
||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||
plconfig.pixel_shader = cursor_fs.get();
|
||||
plconfig.fragment_shader = cursor_fs.get();
|
||||
if (!(m_cursor_pipeline = CreatePipeline(plconfig)))
|
||||
return false;
|
||||
GL_OBJECT_NAME(m_cursor_pipeline, "Cursor Pipeline");
|
||||
|
@ -262,7 +273,7 @@ bool GPUDevice::CreateResources()
|
|||
plconfig.input_layout.vertex_attributes = imgui_attributes;
|
||||
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
||||
plconfig.vertex_shader = imgui_vs.get();
|
||||
plconfig.pixel_shader = imgui_fs.get();
|
||||
plconfig.fragment_shader = imgui_fs.get();
|
||||
|
||||
m_imgui_pipeline = CreatePipeline(plconfig);
|
||||
if (!m_imgui_pipeline)
|
||||
|
@ -287,14 +298,33 @@ void GPUDevice::DestroyResources()
|
|||
m_imgui_pipeline.reset();
|
||||
|
||||
m_linear_sampler.reset();
|
||||
m_point_sampler.reset();
|
||||
m_nearest_sampler.reset();
|
||||
|
||||
m_shader_cache.Close();
|
||||
}
|
||||
|
||||
bool GPUDevice::SetPostProcessingChain(const std::string_view& config)
|
||||
{
|
||||
return false;
|
||||
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; // TODO FIXME m_window_info.surface_format
|
||||
|
||||
m_post_processing_chain.reset();
|
||||
|
||||
if (config.empty())
|
||||
return true;
|
||||
|
||||
m_post_processing_chain = std::make_unique<PostProcessingChain>();
|
||||
if (!m_post_processing_chain->CreateFromString(config) || !m_post_processing_chain->CompilePipelines(hdformat))
|
||||
{
|
||||
m_post_processing_chain.reset();
|
||||
return false;
|
||||
}
|
||||
else if (m_post_processing_chain->IsEmpty())
|
||||
{
|
||||
m_post_processing_chain.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GPUDevice::GetShaderCacheBaseName(const std::string_view& type, bool debug) const
|
||||
|
@ -862,26 +892,10 @@ bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<
|
|||
return false;
|
||||
|
||||
ClearRenderTarget(render_texture.get(), 0);
|
||||
SetFramebuffer(render_fb.get());
|
||||
|
||||
if (HasDisplayTexture())
|
||||
{
|
||||
#if 0
|
||||
if (!m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(render_texture.GetD3DRTV(), draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
|
||||
draw_rect.GetHeight(), static_cast<D3D11Texture*>(m_display_texture),
|
||||
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||
m_display_texture_view_height, width, height);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(), m_display_texture,
|
||||
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||
m_display_texture_view_height, IsUsingLinearFiltering());
|
||||
}
|
||||
}
|
||||
RenderDisplay(render_fb.get(), draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
|
||||
m_display_texture, m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||
m_display_texture_view_height, IsUsingLinearFiltering());
|
||||
|
||||
SetFramebuffer(nullptr);
|
||||
|
||||
|
@ -895,35 +909,27 @@ bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPUDevice::RenderDisplay()
|
||||
void GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||
bool linear_filter)
|
||||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", left, top, width, height);
|
||||
|
||||
#if 0
|
||||
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
|
||||
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; // TODO FIXME m_window_info.surface_format
|
||||
const u32 target_width = target ? target->GetWidth() : m_window_info.surface_width;
|
||||
const u32 target_height = target ? target->GetHeight() : m_window_info.surface_height;
|
||||
const bool postfx =
|
||||
(m_post_processing_chain && m_post_processing_chain->CheckTargets(hdformat, target_width, target_height));
|
||||
if (postfx)
|
||||
{
|
||||
ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height,
|
||||
static_cast<D3D11Texture*>(m_display_texture), m_display_texture_view_x,
|
||||
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
||||
GetWindowWidth(), GetWindowHeight());
|
||||
return;
|
||||
ClearRenderTarget(m_post_processing_chain->GetInputTexture(), 0);
|
||||
SetFramebuffer(m_post_processing_chain->GetInputFramebuffer());
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFramebuffer(target);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!HasDisplayTexture())
|
||||
return;
|
||||
|
||||
RenderDisplay(left, top, width, height, m_display_texture, m_display_texture_view_x, m_display_texture_view_y,
|
||||
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
|
||||
}
|
||||
|
||||
void GPUDevice::RenderDisplay(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture, s32 texture_view_x,
|
||||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter)
|
||||
{
|
||||
SetPipeline(m_display_pipeline.get());
|
||||
SetTextureSampler(0, texture, linear_filter ? m_linear_sampler.get() : m_point_sampler.get());
|
||||
SetTextureSampler(0, texture, linear_filter ? m_linear_sampler.get() : m_nearest_sampler.get());
|
||||
|
||||
const bool linear = IsUsingLinearFiltering();
|
||||
const float position_adjust = linear ? 0.5f : 0.0f;
|
||||
|
@ -937,6 +943,12 @@ void GPUDevice::RenderDisplay(s32 left, s32 top, s32 width, s32 height, GPUTextu
|
|||
|
||||
SetViewportAndScissor(left, top, width, height);
|
||||
Draw(3, 0);
|
||||
|
||||
if (postfx)
|
||||
{
|
||||
m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height,
|
||||
target_width, target_height);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::RenderSoftwareCursor()
|
||||
|
|
|
@ -89,6 +89,9 @@ public:
|
|||
virtual ~GPUSampler();
|
||||
|
||||
virtual void SetDebugName(const std::string_view& name) = 0;
|
||||
|
||||
static Config GetNearestConfig();
|
||||
static Config GetLinearConfig();
|
||||
};
|
||||
|
||||
enum class GPUShaderStage : u8
|
||||
|
@ -119,11 +122,11 @@ class GPUPipeline
|
|||
public:
|
||||
enum class Layout : u8
|
||||
{
|
||||
// 128 byte UBO via push constants, 1 texture.
|
||||
SingleTexture,
|
||||
|
||||
// 1 streamed UBO, 1 texture in PS.
|
||||
HWBatch,
|
||||
SingleTextureUBO,
|
||||
|
||||
// 128 byte UBO via push constants, 1 texture.
|
||||
SingleTexturePushConstants,
|
||||
|
||||
MaxCount
|
||||
};
|
||||
|
@ -254,6 +257,7 @@ public:
|
|||
MaxCount
|
||||
};
|
||||
|
||||
// TODO: purge this?
|
||||
union RasterizationState
|
||||
{
|
||||
BitField<u8, CullMode, 0, 2> cull_mode;
|
||||
|
@ -326,7 +330,7 @@ public:
|
|||
BlendState blend;
|
||||
|
||||
const GPUShader* vertex_shader;
|
||||
const GPUShader* pixel_shader;
|
||||
const GPUShader* fragment_shader;
|
||||
|
||||
GPUTexture::Format color_format;
|
||||
GPUTexture::Format depth_format;
|
||||
|
@ -369,6 +373,9 @@ protected:
|
|||
u32 m_current_position;
|
||||
};
|
||||
|
||||
// TODO: remove
|
||||
class PostProcessingChain;
|
||||
|
||||
class GPUDevice
|
||||
{
|
||||
public:
|
||||
|
@ -412,7 +419,7 @@ public:
|
|||
ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; }
|
||||
|
||||
ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); }
|
||||
ALWAYS_INLINE GPUSampler* GetPointSampler() const { return m_point_sampler.get(); }
|
||||
ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); }
|
||||
|
||||
// Position is relative to the top-left corner of the window.
|
||||
ALWAYS_INLINE s32 GetMousePositionX() const { return m_mouse_position_x; }
|
||||
|
@ -430,22 +437,21 @@ public:
|
|||
ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
|
||||
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
virtual void* GetDevice() const = 0;
|
||||
virtual void* GetContext() const = 0;
|
||||
|
||||
virtual bool HasDevice() const = 0;
|
||||
virtual bool HasSurface() const = 0;
|
||||
|
||||
virtual bool CreateDevice(const WindowInfo& wi, bool vsync) = 0;
|
||||
virtual bool SetupDevice();
|
||||
virtual bool MakeCurrent() = 0;
|
||||
virtual bool DoneCurrent() = 0;
|
||||
|
||||
virtual bool HasSurface() const = 0;
|
||||
virtual void DestroySurface() = 0;
|
||||
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
|
||||
|
||||
virtual bool SupportsFullscreen() const = 0;
|
||||
virtual bool IsFullscreen() = 0;
|
||||
virtual bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
|
||||
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
|
||||
|
||||
virtual bool CreateResources();
|
||||
virtual void DestroyResources();
|
||||
|
||||
|
@ -605,11 +611,11 @@ protected:
|
|||
|
||||
void RenderImGui();
|
||||
|
||||
void RenderDisplay();
|
||||
void RenderSoftwareCursor();
|
||||
|
||||
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture, s32 texture_view_x,
|
||||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
|
||||
void RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||
bool linear_filter);
|
||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
||||
|
||||
Features m_features = {};
|
||||
|
@ -620,9 +626,8 @@ protected:
|
|||
|
||||
GPUShaderCache m_shader_cache;
|
||||
|
||||
std::unique_ptr<GPUSampler> m_point_sampler;
|
||||
std::unique_ptr<GPUSampler> m_nearest_sampler;
|
||||
std::unique_ptr<GPUSampler> m_linear_sampler;
|
||||
std::unique_ptr<GPUSampler> m_border_sampler;
|
||||
|
||||
u64 m_last_frame_displayed_time = 0;
|
||||
|
||||
|
@ -655,6 +660,8 @@ protected:
|
|||
bool m_display_changed = false;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_vsync_enabled = false;
|
||||
|
||||
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
||||
};
|
||||
|
||||
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been called.
|
||||
|
|
|
@ -37,16 +37,6 @@ RenderAPI OpenGLGPUDevice::GetRenderAPI() const
|
|||
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
|
||||
}
|
||||
|
||||
void* OpenGLGPUDevice::GetDevice() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* OpenGLGPUDevice::GetContext() const
|
||||
{
|
||||
return m_gl_context.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUTexture> OpenGLGPUDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||
GPUTexture::Type type, GPUTexture::Format format,
|
||||
const void* data, u32 data_stride,
|
||||
|
@ -273,11 +263,6 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen
|
|||
}
|
||||
}
|
||||
|
||||
bool OpenGLGPUDevice::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(m_gl_context);
|
||||
}
|
||||
|
||||
bool OpenGLGPUDevice::HasSurface() const
|
||||
{
|
||||
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||
|
@ -623,10 +608,12 @@ void OpenGLGPUDevice::DestroyResources()
|
|||
{
|
||||
GPUDevice::DestroyResources();
|
||||
|
||||
#if 0
|
||||
m_post_processing_chain.ClearStages();
|
||||
m_post_processing_input_texture.Destroy();
|
||||
m_post_processing_ubo.reset();
|
||||
m_post_processing_stages.clear();
|
||||
#endif
|
||||
|
||||
if (m_display_vao != 0)
|
||||
{
|
||||
|
@ -695,6 +682,7 @@ void OpenGLGPUDevice::RenderDisplay()
|
|||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
#if 0
|
||||
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(0, left, GetWindowHeight() - top - height, width, height,
|
||||
|
@ -703,6 +691,7 @@ void OpenGLGPUDevice::RenderDisplay()
|
|||
GetWindowWidth(), GetWindowHeight());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
@ -817,6 +806,7 @@ void OpenGLGPUDevice::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32
|
|||
|
||||
bool OpenGLGPUDevice::SetPostProcessingChain(const std::string_view& config)
|
||||
{
|
||||
#if 0
|
||||
if (config.empty())
|
||||
{
|
||||
m_post_processing_input_texture.Destroy();
|
||||
|
@ -882,8 +872,12 @@ bool OpenGLGPUDevice::SetPostProcessingChain(const std::string_view& config)
|
|||
|
||||
m_post_processing_timer.Reset();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool OpenGLGPUDevice::CheckPostProcessingRenderTargets(u32 target_width, u32 target_height)
|
||||
{
|
||||
DebugAssert(!m_post_processing_stages.empty());
|
||||
|
@ -973,7 +967,7 @@ void OpenGLGPUDevice::ApplyPostProcessingChain(GLuint final_target, s32 final_le
|
|||
glBindSampler(0, 0);
|
||||
m_post_processing_ubo->Unbind();
|
||||
}
|
||||
|
||||
#endif
|
||||
void OpenGLGPUDevice::CreateTimestampQueries()
|
||||
{
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
|
|
|
@ -20,10 +20,7 @@ public:
|
|||
~OpenGLGPUDevice();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
|
@ -82,18 +79,6 @@ protected:
|
|||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
|
||||
void RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height, GPUTexture* texture_handle);
|
||||
|
||||
struct PostProcessingStage
|
||||
{
|
||||
GL::Program program;
|
||||
GL::Texture output_texture;
|
||||
u32 uniforms_size;
|
||||
};
|
||||
|
||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
||||
void ApplyPostProcessingChain(GLuint final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
||||
GL::Texture* texture, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
||||
s32 texture_view_height, u32 target_width, u32 target_height);
|
||||
|
||||
void CreateTimestampQueries();
|
||||
void DestroyTimestampQueries();
|
||||
void PopTimestampQuery();
|
||||
|
@ -113,12 +98,6 @@ protected:
|
|||
std::vector<u8> m_texture_repack_buffer;
|
||||
u32 m_texture_stream_buffer_offset = 0;
|
||||
|
||||
FrontendCommon::PostProcessingChain m_post_processing_chain;
|
||||
GL::Texture m_post_processing_input_texture;
|
||||
std::unique_ptr<GL::StreamBuffer> m_post_processing_ubo;
|
||||
std::vector<PostProcessingStage> m_post_processing_stages;
|
||||
Common::Timer m_post_processing_timer;
|
||||
|
||||
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
u8 m_read_timestamp_query = 0;
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "postprocessing_chain.h"
|
||||
#include "../host.h"
|
||||
#include "../settings.h"
|
||||
#include "gpu_device.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string.h"
|
||||
#include "common/path.h"
|
||||
#include "core/host.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/string.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include <sstream>
|
||||
Log_SetChannel(PostProcessingChain);
|
||||
|
||||
namespace FrontendCommon {
|
||||
Log_SetChannel(PostProcessingChain);
|
||||
|
||||
static bool TryLoadingShader(PostProcessingShader* shader, const std::string_view& shader_name)
|
||||
{
|
||||
|
@ -24,7 +26,8 @@ static bool TryLoadingShader(PostProcessingShader* shader, const std::string_vie
|
|||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> resource_str(Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str()));
|
||||
std::optional<std::string> resource_str(
|
||||
Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str()));
|
||||
if (resource_str.has_value() && shader->LoadFromString(std::string(shader_name), std::move(resource_str.value())))
|
||||
return true;
|
||||
|
||||
|
@ -115,6 +118,20 @@ bool PostProcessingChain::CreateFromString(const std::string_view& chain_config)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PostProcessingChain::CompilePipelines(GPUTexture::Format target_format)
|
||||
{
|
||||
for (PostProcessingShader& stage : m_shaders)
|
||||
{
|
||||
if (!stage.CompilePipeline(target_format))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to compile one or more post-processing shaders, disabling.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> PostProcessingChain::GetAvailableShaderNames()
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
|
@ -182,4 +199,93 @@ void PostProcessingChain::ClearStages()
|
|||
m_shaders.clear();
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
||||
bool PostProcessingChain::CheckTargets(GPUTexture::Format format, u32 target_width, u32 target_height)
|
||||
{
|
||||
if (!m_input_texture || m_input_texture->GetFormat() != format || m_input_texture->GetWidth() != target_width ||
|
||||
m_input_texture->GetHeight() != target_height)
|
||||
{
|
||||
m_input_framebuffer.reset();
|
||||
m_input_texture.reset();
|
||||
|
||||
if (!(m_input_texture = g_host_display->CreateTexture(target_width, target_height, 1, 1, 1,
|
||||
GPUTexture::Type::RenderTarget, format)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(m_input_framebuffer = g_host_display->CreateFramebuffer(m_input_texture.get())))
|
||||
{
|
||||
m_input_texture.reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const PostProcessingShader& final_stage = m_shaders.back();
|
||||
for (PostProcessingShader& shader : m_shaders)
|
||||
{
|
||||
if (&shader == &final_stage)
|
||||
continue;
|
||||
|
||||
if (!shader.ResizeOutput(format, target_width, target_height))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_border_sampler)
|
||||
{
|
||||
GPUSampler::Config config = GPUSampler::GetNearestConfig();
|
||||
config.address_u = GPUSampler::AddressMode::ClampToBorder;
|
||||
config.address_v = GPUSampler::AddressMode::ClampToBorder;
|
||||
config.border_color = 0xFF000000u;
|
||||
if (!(m_border_sampler = g_host_display->CreateSampler(config)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
|
||||
s32 final_height, s32 orig_width, s32 orig_height, u32 target_width, u32 target_height)
|
||||
{
|
||||
const u32 window_width = final_target ? final_target->GetWidth() : g_host_display->GetWindowWidth();
|
||||
const u32 window_height = final_target ? final_target->GetHeight() : g_host_display->GetWindowHeight();
|
||||
|
||||
GL_PUSH("PostProcessingChain Apply");
|
||||
|
||||
g_host_display->SetViewportAndScissor(final_left, final_top, final_width, final_height);
|
||||
|
||||
GPUTexture* input = m_input_texture.get();
|
||||
input->MakeReadyForSampling();
|
||||
|
||||
const PostProcessingShader& final_stage = m_shaders.back();
|
||||
for (PostProcessingShader& stage : m_shaders)
|
||||
{
|
||||
const bool is_final = (&stage == &final_stage);
|
||||
|
||||
GL_SCOPE("PostProcessingShader %s", stage.GetName().c_str());
|
||||
|
||||
// Assumes final stage has been cleared already.
|
||||
if (!is_final)
|
||||
g_host_display->ClearRenderTarget(stage.GetOutputTexture(), 0);
|
||||
|
||||
g_host_display->SetFramebuffer(is_final ? final_target : stage.GetOutputFramebuffer());
|
||||
|
||||
g_host_display->SetPipeline(stage.GetPipeline());
|
||||
g_host_display->SetTextureSampler(0, input, m_border_sampler.get());
|
||||
|
||||
const u32 uniforms_size = stage.GetUniformsSize();
|
||||
void* uniforms = g_host_display->MapUniformBuffer(uniforms_size);
|
||||
stage.FillUniformBuffer(uniforms, input->GetWidth(), input->GetHeight(), final_left, final_top, final_width,
|
||||
final_height, window_width, window_height, orig_width, orig_height,
|
||||
static_cast<float>(m_timer.GetTimeSeconds()));
|
||||
g_host_display->UnmapUniformBuffer(uniforms_size);
|
||||
g_host_display->Draw(3, 0);
|
||||
|
||||
if (!is_final)
|
||||
{
|
||||
input = stage.GetOutputTexture();
|
||||
input->MakeReadyForSampling();
|
||||
}
|
||||
}
|
||||
|
||||
GL_POP();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
#include "gpu_device.h"
|
||||
#include "postprocessing_shader.h"
|
||||
|
||||
#include "common/timer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace FrontendCommon {
|
||||
class GPUSampler;
|
||||
class GPUFramebuffer;
|
||||
class GPUTexture;
|
||||
|
||||
class PostProcessingChain
|
||||
{
|
||||
|
@ -14,10 +21,14 @@ public:
|
|||
PostProcessingChain();
|
||||
~PostProcessingChain();
|
||||
|
||||
static std::vector<std::string> GetAvailableShaderNames();
|
||||
|
||||
ALWAYS_INLINE bool IsEmpty() const { return m_shaders.empty(); }
|
||||
ALWAYS_INLINE u32 GetStageCount() const { return static_cast<u32>(m_shaders.size()); }
|
||||
ALWAYS_INLINE const PostProcessingShader& GetShaderStage(u32 i) const { return m_shaders[i]; }
|
||||
ALWAYS_INLINE PostProcessingShader& GetShaderStage(u32 i) { return m_shaders[i]; }
|
||||
ALWAYS_INLINE GPUTexture* GetInputTexture() const { return m_input_texture.get(); }
|
||||
ALWAYS_INLINE GPUFramebuffer* GetInputFramebuffer() const { return m_input_framebuffer.get(); }
|
||||
|
||||
void AddShader(PostProcessingShader shader);
|
||||
bool AddStage(const std::string_view& name);
|
||||
|
@ -29,11 +40,19 @@ public:
|
|||
std::string GetConfigString() const;
|
||||
|
||||
bool CreateFromString(const std::string_view& chain_config);
|
||||
bool CompilePipelines(GPUTexture::Format target_format);
|
||||
|
||||
static std::vector<std::string> GetAvailableShaderNames();
|
||||
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height);
|
||||
|
||||
void Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
||||
s32 orig_width, s32 orig_height, u32 target_width, u32 target_height);
|
||||
|
||||
private:
|
||||
std::vector<PostProcessingShader> m_shaders;
|
||||
};
|
||||
|
||||
} // namespace FrontendCommon
|
||||
std::unique_ptr<GPUSampler> m_border_sampler;
|
||||
std::unique_ptr<GPUTexture> m_input_texture;
|
||||
std::unique_ptr<GPUFramebuffer> m_input_framebuffer;
|
||||
|
||||
Common::Timer m_timer;
|
||||
};
|
||||
|
|
|
@ -2,18 +2,19 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "postprocessing_shader.h"
|
||||
#include "postprocessing_shadergen.h"
|
||||
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/shadergen.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
Log_SetChannel(PostProcessingShader);
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
void ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value)
|
||||
static void ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value)
|
||||
{
|
||||
size_t key_start = 0;
|
||||
while (key_start < line.size() && std::isspace(line[key_start]))
|
||||
|
@ -49,7 +50,7 @@ void ParseKeyValue(const std::string_view& line, std::string_view* key, std::str
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
u32 ParseVector(const std::string_view& line, PostProcessingShader::Option::ValueVector* values)
|
||||
static u32 ParseVector(const std::string_view& line, PostProcessingShader::Option::ValueVector* values)
|
||||
{
|
||||
u32 index = 0;
|
||||
size_t start = 0;
|
||||
|
@ -141,7 +142,7 @@ const PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
FrontendCommon::PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const std::string_view& name)
|
||||
PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const std::string_view& name)
|
||||
{
|
||||
for (Option& option : m_options)
|
||||
{
|
||||
|
@ -238,11 +239,6 @@ void PostProcessingShader::SetConfigString(const std::string_view& str)
|
|||
}
|
||||
}
|
||||
|
||||
bool PostProcessingShader::UsePushConstants() const
|
||||
{
|
||||
return GetUniformsSize() <= PUSH_CONSTANT_SIZE_THRESHOLD;
|
||||
}
|
||||
|
||||
u32 PostProcessingShader::GetUniformsSize() const
|
||||
{
|
||||
// lazy packing. todo improve.
|
||||
|
@ -293,7 +289,66 @@ void PostProcessingShader::FillUniformBuffer(void* buffer, u32 texture_width, s3
|
|||
}
|
||||
}
|
||||
|
||||
FrontendCommon::PostProcessingShader& PostProcessingShader::operator=(const PostProcessingShader& copy)
|
||||
bool PostProcessingShader::CompilePipeline(GPUTexture::Format target_format)
|
||||
{
|
||||
if (m_pipeline)
|
||||
m_pipeline.reset();
|
||||
|
||||
PostProcessingShaderGen shadergen(g_host_display->GetRenderAPI(), g_host_display->GetFeatures().dual_source_blend);
|
||||
|
||||
std::unique_ptr<GPUShader> vs =
|
||||
g_host_display->CreateShader(GPUShaderStage::Vertex, shadergen.GeneratePostProcessingVertexShader(*this));
|
||||
std::unique_ptr<GPUShader> fs =
|
||||
g_host_display->CreateShader(GPUShaderStage::Fragment, shadergen.GeneratePostProcessingFragmentShader(*this));
|
||||
if (!vs || !fs)
|
||||
return false;
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTextureUBO;
|
||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||
plconfig.color_format = target_format;
|
||||
plconfig.depth_format = GPUTexture::Format::Unknown;
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
||||
plconfig.samples = 1;
|
||||
plconfig.per_sample_shading = false;
|
||||
plconfig.vertex_shader = vs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
|
||||
if (!(m_pipeline = g_host_display->CreatePipeline(plconfig)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostProcessingShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height)
|
||||
{
|
||||
if (m_output_texture && m_output_texture->GetFormat() == format && m_output_texture->GetWidth() == width &&
|
||||
m_output_texture->GetHeight() == height)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_output_framebuffer.reset();
|
||||
m_output_texture.reset();
|
||||
|
||||
if (!(m_output_texture =
|
||||
g_host_display->CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, format)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(m_output_framebuffer = g_host_display->CreateFramebuffer(m_output_texture.get())))
|
||||
{
|
||||
m_output_texture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PostProcessingShader& PostProcessingShader::operator=(const PostProcessingShader& copy)
|
||||
{
|
||||
m_name = copy.m_name;
|
||||
m_code = copy.m_code;
|
||||
|
@ -301,7 +356,7 @@ FrontendCommon::PostProcessingShader& PostProcessingShader::operator=(const Post
|
|||
return *this;
|
||||
}
|
||||
|
||||
FrontendCommon::PostProcessingShader& PostProcessingShader::operator=(PostProcessingShader& move)
|
||||
PostProcessingShader& PostProcessingShader::operator=(PostProcessingShader& move)
|
||||
{
|
||||
m_name = std::move(move.m_name);
|
||||
m_code = std::move(move.m_code);
|
||||
|
@ -434,5 +489,3 @@ void PostProcessingShader::LoadOptions()
|
|||
m_options.push_back(std::move(current_option));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/rectangle.h"
|
||||
#include "core/types.h"
|
||||
#include "gpu_device.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace FrontendCommon {
|
||||
class GPUPipeline;
|
||||
class GPUTexture;
|
||||
|
||||
class PostProcessingChain;
|
||||
class PostProcessingShaderGen;
|
||||
|
||||
class PostProcessingShader
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
PUSH_CONSTANT_SIZE_THRESHOLD = 128
|
||||
};
|
||||
friend PostProcessingChain;
|
||||
friend PostProcessingShaderGen;
|
||||
|
||||
public:
|
||||
struct Option
|
||||
{
|
||||
enum : u32
|
||||
|
@ -82,11 +87,7 @@ public:
|
|||
bool LoadFromFile(std::string name, const char* filename);
|
||||
bool LoadFromString(std::string name, std::string code);
|
||||
|
||||
bool UsePushConstants() const;
|
||||
u32 GetUniformsSize() const;
|
||||
void FillUniformBuffer(void* buffer, u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
|
||||
s32 texture_view_width, s32 texture_view_height, u32 window_width, u32 window_height,
|
||||
s32 original_width, s32 original_height, float time) const;
|
||||
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height);
|
||||
|
||||
private:
|
||||
struct CommonUniforms
|
||||
|
@ -105,9 +106,21 @@ private:
|
|||
|
||||
void LoadOptions();
|
||||
|
||||
ALWAYS_INLINE GPUPipeline* GetPipeline() const { return m_pipeline.get(); }
|
||||
ALWAYS_INLINE GPUTexture* GetOutputTexture() const { return m_output_texture.get(); }
|
||||
ALWAYS_INLINE GPUFramebuffer* GetOutputFramebuffer() const { return m_output_framebuffer.get(); }
|
||||
|
||||
u32 GetUniformsSize() const;
|
||||
void FillUniformBuffer(void* buffer, u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
|
||||
s32 texture_view_width, s32 texture_view_height, u32 window_width, u32 window_height,
|
||||
s32 original_width, s32 original_height, float time) const;
|
||||
bool CompilePipeline(GPUTexture::Format target_format);
|
||||
|
||||
std::string m_name;
|
||||
std::string m_code;
|
||||
std::vector<Option> m_options;
|
||||
};
|
||||
|
||||
} // namespace FrontendCommon
|
||||
std::unique_ptr<GPUPipeline> m_pipeline;
|
||||
std::unique_ptr<GPUTexture> m_output_texture;
|
||||
std::unique_ptr<GPUFramebuffer> m_output_framebuffer;
|
||||
};
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include "postprocessing_shadergen.h"
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
PostProcessingShaderGen::PostProcessingShaderGen(RenderAPI render_api, bool supports_dual_source_blend)
|
||||
: ShaderGen(render_api, supports_dual_source_blend)
|
||||
{
|
||||
|
@ -18,7 +16,7 @@ std::string PostProcessingShaderGen::GeneratePostProcessingVertexShader(const Po
|
|||
|
||||
WriteHeader(ss);
|
||||
DeclareTexture(ss, "samp0", 0);
|
||||
WriteUniformBuffer(ss, shader, shader.UsePushConstants());
|
||||
WriteUniformBuffer(ss, shader, false);
|
||||
|
||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
|
||||
ss << R"(
|
||||
|
@ -41,7 +39,7 @@ std::string PostProcessingShaderGen::GeneratePostProcessingFragmentShader(const
|
|||
|
||||
WriteHeader(ss);
|
||||
DeclareTexture(ss, "samp0", 0);
|
||||
WriteUniformBuffer(ss, shader, shader.UsePushConstants());
|
||||
WriteUniformBuffer(ss, shader, false);
|
||||
|
||||
// Rename main, since we need to set up globals
|
||||
if (!m_glsl)
|
||||
|
@ -200,5 +198,3 @@ void PostProcessingShaderGen::WriteUniformBuffer(std::stringstream& ss, const Po
|
|||
|
||||
ss << "};\n\n";
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
|
@ -4,9 +4,6 @@
|
|||
#pragma once
|
||||
#include "core/shadergen.h"
|
||||
#include "postprocessing_shader.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
class PostProcessingShaderGen : public ShaderGen
|
||||
{
|
||||
|
@ -20,5 +17,3 @@ public:
|
|||
private:
|
||||
void WriteUniformBuffer(std::stringstream& ss, const PostProcessingShader& shader, bool use_push_constants);
|
||||
};
|
||||
|
||||
} // namespace FrontendCommon
|
|
@ -46,16 +46,6 @@ RenderAPI VulkanGPUDevice::GetRenderAPI() const
|
|||
return RenderAPI::Vulkan;
|
||||
}
|
||||
|
||||
void* VulkanGPUDevice::GetDevice() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* VulkanGPUDevice::GetContext() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VulkanGPUDevice::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
@ -261,11 +251,6 @@ bool VulkanGPUDevice::SetupDevice()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VulkanGPUDevice::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(g_vulkan_context);
|
||||
}
|
||||
|
||||
bool VulkanGPUDevice::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
|
@ -469,12 +454,14 @@ void main()
|
|||
if (m_post_process_descriptor_set_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
plbuilder.AddDescriptorSet(m_post_process_descriptor_set_layout);
|
||||
plbuilder.AddPushConstants(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
|
||||
FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD);
|
||||
m_post_process_pipeline_layout = plbuilder.Create(device);
|
||||
if (m_post_process_pipeline_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
dslbuilder.AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
@ -552,11 +539,13 @@ void VulkanGPUDevice::DestroyResources()
|
|||
Vulkan::Util::SafeDestroyPipelineLayout(m_post_process_ubo_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_post_process_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_post_process_ubo_descriptor_set_layout);
|
||||
#if 0
|
||||
m_post_processing_input_texture.Destroy(false);
|
||||
Vulkan::Util::SafeDestroyFramebuffer(m_post_processing_input_framebuffer);
|
||||
m_post_processing_stages.clear();
|
||||
m_post_processing_ubo.Destroy(true);
|
||||
m_post_processing_chain.ClearStages();
|
||||
#endif
|
||||
|
||||
Vulkan::Util::SafeDestroyPipeline(m_display_pipeline);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_cursor_pipeline);
|
||||
|
@ -708,6 +697,7 @@ void VulkanGPUDevice::RenderDisplay()
|
|||
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
#if 0
|
||||
if (!m_post_processing_chain.IsEmpty())
|
||||
{
|
||||
ApplyPostProcessingChain(m_swap_chain->GetCurrentFramebuffer(), left, top, width, height,
|
||||
|
@ -716,6 +706,7 @@ void VulkanGPUDevice::RenderDisplay()
|
|||
m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer(), m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
|
||||
RenderDisplay(left, top, width, height, static_cast<Vulkan::Texture*>(m_display_texture), m_display_texture_view_x,
|
||||
|
@ -861,6 +852,7 @@ GPUDevice::AdapterAndModeList VulkanGPUDevice::StaticGetAdapterAndModeList(const
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
VulkanGPUDevice::PostProcessingStage::PostProcessingStage(PostProcessingStage&& move)
|
||||
: pipeline(move.pipeline), output_framebuffer(move.output_framebuffer),
|
||||
output_texture(std::move(move.output_texture)), uniforms_size(move.uniforms_size)
|
||||
|
@ -879,9 +871,11 @@ VulkanGPUDevice::PostProcessingStage::~PostProcessingStage()
|
|||
if (pipeline != VK_NULL_HANDLE)
|
||||
g_vulkan_context->DeferPipelineDestruction(pipeline);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool VulkanGPUDevice::SetPostProcessingChain(const std::string_view& config)
|
||||
{
|
||||
#if 0
|
||||
g_vulkan_context->ExecuteCommandBuffer(true);
|
||||
|
||||
if (config.empty())
|
||||
|
@ -966,8 +960,13 @@ bool VulkanGPUDevice::SetPostProcessingChain(const std::string_view& config)
|
|||
"Post Processing Uniform Buffer");
|
||||
m_post_processing_timer.Reset();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool VulkanGPUDevice::CheckPostProcessingRenderTargets(u32 target_width, u32 target_height)
|
||||
{
|
||||
DebugAssert(!m_post_processing_stages.empty());
|
||||
|
@ -1141,3 +1140,4 @@ void VulkanGPUDevice::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fina
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -24,10 +24,7 @@ public:
|
|||
~VulkanGPUDevice();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
|
@ -71,23 +68,6 @@ protected:
|
|||
float src_rect_height;
|
||||
};
|
||||
|
||||
struct PostProcessingStage
|
||||
{
|
||||
PostProcessingStage() = default;
|
||||
PostProcessingStage(PostProcessingStage&& move);
|
||||
~PostProcessingStage();
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkFramebuffer output_framebuffer = VK_NULL_HANDLE;
|
||||
Vulkan::Texture output_texture;
|
||||
u32 uniforms_size = 0;
|
||||
};
|
||||
|
||||
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
|
||||
void ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top, s32 final_width,
|
||||
s32 final_height, Vulkan::Texture* texture, s32 texture_view_x, s32 texture_view_y,
|
||||
s32 texture_view_width, s32 texture_view_height, u32 target_width, u32 target_height);
|
||||
|
||||
VkRenderPass GetRenderPassForDisplay() const;
|
||||
|
||||
bool CheckStagingBufferSize(u32 required_size);
|
||||
|
@ -125,11 +105,4 @@ protected:
|
|||
VkDescriptorSetLayout m_post_process_ubo_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_post_process_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_post_process_ubo_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
FrontendCommon::PostProcessingChain m_post_processing_chain;
|
||||
Vulkan::Texture m_post_processing_input_texture;
|
||||
VkFramebuffer m_post_processing_input_framebuffer = VK_NULL_HANDLE;
|
||||
Vulkan::StreamBuffer m_post_processing_ubo;
|
||||
std::vector<PostProcessingStage> m_post_processing_stages;
|
||||
Common::Timer m_post_processing_timer;
|
||||
};
|
||||
|
|
|
@ -219,7 +219,7 @@ bool GPU_HW::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
|||
|
||||
void GPU_HW::RestoreGraphicsAPIState()
|
||||
{
|
||||
g_host_display->SetTextureSampler(0, m_vram_read_texture.get(), g_host_display->GetPointSampler());
|
||||
g_host_display->SetTextureSampler(0, m_vram_read_texture.get(), g_host_display->GetNearestSampler());
|
||||
g_host_display->SetFramebuffer(m_vram_framebuffer.get());
|
||||
g_host_display->SetViewport(0, 0, m_vram_texture->GetWidth(), m_vram_texture->GetHeight());
|
||||
SetScissor();
|
||||
|
@ -500,15 +500,17 @@ bool GPU_HW::CreateBuffers()
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
else
|
||||
#endif
|
||||
if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
if (!m_downsample_texture.Create(m_device.Get(), VRAM_WIDTH, VRAM_HEIGHT, 1, 1, 1, GPUTexture::Type::RenderTarget,
|
||||
texture_format))
|
||||
if (!(m_downsample_texture = g_host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, 1, 1, 1,
|
||||
GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT)) ||
|
||||
!(m_downsample_framebuffer = g_host_display->CreateFramebuffer(m_downsample_texture.get())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
g_host_display->SetFramebuffer(m_vram_framebuffer.get());
|
||||
SetFullVRAMDirtyRectangle();
|
||||
|
@ -527,6 +529,8 @@ void GPU_HW::ClearFramebuffer()
|
|||
void GPU_HW::DestroyBuffers()
|
||||
{
|
||||
m_vram_upload_buffer.reset();
|
||||
m_downsample_framebuffer.reset();
|
||||
m_downsample_texture.reset();
|
||||
m_display_framebuffer.reset();
|
||||
m_vram_readback_framebuffer.reset();
|
||||
m_vram_update_depth_framebuffer.reset();
|
||||
|
@ -613,7 +617,7 @@ bool GPU_HW::CompilePipelines()
|
|||
};
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig = {};
|
||||
plconfig.layout = GPUPipeline::Layout::HWBatch;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTextureUBO;
|
||||
plconfig.input_layout.vertex_stride = sizeof(BatchVertex);
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||
|
@ -647,7 +651,8 @@ bool GPU_HW::CompilePipelines()
|
|||
gsl::span<const GPUPipeline::VertexAttribute>(batch_attributes);
|
||||
|
||||
plconfig.vertex_shader = batch_vertex_shaders[BoolToUInt8(textured)].get();
|
||||
plconfig.pixel_shader = batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing].get();
|
||||
plconfig.fragment_shader =
|
||||
batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing].get();
|
||||
|
||||
// TODO: Depth write always on???
|
||||
plconfig.depth.depth_test = depth_test_values[depth_test];
|
||||
|
@ -721,7 +726,7 @@ bool GPU_HW::CompilePipelines()
|
|||
progress.Increment();
|
||||
|
||||
// common state
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTexture;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTexturePushConstants;
|
||||
plconfig.per_sample_shading = false;
|
||||
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
||||
plconfig.vertex_shader = fullscreen_quad_vertex_shader.get();
|
||||
|
@ -737,7 +742,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetAlwaysWriteState();
|
||||
|
||||
if (!(m_vram_fill_pipelines[wrapped][interlaced] = g_host_display->CreatePipeline(plconfig)))
|
||||
|
@ -754,7 +759,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
for (u8 depth_test = 0; depth_test < 2; depth_test++)
|
||||
{
|
||||
plconfig.depth.depth_write = true;
|
||||
|
@ -778,7 +783,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
for (u8 depth_test = 0; depth_test < 2; depth_test++)
|
||||
{
|
||||
plconfig.depth.depth_write = true;
|
||||
|
@ -801,7 +806,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
plconfig.color_format = GPUTexture::Format::Unknown;
|
||||
plconfig.depth_format = VRAM_DS_FORMAT;
|
||||
plconfig.depth = GPUPipeline::DepthState::GetAlwaysWriteState();
|
||||
|
@ -827,7 +832,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
|
||||
if (!(m_vram_readback_pipeline = g_host_display->CreatePipeline(plconfig)))
|
||||
return false;
|
||||
|
@ -849,7 +854,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
|
||||
if (!(m_display_pipelines[depth_24][interlace_mode] = g_host_display->CreatePipeline(plconfig)))
|
||||
return false;
|
||||
|
@ -865,7 +870,7 @@ bool GPU_HW::CompilePipelines()
|
|||
if (!fs)
|
||||
return false;
|
||||
|
||||
plconfig.pixel_shader = fs.get();
|
||||
plconfig.fragment_shader = fs.get();
|
||||
if (!(m_copy_pipeline = g_host_display->CreatePipeline(plconfig)))
|
||||
return false;
|
||||
}
|
||||
|
@ -938,32 +943,23 @@ bool GPU_HW::CompilePipelines()
|
|||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_downsample_composite_pass_pipeline,
|
||||
"Downsample Composite Pass Pipeline");
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
gpbuilder.Clear();
|
||||
gpbuilder.SetRenderPass(m_downsample_render_pass, 0);
|
||||
gpbuilder.SetPipelineLayout(m_single_sampler_pipeline_layout);
|
||||
gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader);
|
||||
gpbuilder.SetNoCullRasterizationState();
|
||||
gpbuilder.SetNoDepthTestState();
|
||||
gpbuilder.SetNoBlendingState();
|
||||
gpbuilder.SetDynamicViewportAndScissorState();
|
||||
|
||||
std::unique_ptr<GPUShader> fs = g_host_display->CreateShaderFromSource(
|
||||
GPUShader::Stage::Pixel, shadergen.GenerateBoxSampleDownsampleFragmentShader());
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
m_downsample_first_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_first_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_downsample_first_pass_pipeline,
|
||||
"Downsample First Pass Pipeline");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
std::unique_ptr<GPUShader> fs =
|
||||
g_host_display->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateBoxSampleDownsampleFragmentShader());
|
||||
if (!fs)
|
||||
return false;
|
||||
|
||||
GL_OBJECT_NAME(fs, "Downsample First Pass Fragment Shader");
|
||||
plconfig.fragment_shader = fs.get();
|
||||
|
||||
if (!(m_downsample_first_pass_pipeline = g_host_display->CreatePipeline(plconfig)))
|
||||
return false;
|
||||
|
||||
GL_OBJECT_NAME(m_downsample_first_pass_pipeline, "Downsample First Pass Pipeline");
|
||||
}
|
||||
|
||||
progress.Increment();
|
||||
|
||||
|
@ -1027,11 +1023,11 @@ void GPU_HW::UpdateDepthBufferFromMaskBit()
|
|||
g_host_display->SetScissor(0, 0, m_vram_texture->GetWidth(), m_vram_texture->GetHeight());
|
||||
g_host_display->SetFramebuffer(m_vram_update_depth_framebuffer.get());
|
||||
g_host_display->SetPipeline(m_vram_update_depth_pipeline.get());
|
||||
g_host_display->SetTextureSampler(0, m_vram_texture.get(), g_host_display->GetPointSampler());
|
||||
g_host_display->SetTextureSampler(0, m_vram_texture.get(), g_host_display->GetNearestSampler());
|
||||
g_host_display->Draw(3, 0);
|
||||
|
||||
// Restore.
|
||||
g_host_display->SetTextureSampler(0, m_vram_read_texture.get(), g_host_display->GetPointSampler());
|
||||
g_host_display->SetTextureSampler(0, m_vram_read_texture.get(), g_host_display->GetNearestSampler());
|
||||
g_host_display->SetFramebuffer(m_vram_framebuffer.get());
|
||||
SetScissor();
|
||||
}
|
||||
|
@ -2087,7 +2083,7 @@ void GPU_HW::ReadVRAM(u32 x, u32 y, u32 width, u32 height)
|
|||
const u32 uniforms[4] = {copy_rect.left, copy_rect.top, copy_rect.GetWidth(), copy_rect.GetHeight()};
|
||||
g_host_display->SetPipeline(m_vram_readback_pipeline.get());
|
||||
g_host_display->SetFramebuffer(m_vram_readback_framebuffer.get());
|
||||
g_host_display->SetTextureSampler(0, m_vram_texture.get(), g_host_display->GetPointSampler());
|
||||
g_host_display->SetTextureSampler(0, m_vram_texture.get(), g_host_display->GetNearestSampler());
|
||||
g_host_display->SetViewportAndScissor(0, 0, encoded_width, encoded_height);
|
||||
g_host_display->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||
g_host_display->Draw(3, 0);
|
||||
|
@ -2436,7 +2432,7 @@ void GPU_HW::UpdateDisplay()
|
|||
}
|
||||
}
|
||||
|
||||
void GPU_HW::DownsampleFramebuffer(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
void GPU_HW::DownsampleFramebuffer(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
DownsampleFramebufferAdaptive(source, left, top, width, height);
|
||||
|
@ -2444,7 +2440,7 @@ void GPU_HW::DownsampleFramebuffer(const GPUTexture* source, u32 left, u32 top,
|
|||
DownsampleFramebufferBoxFilter(source, left, top, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW::DownsampleFramebufferAdaptive(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
void GPU_HW::DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
#if 0
|
||||
CD3D11_BOX src_box(left, top, 0, left + width, top + height, 1);
|
||||
|
@ -2517,31 +2513,25 @@ void GPU_HW::DownsampleFramebufferAdaptive(const GPUTexture* source, u32 left, u
|
|||
#endif
|
||||
}
|
||||
|
||||
void GPU_HW::DownsampleFramebufferBoxFilter(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
#if 0
|
||||
const u32 ds_left = left / m_resolution_scale;
|
||||
const u32 ds_top = top / m_resolution_scale;
|
||||
const u32 ds_width = width / m_resolution_scale;
|
||||
const u32 ds_height = height / m_resolution_scale;
|
||||
static constexpr float clear_color[4] = {};
|
||||
|
||||
m_context->ClearRenderTargetView(m_downsample_texture.GetD3DRTV(), clear_color);
|
||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||
m_context->OMSetRenderTargets(1, m_downsample_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->OMSetBlendState(m_blend_disabled_state.Get(), nullptr, 0xFFFFFFFFu);
|
||||
m_context->VSSetShader(m_screen_quad_vertex_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(m_downsample_first_pass_pixel_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShaderResources(0, 1, source->GetD3DSRVArray());
|
||||
SetViewportAndScissor(ds_left, ds_top, ds_width, ds_height);
|
||||
m_context->Draw(3, 0);
|
||||
source->MakeReadyForSampling();
|
||||
|
||||
g_host_display->ClearRenderTarget(m_downsample_texture.get(), 0);
|
||||
g_host_display->SetFramebuffer(m_downsample_framebuffer.get());
|
||||
g_host_display->SetPipeline(m_downsample_first_pass_pipeline.get());
|
||||
g_host_display->SetTextureSampler(0, source, g_host_display->GetNearestSampler());
|
||||
g_host_display->SetViewportAndScissor(ds_left, ds_top, ds_width, ds_height);
|
||||
g_host_display->Draw(3, 0);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
g_host_display->SetDisplayTexture(&m_downsample_texture, ds_left, ds_top, ds_width, ds_height);
|
||||
#else
|
||||
Panic("Not implemented");
|
||||
#endif
|
||||
g_host_display->SetDisplayTexture(m_downsample_texture.get(), ds_left, ds_top, ds_width, ds_height);
|
||||
}
|
||||
|
||||
void GPU_HW::DrawRendererStats(bool is_idle_frame)
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
protected:
|
||||
enum : u32
|
||||
{
|
||||
// TODO: Remove these
|
||||
VRAM_UPDATE_TEXTURE_BUFFER_SIZE = 4 * 1024 * 1024,
|
||||
VERTEX_BUFFER_SIZE = 4 * 1024 * 1024,
|
||||
UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024,
|
||||
|
@ -374,9 +375,9 @@ protected:
|
|||
SmoothingUBOData GetSmoothingUBO(u32 level, u32 left, u32 top, u32 width, u32 height, u32 tex_width,
|
||||
u32 tex_height) const;
|
||||
|
||||
void DownsampleFramebuffer(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferAdaptive(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferBoxFilter(const GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebuffer(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
|
||||
|
||||
std::unique_ptr<GPUTexture> m_vram_texture;
|
||||
std::unique_ptr<GPUTexture> m_vram_depth_texture;
|
||||
|
@ -458,6 +459,10 @@ protected:
|
|||
// TODO: get rid of this, and use image blits instead where supported
|
||||
std::unique_ptr<GPUPipeline> m_copy_pipeline;
|
||||
|
||||
std::unique_ptr<GPUTexture> m_downsample_texture;
|
||||
std::unique_ptr<GPUFramebuffer> m_downsample_framebuffer;
|
||||
//std::unique_ptr<GPUTexture> m_downsample_weight_texture;
|
||||
//std::unique_ptr<GPUFramebuffer> m_downsample_weight_framebuffer;
|
||||
std::unique_ptr<GPUPipeline> m_downsample_first_pass_pipeline;
|
||||
std::unique_ptr<GPUPipeline> m_downsample_mid_pass_pipeline;
|
||||
std::unique_ptr<GPUPipeline> m_downsample_blur_pass_pipeline;
|
||||
|
|
|
@ -71,7 +71,7 @@ void PostProcessingChainConfigWidget::updateList()
|
|||
|
||||
for (u32 i = 0; i < m_chain.GetStageCount(); i++)
|
||||
{
|
||||
const FrontendCommon::PostProcessingShader& shader = m_chain.GetShaderStage(i);
|
||||
const PostProcessingShader& shader = m_chain.GetShaderStage(i);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(shader.GetName()), m_ui.shaders);
|
||||
item->setData(Qt::UserRole, QVariant(i));
|
||||
|
@ -112,7 +112,7 @@ void PostProcessingChainConfigWidget::onAddButtonClicked()
|
|||
{
|
||||
QMenu menu;
|
||||
|
||||
const std::vector<std::string> shaders(FrontendCommon::PostProcessingChain::GetAvailableShaderNames());
|
||||
const std::vector<std::string> shaders(PostProcessingChain::GetAvailableShaderNames());
|
||||
if (shaders.empty())
|
||||
{
|
||||
menu.addAction(tr("No Shaders Available"))->setEnabled(false);
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
PostProcessingChainConfigWidget(QWidget* parent);
|
||||
~PostProcessingChainConfigWidget();
|
||||
|
||||
ALWAYS_INLINE FrontendCommon::PostProcessingChain& getChain() { return m_chain; }
|
||||
ALWAYS_INLINE PostProcessingChain& getChain() { return m_chain; }
|
||||
|
||||
bool setConfigString(const std::string_view& config_string);
|
||||
void setOptionsButtonVisible(bool visible);
|
||||
|
@ -51,5 +51,5 @@ private:
|
|||
|
||||
Ui::PostProcessingChainConfigWidget m_ui;
|
||||
|
||||
FrontendCommon::PostProcessingChain m_chain;
|
||||
PostProcessingChain m_chain;
|
||||
};
|
||||
|
|
|
@ -71,7 +71,7 @@ void PostProcessingSettingsWidget::updateShaderConfigPanel(s32 index)
|
|||
if (index < 0)
|
||||
return;
|
||||
|
||||
FrontendCommon::PostProcessingShader& shader = m_ui.widget->getChain().GetShaderStage(static_cast<u32>(index));
|
||||
PostProcessingShader& shader = m_ui.widget->getChain().GetShaderStage(static_cast<u32>(index));
|
||||
if (!shader.HasOptions())
|
||||
return;
|
||||
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QSlider>
|
||||
|
||||
using FrontendCommon::PostProcessingShader;
|
||||
|
||||
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
|
||||
FrontendCommon::PostProcessingShader* shader)
|
||||
PostProcessingShader* shader)
|
||||
: QWidget(parent), m_shader(shader)
|
||||
{
|
||||
createUi();
|
||||
|
@ -146,7 +144,7 @@ void PostProcessingShaderConfigWidget::onResetToDefaultsClicked()
|
|||
}
|
||||
|
||||
PostProcessingShaderConfigDialog::PostProcessingShaderConfigDialog(QWidget* parent,
|
||||
FrontendCommon::PostProcessingShader* shader)
|
||||
PostProcessingShader* shader)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("%1 Shader Options").arg(QString::fromStdString(shader->GetName())));
|
||||
|
|
|
@ -13,7 +13,7 @@ class PostProcessingShaderConfigWidget : public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PostProcessingShaderConfigWidget(QWidget* parent, FrontendCommon::PostProcessingShader* shader);
|
||||
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingShader* shader);
|
||||
~PostProcessingShaderConfigWidget();
|
||||
|
||||
QGridLayout* getLayout() { return m_layout; }
|
||||
|
@ -28,7 +28,7 @@ private Q_SLOTS:
|
|||
protected:
|
||||
void createUi();
|
||||
|
||||
FrontendCommon::PostProcessingShader* m_shader;
|
||||
PostProcessingShader* m_shader;
|
||||
QGridLayout* m_layout;
|
||||
};
|
||||
|
||||
|
@ -37,7 +37,7 @@ class PostProcessingShaderConfigDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PostProcessingShaderConfigDialog(QWidget* parent, FrontendCommon::PostProcessingShader* shader);
|
||||
PostProcessingShaderConfigDialog(QWidget* parent, PostProcessingShader* shader);
|
||||
~PostProcessingShaderConfigDialog();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
|
|
@ -378,7 +378,7 @@ static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
|||
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
||||
static std::vector<std::string> s_graphics_adapter_list_cache;
|
||||
static std::vector<std::string> s_fullscreen_mode_list_cache;
|
||||
static FrontendCommon::PostProcessingChain s_postprocessing_chain;
|
||||
static PostProcessingChain s_postprocessing_chain;
|
||||
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||
static std::atomic_bool s_settings_changed{false};
|
||||
static std::atomic_bool s_game_settings_changed{false};
|
||||
|
@ -3866,7 +3866,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
if (MenuButton(ICON_FA_PLUS " Add Shader", "Adds a new shader to the chain."))
|
||||
{
|
||||
ImGuiFullscreen::ChoiceDialogOptions options;
|
||||
for (std::string& name : FrontendCommon::PostProcessingChain::GetAvailableShaderNames())
|
||||
for (std::string& name : PostProcessingChain::GetAvailableShaderNames())
|
||||
options.emplace_back(std::move(name), false);
|
||||
|
||||
OpenChoiceDialog(
|
||||
|
@ -3912,7 +3912,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
for (u32 stage_index = 0; stage_index < s_postprocessing_chain.GetStageCount(); stage_index++)
|
||||
{
|
||||
ImGui::PushID(stage_index);
|
||||
FrontendCommon::PostProcessingShader& stage = s_postprocessing_chain.GetShaderStage(stage_index);
|
||||
PostProcessingShader& stage = s_postprocessing_chain.GetShaderStage(stage_index);
|
||||
str.Fmt("Stage {}: {}", stage_index + 1, stage.GetName());
|
||||
MenuHeading(str);
|
||||
|
||||
|
@ -3936,11 +3936,11 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
postprocessing_action_index = stage_index;
|
||||
}
|
||||
|
||||
for (FrontendCommon::PostProcessingShader::Option& opt : stage.GetOptions())
|
||||
for (PostProcessingShader::Option& opt : stage.GetOptions())
|
||||
{
|
||||
switch (opt.type)
|
||||
{
|
||||
case FrontendCommon::PostProcessingShader::Option::Type::Bool:
|
||||
case PostProcessingShader::Option::Type::Bool:
|
||||
{
|
||||
bool value = (opt.value[0].int_value != 0);
|
||||
tstr.Fmt(ICON_FA_COGS " {}", opt.ui_name);
|
||||
|
@ -3953,7 +3953,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
}
|
||||
break;
|
||||
|
||||
case FrontendCommon::PostProcessingShader::Option::Type::Float:
|
||||
case PostProcessingShader::Option::Type::Float:
|
||||
{
|
||||
tstr.Fmt(ICON_FA_RULER_VERTICAL " {}##{}", opt.ui_name, opt.name);
|
||||
str.Fmt("Value: {} | Default: {} | Minimum: {} | Maximum: {}", opt.value[0].float_value,
|
||||
|
@ -4056,7 +4056,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
}
|
||||
break;
|
||||
|
||||
case FrontendCommon::PostProcessingShader::Option::Type::Int:
|
||||
case PostProcessingShader::Option::Type::Int:
|
||||
{
|
||||
tstr.Fmt(ICON_FA_RULER_VERTICAL " {}##{}", opt.ui_name, opt.name);
|
||||
str.Fmt("Value: {} | Default: {} | Minimum: {} | Maximum: {}", opt.value[0].int_value,
|
||||
|
@ -4168,7 +4168,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||
{
|
||||
case POSTPROCESSING_ACTION_REMOVE:
|
||||
{
|
||||
FrontendCommon::PostProcessingShader& stage = s_postprocessing_chain.GetShaderStage(postprocessing_action_index);
|
||||
PostProcessingShader& stage = s_postprocessing_chain.GetShaderStage(postprocessing_action_index);
|
||||
ShowToast(std::string(), fmt::format("Removed stage {} ({}).", postprocessing_action_index + 1, stage.GetName()));
|
||||
s_postprocessing_chain.RemoveStage(postprocessing_action_index);
|
||||
SavePostProcessingChain();
|
||||
|
@ -4454,7 +4454,9 @@ void FullscreenUI::DrawAchievementsSettingsPage()
|
|||
EndMenuButtons();
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawAchievementsLoginWindow() {}
|
||||
void FullscreenUI::DrawAchievementsLoginWindow()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue