GPUDevice: Move display logic to GPU

This commit is contained in:
Stenzek 2023-08-27 18:13:50 +10:00
parent 6c185ca17b
commit 8db8baf33f
19 changed files with 761 additions and 900 deletions

View file

@ -3978,16 +3978,7 @@ void FullscreenUI::SavePostProcessingChain()
SettingsInterface* bsi = GetEditingSettingsInterface();
const std::string config(s_postprocessing_chain.GetConfigString());
bsi->SetStringValue("Display", "PostProcessChain", config.c_str());
if (bsi->GetBoolValue("Display", "PostProcessing", false))
g_gpu_device->SetPostProcessingChain(config);
if (IsEditingGameSettings(bsi))
{
s_game_settings_interface->Save();
}
else
{
s_settings_changed.store(true, std::memory_order_release);
}
SetSettingsChanged(bsi);
}
enum
@ -4014,11 +4005,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
FSUI_CSTR("Reloads the shaders from disk, applying any changes."),
bsi->GetBoolValue("Display", "PostProcessing", false)))
{
const std::string chain(bsi->GetStringValue("Display", "PostProcessChain", ""));
g_gpu_device->SetPostProcessingChain(chain);
if (chain.empty())
ShowToast(std::string(), FSUI_STR("Post-processing chain is empty."));
else
if (!g_gpu || g_gpu->UpdatePostProcessingChain())
ShowToast(std::string(), FSUI_STR("Post-processing shaders reloaded."));
}

View file

