From bbb75f8dd42899597d93f1cfe20f43d4e3329ef1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 2 Aug 2026 03:07:06 -0400 Subject: [PATCH] Some adjustments between buffer history and ZBC Table --- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 87 +++++++++++-------- .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 10 ++- .../service/nvnflinger/buffer_queue_core.cpp | 26 ++---- .../service/nvnflinger/buffer_queue_core.h | 18 ++-- .../nvnflinger/buffer_queue_producer.cpp | 31 ++++--- 5 files changed, 97 insertions(+), 75 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index b621e5d70d..2a99966027 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/assert.h" #include "common/logging.h" @@ -264,7 +265,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) { } NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { - if (params.type > supported_types) { + if (params.type == 0 || params.type > supported_types) { LOG_ERROR(Service_NVDRV, "ZBCSetTable: invalid type {:#X}", params.type); return NvResult::BadParameter; } @@ -279,42 +280,61 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { color_entry.format = params.format; color_entry.ref_cnt = 1u; - auto color_it = std::ranges::find_if(zbc_colors, - [&](const ZbcColorEntry& color_in_question) { - return color_entry.format == color_in_question.format && - color_entry.color_ds == color_in_question.color_ds && - color_entry.color_l2 == color_in_question.color_l2; - }); + const auto color_end = zbc_colors.begin() + zbc_used_color_entries; + auto color_it = std::find_if(zbc_colors.begin(), color_end, + [&](const ZbcColorEntry& color_in_question) { + return color_entry.format == color_in_question.format && + color_entry.color_ds == color_in_question.color_ds && + color_entry.color_l2 == color_in_question.color_l2; + }); - if (color_it != zbc_colors.end()) { + if (color_it != color_end) { ++color_it->ref_cnt; LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused color entry fmt={:#X}, ref_cnt={:#X}", params.format, color_it->ref_cnt); - } else { - zbc_colors.push_back(color_entry); - LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#X}, index={:#X}", - params.format, zbc_colors.size() - 1); + break; } + + if (zbc_used_color_entries >= zbc_table_size) { + LOG_WARNING(Service_NVDRV, "ZBCSetTable: color table is full, fmt={:#X}", + params.format); + return NvResult::InsufficientMemory; + } + + zbc_colors[zbc_used_color_entries] = color_entry; + LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#X}, index={:#X}", + params.format, zbc_used_color_entries); + ++zbc_used_color_entries; break; } case ZBCTypes::depth: { ZbcDepthEntry depth_entry{params.depth, params.format, 1u}; - auto depth_it = std::ranges::find_if(zbc_depths, - [&](const ZbcDepthEntry& depth_entry_in_question) { - return depth_entry.format == depth_entry_in_question.format && - depth_entry.depth == depth_entry_in_question.depth; - }); + const auto depth_end = zbc_depths.begin() + zbc_used_depth_entries; + auto depth_it = std::find_if(zbc_depths.begin(), depth_end, + [&](const ZbcDepthEntry& depth_entry_in_question) { + return depth_entry.format == depth_entry_in_question.format && + depth_entry.depth == depth_entry_in_question.depth; + }); - if (depth_it != zbc_depths.end()) { + if (depth_it != depth_end) { ++depth_it->ref_cnt; LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused depth entry fmt={:#X}, ref_cnt={:#X}", depth_entry.format, depth_it->ref_cnt); - } else { - zbc_depths.push_back(depth_entry); - LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#X}, index={:#X}", - depth_entry.format, zbc_depths.size() - 1); + break; } + + if (zbc_used_depth_entries >= zbc_table_size) { + LOG_WARNING(Service_NVDRV, "ZBCSetTable: depth table is full, fmt={:#X}", + depth_entry.format); + return NvResult::InsufficientMemory; + } + + zbc_depths[zbc_used_depth_entries] = depth_entry; + LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#X}, index={:#X}", + depth_entry.format, zbc_used_depth_entries); + ++zbc_used_depth_entries; + break; } } @@ -329,35 +349,34 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) { std::scoped_lock lk(zbc_mutex); + if (params.type == 0) { + params.index_size = zbc_table_size; + return NvResult::Success; + } + + if (params.index_size >= zbc_table_size) { + LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid index {:#X}", params.index_size); + return NvResult::BadParameter; + } + switch (static_cast(params.type)) { case ZBCTypes::color: { - if (params.index_size >= zbc_colors.size()) { - LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid color index {:#X}", params.index_size); - return NvResult::BadParameter; - } - const auto& colors = zbc_colors[params.index_size]; std::copy_n(colors.color_ds.begin(), colors.color_ds.size(), std::begin(params.color_ds)); std::copy_n(colors.color_l2.begin(), colors.color_l2.size(), std::begin(params.color_l2)); params.depth = 0; params.ref_cnt = colors.ref_cnt; params.format = colors.format; - params.index_size = static_cast(zbc_colors.size()); break; } case ZBCTypes::depth: { - if (params.index_size >= zbc_depths.size()) { - LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid depth index {:#X}", params.index_size); - return NvResult::BadParameter; - } - const auto& depth_entry = zbc_depths[params.index_size]; std::fill(std::begin(params.color_ds), std::end(params.color_ds), 0); std::fill(std::begin(params.color_l2), std::end(params.color_l2), 0); params.depth = depth_entry.depth; params.ref_cnt = depth_entry.ref_cnt; params.format = depth_entry.format; - params.index_size = static_cast(zbc_depths.size()); + break; } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 80b7abd6a1..8663ba43d7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include "common/common_funcs.h" #include "common/common_types.h" @@ -212,9 +212,13 @@ private: Kernel::KEvent* unknown_event; // ZBC Tables + static constexpr u32 zbc_table_size = 15u; + std::mutex zbc_mutex{}; - std::vector zbc_colors{}; - std::vector zbc_depths{}; + std::array zbc_colors{}; + std::array zbc_depths{}; + u32 zbc_used_color_entries{}; + u32 zbc_used_depth_entries{}; const u32 supported_types = 2u; }; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 30c145e545..a8d87f8850 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -20,33 +20,23 @@ BufferQueueCore::~BufferQueueCore() = default; void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) { std::lock_guard lk(buffer_history_mutex); - auto it = buffer_history_map.find(frame_number); - if (it != buffer_history_map.end()) { - it->second.state = state; - return; - } - - buffer_history_map.emplace(frame_number, BufferHistoryInfo{ + buffer_history_pos = (buffer_history_pos + 1) % BUFFER_HISTORY_SIZE; + buffer_history[buffer_history_pos] = BufferHistoryInfo{ frame_number, queue_time, presentation_time, state - }); - buffer_history_order.push_back(frame_number); - - if (buffer_history_order.size() > BUFFER_HISTORY_SIZE) { - u64 oldest_frame = buffer_history_order.front(); - buffer_history_order.pop_front(); - buffer_history_map.erase(oldest_frame); - } + }; } void BufferQueueCore::UpdateHistory(u64 frame_number, BufferState state) { std::lock_guard lk(buffer_history_mutex); - auto it = buffer_history_map.find(frame_number); - if (it != buffer_history_map.end()) { - it->second.state = state; + for (auto& entry : buffer_history) { + if (entry.frame_number == frame_number) { + entry.state = state; + return; + } } } diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index 86ca19dad3..2978842a10 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -9,14 +9,13 @@ #pragma once +#include #include -#include #include #include #include #include #include -#include #include #include "core/hle/service/nvnflinger/buffer_item.h" @@ -28,12 +27,15 @@ namespace Service::android { +#pragma pack(push, 1) struct BufferHistoryInfo { - u64 frame_number{}; - s64 queue_time{}; - s64 presentation_time{}; - BufferState state{}; + u64 frame_number; + s64 queue_time; + s64 presentation_time; + BufferState state; }; +#pragma pack(pop) +static_assert(sizeof(BufferHistoryInfo) == 0x1C, "BufferHistoryInfo must be 28 bytes"); class IConsumerListener; class IProducerListener; @@ -88,9 +90,9 @@ private: bool buffer_has_been_queued{}; u64 frame_counter{}; - std::unordered_map buffer_history_map{}; + std::array buffer_history{}; + u32 buffer_history_pos{BUFFER_HISTORY_SIZE - 1}; mutable std::mutex buffer_history_mutex{}; - std::deque buffer_history_order; u32 transform_hint{}; bool is_allocating{}; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index c3eb2796e3..3399e3a9c6 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -507,6 +507,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, sticky_transform = sticky_transform_; + const bool track_history = Settings::values.enable_buffer_history.GetValue(); + if (core->queue.empty()) { core->queue.push_back(item); listener_available = core->consumer_listener; @@ -514,7 +516,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, auto front = core->queue.begin(); if (front->is_droppable && core->StillTracking(*front)) { slots[front->slot].buffer_state = BufferState::Free; - if (Settings::values.enable_buffer_history.GetValue()) { + if (track_history) { core->UpdateHistory(front->frame_number, BufferState::Free); } slots[front->slot].frame_number = 0; @@ -529,7 +531,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, } } - if (Settings::values.enable_buffer_history.GetValue()) { + if (track_history) { core->PushHistory(core->frame_counter, slots[slot].queue_time, slots[slot].presentation_time, BufferState::Queued); } @@ -902,26 +904,31 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, const s32 request = parcel_in.Read(); if (request <= 0) { - parcel_out.Write(Status::BadValue); + status = Status::BadValue; parcel_out.Write(0); break; } - std::vector snapshot; + constexpr u32 history_size = BufferQueueCore::BUFFER_HISTORY_SIZE; + std::array snapshot{}; + s32 count{}; { std::scoped_lock lk(core->buffer_history_mutex); - for (auto& [frame, info] : core->buffer_history_map) { - snapshot.push_back(info); + + const u32 newest = core->buffer_history_pos; + for (u32 i = 0; i < history_size; ++i) { + const auto& entry = core->buffer_history[(newest + history_size - i) % history_size]; + if (entry.frame_number == 0) { + break; + } + + snapshot[count] = entry; + ++count; } } - std::sort(snapshot.begin(), snapshot.end(), [](auto& a, auto& b){ - return a.frame_number > b.frame_number; - }); - - const s32 limit = std::min(request, (s32)snapshot.size()); - parcel_out.Write(Status::NoError); + const s32 limit = (std::min)(request, count); parcel_out.Write(limit); for (s32 i = 0; i < limit; ++i) { parcel_out.Write(snapshot[i]);