From 8db8baf33f6f5778c43f87e83fc3be183fcc0195 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 27 Aug 2023 18:13:50 +1000 Subject: [PATCH] GPUDevice: Move display logic to GPU --- src/core/fullscreen_ui.cpp | 17 +- src/core/gpu.cpp | 584 +++++++++++++++++- src/core/gpu.h | 73 ++- src/core/gpu_hw.cpp | 57 +- src/core/gpu_hw.h | 2 +- src/core/gpu_sw.cpp | 32 +- src/core/gpu_sw.h | 2 +- src/core/host.cpp | 19 +- src/core/imgui_overlays.cpp | 15 +- src/core/system.cpp | 57 +- src/duckstation-regtest/regtest_host.cpp | 3 +- src/util/d3d11_device.cpp | 1 - src/util/d3d12_device.cpp | 1 - src/util/gpu_device.cpp | 716 +---------------------- src/util/gpu_device.h | 77 +-- src/util/metal_device.h | 1 - src/util/opengl_device.cpp | 1 - src/util/postprocessing_chain.cpp | 2 +- src/util/vulkan_device.cpp | 1 - 19 files changed, 761 insertions(+), 900 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index d30c45c42..a1e7a75ef 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -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.")); } diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index cca17fdf0..c1be83ddc 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -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 +#include 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(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(window_x) - left_padding; + const float scaled_display_y = static_cast(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 display_vs = + g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader()); + std::unique_ptr 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 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& 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(m_display_texture_view_x) + position_adjust) / + static_cast(m_display_texture->GetWidth()), + (static_cast(m_display_texture_view_y) + position_adjust) / + static_cast(m_display_texture->GetHeight()), + (static_cast(m_display_texture_view_width) - size_adjust) / + static_cast(m_display_texture->GetWidth()), + (static_cast(m_display_texture_view_height) - size_adjust) / + static_cast(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(); + 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 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(window_width) / static_cast(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(m_display_width) / static_cast(m_display_height))) : + 1.0f; + const float display_width = g_settings.display_stretch_vertically ? static_cast(m_display_width) : + static_cast(m_display_width) * x_scale; + const float display_height = g_settings.display_stretch_vertically ? static_cast(m_display_height) / x_scale : + static_cast(m_display_height); + const float active_left = g_settings.display_stretch_vertically ? static_cast(m_display_active_left) : + static_cast(m_display_active_left) * x_scale; + const float active_top = g_settings.display_stretch_vertically ? static_cast(m_display_active_top) / x_scale : + static_cast(m_display_active_top); + const float active_width = g_settings.display_stretch_vertically ? + static_cast(m_display_active_width) : + static_cast(m_display_active_width) * x_scale; + const float active_height = g_settings.display_stretch_vertically ? + static_cast(m_display_active_height) / x_scale : + static_cast(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(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((static_cast(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(static_cast(window_height) - (display_height * scale), 0.0f); + break; + + case DisplayAlignment::Center: + *out_top_padding = + std::max((static_cast(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(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(static_cast(window_width) - (display_width * scale), 0.0f); + break; + + case DisplayAlignment::Center: + *out_left_padding = + std::max((static_cast(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((static_cast(window_height) - (display_height * scale)) / 2.0f, 0.0f); + else + *out_top_padding = 0.0f; + } + } + + if (out_scale) + *out_scale = scale; + + return Common::Rectangle::FromExtents(active_left * scale, active_top * scale, active_width * scale, + active_height * scale); +} + +Common::Rectangle GPU::CalculateDrawRect(s32 window_width, s32 window_height, + bool apply_aspect_ratio /* = true */) const +{ + float left_padding, top_padding; + const Common::Rectangle 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::FromExtents( + static_cast(draw_rc.left + left_padding), static_cast(draw_rc.top + top_padding), + static_cast(draw_rc.GetWidth()), static_cast(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 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 resized_texture_data(resize_width * resize_height); + u32 resized_texture_stride = sizeof(u32) * resize_width; + if (!stbir_resize_uint8(reinterpret_cast(texture_data.data()), width, height, texture_data_stride, + reinterpret_cast(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(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(m_display_active_width) / static_cast(m_display_width); + const float ss_height_scale = static_cast(m_display_active_height) / static_cast(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(static_cast(resize_height) * ss_aspect_ratio); + resize_height = g_settings.display_stretch_vertically ? + static_cast(static_cast(resize_height) / + (m_display_aspect_ratio / + (static_cast(m_display_width) / static_cast(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(m_display_texture_view_x); + const u32 read_y = static_cast(m_display_texture_view_y); + const u32 read_width = static_cast(m_display_texture_view_width); + const u32 read_height = static_cast(m_display_texture_view_height); + + std::vector 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& draw_rect, bool postfx, + std::vector* 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 render_texture = + g_gpu_device->CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat); + if (!render_texture) + return false; + + std::unique_ptr 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(m_display_texture_view_width) / static_cast(m_display_texture_view_height); + const float dar = static_cast(draw_width) / static_cast(draw_height); + if (sar >= dar) + { + // stretch height, preserve width + const float scale = static_cast(m_display_texture_view_width) / static_cast(draw_width); + width = m_display_texture_view_width; + height = static_cast(std::round(static_cast(draw_height) * scale)); + } + else + { + // stretch width, preserve height + const float scale = static_cast(m_display_texture_view_height) / static_cast(draw_height); + width = static_cast(std::round(static_cast(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(static_cast(height) / + (static_cast(width) / static_cast(MAX_TEXTURE_SIZE))); + width = MAX_TEXTURE_SIZE; + } + if (height > MAX_TEXTURE_SIZE) + { + height = MAX_TEXTURE_SIZE; + width = static_cast(static_cast(width) / + (static_cast(height) / static_cast(MAX_TEXTURE_SIZE))); + } + + // Remove padding, it's not part of the framebuffer. + draw_left = 0; + draw_top = 0; + draw_width = static_cast(width); + draw_height = static_cast(height); + } + if (width == 0 || height == 0) + return false; + + std::vector pixels; + u32 pixels_stride; + GPUTexture::Format pixels_format; + if (!RenderScreenshotToBuffer(width, height, + Common::Rectangle::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); diff --git a/src/core/gpu.h b/src/core/gpu.h index 70aa54855..bd579be1b 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -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 #include #include #include +#include #include #include 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 CreateHardwareRenderer(); static std::unique_ptr 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(m_display_texture); } + + /// Helper function for computing the draw rectangle in a larger window. + Common::Rectangle 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& draw_rect, bool postfx, + std::vector* 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 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& 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 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 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; static GP0CommandHandlerTable GenerateGP0CommandHandlerTable(); diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 4c79841a8..72c76e657 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -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(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); + SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, + static_cast(VRAM_WIDTH) / static_cast(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) diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index d8b925e48..e3f819e10 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -243,7 +243,7 @@ private: std::unique_ptr m_vram_read_texture; std::unique_ptr m_vram_readback_texture; std::unique_ptr m_vram_replacement_texture; - std::unique_ptr m_display_texture; + std::unique_ptr m_display_private_texture; // TODO: Move to base. std::unique_ptr m_vram_framebuffer; std::unique_ptr m_vram_update_depth_framebuffer; diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 9fcbfeafd..90e901784 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -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(format)); } - return m_display_texture.get(); + return m_private_display_texture.get(); } template @@ -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(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); + SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, + static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } } diff --git a/src/core/gpu_sw.h b/src/core/gpu_sw.h index 1aee9509e..52b375fa5 100644 --- a/src/core/gpu_sw.h +++ b/src/core/gpu_sw.h @@ -65,7 +65,7 @@ protected: FixedHeapArray 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 m_display_texture; + std::unique_ptr m_private_display_texture; // TODO: Move to base. GPU_SW_Backend m_backend; }; diff --git a/src/core/host.cpp b/src/core/host.cpp index ff8efb97c..89de08735 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -16,6 +16,8 @@ #include "common/log.h" #include "common/string_util.h" +#include "imgui.h" + #include 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(); diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index bf60dd474..a13cc5df9 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -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); } diff --git a/src/core/system.cpp b/src/core/system.cpp index 82003a97e..80e7fb09c 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -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(static_cast(screenshot_width) / @@ -2425,9 +2425,9 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 * std::vector screenshot_buffer; u32 screenshot_stride; GPUTexture::Format screenshot_format; - if (g_gpu_device->RenderScreenshot(screenshot_width, screenshot_height, - Common::Rectangle::FromExtents(0, 0, screenshot_width, screenshot_height), - &screenshot_buffer, &screenshot_stride, &screenshot_format) && + if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height, + Common::Rectangle::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(g_settings.gpu_resolution_scale) : 1.0f; - const float y_scale = - (static_cast(g_gpu_device->GetDisplayWidth()) / static_cast(g_gpu_device->GetDisplayHeight())) / - g_gpu_device->GetDisplayAspectRatio(); + const float y_scale = (static_cast(g_gpu->GetDisplayWidth()) / static_cast(g_gpu->GetDisplayHeight())) / + g_gpu->GetDisplayAspectRatio(); const u32 requested_width = - std::max(static_cast(std::ceil(static_cast(g_gpu_device->GetDisplayWidth()) * scale)), 1); - const u32 requested_height = std::max( - static_cast(std::ceil(static_cast(g_gpu_device->GetDisplayHeight()) * y_scale * scale)), 1); + std::max(static_cast(std::ceil(static_cast(g_gpu->GetDisplayWidth()) * scale)), 1); + const u32 requested_height = + std::max(static_cast(std::ceil(static_cast(g_gpu->GetDisplayHeight()) * y_scale * scale)), 1); Host::RequestResizeHostDisplay(static_cast(requested_width), static_cast(requested_height)); } diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index e428a978b..3d3cc5144 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -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)); } } diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index 869bd71b9..5de6c81ac 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -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" diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index e881906a1..67e354676 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -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" diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index b8ad87082..caaabdf13 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -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 -#include -#include -#include -#include 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 display_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader()); - std::unique_ptr 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 imgui_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader()); std::unique_ptr 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(); - 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& draw_rect, - std::vector* 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 render_texture = - CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat); - if (!render_texture) - return false; - - std::unique_ptr 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(texture_view_x) + position_adjust) / static_cast(texture->GetWidth()), - (static_cast(texture_view_y) + position_adjust) / static_cast(texture->GetHeight()), - (static_cast(texture_view_width) - size_adjust) / static_cast(texture->GetWidth()), - (static_cast(texture_view_height) - size_adjust) / static_cast(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(window_width) / static_cast(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(m_display_width) / static_cast(m_display_height))) : - 1.0f; - const float display_width = g_settings.display_stretch_vertically ? static_cast(m_display_width) : - static_cast(m_display_width) * x_scale; - const float display_height = g_settings.display_stretch_vertically ? static_cast(m_display_height) / x_scale : - static_cast(m_display_height); - const float active_left = g_settings.display_stretch_vertically ? static_cast(m_display_active_left) : - static_cast(m_display_active_left) * x_scale; - const float active_top = g_settings.display_stretch_vertically ? static_cast(m_display_active_top) / x_scale : - static_cast(m_display_active_top); - const float active_width = g_settings.display_stretch_vertically ? - static_cast(m_display_active_width) : - static_cast(m_display_active_width) * x_scale; - const float active_height = g_settings.display_stretch_vertically ? - static_cast(m_display_active_height) / x_scale : - static_cast(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(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((static_cast(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(static_cast(window_height) - (display_height * scale), 0.0f); - break; - - case DisplayAlignment::Center: - *out_top_padding = - std::max((static_cast(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(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(static_cast(window_width) - (display_width * scale), 0.0f); - break; - - case DisplayAlignment::Center: - *out_left_padding = - std::max((static_cast(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((static_cast(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 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(left + left_padding), static_cast(top + top_padding), - static_cast(width), static_cast(height)); -} - -std::tuple 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(window_x) - left_padding; - const float scaled_display_y = static_cast(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 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 resized_texture_data(resize_width * resize_height); - u32 resized_texture_stride = sizeof(u32) * resize_width; - if (!stbir_resize_uint8(reinterpret_cast(texture_data.data()), width, height, texture_data_stride, - reinterpret_cast(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(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 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(m_display_active_width) / static_cast(m_display_width); - const float ss_height_scale = static_cast(m_display_active_height) / static_cast(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(static_cast(resize_height) * ss_aspect_ratio); - resize_height = g_settings.display_stretch_vertically ? - static_cast(static_cast(resize_height) / - (m_display_aspect_ratio / - (static_cast(m_display_width) / static_cast(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(resize_width), - static_cast(resize_height), compress_on_thread); -} - -bool GPUDevice::WriteDisplayTextureToBuffer(std::vector* 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(read_width); - u32 height = static_cast(read_height); - std::vector 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 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 resized_texture_data(resize_width * resize_height); - u32 resized_texture_stride = sizeof(u32) * resize_width; - if (!stbir_resize_uint8(reinterpret_cast(texture_data.data()), width, height, texture_data_stride, - reinterpret_cast(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(m_display_texture_view_width) / static_cast(m_display_texture_view_height); - const float dar = static_cast(draw_width) / static_cast(draw_height); - if (sar >= dar) - { - // stretch height, preserve width - const float scale = static_cast(m_display_texture_view_width) / static_cast(draw_width); - width = m_display_texture_view_width; - height = static_cast(std::round(static_cast(draw_height) * scale)); - } - else - { - // stretch width, preserve height - const float scale = static_cast(m_display_texture_view_height) / static_cast(draw_height); - width = static_cast(std::round(static_cast(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(static_cast(height) / - (static_cast(width) / static_cast(MAX_TEXTURE_SIZE))); - width = MAX_TEXTURE_SIZE; - } - if (height > MAX_TEXTURE_SIZE) - { - height = MAX_TEXTURE_SIZE; - width = static_cast(static_cast(width) / - (static_cast(height) / static_cast(MAX_TEXTURE_SIZE))); - } - - // Remove padding, it's not part of the framebuffer. - draw_left = 0; - draw_top = 0; - draw_width = static_cast(width); - draw_height = static_cast(height); - } - if (width == 0 || height == 0) - return false; - - std::vector pixels; - u32 pixels_stride; - GPUTexture::Format pixels_format; - if (!RenderScreenshot(width, height, - Common::Rectangle::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::CreateDeviceForAPI(RenderAPI api) { switch (api) diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index d59e31291..afe6b6239 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -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& draw_rect, std::vector* 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 CalculateDrawRect(s32 window_width, s32 window_height, - bool apply_aspect_ratio = true) const; - - /// Helper function for converting window coordinates to display coordinates. - std::tuple 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* 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 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 m_imgui_pipeline; std::unique_ptr m_imgui_font_texture; - - std::unique_ptr m_post_processing_chain; }; extern std::unique_ptr g_gpu_device; diff --git a/src/util/metal_device.h b/src/util/metal_device.h index 980d1b2eb..0f6de6815 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -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" diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index 61e54a004..302052587 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -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" diff --git a/src/util/postprocessing_chain.cpp b/src/util/postprocessing_chain.cpp index d6fc1887a..cb61f27b3 100644 --- a/src/util/postprocessing_chain.cpp +++ b/src/util/postprocessing_chain.cpp @@ -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; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index f16b4f347..7112bfe3c 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -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"