@ -12,16 +12,21 @@
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
#include "util/postprocessing_chain.h"
#include "util/shadergen.h"
#include "util/state_wrapper.h"
#include "common/align.h"
#include "common/file_system.h"
#include "common/heap_array.h"
#include "common/log.h"
#include "common/string_util.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
#include <cmath>
#include <thread>
Log_SetChannel(GPU);
@ -54,12 +59,14 @@ bool GPU::Initialize()
m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig();
if (g_settings.display_post_processing && !g_settings.display_post_process_chain.empty() &&
!g_gpu_device->SetPostProcessingChain(g_settings.display_post_process_chain))
if (!CompilePipelines())
{
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
Host::ReportErrorAsync("Error", "Failed to compile base GPU pipelines.");
return false;
}
UpdatePostProcessingChain();
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu);
return true;
@ -501,7 +508,7 @@ float GPU::ComputeVerticalFrequency() const
static_cast<double>(ticks_per_frame));
}
float GPU::GetDisplayAspectRatio() const
float GPU::ComputeDisplayAspectRatio() const
{
if (g_settings.display_force_4_3_for_24bit && m_GPUSTAT.display_area_color_depth_24)
{
@ -984,8 +991,17 @@ void GPU::UpdateCommandTickEvent()
bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, float x_scale, u32* out_tick,
u32* out_line) const
{
auto [display_x, display_y] = g_gpu_device->ConvertWindowCoordinatesToDisplayCoordinates(
window_x, window_y, g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight());
float left_padding, top_padding, scale;
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), &left_padding, &top_padding,
&scale, nullptr);
// convert coordinates to active display region, then to full display region
const float scaled_display_x = static_cast<float>(window_x) - left_padding;
const float scaled_display_y = static_cast<float>(window_y) - top_padding;
// scale back to internal resolution
float display_x = scaled_display_x / scale / x_scale;
float display_y = scaled_display_y / scale;
if (x_scale != 1.0f)
{
@ -1526,6 +1542,562 @@ void GPU::SetTextureWindow(u32 value)
m_draw_mode.texture_window_changed = true;
}
bool GPU::CompilePipelines()
{
ShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend);
GPUPipeline::GraphicsConfig plconfig;
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
plconfig.input_layout.vertex_stride = 0;
plconfig.primitive = GPUPipeline::Primitive::Triangles;
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
plconfig.color_format = g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
plconfig.depth_format = GPUTexture::Format::Unknown;
plconfig.samples = 1;
plconfig.per_sample_shading = false;
std::unique_ptr<GPUShader> display_vs =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
std::unique_ptr<GPUShader> display_fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
if (!display_vs || !display_fs)
return false;
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
plconfig.vertex_shader = display_vs.get();
plconfig.fragment_shader = display_fs.get();
if (!(m_display_pipeline = g_gpu_device->CreatePipeline(plconfig)))
return false;
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
return true;
}
void GPU::ClearDisplayTexture()
{
m_display_texture = nullptr;
m_display_texture_view_x = 0;
m_display_texture_view_y = 0;
m_display_texture_view_width = 0;
m_display_texture_view_height = 0;
}
void GPU::SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height)
{
DebugAssert(texture);
m_display_texture = texture;
m_display_texture_view_x = view_x;
m_display_texture_view_y = view_y;
m_display_texture_view_width = view_width;
m_display_texture_view_height = view_height;
}
void GPU::SetDisplayTextureRect(s32 view_x, s32 view_y, s32 view_width, s32 view_height)
{
m_display_texture_view_x = view_x;
m_display_texture_view_y = view_y;
m_display_texture_view_width = view_width;
m_display_texture_view_height = view_height;
}
void GPU::SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width,
s32 active_height, float display_aspect_ratio)
{
m_display_width = display_width;
m_display_height = display_height;
m_display_active_left = active_left;
m_display_active_top = active_top;
m_display_active_width = active_width;
m_display_active_height = active_height;
m_display_aspect_ratio = display_aspect_ratio;
}
bool GPU::PresentDisplay()
{
if (!HasDisplayTexture())
return g_gpu_device->BeginPresent(false);
const Common::Rectangle<s32> draw_rect =
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight());
return RenderDisplay(nullptr, draw_rect, g_settings.display_linear_filtering, true);
}
bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool linear_filter,
bool postfx)
{
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight());
if (m_display_texture)
m_display_texture->MakeReadyForSampling();
const GPUTexture::Format hdformat =
(target && target->GetRT()) ? target->GetRT()->GetFormat() : g_gpu_device->GetWindowFormat();
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetWindowWidth();
const u32 target_height = target ? target->GetHeight() : g_gpu_device->GetWindowHeight();
const bool really_postfx = (postfx && HasDisplayTexture() && m_post_processing_chain &&
m_post_processing_chain->CheckTargets(hdformat, target_width, target_height));
if (really_postfx)
{
g_gpu_device->ClearRenderTarget(m_post_processing_chain->GetInputTexture(), 0);
g_gpu_device->SetFramebuffer(m_post_processing_chain->GetInputFramebuffer());
}
else
{
if (target)
g_gpu_device->SetFramebuffer(target);
else if (!g_gpu_device->BeginPresent(false))
return false;
}
if (!HasDisplayTexture())
return true;
g_gpu_device->SetPipeline(m_display_pipeline.get());
g_gpu_device->SetTextureSampler(0, m_display_texture,
linear_filter ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());
const float position_adjust = linear_filter ? 0.5f : 0.0f;
const float size_adjust = linear_filter ? 1.0f : 0.0f;
const float uniforms[4] = {(static_cast<float>(m_display_texture_view_x) + position_adjust) /
static_cast<float>(m_display_texture->GetWidth()),
(static_cast<float>(m_display_texture_view_y) + position_adjust) /
static_cast<float>(m_display_texture->GetHeight()),
(static_cast<float>(m_display_texture_view_width) - size_adjust) /
static_cast<float>(m_display_texture->GetWidth()),
(static_cast<float>(m_display_texture_view_height) - size_adjust) /
static_cast<float>(m_display_texture->GetHeight())};
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
g_gpu_device->SetViewportAndScissor(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight());
g_gpu_device->Draw(3, 0);
if (really_postfx)
{
return m_post_processing_chain->Apply(target, draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
draw_rect.GetHeight(), m_display_texture_view_width,
m_display_texture_view_height);
}
else
{
return true;
}
}
bool GPU::UpdatePostProcessingChain()
{
if (!g_settings.display_post_processing || g_settings.display_post_process_chain.empty())
{
m_post_processing_chain.reset();
return true;
}
m_post_processing_chain = std::make_unique<PostProcessingChain>();
if (!m_post_processing_chain->CreateFromString(g_settings.display_post_process_chain))
{
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
m_post_processing_chain.reset();
return false;
}
return true;
}
Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
float* out_top_padding, float* out_scale, float* out_x_scale,
bool apply_aspect_ratio /* = true */) const
{
const float window_ratio = static_cast<float>(window_width) / static_cast<float>(window_height);
const float display_aspect_ratio = g_settings.display_stretch ? window_ratio : m_display_aspect_ratio;
const float x_scale =
apply_aspect_ratio ?
(display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height))) :
1.0f;
const float display_width = g_settings.display_stretch_vertically ? static_cast<float>(m_display_width) :
static_cast<float>(m_display_width) * x_scale;
const float display_height = g_settings.display_stretch_vertically ? static_cast<float>(m_display_height) / x_scale :
static_cast<float>(m_display_height);
const float active_left = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_left) :
static_cast<float>(m_display_active_left) * x_scale;
const float active_top = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_top) / x_scale :
static_cast<float>(m_display_active_top);
const float active_width = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_width) :
static_cast<float>(m_display_active_width) * x_scale;
const float active_height = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_height) / x_scale :
static_cast<float>(m_display_active_height);
if (out_x_scale)
*out_x_scale = x_scale;
// now fit it within the window
float scale;
if ((display_width / display_height) >= window_ratio)
{
// align in middle vertically
scale = static_cast<float>(window_width) / display_width;
if (g_settings.display_integer_scaling)
scale = std::max(std::floor(scale), 1.0f);
if (out_left_padding)
{
if (g_settings.display_integer_scaling)
*out_left_padding = std::max<float>((static_cast<float>(window_width) - display_width * scale) / 2.0f, 0.0f);
else
*out_left_padding = 0.0f;
}
if (out_top_padding)
{
switch (g_settings.display_alignment)
{
case DisplayAlignment::RightOrBottom:
*out_top_padding = std::max<float>(static_cast<float>(window_height) - (display_height * scale), 0.0f);
break;
case DisplayAlignment::Center:
*out_top_padding =
std::max<float>((static_cast<float>(window_height) - (display_height * scale)) / 2.0f, 0.0f);
break;
case DisplayAlignment::LeftOrTop:
default:
*out_top_padding = 0.0f;
break;
}
}
}
else
{
// align in middle horizontally
scale = static_cast<float>(window_height) / display_height;
if (g_settings.display_integer_scaling)
scale = std::max(std::floor(scale), 1.0f);
if (out_left_padding)
{
switch (g_settings.display_alignment)
{
case DisplayAlignment::RightOrBottom:
*out_left_padding = std::max<float>(static_cast<float>(window_width) - (display_width * scale), 0.0f);
break;
case DisplayAlignment::Center:
*out_left_padding =
std::max<float>((static_cast<float>(window_width) - (display_width * scale)) / 2.0f, 0.0f);
break;
case DisplayAlignment::LeftOrTop:
default:
*out_left_padding = 0.0f;
break;
}
}
if (out_top_padding)
{
if (g_settings.display_integer_scaling)
*out_top_padding = std::max<float>((static_cast<float>(window_height) - (display_height * scale)) / 2.0f, 0.0f);
else
*out_top_padding = 0.0f;
}
}
if (out_scale)
*out_scale = scale;
return Common::Rectangle<float>::FromExtents(active_left * scale, active_top * scale, active_width * scale,
active_height * scale);
}
Common::Rectangle<s32> GPU::CalculateDrawRect(s32 window_width, s32 window_height,
bool apply_aspect_ratio /* = true */) const
{
float left_padding, top_padding;
const Common::Rectangle<float> draw_rc =
CalculateDrawRect(window_width, window_height, &left_padding, &top_padding, nullptr, nullptr, apply_aspect_ratio);
// TODO: This should be a float rectangle. But because GL is lame, it only has integer viewports...
return Common::Rectangle<s32>::FromExtents(
static_cast<s32>(draw_rc.left + left_padding), static_cast<s32>(draw_rc.top + top_padding),
static_cast<s32>(draw_rc.GetWidth()), static_cast<s32>(draw_rc.GetHeight()));
}
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
bool clear_alpha, bool flip_y, u32 resize_width, u32 resize_height,
std::vector<u32> texture_data, u32 texture_data_stride,
GPUTexture::Format texture_format)
{
const char* extension = std::strrchr(filename.c_str(), '.');
if (!extension)
{
Log_ErrorPrintf("Unable to determine file extension for '%s'", filename.c_str());
return false;
}
if (!GPUTexture::ConvertTextureDataToRGBA8(width, height, texture_data, texture_data_stride, texture_format))
return false;
if (clear_alpha)
{
for (u32& pixel : texture_data)
pixel |= 0xFF000000;
}
if (flip_y)
GPUTexture::FlipTextureDataRGBA8(width, height, texture_data, texture_data_stride);
if (resize_width > 0 && resize_height > 0 && (resize_width != width || resize_height != height))
{
std::vector<u32> resized_texture_data(resize_width * resize_height);
u32 resized_texture_stride = sizeof(u32) * resize_width;
if (!stbir_resize_uint8(reinterpret_cast<u8*>(texture_data.data()), width, height, texture_data_stride,
reinterpret_cast<u8*>(resized_texture_data.data()), resize_width, resize_height,
resized_texture_stride, 4))
{
Log_ErrorPrintf("Failed to resize texture data from %ux%u to %ux%u", width, height, resize_width, resize_height);
return false;
}
width = resize_width;
height = resize_height;
texture_data = std::move(resized_texture_data);
texture_data_stride = resized_texture_stride;
}
const auto write_func = [](void* context, void* data, int size) {
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
};
bool result = false;
if (StringUtil::Strcasecmp(extension, ".png") == 0)
{
result =
(stbi_write_png_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), texture_data_stride) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".jpg") == 0)
{
result = (stbi_write_jpg_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), 95) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".tga") == 0)
{
result = (stbi_write_tga_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".bmp") == 0)
{
result = (stbi_write_bmp_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
}
if (!result)
{
Log_ErrorPrintf("Unknown extension in filename '%s' or save error: '%s'", filename.c_str(), extension);
return false;
}
return true;
}
bool GPU::WriteDisplayTextureToFile(std::string filename, bool full_resolution /* = true */,
bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = false */)
{
if (!m_display_texture)
return false;
s32 resize_width = 0;
s32 resize_height = std::abs(m_display_texture_view_height);
if (apply_aspect_ratio)
{
const float ss_width_scale = static_cast<float>(m_display_active_width) / static_cast<float>(m_display_width);
const float ss_height_scale = static_cast<float>(m_display_active_height) / static_cast<float>(m_display_height);
const float ss_aspect_ratio = m_display_aspect_ratio * ss_width_scale / ss_height_scale;
resize_width = g_settings.display_stretch_vertically ?
m_display_texture_view_width :
static_cast<s32>(static_cast<float>(resize_height) * ss_aspect_ratio);
resize_height = g_settings.display_stretch_vertically ?
static_cast<s32>(static_cast<float>(resize_height) /
(m_display_aspect_ratio /
(static_cast<float>(m_display_width) / static_cast<float>(m_display_height)))) :
resize_height;
}
else
{
resize_width = m_display_texture_view_width;
}
if (!full_resolution)
{
const s32 resolution_scale = std::abs(m_display_texture_view_height) / m_display_active_height;
resize_height /= resolution_scale;
resize_width /= resolution_scale;
}
if (resize_width <= 0 || resize_height <= 0)
return false;
const u32 read_x = static_cast<u32>(m_display_texture_view_x);
const u32 read_y = static_cast<u32>(m_display_texture_view_y);
const u32 read_width = static_cast<u32>(m_display_texture_view_width);
const u32 read_height = static_cast<u32>(m_display_texture_view_height);
std::vector<u32> texture_data(read_width * read_height);
const u32 texture_data_stride =
Common::AlignUpPow2(GPUTexture::GetPixelSize(m_display_texture->GetFormat()) * read_width, 4);
if (!g_gpu_device->DownloadTexture(m_display_texture, read_x, read_y, read_width, read_height, texture_data.data(),
texture_data_stride))
{
Log_ErrorPrintf("Texture download failed");
RestoreGraphicsAPIState();
return false;
}
RestoreGraphicsAPIState();
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
if (!fp)
{
Log_ErrorPrintf("Can't open file '%s': errno %d", filename.c_str(), errno);
return false;
}
constexpr bool clear_alpha = true;
const bool flip_y = g_gpu_device->UsesLowerLeftOrigin();
if (!compress_on_thread)
{
return CompressAndWriteTextureToFile(read_width, read_height, std::move(filename), std::move(fp), clear_alpha,
flip_y, resize_width, resize_height, std::move(texture_data),
texture_data_stride, m_display_texture->GetFormat());
}
std::thread compress_thread(CompressAndWriteTextureToFile, read_width, read_height, std::move(filename),
std::move(fp), clear_alpha, flip_y, resize_width, resize_height, std::move(texture_data),
texture_data_stride, m_display_texture->GetFormat());
compress_thread.detach();
return true;
}
bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
const GPUTexture::Format hdformat =
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
std::unique_ptr<GPUTexture> render_texture =
g_gpu_device->CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat);
if (!render_texture)
return false;
std::unique_ptr<GPUFramebuffer> render_fb = g_gpu_device->CreateFramebuffer(render_texture.get());
if (!render_fb)
return false;
g_gpu_device->ClearRenderTarget(render_texture.get(), 0);
RenderDisplay(render_fb.get(), draw_rect, g_settings.display_linear_filtering, postfx);
g_gpu_device->SetFramebuffer(nullptr);
const u32 stride = GPUTexture::GetPixelSize(hdformat) * width;
out_pixels->resize(width * height);
if (!g_gpu_device->DownloadTexture(render_texture.get(), 0, 0, width, height, out_pixels->data(), stride))
{
RestoreGraphicsAPIState();
return false;
}
*out_stride = stride;
*out_format = hdformat;
RestoreGraphicsAPIState();
return true;
}
bool GPU::RenderScreenshotToFile(std::string filename, bool internal_resolution /* = false */,
bool compress_on_thread /* = false */)
{
u32 width = g_gpu_device->GetWindowWidth();
u32 height = g_gpu_device->GetWindowHeight();
auto [draw_left, draw_top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (internal_resolution && m_display_texture_view_width != 0 && m_display_texture_view_height != 0)
{
// If internal res, scale the computed draw rectangle to the internal res.
// We re-use the draw rect because it's already been AR corrected.
const float sar =
static_cast<float>(m_display_texture_view_width) / static_cast<float>(m_display_texture_view_height);
const float dar = static_cast<float>(draw_width) / static_cast<float>(draw_height);
if (sar >= dar)
{
// stretch height, preserve width
const float scale = static_cast<float>(m_display_texture_view_width) / static_cast<float>(draw_width);
width = m_display_texture_view_width;
height = static_cast<u32>(std::round(static_cast<float>(draw_height) * scale));
}
else
{
// stretch width, preserve height
const float scale = static_cast<float>(m_display_texture_view_height) / static_cast<float>(draw_height);
width = static_cast<u32>(std::round(static_cast<float>(draw_width) * scale));
height = m_display_texture_view_height;
}
// DX11 won't go past 16K texture size.
constexpr u32 MAX_TEXTURE_SIZE = 16384;
if (width > MAX_TEXTURE_SIZE)
{
height = static_cast<u32>(static_cast<float>(height) /
(static_cast<float>(width) / static_cast<float>(MAX_TEXTURE_SIZE)));
width = MAX_TEXTURE_SIZE;
}
if (height > MAX_TEXTURE_SIZE)
{
height = MAX_TEXTURE_SIZE;
width = static_cast<u32>(static_cast<float>(width) /
(static_cast<float>(height) / static_cast<float>(MAX_TEXTURE_SIZE)));
}
// Remove padding, it's not part of the framebuffer.
draw_left = 0;
draw_top = 0;
draw_width = static_cast<s32>(width);
draw_height = static_cast<s32>(height);
}
if (width == 0 || height == 0)
return false;
std::vector<u32> pixels;
u32 pixels_stride;
GPUTexture::Format pixels_format;
if (!RenderScreenshotToBuffer(width, height,
Common::Rectangle<s32>::FromExtents(draw_left, draw_top, draw_width, draw_height),
!internal_resolution, &pixels, &pixels_stride, &pixels_format))
{
Log_ErrorPrintf("Failed to render %ux%u screenshot", width, height);
return false;
}
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
if (!fp)
{
Log_ErrorPrintf("Can't open file '%s': errno %d", filename.c_str(), errno);
return false;
}
if (!compress_on_thread)
{
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), true,
g_gpu_device->UsesLowerLeftOrigin(), width, height, std::move(pixels),
pixels_stride, pixels_format);
}
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp), true,
g_gpu_device->UsesLowerLeftOrigin(), width, height, std::move(pixels), pixels_stride,
pixels_format);
compress_thread.detach();
return true;
}
bool GPU::DumpVRAMToFile(const char* filename)
{
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);

