Merge pull request #3267 from ReinUsesLisp/remove-maxwell-debugger
yuzu: Remove Maxwell debugger
This commit is contained in:
		@@ -46,7 +46,6 @@
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/telemetry_session.h"
 | 
			
		||||
#include "core/tools/freezer.h"
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/renderer_base.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
@@ -341,7 +340,6 @@ struct System::Impl {
 | 
			
		||||
    std::unique_ptr<Loader::AppLoader> app_loader;
 | 
			
		||||
    std::unique_ptr<VideoCore::RendererBase> renderer;
 | 
			
		||||
    std::unique_ptr<Tegra::GPU> gpu_core;
 | 
			
		||||
    std::shared_ptr<Tegra::DebugContext> debug_context;
 | 
			
		||||
    std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
 | 
			
		||||
    Memory::Memory memory;
 | 
			
		||||
    CpuCoreManager cpu_core_manager;
 | 
			
		||||
@@ -580,14 +578,6 @@ Loader::AppLoader& System::GetAppLoader() const {
 | 
			
		||||
    return *impl->app_loader;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
 | 
			
		||||
    impl->debug_context = std::move(context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Tegra::DebugContext* System::GetGPUDebugContext() const {
 | 
			
		||||
    return impl->debug_context.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
 | 
			
		||||
    impl->virtual_filesystem = std::move(vfs);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -307,10 +307,6 @@ public:
 | 
			
		||||
    Service::SM::ServiceManager& ServiceManager();
 | 
			
		||||
    const Service::SM::ServiceManager& ServiceManager() const;
 | 
			
		||||
 | 
			
		||||
    void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
 | 
			
		||||
 | 
			
		||||
    Tegra::DebugContext* GetGPUDebugContext() const;
 | 
			
		||||
 | 
			
		||||
    void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,6 @@ add_library(video_core STATIC
 | 
			
		||||
    buffer_cache/map_interval.h
 | 
			
		||||
    dma_pusher.cpp
 | 
			
		||||
    dma_pusher.h
 | 
			
		||||
    debug_utils/debug_utils.cpp
 | 
			
		||||
    debug_utils/debug_utils.h
 | 
			
		||||
    engines/const_buffer_engine_interface.h
 | 
			
		||||
    engines/const_buffer_info.h
 | 
			
		||||
    engines/engine_upload.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
namespace Tegra {
 | 
			
		||||
 | 
			
		||||
void DebugContext::DoOnEvent(Event event, void* data) {
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock lock{breakpoint_mutex};
 | 
			
		||||
 | 
			
		||||
        // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
 | 
			
		||||
        // show on debug widgets
 | 
			
		||||
 | 
			
		||||
        // TODO: Should stop the CPU thread here once we multithread emulation.
 | 
			
		||||
 | 
			
		||||
        active_breakpoint = event;
 | 
			
		||||
        at_breakpoint = true;
 | 
			
		||||
 | 
			
		||||
        // Tell all observers that we hit a breakpoint
 | 
			
		||||
        for (auto& breakpoint_observer : breakpoint_observers) {
 | 
			
		||||
            breakpoint_observer->OnMaxwellBreakPointHit(event, data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Wait until another thread tells us to Resume()
 | 
			
		||||
        resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugContext::Resume() {
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard lock{breakpoint_mutex};
 | 
			
		||||
 | 
			
		||||
        // Tell all observers that we are about to resume
 | 
			
		||||
        for (auto& breakpoint_observer : breakpoint_observers) {
 | 
			
		||||
            breakpoint_observer->OnMaxwellResume();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Resume the waiting thread (i.e. OnEvent())
 | 
			
		||||
        at_breakpoint = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resume_from_breakpoint.notify_one();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Tegra
 | 
			
		||||
@@ -1,157 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
namespace Tegra {
 | 
			
		||||
 | 
			
		||||
class DebugContext {
 | 
			
		||||
public:
 | 
			
		||||
    enum class Event {
 | 
			
		||||
        FirstEvent = 0,
 | 
			
		||||
 | 
			
		||||
        MaxwellCommandLoaded = FirstEvent,
 | 
			
		||||
        MaxwellCommandProcessed,
 | 
			
		||||
        IncomingPrimitiveBatch,
 | 
			
		||||
        FinishedPrimitiveBatch,
 | 
			
		||||
 | 
			
		||||
        NumEvents
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Inherit from this class to be notified of events registered to some debug context.
 | 
			
		||||
     * Most importantly this is used for our debugger GUI.
 | 
			
		||||
     *
 | 
			
		||||
     * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
 | 
			
		||||
     * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
 | 
			
		||||
     * access
 | 
			
		||||
     * @todo Evaluate an alternative interface, in which there is only one managing observer and
 | 
			
		||||
     * multiple child observers running (by design) on the same thread.
 | 
			
		||||
     */
 | 
			
		||||
    class BreakPointObserver {
 | 
			
		||||
    public:
 | 
			
		||||
        /// Constructs the object such that it observes events of the given DebugContext.
 | 
			
		||||
        explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
 | 
			
		||||
            : context_weak(debug_context) {
 | 
			
		||||
            std::unique_lock lock{debug_context->breakpoint_mutex};
 | 
			
		||||
            debug_context->breakpoint_observers.push_back(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtual ~BreakPointObserver() {
 | 
			
		||||
            auto context = context_weak.lock();
 | 
			
		||||
            if (context) {
 | 
			
		||||
                {
 | 
			
		||||
                    std::unique_lock lock{context->breakpoint_mutex};
 | 
			
		||||
                    context->breakpoint_observers.remove(this);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // If we are the last observer to be destroyed, tell the debugger context that
 | 
			
		||||
                // it is free to continue. In particular, this is required for a proper yuzu
 | 
			
		||||
                // shutdown, when the emulation thread is waiting at a breakpoint.
 | 
			
		||||
                if (context->breakpoint_observers.empty())
 | 
			
		||||
                    context->Resume();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Action to perform when a breakpoint was reached.
 | 
			
		||||
         * @param event Type of event which triggered the breakpoint
 | 
			
		||||
         * @param data Optional data pointer (if unused, this is a nullptr)
 | 
			
		||||
         * @note This function will perform nothing unless it is overridden in the child class.
 | 
			
		||||
         */
 | 
			
		||||
        virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Action to perform when emulation is resumed from a breakpoint.
 | 
			
		||||
         * @note This function will perform nothing unless it is overridden in the child class.
 | 
			
		||||
         */
 | 
			
		||||
        virtual void OnMaxwellResume() {}
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        /**
 | 
			
		||||
         * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
 | 
			
		||||
         * context_weak.lock(), always compare the result against nullptr.
 | 
			
		||||
         */
 | 
			
		||||
        std::weak_ptr<DebugContext> context_weak;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Simple structure defining a breakpoint state
 | 
			
		||||
     */
 | 
			
		||||
    struct BreakPoint {
 | 
			
		||||
        bool enabled = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Static constructor used to create a shared_ptr of a DebugContext.
 | 
			
		||||
     */
 | 
			
		||||
    static std::shared_ptr<DebugContext> Construct() {
 | 
			
		||||
        return std::shared_ptr<DebugContext>(new DebugContext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used by the emulation core when a given event has happened. If a breakpoint has been set
 | 
			
		||||
     * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
 | 
			
		||||
     * The current thread then is halted until Resume() is called from another thread (or until
 | 
			
		||||
     * emulation is stopped).
 | 
			
		||||
     * @param event Event which has happened
 | 
			
		||||
     * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
 | 
			
		||||
     * Resume() is called.
 | 
			
		||||
     */
 | 
			
		||||
    void OnEvent(Event event, void* data) {
 | 
			
		||||
        // This check is left in the header to allow the compiler to inline it.
 | 
			
		||||
        if (!breakpoints[(int)event].enabled)
 | 
			
		||||
            return;
 | 
			
		||||
        // For the rest of event handling, call a separate function.
 | 
			
		||||
        DoOnEvent(event, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void DoOnEvent(Event event, void* data);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resume from the current breakpoint.
 | 
			
		||||
     * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
 | 
			
		||||
     * Calling from any other thread is safe.
 | 
			
		||||
     */
 | 
			
		||||
    void Resume();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete all set breakpoints and resume emulation.
 | 
			
		||||
     */
 | 
			
		||||
    void ClearBreakpoints() {
 | 
			
		||||
        for (auto& bp : breakpoints) {
 | 
			
		||||
            bp.enabled = false;
 | 
			
		||||
        }
 | 
			
		||||
        Resume();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: Evaluate if access to these members should be hidden behind a public interface.
 | 
			
		||||
    std::array<BreakPoint, static_cast<int>(Event::NumEvents)> breakpoints;
 | 
			
		||||
    Event active_breakpoint{};
 | 
			
		||||
    bool at_breakpoint = false;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    /**
 | 
			
		||||
     * Private default constructor to make sure people always construct this through Construct()
 | 
			
		||||
     * instead.
 | 
			
		||||
     */
 | 
			
		||||
    DebugContext() = default;
 | 
			
		||||
 | 
			
		||||
    /// Mutex protecting current breakpoint state and the observer list.
 | 
			
		||||
    std::mutex breakpoint_mutex;
 | 
			
		||||
 | 
			
		||||
    /// Used by OnEvent to wait for resumption.
 | 
			
		||||
    std::condition_variable resume_from_breakpoint;
 | 
			
		||||
 | 
			
		||||
    /// List of registered observers
 | 
			
		||||
    std::list<BreakPointObserver*> breakpoint_observers;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Tegra
 | 
			
		||||
@@ -7,7 +7,6 @@
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/engines/maxwell_3d.h"
 | 
			
		||||
#include "video_core/engines/shader_type.h"
 | 
			
		||||
#include "video_core/memory_manager.h"
 | 
			
		||||
@@ -273,8 +272,6 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
 | 
			
		||||
    auto debug_context = system.GetGPUDebugContext();
 | 
			
		||||
 | 
			
		||||
    const u32 method = method_call.method;
 | 
			
		||||
 | 
			
		||||
    if (method == cb_data_state.current) {
 | 
			
		||||
@@ -315,10 +312,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
 | 
			
		||||
    ASSERT_MSG(method < Regs::NUM_REGS,
 | 
			
		||||
               "Invalid Maxwell3D register, increase the size of the Regs structure");
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (regs.reg_array[method] != method_call.argument) {
 | 
			
		||||
        regs.reg_array[method] = method_call.argument;
 | 
			
		||||
        const std::size_t dirty_reg = dirty_pointers[method];
 | 
			
		||||
@@ -424,10 +417,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
 | 
			
		||||
@@ -485,12 +474,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
 | 
			
		||||
    ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
 | 
			
		||||
    ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
 | 
			
		||||
 | 
			
		||||
    auto debug_context = system.GetGPUDebugContext();
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Both instance configuration registers can not be set at the same time.
 | 
			
		||||
    ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
 | 
			
		||||
               "Illegal combination of instancing parameters");
 | 
			
		||||
@@ -500,10 +483,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
 | 
			
		||||
        rasterizer.DrawMultiBatch(is_indexed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
 | 
			
		||||
    // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
 | 
			
		||||
    // it's possible that it is incorrect and that there is some other register used to specify the
 | 
			
		||||
@@ -650,12 +629,6 @@ void Maxwell3D::DrawArrays() {
 | 
			
		||||
              regs.vertex_buffer.count);
 | 
			
		||||
    ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
 | 
			
		||||
 | 
			
		||||
    auto debug_context = system.GetGPUDebugContext();
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Both instance configuration registers can not be set at the same time.
 | 
			
		||||
    ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
 | 
			
		||||
               "Illegal combination of instancing parameters");
 | 
			
		||||
@@ -673,10 +646,6 @@ void Maxwell3D::DrawArrays() {
 | 
			
		||||
        rasterizer.DrawBatch(is_indexed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (debug_context) {
 | 
			
		||||
        debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
 | 
			
		||||
    // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
 | 
			
		||||
    // it's possible that it is incorrect and that there is some other register used to specify the
 | 
			
		||||
 
 | 
			
		||||
@@ -78,11 +78,6 @@ add_executable(yuzu
 | 
			
		||||
    configuration/configure_web.cpp
 | 
			
		||||
    configuration/configure_web.h
 | 
			
		||||
    configuration/configure_web.ui
 | 
			
		||||
    debugger/graphics/graphics_breakpoint_observer.cpp
 | 
			
		||||
    debugger/graphics/graphics_breakpoint_observer.h
 | 
			
		||||
    debugger/graphics/graphics_breakpoints.cpp
 | 
			
		||||
    debugger/graphics/graphics_breakpoints.h
 | 
			
		||||
    debugger/graphics/graphics_breakpoints_p.h
 | 
			
		||||
    debugger/console.cpp
 | 
			
		||||
    debugger/console.h
 | 
			
		||||
    debugger/profiler.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QMetaType>
 | 
			
		||||
#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
 | 
			
		||||
 | 
			
		||||
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context,
 | 
			
		||||
                                               const QString& title, QWidget* parent)
 | 
			
		||||
    : QDockWidget(title, parent), BreakPointObserver(debug_context) {
 | 
			
		||||
    qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
 | 
			
		||||
 | 
			
		||||
    connect(this, &BreakPointObserverDock::Resumed, this, &BreakPointObserverDock::OnResumed);
 | 
			
		||||
 | 
			
		||||
    // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
 | 
			
		||||
    //       care of delaying its handling to the GUI thread.
 | 
			
		||||
    connect(this, &BreakPointObserverDock::BreakPointHit, this,
 | 
			
		||||
            &BreakPointObserverDock::OnBreakPointHit, Qt::BlockingQueuedConnection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) {
 | 
			
		||||
    emit BreakPointHit(event, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointObserverDock::OnMaxwellResume() {
 | 
			
		||||
    emit Resumed();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Utility class which forwards calls to OnMaxwellBreakPointHit and OnMaxwellResume to public slots.
 | 
			
		||||
 * This is because the Maxwell breakpoint callbacks are called from a non-GUI thread, while
 | 
			
		||||
 * the widget usually wants to perform reactions in the GUI thread.
 | 
			
		||||
 */
 | 
			
		||||
class BreakPointObserverDock : public QDockWidget,
 | 
			
		||||
                               protected Tegra::DebugContext::BreakPointObserver {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context, const QString& title,
 | 
			
		||||
                           QWidget* parent = nullptr);
 | 
			
		||||
 | 
			
		||||
    void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
 | 
			
		||||
    void OnMaxwellResume() override;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void Resumed();
 | 
			
		||||
    void BreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
 | 
			
		||||
    virtual void OnResumed() = 0;
 | 
			
		||||
};
 | 
			
		||||
@@ -1,221 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QLabel>
 | 
			
		||||
#include <QMetaType>
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
 | 
			
		||||
#include "yuzu/debugger/graphics/graphics_breakpoints_p.h"
 | 
			
		||||
 | 
			
		||||
BreakPointModel::BreakPointModel(std::shared_ptr<Tegra::DebugContext> debug_context,
 | 
			
		||||
                                 QObject* parent)
 | 
			
		||||
    : QAbstractListModel(parent), context_weak(debug_context),
 | 
			
		||||
      at_breakpoint(debug_context->at_breakpoint),
 | 
			
		||||
      active_breakpoint(debug_context->active_breakpoint) {}
 | 
			
		||||
 | 
			
		||||
int BreakPointModel::columnCount(const QModelIndex& parent) const {
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BreakPointModel::rowCount(const QModelIndex& parent) const {
 | 
			
		||||
    return static_cast<int>(Tegra::DebugContext::Event::NumEvents);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
 | 
			
		||||
    const auto event = static_cast<Tegra::DebugContext::Event>(index.row());
 | 
			
		||||
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::DisplayRole: {
 | 
			
		||||
        if (index.column() == 0) {
 | 
			
		||||
            return DebugContextEventToString(event);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Qt::CheckStateRole: {
 | 
			
		||||
        if (index.column() == 0)
 | 
			
		||||
            return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Qt::BackgroundRole: {
 | 
			
		||||
        if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
 | 
			
		||||
            return QBrush(QColor(0xE0, 0xE0, 0x10));
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case Role_IsEnabled: {
 | 
			
		||||
        auto context = context_weak.lock();
 | 
			
		||||
        return context && context->breakpoints[(int)event].enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return QVariant();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
 | 
			
		||||
    if (!index.isValid())
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    Qt::ItemFlags flags = Qt::ItemIsEnabled;
 | 
			
		||||
    if (index.column() == 0)
 | 
			
		||||
        flags |= Qt::ItemIsUserCheckable;
 | 
			
		||||
    return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
 | 
			
		||||
    const auto event = static_cast<Tegra::DebugContext::Event>(index.row());
 | 
			
		||||
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Qt::CheckStateRole: {
 | 
			
		||||
        if (index.column() != 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        auto context = context_weak.lock();
 | 
			
		||||
        if (!context)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        context->breakpoints[(int)event].enabled = value == Qt::Checked;
 | 
			
		||||
        QModelIndex changed_index = createIndex(index.row(), 0);
 | 
			
		||||
        emit dataChanged(changed_index, changed_index);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointModel::OnBreakPointHit(Tegra::DebugContext::Event event) {
 | 
			
		||||
    auto context = context_weak.lock();
 | 
			
		||||
    if (!context)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    active_breakpoint = context->active_breakpoint;
 | 
			
		||||
    at_breakpoint = context->at_breakpoint;
 | 
			
		||||
    emit dataChanged(createIndex(static_cast<int>(event), 0),
 | 
			
		||||
                     createIndex(static_cast<int>(event), 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BreakPointModel::OnResumed() {
 | 
			
		||||
    auto context = context_weak.lock();
 | 
			
		||||
    if (!context)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    at_breakpoint = context->at_breakpoint;
 | 
			
		||||
    emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
 | 
			
		||||
                     createIndex(static_cast<int>(active_breakpoint), 0));
 | 
			
		||||
    active_breakpoint = context->active_breakpoint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) {
 | 
			
		||||
    switch (event) {
 | 
			
		||||
    case Tegra::DebugContext::Event::MaxwellCommandLoaded:
 | 
			
		||||
        return tr("Maxwell command loaded");
 | 
			
		||||
    case Tegra::DebugContext::Event::MaxwellCommandProcessed:
 | 
			
		||||
        return tr("Maxwell command processed");
 | 
			
		||||
    case Tegra::DebugContext::Event::IncomingPrimitiveBatch:
 | 
			
		||||
        return tr("Incoming primitive batch");
 | 
			
		||||
    case Tegra::DebugContext::Event::FinishedPrimitiveBatch:
 | 
			
		||||
        return tr("Finished primitive batch");
 | 
			
		||||
    case Tegra::DebugContext::Event::NumEvents:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return tr("Unknown debug context event");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
 | 
			
		||||
    std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
 | 
			
		||||
    : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
 | 
			
		||||
                                                          debug_context) {
 | 
			
		||||
    setObjectName(QStringLiteral("TegraBreakPointsWidget"));
 | 
			
		||||
 | 
			
		||||
    status_text = new QLabel(tr("Emulation running"));
 | 
			
		||||
    resume_button = new QPushButton(tr("Resume"));
 | 
			
		||||
    resume_button->setEnabled(false);
 | 
			
		||||
 | 
			
		||||
    breakpoint_model = new BreakPointModel(debug_context, this);
 | 
			
		||||
    breakpoint_list = new QTreeView;
 | 
			
		||||
    breakpoint_list->setRootIsDecorated(false);
 | 
			
		||||
    breakpoint_list->setHeaderHidden(true);
 | 
			
		||||
    breakpoint_list->setModel(breakpoint_model);
 | 
			
		||||
 | 
			
		||||
    qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
 | 
			
		||||
 | 
			
		||||
    connect(breakpoint_list, &QTreeView::doubleClicked, this,
 | 
			
		||||
            &GraphicsBreakPointsWidget::OnItemDoubleClicked);
 | 
			
		||||
 | 
			
		||||
    connect(resume_button, &QPushButton::clicked, this,
 | 
			
		||||
            &GraphicsBreakPointsWidget::OnResumeRequested);
 | 
			
		||||
 | 
			
		||||
    connect(this, &GraphicsBreakPointsWidget::BreakPointHit, this,
 | 
			
		||||
            &GraphicsBreakPointsWidget::OnBreakPointHit, Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, &GraphicsBreakPointsWidget::Resumed, this, &GraphicsBreakPointsWidget::OnResumed);
 | 
			
		||||
 | 
			
		||||
    connect(this, &GraphicsBreakPointsWidget::BreakPointHit, breakpoint_model,
 | 
			
		||||
            &BreakPointModel::OnBreakPointHit, Qt::BlockingQueuedConnection);
 | 
			
		||||
    connect(this, &GraphicsBreakPointsWidget::Resumed, breakpoint_model,
 | 
			
		||||
            &BreakPointModel::OnResumed);
 | 
			
		||||
 | 
			
		||||
    connect(this, &GraphicsBreakPointsWidget::BreakPointsChanged,
 | 
			
		||||
            [this](const QModelIndex& top_left, const QModelIndex& bottom_right) {
 | 
			
		||||
                breakpoint_model->dataChanged(top_left, bottom_right);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
    QWidget* main_widget = new QWidget;
 | 
			
		||||
    auto main_layout = new QVBoxLayout;
 | 
			
		||||
    {
 | 
			
		||||
        auto sub_layout = new QHBoxLayout;
 | 
			
		||||
        sub_layout->addWidget(status_text);
 | 
			
		||||
        sub_layout->addWidget(resume_button);
 | 
			
		||||
        main_layout->addLayout(sub_layout);
 | 
			
		||||
    }
 | 
			
		||||
    main_layout->addWidget(breakpoint_list);
 | 
			
		||||
    main_widget->setLayout(main_layout);
 | 
			
		||||
 | 
			
		||||
    setWidget(main_widget);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnMaxwellBreakPointHit(Event event, void* data) {
 | 
			
		||||
    // Process in GUI thread
 | 
			
		||||
    emit BreakPointHit(event, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
 | 
			
		||||
    status_text->setText(tr("Emulation halted at breakpoint"));
 | 
			
		||||
    resume_button->setEnabled(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnMaxwellResume() {
 | 
			
		||||
    // Process in GUI thread
 | 
			
		||||
    emit Resumed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumed() {
 | 
			
		||||
    status_text->setText(tr("Emulation running"));
 | 
			
		||||
    resume_button->setEnabled(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnResumeRequested() {
 | 
			
		||||
    if (auto context = context_weak.lock())
 | 
			
		||||
        context->Resume();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
 | 
			
		||||
    if (!index.isValid())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0);
 | 
			
		||||
    QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole);
 | 
			
		||||
    QVariant new_state = Qt::Unchecked;
 | 
			
		||||
    if (enabled == Qt::Unchecked)
 | 
			
		||||
        new_state = Qt::Checked;
 | 
			
		||||
    breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
class QLabel;
 | 
			
		||||
class QPushButton;
 | 
			
		||||
class QTreeView;
 | 
			
		||||
 | 
			
		||||
class BreakPointModel;
 | 
			
		||||
 | 
			
		||||
class GraphicsBreakPointsWidget : public QDockWidget, Tegra::DebugContext::BreakPointObserver {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
    using Event = Tegra::DebugContext::Event;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit GraphicsBreakPointsWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
 | 
			
		||||
                                       QWidget* parent = nullptr);
 | 
			
		||||
 | 
			
		||||
    void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
 | 
			
		||||
    void OnMaxwellResume() override;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void Resumed();
 | 
			
		||||
    void BreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
			
		||||
    void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
			
		||||
    void OnItemDoubleClicked(const QModelIndex&);
 | 
			
		||||
    void OnResumeRequested();
 | 
			
		||||
    void OnResumed();
 | 
			
		||||
 | 
			
		||||
    QLabel* status_text;
 | 
			
		||||
    QPushButton* resume_button;
 | 
			
		||||
 | 
			
		||||
    BreakPointModel* breakpoint_model;
 | 
			
		||||
    QTreeView* breakpoint_list;
 | 
			
		||||
};
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QAbstractListModel>
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
 | 
			
		||||
class BreakPointModel : public QAbstractListModel {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    enum {
 | 
			
		||||
        Role_IsEnabled = Qt::UserRole,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    BreakPointModel(std::shared_ptr<Tegra::DebugContext> context, QObject* parent);
 | 
			
		||||
 | 
			
		||||
    int columnCount(const QModelIndex& parent = QModelIndex()) const override;
 | 
			
		||||
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
 | 
			
		||||
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
 | 
			
		||||
    Qt::ItemFlags flags(const QModelIndex& index) const override;
 | 
			
		||||
 | 
			
		||||
    bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
 | 
			
		||||
 | 
			
		||||
    void OnBreakPointHit(Tegra::DebugContext::Event event);
 | 
			
		||||
    void OnResumed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static QString DebugContextEventToString(Tegra::DebugContext::Event event);
 | 
			
		||||
 | 
			
		||||
    std::weak_ptr<Tegra::DebugContext> context_weak;
 | 
			
		||||
    bool at_breakpoint;
 | 
			
		||||
    Tegra::DebugContext::Event active_breakpoint;
 | 
			
		||||
};
 | 
			
		||||
@@ -93,7 +93,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 | 
			
		||||
#include "core/perf_stats.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/telemetry_session.h"
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "yuzu/about_dialog.h"
 | 
			
		||||
#include "yuzu/bootmanager.h"
 | 
			
		||||
#include "yuzu/compatdb.h"
 | 
			
		||||
@@ -101,7 +100,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 | 
			
		||||
#include "yuzu/configuration/config.h"
 | 
			
		||||
#include "yuzu/configuration/configure_dialog.h"
 | 
			
		||||
#include "yuzu/debugger/console.h"
 | 
			
		||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
 | 
			
		||||
#include "yuzu/debugger/profiler.h"
 | 
			
		||||
#include "yuzu/debugger/wait_tree.h"
 | 
			
		||||
#include "yuzu/discord.h"
 | 
			
		||||
@@ -187,8 +185,6 @@ GMainWindow::GMainWindow()
 | 
			
		||||
      provider(std::make_unique<FileSys::ManualContentProvider>()) {
 | 
			
		||||
    InitializeLogging();
 | 
			
		||||
 | 
			
		||||
    debug_context = Tegra::DebugContext::Construct();
 | 
			
		||||
 | 
			
		||||
    setAcceptDrops(true);
 | 
			
		||||
    ui.setupUi(this);
 | 
			
		||||
    statusBar()->hide();
 | 
			
		||||
@@ -495,11 +491,6 @@ void GMainWindow::InitializeDebugWidgets() {
 | 
			
		||||
    debug_menu->addAction(microProfileDialog->toggleViewAction());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
 | 
			
		||||
    addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
 | 
			
		||||
    graphicsBreakpointsWidget->hide();
 | 
			
		||||
    debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
 | 
			
		||||
 | 
			
		||||
    waitTreeWidget = new WaitTreeWidget(this);
 | 
			
		||||
    addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
 | 
			
		||||
    waitTreeWidget->hide();
 | 
			
		||||
@@ -869,8 +860,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
 | 
			
		||||
    Core::System& system{Core::System::GetInstance()};
 | 
			
		||||
    system.SetFilesystem(vfs);
 | 
			
		||||
 | 
			
		||||
    system.SetGPUDebugContext(debug_context);
 | 
			
		||||
 | 
			
		||||
    system.SetAppletFrontendSet({
 | 
			
		||||
        nullptr,                                     // Parental Controls
 | 
			
		||||
        std::make_unique<QtErrorDisplay>(*this),     //
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ class Config;
 | 
			
		||||
class EmuThread;
 | 
			
		||||
class GameList;
 | 
			
		||||
class GImageInfo;
 | 
			
		||||
class GraphicsBreakPointsWidget;
 | 
			
		||||
class GRenderWindow;
 | 
			
		||||
class LoadingScreen;
 | 
			
		||||
class MicroProfileDialog;
 | 
			
		||||
@@ -42,10 +41,6 @@ class ManualContentProvider;
 | 
			
		||||
class VfsFilesystem;
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
namespace Tegra {
 | 
			
		||||
class DebugContext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum class EmulatedDirectoryTarget {
 | 
			
		||||
    NAND,
 | 
			
		||||
    SDMC,
 | 
			
		||||
@@ -223,8 +218,6 @@ private:
 | 
			
		||||
 | 
			
		||||
    Ui::MainWindow ui;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Tegra::DebugContext> debug_context;
 | 
			
		||||
 | 
			
		||||
    GRenderWindow* render_window;
 | 
			
		||||
    GameList* game_list;
 | 
			
		||||
    LoadingScreen* loading_screen;
 | 
			
		||||
@@ -255,7 +248,6 @@ private:
 | 
			
		||||
    // Debugger panes
 | 
			
		||||
    ProfilerWidget* profilerWidget;
 | 
			
		||||
    MicroProfileDialog* microProfileDialog;
 | 
			
		||||
    GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
 | 
			
		||||
    WaitTreeWidget* waitTreeWidget;
 | 
			
		||||
 | 
			
		||||
    QAction* actions_recent_files[max_recent_files_item];
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user