diff options
Diffstat (limited to 'src/video_core')
54 files changed, 545 insertions, 144 deletions
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index be2113f5a..10975884b 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -212,7 +212,7 @@ public: void FlushCachedWrites() noexcept { flags &= ~BufferFlagBits::CachedWrites; const u64 num_words = NumWords(); - const u64* const cached_words = Array<Type::CachedCPU>(); + u64* const cached_words = Array<Type::CachedCPU>(); u64* const untracked_words = Array<Type::Untracked>(); u64* const cpu_words = Array<Type::CPU>(); for (u64 word_index = 0; word_index < num_words; ++word_index) { @@ -220,6 +220,7 @@ public: NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); untracked_words[word_index] |= cached_bits; cpu_words[word_index] |= cached_bits; + cached_words[word_index] = 0; } } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 200d792dd..3f2bf6294 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -76,8 +76,9 @@ class BufferCache { static constexpr BufferId NULL_BUFFER_ID{0}; - static constexpr u64 EXPECTED_MEMORY = 512_MiB; - static constexpr u64 CRITICAL_MEMORY = 1_GiB; + static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB; + static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB; + static constexpr s64 TARGET_THRESHOLD = 4_GiB; using Maxwell = Tegra::Engines::Maxwell3D::Regs; @@ -436,6 +437,8 @@ private: Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; u64 frame_tick = 0; u64 total_used_memory = 0; + u64 minimum_memory = 0; + u64 critical_memory = 0; std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; }; @@ -451,11 +454,30 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, // Ensure the first slot is used for the null buffer void(slot_buffers.insert(runtime, NullBufferParams{})); common_ranges.clear(); + + if (!runtime.CanReportMemoryUsage()) { + minimum_memory = DEFAULT_EXPECTED_MEMORY; + critical_memory = DEFAULT_CRITICAL_MEMORY; + return; + } + + const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory()); + const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB; + const s64 min_spacing_critical = device_memory - 1_GiB; + const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD); + const s64 min_vacancy_expected = (6 * mem_threshold) / 10; + const s64 min_vacancy_critical = (3 * mem_threshold) / 10; + minimum_memory = static_cast<u64>( + std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected), + DEFAULT_EXPECTED_MEMORY)); + critical_memory = static_cast<u64>( + std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical), + DEFAULT_CRITICAL_MEMORY)); } template <class P> void BufferCache<P>::RunGarbageCollector() { - const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; + const bool aggressive_gc = total_used_memory >= critical_memory; const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; int num_iterations = aggressive_gc ? 64 : 32; const auto clean_up = [this, &num_iterations](BufferId buffer_id) { @@ -486,7 +508,11 @@ void BufferCache<P>::TickFrame() { const bool skip_preferred = hits * 256 < shots * 251; uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; - if (total_used_memory >= EXPECTED_MEMORY) { + // If we can obtain the memory info, use it instead of the estimate. + if (runtime.CanReportMemoryUsage()) { + total_used_memory = runtime.GetDeviceMemoryUsage(); + } + if (total_used_memory >= minimum_memory) { RunGarbageCollector(); } ++frame_tick; @@ -1285,7 +1311,20 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { const GPUVAddr gpu_addr_begin = array.StartAddress(); const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); + u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); + if (address_size >= 64_MiB) { + // Reported vertex buffer size is very large, cap to mapped buffer size + GPUVAddr submapped_addr_end = gpu_addr_begin; + + const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)}; + if (ranges.size() > 0) { + const auto& [addr, size] = *ranges.begin(); + submapped_addr_end = addr + size; + } + + address_size = + std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin)); + } const u32 size = address_size; // TODO: Analyze stride and number of vertices if (array.enable == 0 || size == 0 || !cpu_addr) { vertex_buffers[index] = NULL_BINDING; diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 81fac94bf..40f7755e8 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -56,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; return PREFERRED_CPU_FMT; } + +// List all the currently available hwcontext in ffmpeg +std::vector<AVHWDeviceType> ListSupportedContexts() { + std::vector<AVHWDeviceType> contexts{}; + AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; + do { + current_device_type = av_hwdevice_iterate_types(current_device_type); + contexts.push_back(current_device_type); + } while (current_device_type != AV_HWDEVICE_TYPE_NONE); + return contexts; +} + } // namespace void AVFrameDeleter(AVFrame* ptr) { @@ -76,17 +88,6 @@ Codec::~Codec() { av_buffer_unref(&av_gpu_decoder); } -// List all the currently available hwcontext in ffmpeg -static std::vector<AVHWDeviceType> ListSupportedContexts() { - std::vector<AVHWDeviceType> contexts{}; - AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; - do { - current_device_type = av_hwdevice_iterate_types(current_device_type); - contexts.push_back(current_device_type); - } while (current_device_type != AV_HWDEVICE_TYPE_NONE); - return contexts; -} - bool Codec::CreateGpuAvDevice() { static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; static const auto supported_contexts = ListSupportedContexts(); @@ -96,6 +97,8 @@ bool Codec::CreateGpuAvDevice() { LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); continue; } + // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create + av_buffer_unref(&av_gpu_decoder); const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); if (hwdevice_res < 0) { LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", @@ -127,15 +130,19 @@ bool Codec::CreateGpuAvDevice() { av_codec->name, av_hwdevice_get_type_name(type)); break; } - if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { - av_codec_ctx->pix_fmt = config->pix_fmt; - if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { + if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { +#if defined(__unix__) + // Some linux decoding backends are reported to crash with this config method + // TODO(ameerj): Properly support this method + if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) { // skip zero-copy decoders, we don't currently support them LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", av_hwdevice_get_type_name(type), config->methods); continue; } +#endif LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); + av_codec_ctx->pix_fmt = config->pix_fmt; return true; } } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 54a902f56..7399e760f 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -214,6 +214,11 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume regs.index_array.first = regs.small_index.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; return DrawArrays(); + case MAXWELL3D_REG_INDEX(small_index_2): + regs.index_array.count = regs.small_index_2.count; + regs.index_array.first = regs.small_index_2.first; + dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + return DrawArrays(); case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; return; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 3f5b38e55..d36dc3daa 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1220,7 +1220,12 @@ public: BitField<16, 16, u32> count; } small_index; - INSERT_PADDING_WORDS_NOINIT(0x6); + union { + BitField<0, 16, u32> first; + BitField<16, 16, u32> count; + } small_index_2; + + INSERT_PADDING_WORDS_NOINIT(0x5); INSERT_PADDING_WORDS_NOINIT(0x1F); diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index b1d455e30..93349bb78 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -6,41 +6,22 @@ #include "common/common_types.h" #include "common/math_util.h" +#include "core/hle/service/nvflinger/buffer_transform_flags.h" +#include "core/hle/service/nvflinger/pixel_format.h" namespace Tegra { + /** * Struct describing framebuffer configuration */ struct FramebufferConfig { - enum class PixelFormat : u32 { - A8B8G8R8_UNORM = 1, - RGB565_UNORM = 4, - B8G8R8A8_UNORM = 5, - }; - - enum class TransformFlags : u32 { - /// No transform flags are set - Unset = 0x00, - /// Flip source image horizontally (around the vertical axis) - FlipH = 0x01, - /// Flip source image vertically (around the horizontal axis) - FlipV = 0x02, - /// Rotate source image 90 degrees clockwise - Rotate90 = 0x04, - /// Rotate source image 180 degrees - Rotate180 = 0x03, - /// Rotate source image 270 degrees clockwise - Rotate270 = 0x07, - }; - VAddr address{}; u32 offset{}; u32 width{}; u32 height{}; u32 stride{}; - PixelFormat pixel_format{}; - - TransformFlags transform_flags{}; + Service::android::PixelFormat pixel_format{}; + Service::android::BufferTransformFlags transform_flags{}; Common::Rectangle<int> crop_rect; }; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index ba9ba082f..789af452d 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -230,7 +230,7 @@ struct GPU::Impl { void IncrementSyncPoint(u32 syncpoint_id) { auto& syncpoint = syncpoints.at(syncpoint_id); syncpoint++; - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; sync_cv.notify_all(); auto& interrupt = syncpt_interrupts.at(syncpoint_id); if (!interrupt.empty()) { @@ -252,7 +252,7 @@ struct GPU::Impl { } void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; auto& interrupt = syncpt_interrupts.at(syncpoint_id); bool contains = std::any_of(interrupt.begin(), interrupt.end(), [value](u32 in_value) { return in_value == value; }); @@ -263,7 +263,7 @@ struct GPU::Impl { } [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; auto& interrupt = syncpt_interrupts.at(syncpoint_id); const auto iter = std::find_if(interrupt.begin(), interrupt.end(), diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 26b8ea233..97c029140 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -8,6 +8,7 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "video_core/cdma_pusher.h" #include "video_core/framebuffer_config.h" diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9547f277a..4e8999915 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -56,7 +56,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, if (next.block) { // We have to lock the write_lock to ensure that the condition_variable wait not get a // race between the check and the lock itself. - std::lock_guard lk(state.write_lock); + std::scoped_lock lk{state.write_lock}; state.cv.notify_all(); } } diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index af05d47d1..190fc6aea 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -18,6 +18,7 @@ set(SHADER_FILES full_screen_triangle.vert fxaa.frag fxaa.vert + opengl_convert_s8d24.comp opengl_copy_bc4.comp opengl_present.frag opengl_present.vert diff --git a/src/video_core/host_shaders/opengl_convert_s8d24.comp b/src/video_core/host_shaders/opengl_convert_s8d24.comp new file mode 100644 index 000000000..83e1ab176 --- /dev/null +++ b/src/video_core/host_shaders/opengl_convert_s8d24.comp @@ -0,0 +1,18 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 430 core + +layout(local_size_x = 16, local_size_y = 8) in; + +layout(binding = 0, rgba8ui) restrict uniform uimage2D destination; +layout(location = 0) uniform uvec3 size; + +void main() { + if (any(greaterThanEqual(gl_GlobalInvocationID, size))) { + return; + } + uvec4 components = imageLoad(destination, ivec2(gl_GlobalInvocationID.xy)); + imageStore(destination, ivec2(gl_GlobalInvocationID.xy), components.wxyz); +} diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index c9627cffd..9756a81d6 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -26,6 +26,10 @@ void RendererBase::UpdateCurrentFramebufferLayout() { render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); } +bool RendererBase::IsScreenshotPending() const { + return renderer_settings.screenshot_requested; +} + void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback, const Layout::FramebufferLayout& layout) { if (renderer_settings.screenshot_requested) { diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index c5f974080..30d19b178 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -83,6 +83,9 @@ public: /// Refreshes the settings common to all renderers void RefreshBaseSettings(); + /// Returns true if a screenshot is being processed + bool IsScreenshotPending() const; + /// Request a screenshot of the next frame void RequestScreenshot(void* data, std::function<void(bool)> callback, const Layout::FramebufferLayout& layout); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index d4dd10bb6..f1f7b384b 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -135,6 +135,20 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_) buffer.Create(); glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY); } + + device_access_memory = [this]() -> u64 { + if (device.CanReportMemoryUsage()) { + return device.GetCurrentDedicatedVideoMemory() + 512_MiB; + } + return 2_GiB; // Return minimum requirements + }(); +} + +u64 BufferCacheRuntime::GetDeviceMemoryUsage() const { + if (device.CanReportMemoryUsage()) { + return device_access_memory - device.GetCurrentDedicatedVideoMemory(); + } + return 2_GiB; } void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 7287731b6..a8699f28c 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -89,6 +89,8 @@ public: void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format); + u64 GetDeviceMemoryUsage() const; + void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { const GLuint handle = fast_uniforms[stage][binding_index].handle; const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); @@ -151,6 +153,14 @@ public: use_storage_buffers = use_storage_buffers_; } + u64 GetDeviceLocalMemory() const { + return device_access_memory; + } + + bool CanReportMemoryUsage() const { + return device.CanReportMemoryUsage(); + } + private: static constexpr std::array PABO_LUT{ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, @@ -184,6 +194,8 @@ private: std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms; u32 index_buffer_offset = 0; + + u64 device_access_memory; }; struct BufferCacheParams { diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 715cd3a48..597301eeb 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -13,12 +13,15 @@ #include <glad/glad.h> +#include "common/literals.h" #include "common/logging/log.h" #include "common/settings.h" #include "shader_recompiler/stage.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_resource_manager.h" +using namespace Common::Literals; + namespace OpenGL { namespace { constexpr std::array LIMIT_UBOS = { @@ -165,6 +168,7 @@ Device::Device() { has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; need_fastmath_off = is_nvidia; + can_report_memory = GLAD_GL_NVX_gpu_memory_info; // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive // uniform buffers as "push constants" @@ -276,4 +280,10 @@ void main() { })"); } +u64 Device::GetCurrentDedicatedVideoMemory() const { + GLint cur_avail_mem_kb = 0; + glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb); + return static_cast<u64>(cur_avail_mem_kb) * 1_KiB; +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 95c2e8d38..9bb0b9148 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -20,6 +20,8 @@ public: [[nodiscard]] std::string GetVendorName() const; + u64 GetCurrentDedicatedVideoMemory() const; + u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept { return max_uniform_buffers[static_cast<size_t>(stage)]; } @@ -168,6 +170,10 @@ public: return vendor_name == "ATI Technologies Inc."; } + bool CanReportMemoryUsage() const { + return can_report_memory; + } + private: static bool TestVariableAoffi(); static bool TestPreciseBug(); @@ -210,6 +216,7 @@ private: bool need_fastmath_off{}; bool has_cbuf_ftou_bug{}; bool has_bool_ref_bug{}; + bool can_report_memory{}; std::string vendor_name; }; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 9e6732abd..fd40966d5 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -253,7 +253,7 @@ GraphicsPipeline::GraphicsPipeline( } } if (in_parallel) { - std::lock_guard lock{built_mutex}; + std::scoped_lock lock{built_mutex}; built_fence.Create(); // Flush this context to ensure compilation commands and fence are in the GPU pipe. glFlush(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4d632d211..8ef79753f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -15,8 +15,9 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" +#include "common/scope_exit.h" #include "common/settings.h" -#include "core/memory.h" + #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -210,6 +211,7 @@ void RasterizerOpenGL::Clear() { void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { MICROPROFILE_SCOPE(OpenGL_Drawing); + SCOPE_EXIT({ gpu.TickWork(); }); query_cache.UpdateCounters(); GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; @@ -265,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { ++num_queued_commands; has_written_global_memory |= pipeline->WritesGlobalMemory(); - - gpu.TickWork(); } void RasterizerOpenGL::DispatchCompute() { @@ -520,6 +520,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); + screen_info.texture.width = image_view->size.width; + screen_info.texture.height = image_view->size.height; screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format); return true; @@ -557,12 +559,19 @@ void RasterizerOpenGL::SyncViewport() { const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; const bool dirty_clip_control = flags[Dirty::ClipControl]; - if (dirty_clip_control || flags[Dirty::FrontFace]) { + if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) { flags[Dirty::FrontFace] = false; GLenum mode = MaxwellToGL::FrontFace(regs.front_face); + bool flip_faces = false; if (regs.screen_y_control.triangle_rast_flip != 0 && regs.viewport_transform[0].scale_y < 0.0f) { + flip_faces = !flip_faces; + } + if (regs.viewport_transform[0].scale_z < 0.0f) { + flip_faces = !flip_faces; + } + if (flip_faces) { switch (mode) { case GL_CW: mode = GL_CCW; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6423992c3..05c5e702c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -258,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { ctx->pools.ReleaseContents(); auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); } @@ -280,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, } ctx->pools.ReleaseContents(); auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { graphics_cache.emplace(key, std::move(pipeline)); } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 3c1f79a27..f8c6e5c7e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -182,6 +182,26 @@ GLenum AttachmentType(PixelFormat format) { } } +GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) { + switch (source) { + case SwizzleSource::Zero: + return GL_ZERO; + case SwizzleSource::R: + return GL_ALPHA; + case SwizzleSource::G: + return GL_BLUE; + case SwizzleSource::B: + return GL_GREEN; + case SwizzleSource::A: + return GL_RED; + case SwizzleSource::OneInt: + case SwizzleSource::OneFloat: + return GL_ONE; + } + UNREACHABLE_MSG("Invalid swizzle source={}", source); + return GL_NONE; +} + void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) { switch (format) { case PixelFormat::D24_UNORM_S8_UINT: @@ -192,6 +212,12 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4 TextureMode(format, swizzle[0] == SwizzleSource::R)); std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); break; + case PixelFormat::A5B5G5R1_UNORM: { + std::array<GLint, 4> gl_swizzle; + std::ranges::transform(swizzle, gl_swizzle.begin(), ConvertA5B5G5R1_UNORM); + glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); + return; + } default: break; } @@ -409,8 +435,8 @@ ImageBufferMap::~ImageBufferMap() { TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager, StateTracker& state_tracker_) - : device{device_}, state_tracker{state_tracker_}, - util_shaders(program_manager), resolution{Settings::values.resolution_info} { + : device{device_}, state_tracker{state_tracker_}, util_shaders(program_manager), + format_conversion_pass{util_shaders}, resolution{Settings::values.resolution_info} { static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D}; for (size_t i = 0; i < TARGETS.size(); ++i) { const GLenum target = TARGETS[i]; @@ -484,6 +510,13 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& rescale_read_fbos[i].Create(); } } + + device_access_memory = [this]() -> u64 { + if (device.CanReportMemoryUsage()) { + return device.GetCurrentDedicatedVideoMemory() + 512_MiB; + } + return 2_GiB; // Return minimum requirements + }(); } TextureCacheRuntime::~TextureCacheRuntime() = default; @@ -500,13 +533,11 @@ ImageBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) { return download_buffers.RequestMap(size, false); } -u64 TextureCacheRuntime::GetDeviceLocalMemory() const { - if (GLAD_GL_NVX_gpu_memory_info) { - GLint cur_avail_mem_kb = 0; - glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb); - return static_cast<u64>(cur_avail_mem_kb) * 1_KiB; +u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { + if (device.CanReportMemoryUsage()) { + return device_access_memory - device.GetCurrentDedicatedVideoMemory(); } - return 2_GiB; // Return minimum requirements + return 2_GiB; } void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image, @@ -686,6 +717,7 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, } if (IsConverted(runtime->device, info.format, info.type)) { flags |= ImageFlagBits::Converted; + flags |= ImageFlagBits::CostlyLoad; gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; gl_format = GL_RGBA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; @@ -1319,6 +1351,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM Framebuffer::~Framebuffer() = default; +FormatConversionPass::FormatConversionPass(UtilShaders& util_shaders_) + : util_shaders{util_shaders_} {} + void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies) { const GLenum dst_target = ImageTarget(dst_image.info); @@ -1351,6 +1386,12 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, dst_origin.z, region.width, region.height, region.depth, dst_image.GlFormat(), dst_image.GlType(), nullptr); } + + // Swap component order of S8D24 to ABGR8 reinterprets + if (src_image.info.format == PixelFormat::D24_UNORM_S8_UINT && + dst_image.info.format == PixelFormat::A8B8G8R8_UNORM) { + util_shaders.ConvertS8D24(dst_image, copies); + } } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 7f425631f..672fa8dde 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -10,6 +10,7 @@ #include <glad/glad.h> #include "shader_recompiler/shader_info.h" +#include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/util_shaders.h" #include "video_core/texture_cache/image_view_base.h" @@ -21,7 +22,6 @@ struct ResolutionScalingInfo; namespace OpenGL { -class Device; class ProgramManager; class StateTracker; @@ -55,13 +55,14 @@ struct FormatProperties { class FormatConversionPass { public: - FormatConversionPass() = default; + explicit FormatConversionPass(UtilShaders& util_shaders); ~FormatConversionPass() = default; void ConvertImage(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); private: + UtilShaders& util_shaders; OGLBuffer intermediate_pbo; size_t pbo_size{}; }; @@ -83,7 +84,15 @@ public: ImageBufferMap DownloadStagingBuffer(size_t size); - u64 GetDeviceLocalMemory() const; + u64 GetDeviceLocalMemory() const { + return device_access_memory; + } + + u64 GetDeviceMemoryUsage() const; + + bool CanReportMemoryUsage() const { + return device.CanReportMemoryUsage(); + } bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) { return true; @@ -172,6 +181,7 @@ private: std::array<OGLFramebuffer, 4> rescale_draw_fbos; std::array<OGLFramebuffer, 4> rescale_read_fbos; const Settings::ResolutionScalingInfo& resolution; + u64 device_access_memory; }; class Image : public VideoCommon::ImageBase { diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index db5bf1d30..03adf3d4c 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -30,6 +30,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM + {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT @@ -87,6 +88,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM + {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 795c97831..3a3c213bb 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -208,6 +208,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf // Framebuffer orientation handling framebuffer_transform_flags = framebuffer.transform_flags; framebuffer_crop_rect = framebuffer.crop_rect; + framebuffer_width = framebuffer.width; + framebuffer_height = framebuffer.height; const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; screen_info.was_accelerated = @@ -323,12 +325,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, GLint internal_format; switch (framebuffer.pixel_format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: internal_format = GL_RGBA8; texture.gl_format = GL_RGBA; texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; break; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: internal_format = GL_RGB565; texture.gl_format = GL_RGB; texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; @@ -464,8 +466,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { const auto& texcoords = screen_info.display_texcoords; auto left = texcoords.left; auto right = texcoords.right; - if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) { - if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { + if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) { + if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) { // Flip the framebuffer vertically left = texcoords.right; right = texcoords.left; @@ -480,9 +482,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented"); ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); + f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); + f32 scale_v = + static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); + // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering // (e.g. handheld mode) on a 1920x1080 framebuffer. - f32 scale_u = 1.f, scale_v = 1.f; if (framebuffer_crop_rect.GetWidth() > 0) { scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / static_cast<f32>(screen_info.texture.width); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 35706cf05..ae9558a33 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -46,7 +46,7 @@ struct TextureInfo { GLsizei height; GLenum gl_format; GLenum gl_type; - Tegra::FramebufferConfig::PixelFormat pixel_format; + Service::android::PixelFormat pixel_format; }; /// Structure used for storing information about the display target for the Switch screen @@ -135,8 +135,10 @@ private: std::vector<u8> gl_framebuffer_data; /// Used for transforming the framebuffer orientation - Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; + Service::android::BufferTransformFlags framebuffer_transform_flags{}; Common::Rectangle<int> framebuffer_crop_rect; + u32 framebuffer_width; + u32 framebuffer_height; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 897c380b3..04c482a09 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -13,6 +13,7 @@ #include "video_core/host_shaders/astc_decoder_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" +#include "video_core/host_shaders/opengl_convert_s8d24_comp.h" #include "video_core/host_shaders/opengl_copy_bc4_comp.h" #include "video_core/host_shaders/pitch_unswizzle_comp.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -50,7 +51,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_) block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), - copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { + copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)), + convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) { const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); swizzle_table_buffer.Create(); glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); @@ -248,6 +250,26 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im program_manager.RestoreGuestCompute(); } +void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) { + static constexpr GLuint BINDING_DESTINATION = 0; + static constexpr GLuint LOC_SIZE = 0; + + program_manager.BindComputeProgram(convert_s8d24_program.handle); + for (const ImageCopy& copy : copies) { + ASSERT(copy.src_subresource.base_layer == 0); + ASSERT(copy.src_subresource.num_layers == 1); + ASSERT(copy.dst_subresource.base_layer == 0); + ASSERT(copy.dst_subresource.num_layers == 1); + + glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth); + glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(), + copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI); + glDispatchCompute(Common::DivCeil(copy.extent.width, 16u), + Common::DivCeil(copy.extent.height, 8u), copy.extent.depth); + } + program_manager.RestoreGuestCompute(); +} + GLenum StoreFormat(u32 bytes_per_block) { switch (bytes_per_block) { case 1: diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h index 5de95ea7a..5c132e67f 100644 --- a/src/video_core/renderer_opengl/util_shaders.h +++ b/src/video_core/renderer_opengl/util_shaders.h @@ -39,6 +39,8 @@ public: void CopyBC4(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); + void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies); + private: ProgramManager& program_manager; @@ -49,6 +51,7 @@ private: OGLProgram block_linear_unswizzle_3d_program; OGLProgram pitch_unswizzle_program; OGLProgram copy_bc4_program; + OGLProgram convert_s8d24_program; }; GLenum StoreFormat(u32 bytes_per_block); diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index ec03cca38..abda1c490 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -367,17 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, PipelineLayoutCreateInfo(two_textures_set_layout.address()))), full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), + blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), - nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { - if (device.IsExtShaderStencilExportSupported()) { - blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV); - } -} + nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {} BlitImageHelper::~BlitImageHelper() = default; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 1c136c410..a2c6d0e6c 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -127,6 +127,7 @@ struct FormatTuple { {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) + {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT @@ -184,6 +185,7 @@ struct FormatTuple { {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM + {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp index bfec931a6..7ccadf084 100644 --- a/src/video_core/renderer_vulkan/pipeline_statistics.cpp +++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp @@ -57,7 +57,7 @@ void PipelineStatistics::Collect(VkPipeline pipeline) { stage_stats.basic_block_count = GetUint64(statistic); } } - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; collected_stats.push_back(stage_stats); } } @@ -66,7 +66,7 @@ void PipelineStatistics::Report() const { double num{}; Stats total; { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; for (const Stats& stats : collected_stats) { total.code_size += stats.code_size; total.register_count += stats.register_count; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 0ec85682b..b866e9103 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -94,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { switch (framebuffer.pixel_format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: return VK_FORMAT_A8B8G8R8_UNORM_PACK32; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: return VK_FORMAT_R5G6B5_UNORM_PACK16; - case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: + case Service::android::PixelFormat::Bgra8888: return VK_FORMAT_B8G8R8A8_UNORM; default: UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", @@ -1390,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi auto right = texcoords.right; switch (framebuffer_transform_flags) { - case Tegra::FramebufferConfig::TransformFlags::Unset: + case Service::android::BufferTransformFlags::Unset: break; - case Tegra::FramebufferConfig::TransformFlags::FlipV: + case Service::android::BufferTransformFlags::FlipV: // Flip the framebuffer vertically left = texcoords.right; right = texcoords.left; @@ -1406,8 +1406,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); - f32 scale_u = 1.0f; - f32 scale_v = 1.0f; + f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); + f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); + // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering // (e.g. handheld mode) on a 1920x1080 framebuffer. if (!fsr) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 5ffd93499..def838c34 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -141,6 +141,18 @@ StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size) { return staging_pool.Request(size, MemoryUsage::Download); } +u64 BufferCacheRuntime::GetDeviceLocalMemory() const { + return device.GetDeviceLocalMemory(); +} + +u64 BufferCacheRuntime::GetDeviceMemoryUsage() const { + return device.GetDeviceMemoryUsage(); +} + +bool BufferCacheRuntime::CanReportMemoryUsage() const { + return device.CanReportMemoryUsage(); +} + void BufferCacheRuntime::Finish() { scheduler.Finish(); } diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 1ee0d8420..d7fdd18ff 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -65,6 +65,12 @@ public: void Finish(); + u64 GetDeviceLocalMemory() const; + + u64 GetDeviceMemoryUsage() const; + + bool CanReportMemoryUsage() const; + [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index de36bcdb7..97b3594c2 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -77,7 +77,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript if (pipeline_statistics) { pipeline_statistics->Collect(*pipeline); } - std::lock_guard lock{build_mutex}; + std::scoped_lock lock{build_mutex}; is_built = true; build_condvar.notify_one(); if (shader_notify) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d514b71d0..8959d6059 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -258,7 +258,7 @@ GraphicsPipeline::GraphicsPipeline( pipeline_statistics->Collect(*pipeline); } - std::lock_guard lock{build_mutex}; + std::scoped_lock lock{build_mutex}; is_built = true; build_condvar.notify_one(); if (shader_notify) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 27e59df73..336d1e9dc 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -404,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { ShaderPools pools; auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); } @@ -434,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), state.statistics.get(), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; graphics_cache.emplace(key, std::move(pipeline)); ++state.built; if (state.has_loaded) { diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index 451ffe019..d22bb6694 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -36,7 +36,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto [pair, is_new] = cache.try_emplace(key); if (!is_new) { return *pair->second; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index ad320991b..6a9416457 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -73,7 +73,7 @@ void VKScheduler::DispatchWork() { return; } { - std::lock_guard lock{work_mutex}; + std::scoped_lock lock{work_mutex}; work_queue.push(std::move(chunk)); } work_cv.notify_one(); @@ -157,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) { if (has_submit) { AllocateWorkerCommandBuffer(); } - std::lock_guard reserve_lock{reserve_mutex}; + std::scoped_lock reserve_lock{reserve_mutex}; chunk_reserve.push_back(std::move(work)); } while (!stop_token.stop_requested()); } @@ -282,7 +282,7 @@ void VKScheduler::EndRenderPass() { } void VKScheduler::AcquireNewChunk() { - std::lock_guard lock{reserve_mutex}; + std::scoped_lock lock{reserve_mutex}; if (chunk_reserve.empty()) { chunk = std::make_unique<CommandChunk>(); return; diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 5d5329abf..64a58304b 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -118,7 +118,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem .image = nullptr, .buffer = *stream_buffer, }; - const auto memory_properties = device.GetPhysical().GetMemoryProperties(); + const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties; VkMemoryAllocateInfo stream_memory_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = make_dedicated ? &dedicated_info : nullptr, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 83a23b66a..49691ce0c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -438,6 +438,32 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { } } +[[nodiscard]] SwizzleSource SwapGreenRed(SwizzleSource value) { + switch (value) { + case SwizzleSource::R: + return SwizzleSource::G; + case SwizzleSource::G: + return SwizzleSource::R; + default: + return value; + } +} + +[[nodiscard]] SwizzleSource SwapSpecial(SwizzleSource value) { + switch (value) { + case SwizzleSource::A: + return SwizzleSource::R; + case SwizzleSource::R: + return SwizzleSource::A; + case SwizzleSource::G: + return SwizzleSource::B; + case SwizzleSource::B: + return SwizzleSource::G; + default: + return value; + } +} + void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, VkImageAspectFlags aspect_mask, bool is_initialized, std::span<const VkBufferImageCopy> copies) { @@ -554,14 +580,25 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im }; } -[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { +void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle, + bool emulate_bgr565) { switch (format) { case PixelFormat::A1B5G5R5_UNORM: - return true; + std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); + break; case PixelFormat::B5G6R5_UNORM: - return emulate_bgr565; + if (emulate_bgr565) { + std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); + } + break; + case PixelFormat::A5B5G5R1_UNORM: + std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial); + break; + case PixelFormat::R4G4_UNORM: + std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed); + break; default: - return false; + break; } } @@ -1189,6 +1226,14 @@ u64 TextureCacheRuntime::GetDeviceLocalMemory() const { return device.GetDeviceLocalMemory(); } +u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { + return device.GetDeviceMemoryUsage(); +} + +bool TextureCacheRuntime::CanReportMemoryUsage() const { + return device.CanReportMemoryUsage(); +} + void TextureCacheRuntime::TickFrame() {} Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, @@ -1203,6 +1248,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu } else { flags |= VideoCommon::ImageFlagBits::Converted; } + flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (runtime->device.HasDebuggingToolAttached()) { original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); @@ -1442,8 +1488,7 @@ bool Image::BlitScaleHelper(bool scale_up) { runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, src_region, operation, BLIT_OPERATION); - } else if (!runtime->device.IsBlitDepthStencilSupported() && - aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (!blit_framebuffer) { blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); } @@ -1488,9 +1533,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI }; if (!info.IsRenderTarget()) { swizzle = info.Swizzle(); - if (IsFormatFlipped(format, device->MustEmulateBGR565())) { - std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); - } + TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565()); if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index c81130dd2..cb15b4a1c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -55,6 +55,10 @@ public: u64 GetDeviceLocalMemory() const; + u64 GetDeviceMemoryUsage() const; + + bool CanReportMemoryUsage() const; + void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 87636857d..75031767a 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -25,7 +25,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) { } void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { - std::lock_guard lock{invalidation_mutex}; + std::scoped_lock lock{invalidation_mutex}; InvalidatePagesInRegion(addr, size); } diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a36015c8c..5f428d35d 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -190,13 +190,13 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) } } -PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { +PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) { switch (format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: return PixelFormat::A8B8G8R8_UNORM; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: return PixelFormat::R5G6B5_UNORM; - case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: + case Service::android::PixelFormat::Bgra8888: return PixelFormat::B8G8R8A8_UNORM; default: UNIMPLEMENTED_MSG("Unimplemented format={}", format); diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 33e8d24ab..86fea61ae 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -25,6 +25,7 @@ enum class PixelFormat { A2B10G10R10_UNORM, A2B10G10R10_UINT, A1B5G5R5_UNORM, + A5B5G5R1_UNORM, R8_UNORM, R8_SNORM, R8_SINT, @@ -82,6 +83,7 @@ enum class PixelFormat { BC3_SRGB, BC7_SRGB, A4B4G4R4_UNORM, + R4G4_UNORM, ASTC_2D_4X4_SRGB, ASTC_2D_8X8_SRGB, ASTC_2D_8X5_SRGB, @@ -156,6 +158,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 1, // A2B10G10R10_UNORM 1, // A2B10G10R10_UINT 1, // A1B5G5R5_UNORM + 1, // A5B5G5R1_UNORM 1, // R8_UNORM 1, // R8_SNORM 1, // R8_SINT @@ -213,6 +216,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM + 1, // R4G4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 8, // ASTC_2D_8X5_SRGB @@ -256,6 +260,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 1, // A2B10G10R10_UNORM 1, // A2B10G10R10_UINT 1, // A1B5G5R5_UNORM + 1, // A5B5G5R1_UNORM 1, // R8_UNORM 1, // R8_SNORM 1, // R8_SINT @@ -313,6 +318,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM + 1, // R4G4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 5, // ASTC_2D_8X5_SRGB @@ -356,6 +362,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 32, // A2B10G10R10_UNORM 32, // A2B10G10R10_UINT 16, // A1B5G5R5_UNORM + 16, // A5B5G5R1_UNORM 8, // R8_UNORM 8, // R8_SNORM 8, // R8_SINT @@ -413,6 +420,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 128, // BC3_SRGB 128, // BC7_UNORM 16, // A4B4G4R4_UNORM + 8, // R4G4_UNORM 128, // ASTC_2D_4X4_SRGB 128, // ASTC_2D_8X8_SRGB 128, // ASTC_2D_8X5_SRGB @@ -460,7 +468,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format); PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); -PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); +PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format); SurfaceType GetFormatType(PixelFormat pixel_format); diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index afa807d5d..20e64a7c2 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -63,6 +63,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::A1B5G5R5_UNORM; case Hash(TextureFormat::A4B4G4R4, UNORM): return PixelFormat::A4B4G4R4_UNORM; + case Hash(TextureFormat::G4R4, UNORM): + return PixelFormat::R4G4_UNORM; + case Hash(TextureFormat::A5B5G5R1, UNORM): + return PixelFormat::A5B5G5R1_UNORM; case Hash(TextureFormat::R8, UNORM): return PixelFormat::R8_UNORM; case Hash(TextureFormat::R8, SNORM): @@ -143,6 +147,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::S8_UINT_D24_UNORM; case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): return PixelFormat::S8_UINT_D24_UNORM; + case Hash(TextureFormat::D24S8, UNORM, UINT, UINT, UINT, LINEAR): + return PixelFormat::D24_UNORM_S8_UINT; case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): return PixelFormat::D32_FLOAT_S8_UINT; case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index b2c81057b..6f5afc5a9 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -38,6 +38,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str return "A2B10G10R10_UINT"; case PixelFormat::A1B5G5R5_UNORM: return "A1B5G5R5_UNORM"; + case PixelFormat::A5B5G5R1_UNORM: + return "A5B5G5R1_UNORM"; case PixelFormat::R8_UNORM: return "R8_UNORM"; case PixelFormat::R8_SNORM: @@ -152,6 +154,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str return "BC7_SRGB"; case PixelFormat::A4B4G4R4_UNORM: return "A4B4G4R4_UNORM"; + case PixelFormat::R4G4_UNORM: + return "R4G4_UNORM"; case PixelFormat::ASTC_2D_4X4_SRGB: return "ASTC_2D_4X4_SRGB"; case PixelFormat::ASTC_2D_8X8_SRGB: diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 89c111c00..dd0106432 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -33,11 +33,12 @@ enum class ImageFlagBits : u32 { ///< garbage collection priority Alias = 1 << 11, ///< This image has aliases and has priority on garbage ///< collection + CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back. // Rescaler - Rescaled = 1 << 12, - CheckingRescalable = 1 << 13, - IsRescalable = 1 << 14, + Rescaled = 1 << 13, + CheckingRescalable = 1 << 14, + IsRescalable = 1 << 15, }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 72eeb8bbd..8fef74117 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -50,14 +50,20 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& void(slot_samplers.insert(runtime, sampler_descriptor)); if constexpr (HAS_DEVICE_MEMORY_INFO) { - const auto device_memory = runtime.GetDeviceLocalMemory(); - const u64 possible_expected_memory = (device_memory * 4) / 10; - const u64 possible_critical_memory = (device_memory * 7) / 10; - expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB); - critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB); - minimum_memory = 0; + const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory()); + const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB; + const s64 min_spacing_critical = device_memory - 1_GiB; + const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD); + const s64 min_vacancy_expected = (6 * mem_threshold) / 10; + const s64 min_vacancy_critical = (3 * mem_threshold) / 10; + expected_memory = static_cast<u64>( + std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected), + DEFAULT_EXPECTED_MEMORY)); + critical_memory = static_cast<u64>( + std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical), + DEFAULT_CRITICAL_MEMORY)); + minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2); } else { - // On OpenGL we can be more conservatives as the driver takes care. expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; minimum_memory = 0; @@ -66,18 +72,21 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& template <class P> void TextureCache<P>::RunGarbageCollector() { - const bool high_priority_mode = total_used_memory >= expected_memory; - const bool aggressive_mode = total_used_memory >= critical_memory; - const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL; - size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10); - const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { + bool high_priority_mode = total_used_memory >= expected_memory; + bool aggressive_mode = total_used_memory >= critical_memory; + const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL; + size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10); + const auto clean_up = [this, &num_iterations, &high_priority_mode, + &aggressive_mode](ImageId image_id) { if (num_iterations == 0) { return true; } --num_iterations; auto& image = slot_images[image_id]; - const bool must_download = image.IsSafeDownload(); - if (!high_priority_mode && must_download) { + const bool must_download = + image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); + if (!high_priority_mode && + (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) { return false; } if (must_download) { @@ -92,6 +101,18 @@ void TextureCache<P>::RunGarbageCollector() { } UnregisterImage(image_id); DeleteImage(image_id, image.scale_tick > frame_tick + 5); + if (total_used_memory < critical_memory) { + if (aggressive_mode) { + // Sink the aggresiveness. + num_iterations >>= 2; + aggressive_mode = false; + return false; + } + if (high_priority_mode && total_used_memory < expected_memory) { + num_iterations >>= 1; + high_priority_mode = false; + } + } return false; }; lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); @@ -99,6 +120,10 @@ void TextureCache<P>::RunGarbageCollector() { template <class P> void TextureCache<P>::TickFrame() { + // If we can obtain the memory info, use it instead of the estimate. + if (runtime.CanReportMemoryUsage()) { + total_used_memory = runtime.GetDeviceMemoryUsage(); + } if (total_used_memory > minimum_memory) { RunGarbageCollector(); } @@ -106,6 +131,7 @@ void TextureCache<P>::TickFrame() { sentenced_framebuffers.Tick(); sentenced_image_view.Tick(); runtime.TickFrame(); + critical_gc = 0; ++frame_tick; } @@ -1052,6 +1078,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA for (const ImageId overlap_id : overlap_ids) { Image& overlap = slot_images[overlap_id]; + if (True(overlap.flags & ImageFlagBits::GpuModified)) { + new_image.flags |= ImageFlagBits::GpuModified; + } if (overlap.info.num_samples != new_image.info.num_samples) { LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); } else { @@ -1414,6 +1443,10 @@ void TextureCache<P>::RegisterImage(ImageId image_id) { tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); } total_used_memory += Common::AlignUp(tentative_size, 1024); + if (total_used_memory > critical_memory && critical_gc < GC_EMERGENCY_COUNTS) { + RunGarbageCollector(); + critical_gc++; + } image.lru_index = lru_cache.Insert(image_id, frame_tick); ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, @@ -1704,6 +1737,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) { most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); aliased_images.push_back(&aliased); any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); + if (True(aliased_image.flags & ImageFlagBits::GpuModified)) { + image.flags |= ImageFlagBits::GpuModified; + } } } if (aliased_images.empty()) { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 647ca0730..b1324edf3 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -59,8 +59,10 @@ class TextureCache { /// True when the API can provide info about the memory of the device. static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; - static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; - static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; + static constexpr s64 TARGET_THRESHOLD = 4_GiB; + static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; + static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; + static constexpr size_t GC_EMERGENCY_COUNTS = 2; using Runtime = typename P::Runtime; using Image = typename P::Image; @@ -372,6 +374,7 @@ private: u64 minimum_memory; u64 expected_memory; u64 critical_memory; + size_t critical_gc; SlotVector<Image> slot_images; SlotVector<ImageMapView> slot_map_views; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 32c10d675..bd05a1f84 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -12,12 +12,14 @@ #include <vector> #include "common/assert.h" +#include "common/literals.h" #include "common/settings.h" #include "video_core/vulkan_common/nsight_aftermath_tracker.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { +using namespace Common::Literals; namespace { namespace Alternatives { constexpr std::array STENCIL8_UINT{ @@ -43,6 +45,12 @@ constexpr std::array B5G6R5_UNORM_PACK16{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, }; + +constexpr std::array R4G4_UNORM_PACK8{ + VK_FORMAT_R8_UNORM, + VK_FORMAT_UNDEFINED, +}; + } // namespace Alternatives enum class NvidiaArchitecture { @@ -93,6 +101,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); case VK_FORMAT_B5G6R5_UNORM_PACK16: return Alternatives::B5G6R5_UNORM_PACK16.data(); + case VK_FORMAT_R4G4_UNORM_PACK8: + return Alternatives::R4G4_UNORM_PACK8.data(); default: return nullptr; } @@ -120,6 +130,8 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica VK_FORMAT_A8B8G8R8_SRGB_PACK32, VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_B5G6R5_UNORM_PACK16, + VK_FORMAT_R5G5B5A1_UNORM_PACK16, + VK_FORMAT_B5G5R5A1_UNORM_PACK16, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UINT_PACK32, VK_FORMAT_A1R5G5B5_UNORM_PACK16, @@ -158,7 +170,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_R4G4_UNORM_PACK8, VK_FORMAT_R4G4B4A4_UNORM_PACK16, + VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM, VK_FORMAT_S8_UINT, @@ -596,6 +610,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); + is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; + is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || + properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; + CollectPhysicalMemoryInfo(); CollectTelemetryParameters(); CollectToolingInfo(); @@ -614,6 +633,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR khr_push_descriptor = false; break; } + const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff; + if (nv_major_version >= 510) { + LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); + cant_blit_msaa = true; + } } const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; if (ext_extended_dynamic_state && is_radv) { @@ -724,7 +748,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags } void Device::ReportLoss() const { - LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); + LOG_CRITICAL(Render_Vulkan, "Device loss occurred!"); // Wait for the log to flush and for Nsight Aftermath to dump the results std::this_thread::sleep_for(std::chrono::seconds{15}); @@ -985,6 +1009,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, false); test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); + test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true); if (Settings::values.enable_nsight_aftermath) { test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); @@ -997,7 +1022,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VkPhysicalDeviceFeatures2KHR features{}; features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; - VkPhysicalDeviceProperties2KHR physical_properties; + VkPhysicalDeviceProperties2KHR physical_properties{}; physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; if (has_khr_shader_float16_int8) { @@ -1267,15 +1292,50 @@ void Device::CollectTelemetryParameters() { vendor_name = driver.driverName; } +u64 Device::GetDeviceMemoryUsage() const { + VkPhysicalDeviceMemoryBudgetPropertiesEXT budget; + budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + budget.pNext = nullptr; + physical.GetMemoryProperties(&budget); + u64 result{}; + for (const size_t heap : valid_heap_memory) { + result += budget.heapUsage[heap]; + } + return result; +} + void Device::CollectPhysicalMemoryInfo() { - const auto mem_properties = physical.GetMemoryProperties(); + VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; + budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr); + const auto& mem_properties = mem_info.memoryProperties; const size_t num_properties = mem_properties.memoryHeapCount; device_access_memory = 0; + u64 device_initial_usage = 0; + u64 local_memory = 0; for (size_t element = 0; element < num_properties; ++element) { - if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { - device_access_memory += mem_properties.memoryHeaps[element].size; + const bool is_heap_local = + (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0; + if (!is_integrated && !is_heap_local) { + continue; } + valid_heap_memory.push_back(element); + if (is_heap_local) { + local_memory += mem_properties.memoryHeaps[element].size; + } + if (ext_memory_budget) { + device_initial_usage += budget.heapUsage[element]; + device_access_memory += budget.heapBudget[element]; + continue; + } + device_access_memory += mem_properties.memoryHeaps[element].size; + } + if (!is_integrated) { + return; } + const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage); + device_access_memory = static_cast<u64>(std::max<s64>( + std::min<s64>(available_memory - 8_GiB, 4_GiB), static_cast<s64>(local_memory))); } void Device::CollectToolingInfo() { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 1c7c18bcf..2d709d069 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -341,6 +341,12 @@ public: return device_access_memory; } + bool CanReportMemoryUsage() const { + return ext_memory_budget; + } + + u64 GetDeviceMemoryUsage() const; + u32 GetSetsPerPool() const { return sets_per_pool; } @@ -421,6 +427,9 @@ private: bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list ///< topologies. bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. + bool is_integrated{}; ///< Is GPU an iGPU. + bool is_virtual{}; ///< Is GPU a virtual GPU. + bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. @@ -445,6 +454,7 @@ private: bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. + bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget. bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit bool has_renderdoc{}; ///< Has RenderDoc attached @@ -456,6 +466,7 @@ private: // Telemetry parameters std::string vendor_name; ///< Device's driver name. std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. + std::vector<size_t> valid_heap_memory; ///< Heaps used. /// Format properties dictionary. std::unordered_map<VkFormat, VkFormatProperties> format_properties; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 300a61205..e6e97b332 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -227,7 +227,7 @@ void MemoryCommit::Release() { } MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) - : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, + : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, export_allocations{export_allocations_}, buffer_image_granularity{ device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index a794f16dd..742cc39da 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -237,8 +237,8 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && - X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && - X(vkGetPhysicalDeviceQueueFamilyProperties); + X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) && + X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties); #undef X } @@ -926,9 +926,12 @@ std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR( return modes; } -VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { - VkPhysicalDeviceMemoryProperties properties; - dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); +VkPhysicalDeviceMemoryProperties2 PhysicalDevice::GetMemoryProperties( + void* next_structures) const noexcept { + VkPhysicalDeviceMemoryProperties2 properties{}; + properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + properties.pNext = next_structures; + dld->vkGetPhysicalDeviceMemoryProperties2(physical_device, &properties); return properties; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 53bac627f..0a5f9931c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -172,6 +172,7 @@ struct InstanceDispatch { PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; + PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; @@ -950,7 +951,8 @@ public: std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; - VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; + VkPhysicalDeviceMemoryProperties2 GetMemoryProperties( + void* next_structures = nullptr) const noexcept; private: VkPhysicalDevice physical_device = nullptr; |