View file

@ -2,23 +2,32 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/bitfield.h"
#include "common/fifo_queue.h"
#include "common/rectangle.h"
#include "gpu_types.h"
#include "timers.h"
#include "types.h"
#include "util/gpu_texture.h"
#include "common/bitfield.h"
#include "common/fifo_queue.h"
#include "common/rectangle.h"
#include <algorithm>
#include <array>
#include <deque>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
class StateWrapper;
class GPUDevice;
class GPUFramebuffer;
class GPUTexture;
class GPUPipeline;
class PostProcessingChain;
class TimingEvent;
@ -157,7 +166,7 @@ public:
float ComputeHorizontalFrequency() const;
float ComputeVerticalFrequency() const;
float GetDisplayAspectRatio() const;
float ComputeDisplayAspectRatio() const;
static std::unique_ptr<GPU> CreateHardwareRenderer();
static std::unique_ptr<GPU> CreateSoftwareRenderer();
@ -175,6 +184,31 @@ public:
// Ensures all buffered vertices are drawn.
virtual void FlushRender();
ALWAYS_INLINE const void* GetDisplayTextureHandle() const { return m_display_texture; }
ALWAYS_INLINE s32 GetDisplayWidth() const { return m_display_width; }
ALWAYS_INLINE s32 GetDisplayHeight() const { return m_display_height; }
ALWAYS_INLINE float GetDisplayAspectRatio() const { return m_display_aspect_ratio; }
ALWAYS_INLINE bool HasDisplayTexture() const { return static_cast<bool>(m_display_texture); }
/// Helper function for computing the draw rectangle in a larger window.
Common::Rectangle<s32> CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio = true) const;
/// Helper function to save current display texture to PNG.
bool WriteDisplayTextureToFile(std::string filename, bool full_resolution = true, bool apply_aspect_ratio = true,
bool compress_on_thread = false);
/// Renders the display, optionally with postprocessing to the specified image.
bool RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format);
/// Helper function to save screenshot to PNG.
bool RenderScreenshotToFile(std::string filename, bool internal_resolution = false, bool compress_on_thread = false);
/// Draws the current display texture, with any post-processing.
bool PresentDisplay();
bool UpdatePostProcessingChain();
protected:
TickCount CRTCTicksToSystemTicks(TickCount crtc_ticks, TickCount fractional_ticks) const;
TickCount SystemTicksToCRTCTicks(TickCount sysclk_ticks, TickCount* fractional_ticks) const;
@ -542,6 +576,35 @@ protected:
TickCount m_max_run_ahead = 128;
u32 m_fifo_size = 128;
void ClearDisplayTexture();
void SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height);
void SetDisplayTextureRect(s32 view_x, s32 view_y, s32 view_width, s32 view_height);
void SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width,
s32 active_height, float display_aspect_ratio);
Common::Rectangle<float> CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
float* out_top_padding, float* out_scale, float* out_x_scale,
bool apply_aspect_ratio = true) const;
bool RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool linear_filter, bool postfx);
s32 m_display_width = 0;
s32 m_display_height = 0;
s32 m_display_active_left = 0;
s32 m_display_active_top = 0;
s32 m_display_active_width = 0;
s32 m_display_active_height = 0;
float m_display_aspect_ratio = 1.0f;
std::unique_ptr<GPUPipeline> m_display_pipeline;
GPUTexture* m_display_texture = nullptr;
s32 m_display_texture_view_x = 0;
s32 m_display_texture_view_y = 0;
s32 m_display_texture_view_width = 0;
s32 m_display_texture_view_height = 0;
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
struct Stats
{
u32 num_vram_reads;
@ -555,6 +618,8 @@ protected:
Stats m_last_stats = {};
private:
bool CompilePipelines();
using GP0CommandHandler = bool (GPU::*)();
using GP0CommandHandlerTable = std::array<GP0CommandHandler, 256>;
static GP0CommandHandlerTable GenerateGP0CommandHandlerTable();

View file

@ -125,8 +125,6 @@ GPU_HW::GPU_HW() : GPU()
GPU_HW::~GPU_HW()
{
g_gpu_device->ClearDisplayTexture();
if (m_sw_renderer)
{
m_sw_renderer->Shutdown();
@ -337,7 +335,6 @@ void GPU_HW::UpdateSettings()
{
RestoreGraphicsAPIState();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
g_gpu_device->ClearDisplayTexture();
DestroyBuffers();
}
@ -503,7 +500,7 @@ bool GPU_HW::CreateBuffers()
GPUTexture::Type::DepthStencil, VRAM_DS_FORMAT)) ||
!(m_vram_read_texture = g_gpu_device->CreateTexture(texture_width, texture_height, 1, 1, 1,
GPUTexture::Type::Texture, VRAM_RT_FORMAT)) ||
!(m_display_texture = g_gpu_device->CreateTexture(
!(m_display_private_texture = g_gpu_device->CreateTexture(
((m_downsample_mode == GPUDownsampleMode::Adaptive) ? VRAM_WIDTH : GPU_MAX_DISPLAY_WIDTH) *
m_resolution_scale,
GPU_MAX_DISPLAY_HEIGHT * m_resolution_scale, 1, 1, 1, GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT)) ||
@ -516,14 +513,14 @@ bool GPU_HW::CreateBuffers()
GL_OBJECT_NAME(m_vram_texture, "VRAM Texture");
GL_OBJECT_NAME(m_vram_depth_texture, "VRAM Depth Texture");
GL_OBJECT_NAME(m_vram_read_texture, "VRAM Read Texture");
GL_OBJECT_NAME(m_display_texture, "Display Texture");
GL_OBJECT_NAME(m_display_private_texture, "Display Texture");
GL_OBJECT_NAME(m_vram_readback_texture, "VRAM Readback Texture");
// vram framebuffer has both colour and depth
if (!(m_vram_framebuffer = g_gpu_device->CreateFramebuffer(m_vram_texture.get(), m_vram_depth_texture.get())) ||
!(m_vram_update_depth_framebuffer = g_gpu_device->CreateFramebuffer(m_vram_depth_texture.get())) ||
!(m_vram_readback_framebuffer = g_gpu_device->CreateFramebuffer(m_vram_readback_texture.get())) ||
!(m_display_framebuffer = g_gpu_device->CreateFramebuffer(m_display_texture.get())))
!(m_display_framebuffer = g_gpu_device->CreateFramebuffer(m_display_private_texture.get())))
{
return false;
}
@ -578,12 +575,14 @@ void GPU_HW::ClearFramebuffer()
g_gpu_device->ClearRenderTarget(m_vram_texture.get(), 0);
g_gpu_device->ClearDepth(m_vram_depth_texture.get(), m_pgxp_depth_buffer ? 1.0f : 0.0f);
ClearVRAMDirtyRectangle();
g_gpu_device->ClearRenderTarget(m_display_texture.get(), 0);
g_gpu_device->ClearRenderTarget(m_display_private_texture.get(), 0);
m_last_depth_z = 1.0f;
}
void GPU_HW::DestroyBuffers()
{
ClearDisplayTexture();
m_vram_upload_buffer.reset();
m_downsample_weight_framebuffer.reset();
m_downsample_weight_texture.reset();
@ -599,7 +598,7 @@ void GPU_HW::DestroyBuffers()
m_vram_depth_texture.reset();
m_vram_texture.reset();
m_vram_readback_texture.reset();
m_display_texture.reset();
m_display_private_texture.reset();
}
bool GPU_HW::CompilePipelines()
@ -1147,8 +1146,8 @@ void GPU_HW::DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32
void GPU_HW::ClearDisplay()
{
g_gpu_device->ClearDisplayTexture();
g_gpu_device->ClearRenderTarget(m_display_texture.get(), 0xFF000000u);
ClearDisplayTexture();
g_gpu_device->ClearRenderTarget(m_display_private_texture.get(), 0xFF000000u);
}
void GPU_HW::HandleFlippedQuadTextureCoordinates(BatchVertex* vertices)
@ -2385,25 +2384,23 @@ void GPU_HW::UpdateDisplay()
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture();
g_gpu_device->SetDisplayTexture(m_vram_read_texture.get(), 0, 0, m_vram_read_texture->GetWidth(),
m_vram_read_texture->GetHeight());
SetDisplayTexture(m_vram_read_texture.get(), 0, 0, m_vram_read_texture->GetWidth(),
m_vram_read_texture->GetHeight());
}
else
{
g_gpu_device->SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(),
m_vram_texture->GetHeight());
SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(), m_vram_texture->GetHeight());
}
g_gpu_device->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
// TODO: use a dynamically sized texture
g_gpu_device->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, m_crtc_state.display_origin_left,
m_crtc_state.display_origin_top, m_crtc_state.display_vram_width,
m_crtc_state.display_vram_height, ComputeDisplayAspectRatio());
const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale;
const u32 vram_offset_x = m_crtc_state.display_vram_left;
@ -2418,7 +2415,7 @@ void GPU_HW::UpdateDisplay()
if (IsDisplayDisabled())
{
g_gpu_device->ClearDisplayTexture();
ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
@ -2432,15 +2429,15 @@ void GPU_HW::UpdateDisplay()
}
else
{
g_gpu_device->SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
scaled_display_height);
}
}
else
{
// TODO: discard vs load for interlaced
if (interlaced == InterlacedRenderMode::None)
g_gpu_device->InvalidateRenderTarget(m_display_texture.get());
g_gpu_device->InvalidateRenderTarget(m_display_private_texture.get());
g_gpu_device->SetFramebuffer(m_display_framebuffer.get());
g_gpu_device->SetPipeline(
@ -2454,16 +2451,16 @@ void GPU_HW::UpdateDisplay()
reinterpret_crop_left, reinterpret_field_offset};
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
Assert(scaled_display_width <= m_display_texture->GetWidth() &&
scaled_display_height <= m_display_texture->GetHeight());
Assert(scaled_display_width <= m_display_private_texture->GetWidth() &&
scaled_display_height <= m_display_private_texture->GetHeight());
g_gpu_device->SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height);
g_gpu_device->Draw(3, 0);
if (IsUsingDownsampling())
DownsampleFramebuffer(m_display_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
DownsampleFramebuffer(m_display_private_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
else
g_gpu_device->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
SetDisplayTexture(m_display_private_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
RestoreGraphicsAPIState();
}
@ -2570,7 +2567,7 @@ void GPU_HW::DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top
RestoreGraphicsAPIState();
g_gpu_device->SetDisplayTexture(m_downsample_render_texture.get(), 0, 0, width, height);
SetDisplayTexture(m_downsample_render_texture.get(), 0, 0, width, height);
}
void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
@ -2591,7 +2588,7 @@ void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 to
RestoreGraphicsAPIState();
g_gpu_device->SetDisplayTexture(m_downsample_render_texture.get(), ds_left, ds_top, ds_width, ds_height);
SetDisplayTexture(m_downsample_render_texture.get(), ds_left, ds_top, ds_width, ds_height);
}
void GPU_HW::DrawRendererStats(bool is_idle_frame)

View file

@ -243,7 +243,7 @@ private:
std::unique_ptr<GPUTexture> m_vram_read_texture;
std::unique_ptr<GPUTexture> m_vram_readback_texture;
std::unique_ptr<GPUTexture> m_vram_replacement_texture;
std::unique_ptr<GPUTexture> m_display_texture;
std::unique_ptr<GPUTexture> m_display_private_texture; // TODO: Move to base.
std::unique_ptr<GPUFramebuffer> m_vram_framebuffer;
std::unique_ptr<GPUFramebuffer> m_vram_update_depth_framebuffer;

View file

@ -43,7 +43,6 @@ GPU_SW::GPU_SW()
GPU_SW::~GPU_SW()
{
m_backend.Shutdown();
g_gpu_device->ClearDisplayTexture();
}
const Threading::Thread* GPU_SW::GetSWThread() const
@ -106,18 +105,18 @@ void GPU_SW::UpdateSettings()
GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format)
{
if (!m_display_texture || m_display_texture->GetWidth() != width || m_display_texture->GetHeight() != height ||
m_display_texture->GetFormat() != format)
if (!m_private_display_texture || m_private_display_texture->GetWidth() != width ||
m_private_display_texture->GetHeight() != height || m_private_display_texture->GetFormat() != format)
{
g_gpu_device->ClearDisplayTexture();
m_display_texture.reset();
m_display_texture =
ClearDisplayTexture();
m_private_display_texture.reset();
m_private_display_texture =
g_gpu_device->CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, format, nullptr, 0, true);
if (!m_display_texture)
if (!m_private_display_texture)
Log_ErrorPrintf("Failed to create %ux%u %u texture", width, height, static_cast<u32>(format));
}
return m_display_texture.get();
return m_private_display_texture.get();
}
template<GPUTexture::Format out_format, typename out_type>
@ -321,7 +320,7 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
else
texture->Update(0, 0, width, height, m_display_texture_buffer.data(), output_stride);
g_gpu_device->SetDisplayTexture(texture, 0, 0, width, height);
SetDisplayTexture(texture, 0, 0, width, height);
}
void GPU_SW::CopyOut15Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 width, u32 height, u32 field,
@ -479,7 +478,7 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
else
texture->Update(0, 0, width, height, m_display_texture_buffer.data(), output_stride);
g_gpu_device->SetDisplayTexture(texture, 0, 0, width, height);
SetDisplayTexture(texture, 0, 0, width, height);
}
void GPU_SW::CopyOut24Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height,
@ -516,14 +515,13 @@ void GPU_SW::UpdateDisplay()
if (!g_settings.debugging.show_vram)
{
g_gpu_device->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, m_crtc_state.display_origin_left,
m_crtc_state.display_origin_top, m_crtc_state.display_vram_width,
m_crtc_state.display_vram_height, ComputeDisplayAspectRatio());
if (IsDisplayDisabled())
{
g_gpu_device->ClearDisplayTexture();
ClearDisplayTexture();
return;
}
@ -564,8 +562,8 @@ void GPU_SW::UpdateDisplay()
else
{
CopyOut15Bit(m_16bit_display_format, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false, false);
g_gpu_device->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
}

View file

@ -65,7 +65,7 @@ protected:
FixedHeapArray<u8, GPU_MAX_DISPLAY_WIDTH * GPU_MAX_DISPLAY_HEIGHT * sizeof(u32)> m_display_texture_buffer;
GPUTexture::Format m_16bit_display_format = GPUTexture::Format::RGB565;
GPUTexture::Format m_24bit_display_format = GPUTexture::Format::RGBA8;
std::unique_ptr<GPUTexture> m_display_texture;
std::unique_ptr<GPUTexture> m_private_display_texture; // TODO: Move to base.
GPU_SW_Backend m_backend;
};

View file

@ -16,6 +16,8 @@
#include "common/log.h"
#include "common/string_util.h"
#include "imgui.h"
#include <cstdarg>
Log_SetChannel(Host);
@ -356,7 +358,22 @@ void Host::RenderDisplay(bool skip_present)
ImGuiManager::RenderOverlayWindows();
ImGuiManager::RenderDebugWindows();
g_gpu_device->Render(skip_present);
bool do_present;
if (g_gpu && !skip_present)
do_present = g_gpu->PresentDisplay();
else
do_present = g_gpu_device->BeginPresent(skip_present);
if (do_present)
{
g_gpu_device->RenderImGui();
g_gpu_device->EndPresent();
}
else
{
// Still need to kick ImGui or it gets cranky.
ImGui::Render();
}
ImGuiManager::NewFrame();

View file

@ -203,7 +203,15 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/,
ImGui::PopStyleVar(2);
ImGui::EndFrame();
g_gpu_device->Render(false);
// TODO: Glass effect or something.
if (g_gpu_device->BeginPresent(false))
{
g_gpu_device->RenderImGui();
g_gpu_device->EndPresent();
}
ImGui::NewFrame();
}
@ -443,9 +451,10 @@ void ImGuiManager::DrawPerformanceOverlay()
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushFont(fixed_font);
if (ImGui::Begin("##frame_times", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs))
{
ImGui::PushFont(fixed_font);
auto [min, max] = GetMinMax(System::GetFrameTimeHistory());
// add a little bit of space either side, so we're not constantly resizing
@ -487,9 +496,9 @@ void ImGuiManager::DrawPerformanceOverlay()
win_dl->AddText(
ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y + history_size.y - fixed_font->FontSize),
IM_COL32(255, 255, 255, 255), text.GetCharArray(), text.GetCharArray() + text.GetLength());
ImGui::PopFont();
}
ImGui::End();
ImGui::PopFont();
ImGui::PopStyleVar(5);
ImGui::PopStyleColor(3);
}

View file

@ -2415,7 +2415,7 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 *
if (screenshot_size > 0)
{
// assume this size is the width
const float display_aspect_ratio = g_gpu_device->GetDisplayAspectRatio();
const float display_aspect_ratio = g_gpu->GetDisplayAspectRatio();
const u32 screenshot_width = screenshot_size;
const u32 screenshot_height =
std::max(1u, static_cast<u32>(static_cast<float>(screenshot_width) /
@ -2425,9 +2425,9 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 *
std::vector<u32> screenshot_buffer;
u32 screenshot_stride;
GPUTexture::Format screenshot_format;
if (g_gpu_device->RenderScreenshot(screenshot_width, screenshot_height,
Common::Rectangle<s32>::FromExtents(0, 0, screenshot_width, screenshot_height),
&screenshot_buffer, &screenshot_stride, &screenshot_format) &&
if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height,
Common::Rectangle<s32>::FromExtents(0, 0, screenshot_width, screenshot_height),
false, &screenshot_buffer, &screenshot_stride, &screenshot_format) &&
GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
screenshot_format))
{
@ -3664,15 +3664,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
if (g_settings.display_post_processing != old_settings.display_post_processing ||
g_settings.display_post_process_chain != old_settings.display_post_process_chain)
{
if (g_settings.display_post_processing && !g_settings.display_post_process_chain.empty())
{
if (!g_gpu_device->SetPostProcessingChain(g_settings.display_post_process_chain))
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
}
else
{
g_gpu_device->SetPostProcessingChain({});
}
g_gpu->UpdatePostProcessingChain();
}
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
@ -4192,8 +4184,8 @@ bool System::SaveScreenshot(const char* filename /* = nullptr */, bool full_reso
return false;
}
const bool screenshot_saved = g_gpu_device->WriteScreenshotToFile(
filename, g_settings.display_internal_resolution_screenshots, compress_on_thread);
const bool screenshot_saved =
g_gpu->RenderScreenshotToFile(filename, g_settings.display_internal_resolution_screenshots, compress_on_thread);
if (!screenshot_saved)
{
@ -4539,18 +4531,12 @@ void System::TogglePostProcessing()
return;
g_settings.display_post_processing = !g_settings.display_post_processing;
if (g_settings.display_post_processing)
{
Host::AddKeyedOSDMessage("PostProcessing", TRANSLATE_STR("OSDMessage", "Post-processing is now enabled."), 10.0f);
if (!g_gpu_device->SetPostProcessingChain(g_settings.display_post_process_chain))
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
}
else
{
Host::AddKeyedOSDMessage("PostProcessing", TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."), 10.0f);
g_gpu_device->SetPostProcessingChain({});
}
Host::AddKeyedOSDMessage("PostProcessing",
g_settings.display_post_processing ?
TRANSLATE_STR("OSDMessage", "Post-processing is now enabled.") :
TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."),
Host::OSD_QUICK_DURATION);
g_gpu->UpdatePostProcessingChain();
}
void System::ReloadPostProcessingShaders()
@ -4558,10 +4544,8 @@ void System::ReloadPostProcessingShaders()
if (!IsValid() || !g_settings.display_post_processing)
return;
if (!g_gpu_device->SetPostProcessingChain(g_settings.display_post_process_chain))
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post-processing shader chain."), 20.0f);
else
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), 10.0f);
if (!g_gpu->UpdatePostProcessingChain())
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), Host::OSD_ERROR_DURATION);
}
void System::ToggleWidescreen()
@ -4659,14 +4643,13 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
if (scale == 0.0f)
scale = g_gpu->IsHardwareRenderer() ? static_cast<float>(g_settings.gpu_resolution_scale) : 1.0f;
const float y_scale =
(static_cast<float>(g_gpu_device->GetDisplayWidth()) / static_cast<float>(g_gpu_device->GetDisplayHeight())) /
g_gpu_device->GetDisplayAspectRatio();
const float y_scale = (static_cast<float>(g_gpu->GetDisplayWidth()) / static_cast<float>(g_gpu->GetDisplayHeight())) /
g_gpu->GetDisplayAspectRatio();
const u32 requested_width =
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu_device->GetDisplayWidth()) * scale)), 1);
const u32 requested_height = std::max<u32>(
static_cast<u32>(std::ceil(static_cast<float>(g_gpu_device->GetDisplayHeight()) * y_scale * scale)), 1);
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetDisplayWidth()) * scale)), 1);
const u32 requested_height =
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetDisplayHeight()) * y_scale * scale)), 1);
Host::RequestResizeHostDisplay(static_cast<s32>(requested_width), static_cast<s32>(requested_height));
}

View file

@ -3,6 +3,7 @@
#include "core/achievements.h"
#include "core/game_list.h"
#include "core/gpu.h"
#include "core/host.h"
#include "core/system.h"
@ -306,7 +307,7 @@ void Host::BeginPresentFrame()
if (s_frame_dump_interval > 0 && (s_frame_dump_interval == 1 || (frame % s_frame_dump_interval) == 0))
{
std::string dump_filename(RegTestHost::GetFrameDumpFilename(frame));
g_gpu_device->WriteDisplayTextureToFile(std::move(dump_filename));
g_gpu->WriteDisplayTextureToFile(std::move(dump_filename));
}
}

View file

@ -6,7 +6,6 @@
#include "d3d11_pipeline.h"
#include "d3d11_texture.h"
#include "d3d_common.h"
#include "postprocessing_chain.h" // TODO: Remove me
#include "common/align.h"
#include "common/assert.h"

View file

@ -7,7 +7,6 @@
#include "d3d12_stream_buffer.h"
#include "d3d12_texture.h"
#include "d3d_common.h"
#include "postprocessing_chain.h" // TODO: Remove me
#include "core/host.h"

View file

@ -2,17 +2,12 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "gpu_device.h"
#include "core/host.h"
#include "core/settings.h"
#include "core/system.h"
#include "postprocessing_chain.h"
#include "core/host.h" // TODO: Remove, needed for getting fullscreen mode.
#include "core/settings.h" // TODO: Remove, needed for dump directory.
#include "shadergen.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/file_system.h"
#include "common/hash_combine.h"
#include "common/heap_array.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
@ -20,14 +15,6 @@
#include "fmt/format.h"
#include "imgui.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
#include <cerrno>
#include <cmath>
#include <cstring>
#include <thread>
#include <vector>
Log_SetChannel(GPUDevice);
@ -258,7 +245,6 @@ bool GPUDevice::Create(const std::string_view& adapter, const std::string_view&
void GPUDevice::Destroy()
{
m_post_processing_chain.reset();
if (HasSurface())
DestroySurface();
DestroyResources();
@ -416,32 +402,6 @@ bool GPUDevice::CreateResources()
ShaderGen shadergen(GetRenderAPI(), m_features.dual_source_blend);
GPUPipeline::GraphicsConfig plconfig;
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
plconfig.input_layout.vertex_stride = 0;
plconfig.primitive = GPUPipeline::Primitive::Triangles;
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
plconfig.color_format = HasSurface() ? m_window_info.surface_format : GPUTexture::Format::RGBA8;
plconfig.depth_format = GPUTexture::Format::Unknown;
plconfig.samples = 1;
plconfig.per_sample_shading = false;
std::unique_ptr<GPUShader> display_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
std::unique_ptr<GPUShader> display_fs =
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
if (!display_vs || !display_fs)
return false;
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
plconfig.vertex_shader = display_vs.get();
plconfig.fragment_shader = display_fs.get();
if (!(m_display_pipeline = CreatePipeline(plconfig)))
return false;
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
std::unique_ptr<GPUShader> imgui_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader());
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
if (!imgui_vs || !imgui_fs)
@ -458,11 +418,20 @@ bool GPUDevice::CreateResources()
GPUPipeline::VertexAttribute::Type::UNorm8, 4, offsetof(ImDrawVert, col)),
};
GPUPipeline::GraphicsConfig plconfig;
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
plconfig.input_layout.vertex_attributes = imgui_attributes;
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
plconfig.primitive = GPUPipeline::Primitive::Triangles;
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
plconfig.color_format = HasSurface() ? m_window_info.surface_format : GPUTexture::Format::RGBA8;
plconfig.depth_format = GPUTexture::Format::Unknown;
plconfig.samples = 1;
plconfig.per_sample_shading = false;
plconfig.vertex_shader = imgui_vs.get();
plconfig.fragment_shader = imgui_fs.get();
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
m_imgui_pipeline = CreatePipeline(plconfig);
if (!m_imgui_pipeline)
@ -480,7 +449,6 @@ void GPUDevice::DestroyResources()
m_imgui_font_texture.reset();
m_imgui_pipeline.reset();
m_display_pipeline.reset();
m_imgui_pipeline.reset();
m_linear_sampler.reset();
@ -489,32 +457,6 @@ void GPUDevice::DestroyResources()
m_shader_cache.Close();
}
bool GPUDevice::SetPostProcessingChain(const std::string_view& config)
{
m_post_processing_chain.reset();
if (config.empty())
return true;
else if (m_window_info.surface_format == GPUTexture::Format::Unknown)
return false;
m_post_processing_chain = std::make_unique<PostProcessingChain>();
if (!m_post_processing_chain->CreateFromString(config) ||
!m_post_processing_chain->CheckTargets(m_window_info.surface_format, m_window_info.surface_width,
m_window_info.surface_height))
{
m_post_processing_chain.reset();
return false;
}
else if (m_post_processing_chain->IsEmpty())
{
m_post_processing_chain.reset();
return true;
}
return true;
}
void GPUDevice::RenderImGui()
{
GL_SCOPE("RenderImGui");
@ -786,45 +728,6 @@ void GPUDevice::ThrottlePresentation()
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
}
void GPUDevice::ClearDisplayTexture()
{
m_display_texture = nullptr;
m_display_texture_view_x = 0;
m_display_texture_view_y = 0;
m_display_texture_view_width = 0;
m_display_texture_view_height = 0;
}
void GPUDevice::SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height)
{
DebugAssert(texture);
m_display_texture = texture;
m_display_texture_view_x = view_x;
m_display_texture_view_y = view_y;
m_display_texture_view_width = view_width;
m_display_texture_view_height = view_height;
}
void GPUDevice::SetDisplayTextureRect(s32 view_x, s32 view_y, s32 view_width, s32 view_height)
{
m_display_texture_view_x = view_x;
m_display_texture_view_y = view_y;
m_display_texture_view_width = view_width;
m_display_texture_view_height = view_height;
}
void GPUDevice::SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top,
s32 active_width, s32 active_height, float display_aspect_ratio)
{
m_display_width = display_width;
m_display_height = display_height;
m_display_active_left = active_left;
m_display_active_top = active_top;
m_display_active_width = active_width;
m_display_active_height = active_height;
m_display_aspect_ratio = display_aspect_ratio;
}
bool GPUDevice::GetHostRefreshRate(float* refresh_rate)
{
if (m_window_info.surface_refresh_rate > 0.0f)
@ -846,601 +749,6 @@ float GPUDevice::GetAndResetAccumulatedGPUTime()
return 0.0f;
}
bool GPUDevice::IsUsingLinearFiltering() const
{
return g_settings.display_linear_filtering;
}
bool GPUDevice::Render(bool skip_present)
{
// Moved here because there can be draws after UpdateDisplay().
if (HasDisplayTexture())
m_display_texture->MakeReadyForSampling();
if (skip_present)
{
// Should never return true here..
if (UNLIKELY(BeginPresent(skip_present)))
Panic("BeginPresent() returned true when skipping...");
// Need to kick ImGui state.
ImGui::Render();
return false;
}
bool render_frame;
if (HasDisplayTexture())
{
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
render_frame = RenderDisplay(nullptr, 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());
}
else
{
render_frame = BeginPresent(false);
}
if (!render_frame)
{
// Window minimized etc.
ImGui::Render();
return false;
}
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
RenderImGui();
EndPresent();
return true;
}
bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
const GPUTexture::Format hdformat = HasSurface() ? m_window_info.surface_format : GPUTexture::Format::RGBA8;
std::unique_ptr<GPUTexture> render_texture =
CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat);
if (!render_texture)
return false;
std::unique_ptr<GPUFramebuffer> render_fb = CreateFramebuffer(render_texture.get());
if (!render_fb)
return false;
ClearRenderTarget(render_texture.get(), 0);
if (m_display_texture)
{
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);
const u32 stride = GPUTexture::GetPixelSize(hdformat) * width;
out_pixels->resize(width * height);
if (!DownloadTexture(render_texture.get(), 0, 0, width, height, out_pixels->data(), stride))
return false;
*out_stride = stride;
*out_format = hdformat;
return true;
}
bool 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)
{
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", left, top, width, height);
const GPUTexture::Format hdformat =
(target && target->GetRT()) ? target->GetRT()->GetFormat() : 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)
{
ClearRenderTarget(m_post_processing_chain->GetInputTexture(), 0);
SetFramebuffer(m_post_processing_chain->GetInputFramebuffer());
}
else
{
if (target)
SetFramebuffer(target);
else if (!BeginPresent(false))
return false;
}
SetPipeline(m_display_pipeline.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;
const float size_adjust = linear ? 1.0f : 0.0f;
const float uniforms[4] = {
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture->GetHeight()),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture->GetHeight())};
PushUniformBuffer(uniforms, sizeof(uniforms));
SetViewportAndScissor(left, top, width, height);
Draw(3, 0);
if (postfx)
{
return m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height);
}
else
{
return true;
}
}
void GPUDevice::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left, float* out_top,
float* out_width, float* out_height, float* out_left_padding, float* out_top_padding,
float* out_scale, float* out_x_scale, bool apply_aspect_ratio /* = true */) const
{
const float window_ratio = static_cast<float>(window_width) / static_cast<float>(window_height);
const float display_aspect_ratio = g_settings.display_stretch ? window_ratio : m_display_aspect_ratio;
const float x_scale =
apply_aspect_ratio ?
(display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height))) :
1.0f;
const float display_width = g_settings.display_stretch_vertically ? static_cast<float>(m_display_width) :
static_cast<float>(m_display_width) * x_scale;
const float display_height = g_settings.display_stretch_vertically ? static_cast<float>(m_display_height) / x_scale :
static_cast<float>(m_display_height);
const float active_left = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_left) :
static_cast<float>(m_display_active_left) * x_scale;
const float active_top = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_top) / x_scale :
static_cast<float>(m_display_active_top);
const float active_width = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_width) :
static_cast<float>(m_display_active_width) * x_scale;
const float active_height = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_height) / x_scale :
static_cast<float>(m_display_active_height);
if (out_x_scale)
*out_x_scale = x_scale;
// now fit it within the window
float scale;
if ((display_width / display_height) >= window_ratio)
{
// align in middle vertically
scale = static_cast<float>(window_width) / display_width;
if (g_settings.display_integer_scaling)
scale = std::max(std::floor(scale), 1.0f);
if (out_left_padding)
{
if (g_settings.display_integer_scaling)
*out_left_padding = std::max<float>((static_cast<float>(window_width) - display_width * scale) / 2.0f, 0.0f);
else
*out_left_padding = 0.0f;
}
if (out_top_padding)
{
switch (g_settings.display_alignment)
{
case DisplayAlignment::RightOrBottom:
*out_top_padding = std::max<float>(static_cast<float>(window_height) - (display_height * scale), 0.0f);
break;
case DisplayAlignment::Center:
*out_top_padding =
std::max<float>((static_cast<float>(window_height) - (display_height * scale)) / 2.0f, 0.0f);
break;
case DisplayAlignment::LeftOrTop:
default:
*out_top_padding = 0.0f;
break;
}
}
}
else
{
// align in middle horizontally
scale = static_cast<float>(window_height) / display_height;
if (g_settings.display_integer_scaling)
scale = std::max(std::floor(scale), 1.0f);
if (out_left_padding)
{
switch (g_settings.display_alignment)
{
case DisplayAlignment::RightOrBottom:
*out_left_padding = std::max<float>(static_cast<float>(window_width) - (display_width * scale), 0.0f);
break;
case DisplayAlignment::Center:
*out_left_padding =
std::max<float>((static_cast<float>(window_width) - (display_width * scale)) / 2.0f, 0.0f);
break;
case DisplayAlignment::LeftOrTop:
default:
*out_left_padding = 0.0f;
break;
}
}
if (out_top_padding)
{
if (g_settings.display_integer_scaling)
*out_top_padding = std::max<float>((static_cast<float>(window_height) - (display_height * scale)) / 2.0f, 0.0f);
else
*out_top_padding = 0.0f;
}
}
*out_width = active_width * scale;
*out_height = active_height * scale;
*out_left = active_left * scale;
*out_top = active_top * scale;
if (out_scale)
*out_scale = scale;
}
std::tuple<s32, s32, s32, s32> GPUDevice::CalculateDrawRect(s32 window_width, s32 window_height,
bool apply_aspect_ratio /* = true */) const
{
float left, top, width, height, left_padding, top_padding;
CalculateDrawRect(window_width, window_height, &left, &top, &width, &height, &left_padding, &top_padding, nullptr,
nullptr, apply_aspect_ratio);
return std::make_tuple(static_cast<s32>(left + left_padding), static_cast<s32>(top + top_padding),
static_cast<s32>(width), static_cast<s32>(height));
}
std::tuple<float, float> GPUDevice::ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y,
s32 window_width,
s32 window_height) const
{
float left, top, width, height, left_padding, top_padding;
float scale, x_scale;
CalculateDrawRect(window_width, window_height, &left, &top, &width, &height, &left_padding, &top_padding, &scale,
&x_scale);
// convert coordinates to active display region, then to full display region
const float scaled_display_x = static_cast<float>(window_x) - left_padding;
const float scaled_display_y = static_cast<float>(window_y) - top_padding;
// scale back to internal resolution
const float display_x = scaled_display_x / scale / x_scale;
const float display_y = scaled_display_y / scale;
return std::make_tuple(display_x, display_y);
}
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
bool clear_alpha, bool flip_y, u32 resize_width, u32 resize_height,
std::vector<u32> texture_data, u32 texture_data_stride,
GPUTexture::Format texture_format)
{
const char* extension = std::strrchr(filename.c_str(), '.');
if (!extension)
{
Log_ErrorPrintf("Unable to determine file extension for '%s'", filename.c_str());
return false;
}
if (!GPUTexture::ConvertTextureDataToRGBA8(width, height, texture_data, texture_data_stride, texture_format))
return false;
if (clear_alpha)
{
for (u32& pixel : texture_data)
pixel |= 0xFF000000;
}
if (flip_y)
GPUTexture::FlipTextureDataRGBA8(width, height, texture_data, texture_data_stride);
if (resize_width > 0 && resize_height > 0 && (resize_width != width || resize_height != height))
{
std::vector<u32> resized_texture_data(resize_width * resize_height);
u32 resized_texture_stride = sizeof(u32) * resize_width;
if (!stbir_resize_uint8(reinterpret_cast<u8*>(texture_data.data()), width, height, texture_data_stride,
reinterpret_cast<u8*>(resized_texture_data.data()), resize_width, resize_height,
resized_texture_stride, 4))
{
Log_ErrorPrintf("Failed to resize texture data from %ux%u to %ux%u", width, height, resize_width, resize_height);
return false;
}
width = resize_width;
height = resize_height;
texture_data = std::move(resized_texture_data);
texture_data_stride = resized_texture_stride;
}
const auto write_func = [](void* context, void* data, int size) {
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
};
bool result = false;
if (StringUtil::Strcasecmp(extension, ".png") == 0)
{
result =
(stbi_write_png_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), texture_data_stride) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".jpg") == 0)
{
result = (stbi_write_jpg_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), 95) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".tga") == 0)
{
result = (stbi_write_tga_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
}
else if (StringUtil::Strcasecmp(extension, ".bmp") == 0)
{
result = (stbi_write_bmp_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
}
if (!result)
{
Log_ErrorPrintf("Unknown extension in filename '%s' or save error: '%s'", filename.c_str(), extension);
return false;
}
return true;
}
bool GPUDevice::WriteTextureToFile(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, std::string filename,
bool clear_alpha /* = true */, bool flip_y /* = false */, u32 resize_width /* = 0 */,
u32 resize_height /* = 0 */, bool compress_on_thread /* = false */)
{
std::vector<u32> texture_data(width * height);
u32 texture_data_stride = Common::AlignUpPow2(GPUTexture::GetPixelSize(texture->GetFormat()) * width, 4);
if (!DownloadTexture(texture, x, y, width, height, texture_data.data(), texture_data_stride))
{
Log_ErrorPrintf("Texture download failed");
return false;
}
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
if (!fp)
{
Log_ErrorPrintf("Can't open file '%s': errno %d", filename.c_str(), errno);
return false;
}
if (!compress_on_thread)
{
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), clear_alpha, flip_y,
resize_width, resize_height, std::move(texture_data), texture_data_stride,
texture->GetFormat());
}
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp),
clear_alpha, flip_y, resize_width, resize_height, std::move(texture_data),
texture_data_stride, texture->GetFormat());
compress_thread.detach();
return true;
}
bool GPUDevice::WriteDisplayTextureToFile(std::string filename, bool full_resolution /* = true */,
bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = false */)
{
if (!m_display_texture)
return false;
s32 resize_width = 0;
s32 resize_height = std::abs(m_display_texture_view_height);
if (apply_aspect_ratio)
{
const float ss_width_scale = static_cast<float>(m_display_active_width) / static_cast<float>(m_display_width);
const float ss_height_scale = static_cast<float>(m_display_active_height) / static_cast<float>(m_display_height);
const float ss_aspect_ratio = m_display_aspect_ratio * ss_width_scale / ss_height_scale;
resize_width = g_settings.display_stretch_vertically ?
m_display_texture_view_width :
static_cast<s32>(static_cast<float>(resize_height) * ss_aspect_ratio);
resize_height = g_settings.display_stretch_vertically ?
static_cast<s32>(static_cast<float>(resize_height) /
(m_display_aspect_ratio /
(static_cast<float>(m_display_width) / static_cast<float>(m_display_height)))) :
resize_height;
}
else
{
resize_width = m_display_texture_view_width;
}
if (!full_resolution)
{
const s32 resolution_scale = std::abs(m_display_texture_view_height) / m_display_active_height;
resize_height /= resolution_scale;
resize_width /= resolution_scale;
}
if (resize_width <= 0 || resize_height <= 0)
return false;
const bool flip_y = (m_display_texture_view_height < 0);
s32 read_height = m_display_texture_view_height;
s32 read_y = m_display_texture_view_y;
if (flip_y)
{
read_height = -m_display_texture_view_height;
read_y =
(m_display_texture->GetHeight() - read_height) - (m_display_texture->GetHeight() - m_display_texture_view_y);
}
return WriteTextureToFile(m_display_texture, m_display_texture_view_x, read_y, m_display_texture_view_width,
read_height, std::move(filename), true, flip_y, static_cast<u32>(resize_width),
static_cast<u32>(resize_height), compress_on_thread);
}
bool GPUDevice::WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resize_width /* = 0 */,
u32 resize_height /* = 0 */, bool clear_alpha /* = true */)
{
if (!m_display_texture)
return false;
const bool flip_y = (m_display_texture_view_height < 0);
s32 read_width = m_display_texture_view_width;
s32 read_height = m_display_texture_view_height;
s32 read_x = m_display_texture_view_x;
s32 read_y = m_display_texture_view_y;
if (flip_y)
{
read_height = -m_display_texture_view_height;
read_y =
(m_display_texture->GetHeight() - read_height) - (m_display_texture->GetHeight() - m_display_texture_view_y);
}
u32 width = static_cast<u32>(read_width);
u32 height = static_cast<u32>(read_height);
std::vector<u32> texture_data(width * height);
u32 texture_data_stride = Common::AlignUpPow2(m_display_texture->GetPixelSize() * width, 4);
if (!DownloadTexture(m_display_texture, read_x, read_y, width, height, texture_data.data(), texture_data_stride))
{
Log_ErrorPrintf("Failed to download texture from GPU.");
return false;
}
if (!GPUTexture::ConvertTextureDataToRGBA8(width, height, texture_data, texture_data_stride,
m_display_texture->GetFormat()))
{
return false;
}
if (clear_alpha)
{
for (u32& pixel : texture_data)
pixel |= 0xFF000000;
}
if (flip_y)
{
std::vector<u32> temp(width);
for (u32 flip_row = 0; flip_row < (height / 2); flip_row++)
{
u32* top_ptr = &texture_data[flip_row * width];
u32* bottom_ptr = &texture_data[((height - 1) - flip_row) * width];
std::memcpy(temp.data(), top_ptr, texture_data_stride);
std::memcpy(top_ptr, bottom_ptr, texture_data_stride);
std::memcpy(bottom_ptr, temp.data(), texture_data_stride);
}
}
if (resize_width > 0 && resize_height > 0 && (resize_width != width || resize_height != height))
{
std::vector<u32> resized_texture_data(resize_width * resize_height);
u32 resized_texture_stride = sizeof(u32) * resize_width;
if (!stbir_resize_uint8(reinterpret_cast<u8*>(texture_data.data()), width, height, texture_data_stride,
reinterpret_cast<u8*>(resized_texture_data.data()), resize_width, resize_height,
resized_texture_stride, 4))
{
Log_ErrorPrintf("Failed to resize texture data from %ux%u to %ux%u", width, height, resize_width, resize_height);
return false;
}
width = resize_width;
height = resize_height;
*buffer = std::move(resized_texture_data);
texture_data_stride = resized_texture_stride;
}
else
{
*buffer = texture_data;
}
return true;
}
bool GPUDevice::WriteScreenshotToFile(std::string filename, bool internal_resolution /* = false */,
bool compress_on_thread /* = false */)
{
u32 width = m_window_info.surface_width;
u32 height = m_window_info.surface_height;
auto [draw_left, draw_top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (internal_resolution && m_display_texture_view_width != 0 && m_display_texture_view_height != 0)
{
// If internal res, scale the computed draw rectangle to the internal res.
// We re-use the draw rect because it's already been AR corrected.
const float sar =
static_cast<float>(m_display_texture_view_width) / static_cast<float>(m_display_texture_view_height);
const float dar = static_cast<float>(draw_width) / static_cast<float>(draw_height);
if (sar >= dar)
{
// stretch height, preserve width
const float scale = static_cast<float>(m_display_texture_view_width) / static_cast<float>(draw_width);
width = m_display_texture_view_width;
height = static_cast<u32>(std::round(static_cast<float>(draw_height) * scale));
}
else
{
// stretch width, preserve height
const float scale = static_cast<float>(m_display_texture_view_height) / static_cast<float>(draw_height);
width = static_cast<u32>(std::round(static_cast<float>(draw_width) * scale));
height = m_display_texture_view_height;
}
// DX11 won't go past 16K texture size.
constexpr u32 MAX_TEXTURE_SIZE = 16384;
if (width > MAX_TEXTURE_SIZE)
{
height = static_cast<u32>(static_cast<float>(height) /
(static_cast<float>(width) / static_cast<float>(MAX_TEXTURE_SIZE)));
width = MAX_TEXTURE_SIZE;
}
if (height > MAX_TEXTURE_SIZE)
{
height = MAX_TEXTURE_SIZE;
width = static_cast<u32>(static_cast<float>(width) /
(static_cast<float>(height) / static_cast<float>(MAX_TEXTURE_SIZE)));
}
// Remove padding, it's not part of the framebuffer.
draw_left = 0;
draw_top = 0;
draw_width = static_cast<s32>(width);
draw_height = static_cast<s32>(height);
}
if (width == 0 || height == 0)
return false;
std::vector<u32> pixels;
u32 pixels_stride;
GPUTexture::Format pixels_format;
if (!RenderScreenshot(width, height,
Common::Rectangle<s32>::FromExtents(draw_left, draw_top, draw_width, draw_height), &pixels,
&pixels_stride, &pixels_format))
{
Log_ErrorPrintf("Failed to render %ux%u screenshot", width, height);
return false;
}
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
if (!fp)
{
Log_ErrorPrintf("Can't open file '%s': errno %d", filename.c_str(), errno);
return false;
}
if (!compress_on_thread)
{
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), true, UsesLowerLeftOrigin(),
width, height, std::move(pixels), pixels_stride, pixels_format);
}
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp), true,
UsesLowerLeftOrigin(), width, height, std::move(pixels), pixels_stride, pixels_format);
compress_thread.detach();
return true;
}
std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
{
switch (api)

View file

@ -419,9 +419,6 @@ protected:
u32 m_current_position;
};
// TODO: remove
class PostProcessingChain;
class GPUDevice
{
public:
@ -506,10 +503,6 @@ public:
ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); }
ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); }
ALWAYS_INLINE const void* GetDisplayTextureHandle() const { return m_display_texture; }
ALWAYS_INLINE s32 GetDisplayWidth() const { return m_display_width; }
ALWAYS_INLINE s32 GetDisplayHeight() const { return m_display_height; }
ALWAYS_INLINE float GetDisplayAspectRatio() const { return m_display_aspect_ratio; }
ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
virtual RenderAPI GetRenderAPI() const = 0;
@ -525,8 +518,6 @@ public:
virtual bool SupportsExclusiveFullscreen() const;
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
bool SetPostProcessingChain(const std::string_view& config);
/// Call when the window size changes externally to recreate any resources.
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
@ -596,11 +587,9 @@ public:
/// Returns false if the window was completely occluded.
virtual bool BeginPresent(bool skip_present) = 0;
virtual void EndPresent() = 0;
bool Render(bool skip_present);
/// Renders the display with postprocessing to the specified image.
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
u32* out_stride, GPUTexture::Format* out_format);
/// Renders ImGui screen elements. Call before EndPresent().
void RenderImGui();
ALWAYS_INLINE bool IsVsyncEnabled() const { return m_vsync_enabled; }
virtual void SetVSync(bool enabled) = 0;
@ -613,12 +602,6 @@ public:
bool ShouldSkipDisplayingFrame();
void ThrottlePresentation();
void ClearDisplayTexture();
void SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height);
void SetDisplayTextureRect(s32 view_x, s32 view_y, s32 view_width, s32 view_height);
void SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width,
s32 active_height, float display_aspect_ratio);
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
virtual bool GetHostRefreshRate(float* refresh_rate);
@ -629,30 +612,6 @@ public:
/// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime();
/// Helper function for computing the draw rectangle in a larger window.
std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height,
bool apply_aspect_ratio = true) const;
/// Helper function for converting window coordinates to display coordinates.
std::tuple<float, float> ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y, s32 window_width,
s32 window_height) const;
/// Helper function to save texture data to a PNG. If flip_y is set, the image will be flipped aka OpenGL.
bool WriteTextureToFile(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, std::string filename,
bool clear_alpha = true, bool flip_y = false, u32 resize_width = 0, u32 resize_height = 0,
bool compress_on_thread = false);
/// Helper function to save current display texture to PNG.
bool WriteDisplayTextureToFile(std::string filename, bool full_resolution = true, bool apply_aspect_ratio = true,
bool compress_on_thread = false);
/// Helper function to save current display texture to a buffer.
bool WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resize_width = 0, u32 resize_height = 0,
bool clear_alpha = true);
/// Helper function to save screenshot to PNG.
bool WriteScreenshotToFile(std::string filename, bool internal_resolution = false, bool compress_on_thread = false);
protected:
virtual bool CreateDevice(const std::string_view& adapter, bool threaded_presentation) = 0;
virtual void DestroyDevice() = 0;
@ -684,47 +643,17 @@ protected:
bool m_debug_device = false;
private:
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
void OpenShaderCache(const std::string_view& base_path, u32 version);
void CloseShaderCache();
bool CreateResources();
void DestroyResources();
bool IsUsingLinearFiltering() const;
void CalculateDrawRect(s32 window_width, s32 window_height, float* out_left, float* out_top, float* out_width,
float* out_height, float* out_left_padding, float* out_top_padding, float* out_scale,
float* out_x_scale, bool apply_aspect_ratio = true) const;
void RenderImGui();
bool 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);
// TODO: Move out.
u64 m_last_frame_displayed_time = 0;
s32 m_display_width = 0;
s32 m_display_height = 0;
s32 m_display_active_left = 0;
s32 m_display_active_top = 0;
s32 m_display_active_width = 0;
s32 m_display_active_height = 0;
float m_display_aspect_ratio = 1.0f;
float m_display_frame_interval = 0.0f;
std::unique_ptr<GPUPipeline> m_display_pipeline;
GPUTexture* m_display_texture = nullptr;
s32 m_display_texture_view_x = 0;
s32 m_display_texture_view_y = 0;
s32 m_display_texture_view_width = 0;
s32 m_display_texture_view_height = 0;
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
std::unique_ptr<GPUTexture> m_imgui_font_texture;
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
};
extern std::unique_ptr<GPUDevice> g_gpu_device;

View file

@ -18,7 +18,6 @@
#include "gpu_device.h"
#include "metal_stream_buffer.h"
#include "postprocessing_chain.h"
#include "window_info.h"
#include "common/rectangle.h"

View file

@ -5,7 +5,6 @@
#include "opengl_pipeline.h"
#include "opengl_stream_buffer.h"
#include "opengl_texture.h"
#include "postprocessing_chain.h" // TODO: Remove me
#include "core/host.h"

View file

@ -261,7 +261,7 @@ bool PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
{
const bool is_final = (stage.get() == m_shaders.back().get());
if (!stage->Apply(input, is_final ? nullptr : output_fb, final_left, final_top, final_width, final_height,
if (!stage->Apply(input, is_final ? final_target : output_fb, final_left, final_top, final_width, final_height,
orig_width, orig_height, m_target_width, m_target_height))
{
return false;

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "vulkan_device.h"
#include "postprocessing_chain.h" // TODO: Remove me
#include "vulkan_builders.h"
#include "vulkan_pipeline.h"
#include "vulkan_stream_buffer.h"