mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #273 from Subv/textures
GPU: Added code to unswizzle textures and ported the surface viewer from citra
This commit is contained in:
		
						commit
						e9315ace9f
					
				@ -13,6 +13,7 @@
 | 
				
			|||||||
#include "core/memory.h"
 | 
					#include "core/memory.h"
 | 
				
			||||||
#include "core/perf_stats.h"
 | 
					#include "core/perf_stats.h"
 | 
				
			||||||
#include "core/telemetry_session.h"
 | 
					#include "core/telemetry_session.h"
 | 
				
			||||||
 | 
					#include "video_core/debug_utils/debug_utils.h"
 | 
				
			||||||
#include "video_core/gpu.h"
 | 
					#include "video_core/gpu.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EmuWindow;
 | 
					class EmuWindow;
 | 
				
			||||||
@ -135,6 +136,14 @@ public:
 | 
				
			|||||||
        return *app_loader;
 | 
					        return *app_loader;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
 | 
				
			||||||
 | 
					        debug_context = std::move(context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
 | 
				
			||||||
 | 
					        return debug_context;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Initialize the emulated system.
 | 
					     * Initialize the emulated system.
 | 
				
			||||||
@ -154,6 +163,8 @@ private:
 | 
				
			|||||||
    std::unique_ptr<Kernel::Scheduler> scheduler;
 | 
					    std::unique_ptr<Kernel::Scheduler> scheduler;
 | 
				
			||||||
    std::unique_ptr<Tegra::GPU> gpu_core;
 | 
					    std::unique_ptr<Tegra::GPU> gpu_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<Tegra::DebugContext> debug_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Kernel::SharedPtr<Kernel::Process> current_process;
 | 
					    Kernel::SharedPtr<Kernel::Process> current_process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// When true, signals that a reschedule should happen
 | 
					    /// When true, signals that a reschedule should happen
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
add_library(video_core STATIC
 | 
					add_library(video_core STATIC
 | 
				
			||||||
    command_processor.cpp
 | 
					    command_processor.cpp
 | 
				
			||||||
    command_processor.h
 | 
					    command_processor.h
 | 
				
			||||||
 | 
					    debug_utils/debug_utils.cpp
 | 
				
			||||||
 | 
					    debug_utils/debug_utils.h
 | 
				
			||||||
    engines/fermi_2d.cpp
 | 
					    engines/fermi_2d.cpp
 | 
				
			||||||
    engines/fermi_2d.h
 | 
					    engines/fermi_2d.h
 | 
				
			||||||
    engines/maxwell_3d.cpp
 | 
					    engines/maxwell_3d.cpp
 | 
				
			||||||
@ -31,6 +33,9 @@ add_library(video_core STATIC
 | 
				
			|||||||
    renderer_opengl/gl_stream_buffer.h
 | 
					    renderer_opengl/gl_stream_buffer.h
 | 
				
			||||||
    renderer_opengl/renderer_opengl.cpp
 | 
					    renderer_opengl/renderer_opengl.cpp
 | 
				
			||||||
    renderer_opengl/renderer_opengl.h
 | 
					    renderer_opengl/renderer_opengl.h
 | 
				
			||||||
 | 
					    textures/decoders.cpp
 | 
				
			||||||
 | 
					    textures/decoders.h
 | 
				
			||||||
 | 
					    textures/texture.h
 | 
				
			||||||
    utils.h
 | 
					    utils.h
 | 
				
			||||||
    video_core.cpp
 | 
					    video_core.cpp
 | 
				
			||||||
    video_core.h
 | 
					    video_core.h
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										64
									
								
								src/video_core/debug_utils/debug_utils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/video_core/debug_utils/debug_utils.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					// Copyright 2014 Citra Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "common/bit_field.h"
 | 
				
			||||||
 | 
					#include "common/color.h"
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/file_util.h"
 | 
				
			||||||
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
 | 
					#include "common/math_util.h"
 | 
				
			||||||
 | 
					#include "common/vector_math.h"
 | 
				
			||||||
 | 
					#include "video_core/debug_utils/debug_utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DebugContext::DoOnEvent(Event event, void* data) {
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::unique_lock<std::mutex> 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<std::mutex> 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
 | 
				
			||||||
							
								
								
									
										163
									
								
								src/video_core/debug_utils/debug_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/video_core/debug_utils/debug_utils.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					// Copyright 2014 Citra Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <iterator>
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/vector_math.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					        BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
 | 
				
			||||||
 | 
					            : context_weak(debug_context) {
 | 
				
			||||||
 | 
					            std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
 | 
				
			||||||
 | 
					            debug_context->breakpoint_observers.push_back(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual ~BreakPointObserver() {
 | 
				
			||||||
 | 
					            auto context = context_weak.lock();
 | 
				
			||||||
 | 
					            if (context) {
 | 
				
			||||||
 | 
					                std::unique_lock<std::mutex> 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, (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
 | 
				
			||||||
@ -2,8 +2,13 @@
 | 
				
			|||||||
// Licensed under GPLv2 or any later version
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
#include "common/assert.h"
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "core/core.h"
 | 
				
			||||||
 | 
					#include "video_core/debug_utils/debug_utils.h"
 | 
				
			||||||
#include "video_core/engines/maxwell_3d.h"
 | 
					#include "video_core/engines/maxwell_3d.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/decoders.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Tegra {
 | 
					namespace Tegra {
 | 
				
			||||||
namespace Engines {
 | 
					namespace Engines {
 | 
				
			||||||
@ -46,6 +51,8 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
 | 
				
			|||||||
    ASSERT_MSG(method < Regs::NUM_REGS,
 | 
					    ASSERT_MSG(method < Regs::NUM_REGS,
 | 
				
			||||||
               "Invalid Maxwell3D register, increase the size of the Regs structure");
 | 
					               "Invalid Maxwell3D register, increase the size of the Regs structure");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // It is an error to write to a register other than the current macro's ARG register before it
 | 
					    // It is an error to write to a register other than the current macro's ARG register before it
 | 
				
			||||||
    // has finished execution.
 | 
					    // has finished execution.
 | 
				
			||||||
    if (executing_macro != 0) {
 | 
					    if (executing_macro != 0) {
 | 
				
			||||||
@ -72,6 +79,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (debug_context) {
 | 
				
			||||||
 | 
					        debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    regs.reg_array[method] = value;
 | 
					    regs.reg_array[method] = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
 | 
					#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
 | 
				
			||||||
@ -137,6 +148,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#undef MAXWELL3D_REG_INDEX
 | 
					#undef MAXWELL3D_REG_INDEX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (debug_context) {
 | 
				
			||||||
 | 
					        debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Maxwell3D::ProcessQueryGet() {
 | 
					void Maxwell3D::ProcessQueryGet() {
 | 
				
			||||||
@ -160,6 +175,15 @@ void Maxwell3D::ProcessQueryGet() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void Maxwell3D::DrawArrays() {
 | 
					void Maxwell3D::DrawArrays() {
 | 
				
			||||||
    LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
 | 
					    LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
 | 
				
			||||||
 | 
					    auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (debug_context) {
 | 
				
			||||||
 | 
					        debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (debug_context) {
 | 
				
			||||||
 | 
					        debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
 | 
					void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
 | 
				
			||||||
@ -270,5 +294,50 @@ void Maxwell3D::ProcessCBData(u32 value) {
 | 
				
			|||||||
    regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
 | 
					    regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<Texture::TICEntry> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) {
 | 
				
			||||||
 | 
					    std::vector<Texture::TICEntry> textures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)];
 | 
				
			||||||
 | 
					    auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index];
 | 
				
			||||||
 | 
					    ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GPUVAddr tic_base_address = regs.tic.TICAddress();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Offset into the texture constbuffer where the texture info begins.
 | 
				
			||||||
 | 
					    static constexpr size_t TextureInfoOffset = 0x20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
 | 
				
			||||||
 | 
					         current_texture < tex_info_buffer_end; current_texture += 4) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Texture::TextureHandle tex_info{
 | 
				
			||||||
 | 
					            Memory::Read32(memory_manager.PhysicalToVirtualAddress(current_texture))};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (tex_info.tic_id != 0 || tex_info.tsc_id != 0) {
 | 
				
			||||||
 | 
					            GPUVAddr tic_address_gpu =
 | 
				
			||||||
 | 
					                tic_base_address + tex_info.tic_id * sizeof(Texture::TICEntry);
 | 
				
			||||||
 | 
					            VAddr tic_address_cpu = memory_manager.PhysicalToVirtualAddress(tic_address_gpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Texture::TICEntry tic_entry;
 | 
				
			||||||
 | 
					            Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto r_type = tic_entry.r_type.Value();
 | 
				
			||||||
 | 
					            auto g_type = tic_entry.g_type.Value();
 | 
				
			||||||
 | 
					            auto b_type = tic_entry.b_type.Value();
 | 
				
			||||||
 | 
					            auto a_type = tic_entry.a_type.Value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // TODO(Subv): Different data types for separate components are not supported
 | 
				
			||||||
 | 
					            ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto format = tic_entry.format.Value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            textures.push_back(tic_entry);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return textures;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Engines
 | 
					} // namespace Engines
 | 
				
			||||||
} // namespace Tegra
 | 
					} // namespace Tegra
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
#include "common/common_funcs.h"
 | 
					#include "common/common_funcs.h"
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "video_core/memory_manager.h"
 | 
					#include "video_core/memory_manager.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Tegra {
 | 
					namespace Tegra {
 | 
				
			||||||
namespace Engines {
 | 
					namespace Engines {
 | 
				
			||||||
@ -21,12 +22,6 @@ public:
 | 
				
			|||||||
    explicit Maxwell3D(MemoryManager& memory_manager);
 | 
					    explicit Maxwell3D(MemoryManager& memory_manager);
 | 
				
			||||||
    ~Maxwell3D() = default;
 | 
					    ~Maxwell3D() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Write the value to the register identified by method.
 | 
					 | 
				
			||||||
    void WriteReg(u32 method, u32 value, u32 remaining_params);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Uploads the code for a GPU macro program associated with the specified entry.
 | 
					 | 
				
			||||||
    void SubmitMacroCode(u32 entry, std::vector<u32> code);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Register structure of the Maxwell3D engine.
 | 
					    /// Register structure of the Maxwell3D engine.
 | 
				
			||||||
    /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
 | 
					    /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
 | 
				
			||||||
    struct Regs {
 | 
					    struct Regs {
 | 
				
			||||||
@ -430,6 +425,15 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    State state{};
 | 
					    State state{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write the value to the register identified by method.
 | 
				
			||||||
 | 
					    void WriteReg(u32 method, u32 value, u32 remaining_params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Uploads the code for a GPU macro program associated with the specified entry.
 | 
				
			||||||
 | 
					    void SubmitMacroCode(u32 entry, std::vector<u32> code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a list of enabled textures for the specified shader stage.
 | 
				
			||||||
 | 
					    std::vector<Texture::TICEntry> GetStageTextures(Regs::ShaderStage stage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    MemoryManager& memory_manager;
 | 
					    MemoryManager& memory_manager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -18,4 +18,8 @@ GPU::GPU() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
GPU::~GPU() = default;
 | 
					GPU::~GPU() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
 | 
				
			||||||
 | 
					    return *maxwell_3d;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Tegra
 | 
					} // namespace Tegra
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Tegra {
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class RenderTargetFormat {
 | 
				
			||||||
 | 
					    RGBA8_UNORM = 0xD5,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DebugContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Struct describing framebuffer configuration
 | 
					 * Struct describing framebuffer configuration
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -66,6 +72,9 @@ public:
 | 
				
			|||||||
    /// Processes a command list stored at the specified address in GPU memory.
 | 
					    /// Processes a command list stored at the specified address in GPU memory.
 | 
				
			||||||
    void ProcessCommandList(GPUVAddr address, u32 size);
 | 
					    void ProcessCommandList(GPUVAddr address, u32 size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a reference to the Maxwell3D GPU engine.
 | 
				
			||||||
 | 
					    const Engines::Maxwell3D& Get3DEngine() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::unique_ptr<MemoryManager> memory_manager;
 | 
					    std::unique_ptr<MemoryManager> memory_manager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Engines::Maxwell3D& Maxwell3D() {
 | 
					    Engines::Maxwell3D& Maxwell3D() {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										105
									
								
								src/video_core/textures/decoders.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/video_core/textures/decoders.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/decoders.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					namespace Texture {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Calculates the offset of an (x, y) position within a swizzled texture.
 | 
				
			||||||
 | 
					 * Taken from the Tegra X1 TRM.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) {
 | 
				
			||||||
 | 
					    u32 image_width_in_gobs = image_width * bytes_per_pixel / 64;
 | 
				
			||||||
 | 
					    u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
 | 
				
			||||||
 | 
					                      (x * bytes_per_pixel / 64) * 512 * block_height +
 | 
				
			||||||
 | 
					                      (y % (8 * block_height) / 8) * 512;
 | 
				
			||||||
 | 
					    x *= bytes_per_pixel;
 | 
				
			||||||
 | 
					    u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
 | 
				
			||||||
 | 
					                  (y % 2) * 16 + (x % 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return address;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
 | 
				
			||||||
 | 
					                             u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
 | 
				
			||||||
 | 
					                             u32 block_height) {
 | 
				
			||||||
 | 
					    u8* data_ptrs[2];
 | 
				
			||||||
 | 
					    for (unsigned y = 0; y < height; ++y) {
 | 
				
			||||||
 | 
					        for (unsigned x = 0; x < width; ++x) {
 | 
				
			||||||
 | 
					            u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height);
 | 
				
			||||||
 | 
					            u32 pixel_index = (x + y * width) * out_bytes_per_pixel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
 | 
				
			||||||
 | 
					            data_ptrs[!unswizzle] = &unswizzled_data[pixel_index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u32 BytesPerPixel(TextureFormat format) {
 | 
				
			||||||
 | 
					    switch (format) {
 | 
				
			||||||
 | 
					    case TextureFormat::DXT1:
 | 
				
			||||||
 | 
					        // In this case a 'pixel' actually refers to a 4x4 tile.
 | 
				
			||||||
 | 
					        return 8;
 | 
				
			||||||
 | 
					    case TextureFormat::A8R8G8B8:
 | 
				
			||||||
 | 
					        return 4;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_MSG("Format not implemented");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height) {
 | 
				
			||||||
 | 
					    u8* data = Memory::GetPointer(address);
 | 
				
			||||||
 | 
					    u32 bytes_per_pixel = BytesPerPixel(format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static constexpr u32 DefaultBlockHeight = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (format) {
 | 
				
			||||||
 | 
					    case TextureFormat::DXT1:
 | 
				
			||||||
 | 
					        // In the DXT1 format, each 4x4 tile is swizzled instead of just individual pixel values.
 | 
				
			||||||
 | 
					        CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
 | 
				
			||||||
 | 
					                         unswizzled_data.data(), true, DefaultBlockHeight);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case TextureFormat::A8R8G8B8:
 | 
				
			||||||
 | 
					        CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
 | 
				
			||||||
 | 
					                         unswizzled_data.data(), true, DefaultBlockHeight);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_MSG("Format not implemented");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return unswizzled_data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
 | 
				
			||||||
 | 
					                              u32 height) {
 | 
				
			||||||
 | 
					    std::vector<u8> rgba_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO(Subv): Implement.
 | 
				
			||||||
 | 
					    switch (format) {
 | 
				
			||||||
 | 
					    case TextureFormat::DXT1:
 | 
				
			||||||
 | 
					    case TextureFormat::A8R8G8B8:
 | 
				
			||||||
 | 
					        // TODO(Subv): For the time being just forward the same data without any decoding.
 | 
				
			||||||
 | 
					        rgba_data = texture_data;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_MSG("Format not implemented");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return rgba_data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Texture
 | 
				
			||||||
 | 
					} // namespace Tegra
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/video_core/textures/decoders.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/video_core/textures/decoders.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					namespace Texture {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Unswizzles a swizzled texture without changing its format.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Decodes an unswizzled texture into a A8R8G8B8 texture.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
 | 
				
			||||||
 | 
					                              u32 height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Texture
 | 
				
			||||||
 | 
					} // namespace Tegra
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/video_core/textures/texture.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/video_core/textures/texture.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/bit_field.h"
 | 
				
			||||||
 | 
					#include "common/common_funcs.h"
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "video_core/memory_manager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					namespace Texture {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class TextureFormat : u32 {
 | 
				
			||||||
 | 
					    A8R8G8B8 = 8,
 | 
				
			||||||
 | 
					    DXT1 = 0x24,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					union TextureHandle {
 | 
				
			||||||
 | 
					    u32 raw;
 | 
				
			||||||
 | 
					    BitField<0, 20, u32> tic_id;
 | 
				
			||||||
 | 
					    BitField<20, 12, u32> tsc_id;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct TICEntry {
 | 
				
			||||||
 | 
					    union {
 | 
				
			||||||
 | 
					        u32 raw;
 | 
				
			||||||
 | 
					        BitField<0, 7, TextureFormat> format;
 | 
				
			||||||
 | 
					        BitField<7, 3, u32> r_type;
 | 
				
			||||||
 | 
					        BitField<10, 3, u32> g_type;
 | 
				
			||||||
 | 
					        BitField<13, 3, u32> b_type;
 | 
				
			||||||
 | 
					        BitField<16, 3, u32> a_type;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    u32 address_low;
 | 
				
			||||||
 | 
					    u16 address_high;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(6);
 | 
				
			||||||
 | 
					    u16 width_minus_1;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(2);
 | 
				
			||||||
 | 
					    u16 height_minus_1;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GPUVAddr Address() const {
 | 
				
			||||||
 | 
					        return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u32 Width() const {
 | 
				
			||||||
 | 
					        return width_minus_1 + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u32 Height() const {
 | 
				
			||||||
 | 
					        return height_minus_1 + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns the number of bytes per pixel of the input texture format.
 | 
				
			||||||
 | 
					u32 BytesPerPixel(TextureFormat format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Texture
 | 
				
			||||||
 | 
					} // namespace Tegra
 | 
				
			||||||
@ -23,6 +23,13 @@ add_executable(yuzu
 | 
				
			|||||||
    configuration/configure_input.h
 | 
					    configuration/configure_input.h
 | 
				
			||||||
    configuration/configure_system.cpp
 | 
					    configuration/configure_system.cpp
 | 
				
			||||||
    configuration/configure_system.h
 | 
					    configuration/configure_system.h
 | 
				
			||||||
 | 
					    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/graphics/graphics_surface.cpp
 | 
				
			||||||
 | 
					    debugger/graphics/graphics_surface.h
 | 
				
			||||||
    debugger/profiler.cpp
 | 
					    debugger/profiler.cpp
 | 
				
			||||||
    debugger/profiler.h
 | 
					    debugger/profiler.h
 | 
				
			||||||
    debugger/registers.cpp
 | 
					    debugger/registers.cpp
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					// 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, SIGNAL(Resumed()), this, SLOT(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, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this,
 | 
				
			||||||
 | 
					            SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) {
 | 
				
			||||||
 | 
					    emit BreakPointHit(event, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BreakPointObserverDock::OnMaxwellResume() {
 | 
				
			||||||
 | 
					    emit Resumed();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
					    virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
 | 
				
			||||||
 | 
					    virtual void OnResumed() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
					    void Resumed();
 | 
				
			||||||
 | 
					    void BreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										212
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,212 @@
 | 
				
			|||||||
 | 
					// 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) {
 | 
				
			||||||
 | 
					            static const std::map<Tegra::DebugContext::Event, QString> map = {
 | 
				
			||||||
 | 
					                {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")},
 | 
				
			||||||
 | 
					                {Tegra::DebugContext::Event::MaxwellCommandProcessed,
 | 
				
			||||||
 | 
					                 tr("Maxwell command processed")},
 | 
				
			||||||
 | 
					                {Tegra::DebugContext::Event::IncomingPrimitiveBatch,
 | 
				
			||||||
 | 
					                 tr("Incoming primitive batch")},
 | 
				
			||||||
 | 
					                {Tegra::DebugContext::Event::FinishedPrimitiveBatch,
 | 
				
			||||||
 | 
					                 tr("Finished primitive batch")},
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            DEBUG_ASSERT(map.size() == static_cast<size_t>(Tegra::DebugContext::Event::NumEvents));
 | 
				
			||||||
 | 
					            return (map.find(event) != map.end()) ? map.at(event) : QString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
 | 
				
			||||||
 | 
					    std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
 | 
				
			||||||
 | 
					    : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
 | 
				
			||||||
 | 
					                                                          debug_context) {
 | 
				
			||||||
 | 
					    setObjectName("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, SIGNAL(doubleClicked(const QModelIndex&)), this,
 | 
				
			||||||
 | 
					            SLOT(OnItemDoubleClicked(const QModelIndex&)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this,
 | 
				
			||||||
 | 
					            SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), breakpoint_model,
 | 
				
			||||||
 | 
					            SLOT(OnBreakPointHit(Tegra::DebugContext::Event)), Qt::BlockingQueuedConnection);
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
 | 
				
			||||||
 | 
					            breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public slots:
 | 
				
			||||||
 | 
					    void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
				
			||||||
 | 
					    void OnItemDoubleClicked(const QModelIndex&);
 | 
				
			||||||
 | 
					    void OnResumeRequested();
 | 
				
			||||||
 | 
					    void OnResumed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
					    void Resumed();
 | 
				
			||||||
 | 
					    void BreakPointHit(Tegra::DebugContext::Event event, void* data);
 | 
				
			||||||
 | 
					    void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    QLabel* status_text;
 | 
				
			||||||
 | 
					    QPushButton* resume_button;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BreakPointModel* breakpoint_model;
 | 
				
			||||||
 | 
					    QTreeView* breakpoint_list;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/yuzu/debugger/graphics/graphics_breakpoints_p.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public slots:
 | 
				
			||||||
 | 
					    void OnBreakPointHit(Tegra::DebugContext::Event event);
 | 
				
			||||||
 | 
					    void OnResumed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    std::weak_ptr<Tegra::DebugContext> context_weak;
 | 
				
			||||||
 | 
					    bool at_breakpoint;
 | 
				
			||||||
 | 
					    Tegra::DebugContext::Event active_breakpoint;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										452
									
								
								src/yuzu/debugger/graphics/graphics_surface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								src/yuzu/debugger/graphics/graphics_surface.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,452 @@
 | 
				
			|||||||
 | 
					// Copyright 2014 Citra Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QBoxLayout>
 | 
				
			||||||
 | 
					#include <QComboBox>
 | 
				
			||||||
 | 
					#include <QDebug>
 | 
				
			||||||
 | 
					#include <QFileDialog>
 | 
				
			||||||
 | 
					#include <QLabel>
 | 
				
			||||||
 | 
					#include <QMouseEvent>
 | 
				
			||||||
 | 
					#include <QPushButton>
 | 
				
			||||||
 | 
					#include <QScrollArea>
 | 
				
			||||||
 | 
					#include <QSpinBox>
 | 
				
			||||||
 | 
					#include "core/core.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/maxwell_3d.h"
 | 
				
			||||||
 | 
					#include "video_core/gpu.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/decoders.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					#include "video_core/utils.h"
 | 
				
			||||||
 | 
					#include "yuzu/debugger/graphics/graphics_surface.h"
 | 
				
			||||||
 | 
					#include "yuzu/util/spinbox.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static Tegra::Texture::TextureFormat ConvertToTextureFormat(
 | 
				
			||||||
 | 
					    Tegra::RenderTargetFormat render_target_format) {
 | 
				
			||||||
 | 
					    switch (render_target_format) {
 | 
				
			||||||
 | 
					    case Tegra::RenderTargetFormat::RGBA8_UNORM:
 | 
				
			||||||
 | 
					        return Tegra::Texture::TextureFormat::A8R8G8B8;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_MSG("Unimplemented RT format");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
 | 
				
			||||||
 | 
					    : QLabel(parent), surface_widget(surface_widget_) {}
 | 
				
			||||||
 | 
					SurfacePicture::~SurfacePicture() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurfacePicture::mousePressEvent(QMouseEvent* event) {
 | 
				
			||||||
 | 
					    // Only do something while the left mouse button is held down
 | 
				
			||||||
 | 
					    if (!(event->buttons() & Qt::LeftButton))
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pixmap() == nullptr)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (surface_widget)
 | 
				
			||||||
 | 
					        surface_widget->Pick(event->x() * pixmap()->width() / width(),
 | 
				
			||||||
 | 
					                             event->y() * pixmap()->height() / height());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
 | 
				
			||||||
 | 
					    // We also want to handle the event if the user moves the mouse while holding down the LMB
 | 
				
			||||||
 | 
					    mousePressEvent(event);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
 | 
				
			||||||
 | 
					                                             QWidget* parent)
 | 
				
			||||||
 | 
					    : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent),
 | 
				
			||||||
 | 
					      surface_source(Source::RenderTarget0) {
 | 
				
			||||||
 | 
					    setObjectName("MaxwellSurface");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_source_list = new QComboBox;
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 0"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 1"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 2"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 3"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 4"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 5"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 6"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Render Target 7"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Z Buffer"));
 | 
				
			||||||
 | 
					    surface_source_list->addItem(tr("Custom"));
 | 
				
			||||||
 | 
					    surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_address_control = new CSpinBox;
 | 
				
			||||||
 | 
					    surface_address_control->SetBase(16);
 | 
				
			||||||
 | 
					    surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF);
 | 
				
			||||||
 | 
					    surface_address_control->SetPrefix("0x");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned max_dimension = 16384; // TODO: Find actual maximum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_width_control = new QSpinBox;
 | 
				
			||||||
 | 
					    surface_width_control->setRange(0, max_dimension);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_height_control = new QSpinBox;
 | 
				
			||||||
 | 
					    surface_height_control->setRange(0, max_dimension);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_picker_x_control = new QSpinBox;
 | 
				
			||||||
 | 
					    surface_picker_x_control->setRange(0, max_dimension - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_picker_y_control = new QSpinBox;
 | 
				
			||||||
 | 
					    surface_picker_y_control->setRange(0, max_dimension - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_format_control = new QComboBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Color formats sorted by Maxwell texture format index
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("None"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("A8R8G8B8"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("Unknown"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("DXT1"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("DXT23"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("DXT45"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("DXN1"));
 | 
				
			||||||
 | 
					    surface_format_control->addItem(tr("DXN2"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_info_label = new QLabel();
 | 
				
			||||||
 | 
					    surface_info_label->setWordWrap(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_picture_label = new SurfacePicture(0, this);
 | 
				
			||||||
 | 
					    surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 | 
				
			||||||
 | 
					    surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
 | 
				
			||||||
 | 
					    surface_picture_label->setScaledContents(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto scroll_area = new QScrollArea();
 | 
				
			||||||
 | 
					    scroll_area->setBackgroundRole(QPalette::Dark);
 | 
				
			||||||
 | 
					    scroll_area->setWidgetResizable(false);
 | 
				
			||||||
 | 
					    scroll_area->setWidget(surface_picture_label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Connections
 | 
				
			||||||
 | 
					    connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
 | 
				
			||||||
 | 
					    connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfaceSourceChanged(int)));
 | 
				
			||||||
 | 
					    connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfaceAddressChanged(qint64)));
 | 
				
			||||||
 | 
					    connect(surface_width_control, SIGNAL(valueChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfaceWidthChanged(int)));
 | 
				
			||||||
 | 
					    connect(surface_height_control, SIGNAL(valueChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfaceHeightChanged(int)));
 | 
				
			||||||
 | 
					    connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfaceFormatChanged(int)));
 | 
				
			||||||
 | 
					    connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfacePickerXChanged(int)));
 | 
				
			||||||
 | 
					    connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
 | 
				
			||||||
 | 
					            SLOT(OnSurfacePickerYChanged(int)));
 | 
				
			||||||
 | 
					    connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto main_widget = new QWidget;
 | 
				
			||||||
 | 
					    auto main_layout = new QVBoxLayout;
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					        sub_layout->addWidget(new QLabel(tr("Source:")));
 | 
				
			||||||
 | 
					        sub_layout->addWidget(surface_source_list);
 | 
				
			||||||
 | 
					        main_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					        sub_layout->addWidget(new QLabel(tr("GPU Address:")));
 | 
				
			||||||
 | 
					        sub_layout->addWidget(surface_address_control);
 | 
				
			||||||
 | 
					        main_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					        sub_layout->addWidget(new QLabel(tr("Width:")));
 | 
				
			||||||
 | 
					        sub_layout->addWidget(surface_width_control);
 | 
				
			||||||
 | 
					        main_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					        sub_layout->addWidget(new QLabel(tr("Height:")));
 | 
				
			||||||
 | 
					        sub_layout->addWidget(surface_height_control);
 | 
				
			||||||
 | 
					        main_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					        sub_layout->addWidget(new QLabel(tr("Format:")));
 | 
				
			||||||
 | 
					        sub_layout->addWidget(surface_format_control);
 | 
				
			||||||
 | 
					        main_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    main_layout->addWidget(scroll_area);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto info_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto xy_layout = new QVBoxLayout;
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					                sub_layout->addWidget(new QLabel(tr("X:")));
 | 
				
			||||||
 | 
					                sub_layout->addWidget(surface_picker_x_control);
 | 
				
			||||||
 | 
					                xy_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                auto sub_layout = new QHBoxLayout;
 | 
				
			||||||
 | 
					                sub_layout->addWidget(new QLabel(tr("Y:")));
 | 
				
			||||||
 | 
					                sub_layout->addWidget(surface_picker_y_control);
 | 
				
			||||||
 | 
					                xy_layout->addLayout(sub_layout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        info_layout->addLayout(xy_layout);
 | 
				
			||||||
 | 
					        surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
 | 
				
			||||||
 | 
					        info_layout->addWidget(surface_info_label);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    main_layout->addLayout(info_layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    main_layout->addWidget(save_surface);
 | 
				
			||||||
 | 
					    main_widget->setLayout(main_layout);
 | 
				
			||||||
 | 
					    setWidget(main_widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Load current data - TODO: Make sure this works when emulation is not running
 | 
				
			||||||
 | 
					    if (debug_context && debug_context->at_breakpoint) {
 | 
				
			||||||
 | 
					        emit Update();
 | 
				
			||||||
 | 
					        widget()->setEnabled(debug_context->at_breakpoint);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        widget()->setEnabled(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
 | 
				
			||||||
 | 
					    emit Update();
 | 
				
			||||||
 | 
					    widget()->setEnabled(true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnResumed() {
 | 
				
			||||||
 | 
					    widget()->setEnabled(false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
 | 
				
			||||||
 | 
					    surface_source = static_cast<Source>(new_value);
 | 
				
			||||||
 | 
					    emit Update();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
 | 
				
			||||||
 | 
					    if (surface_address != new_value) {
 | 
				
			||||||
 | 
					        surface_address = static_cast<Tegra::GPUVAddr>(new_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
 | 
				
			||||||
 | 
					        emit Update();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
 | 
				
			||||||
 | 
					    if (surface_width != static_cast<unsigned>(new_value)) {
 | 
				
			||||||
 | 
					        surface_width = static_cast<unsigned>(new_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
 | 
				
			||||||
 | 
					        emit Update();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
 | 
				
			||||||
 | 
					    if (surface_height != static_cast<unsigned>(new_value)) {
 | 
				
			||||||
 | 
					        surface_height = static_cast<unsigned>(new_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
 | 
				
			||||||
 | 
					        emit Update();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
 | 
				
			||||||
 | 
					    if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) {
 | 
				
			||||||
 | 
					        surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
 | 
				
			||||||
 | 
					        emit Update();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
 | 
				
			||||||
 | 
					    if (surface_picker_x != new_value) {
 | 
				
			||||||
 | 
					        surface_picker_x = new_value;
 | 
				
			||||||
 | 
					        Pick(surface_picker_x, surface_picker_y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
 | 
				
			||||||
 | 
					    if (surface_picker_y != new_value) {
 | 
				
			||||||
 | 
					        surface_picker_y = new_value;
 | 
				
			||||||
 | 
					        Pick(surface_picker_x, surface_picker_y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::Pick(int x, int y) {
 | 
				
			||||||
 | 
					    surface_picker_x_control->setValue(x);
 | 
				
			||||||
 | 
					    surface_picker_y_control->setValue(y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
 | 
				
			||||||
 | 
					        y >= static_cast<int>(surface_height)) {
 | 
				
			||||||
 | 
					        surface_info_label->setText(tr("Pixel out of bounds"));
 | 
				
			||||||
 | 
					        surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>"));
 | 
				
			||||||
 | 
					    surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::OnUpdate() {
 | 
				
			||||||
 | 
					    auto& gpu = Core::System::GetInstance().GPU();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QPixmap pixmap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (surface_source) {
 | 
				
			||||||
 | 
					    case Source::RenderTarget0:
 | 
				
			||||||
 | 
					    case Source::RenderTarget1:
 | 
				
			||||||
 | 
					    case Source::RenderTarget2:
 | 
				
			||||||
 | 
					    case Source::RenderTarget3:
 | 
				
			||||||
 | 
					    case Source::RenderTarget4:
 | 
				
			||||||
 | 
					    case Source::RenderTarget5:
 | 
				
			||||||
 | 
					    case Source::RenderTarget6:
 | 
				
			||||||
 | 
					    case Source::RenderTarget7: {
 | 
				
			||||||
 | 
					        // TODO: Store a reference to the registers in the debug context instead of accessing them
 | 
				
			||||||
 | 
					        // directly...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto& registers = gpu.Get3DEngine().regs;
 | 
				
			||||||
 | 
					        auto& rt = registers.rt[static_cast<size_t>(surface_source) -
 | 
				
			||||||
 | 
					                                static_cast<size_t>(Source::RenderTarget0)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        surface_address = rt.Address();
 | 
				
			||||||
 | 
					        surface_width = rt.horiz;
 | 
				
			||||||
 | 
					        surface_height = rt.vert;
 | 
				
			||||||
 | 
					        if (rt.format != 0) {
 | 
				
			||||||
 | 
					            surface_format =
 | 
				
			||||||
 | 
					                ConvertToTextureFormat(static_cast<Tegra::RenderTargetFormat>(rt.format));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case Source::Custom: {
 | 
				
			||||||
 | 
					        // Keep user-specified values
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_address_control->SetValue(surface_address);
 | 
				
			||||||
 | 
					    surface_width_control->setValue(surface_width);
 | 
				
			||||||
 | 
					    surface_height_control->setValue(surface_height);
 | 
				
			||||||
 | 
					    surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (surface_address == 0) {
 | 
				
			||||||
 | 
					        surface_picture_label->hide();
 | 
				
			||||||
 | 
					        surface_info_label->setText(tr("(invalid surface address)"));
 | 
				
			||||||
 | 
					        surface_info_label->setAlignment(Qt::AlignCenter);
 | 
				
			||||||
 | 
					        surface_picker_x_control->setEnabled(false);
 | 
				
			||||||
 | 
					        surface_picker_y_control->setEnabled(false);
 | 
				
			||||||
 | 
					        save_surface->setEnabled(false);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: Implement a good way to visualize alpha components!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
 | 
				
			||||||
 | 
					    VAddr address = gpu.memory_manager->PhysicalToVirtualAddress(surface_address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto unswizzled_data =
 | 
				
			||||||
 | 
					        Tegra::Texture::UnswizzleTexture(address, surface_format, surface_width, surface_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
 | 
				
			||||||
 | 
					                                                      surface_width, surface_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    surface_picture_label->show();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned int y = 0; y < surface_height; ++y) {
 | 
				
			||||||
 | 
					        for (unsigned int x = 0; x < surface_width; ++x) {
 | 
				
			||||||
 | 
					            Math::Vec4<u8> color;
 | 
				
			||||||
 | 
					            color[0] = texture_data[x + y * surface_width + 0];
 | 
				
			||||||
 | 
					            color[1] = texture_data[x + y * surface_width + 1];
 | 
				
			||||||
 | 
					            color[2] = texture_data[x + y * surface_width + 2];
 | 
				
			||||||
 | 
					            color[3] = texture_data[x + y * surface_width + 3];
 | 
				
			||||||
 | 
					            decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pixmap = QPixmap::fromImage(decoded_image);
 | 
				
			||||||
 | 
					    surface_picture_label->setPixmap(pixmap);
 | 
				
			||||||
 | 
					    surface_picture_label->resize(pixmap.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update the info with pixel data
 | 
				
			||||||
 | 
					    surface_picker_x_control->setEnabled(true);
 | 
				
			||||||
 | 
					    surface_picker_y_control->setEnabled(true);
 | 
				
			||||||
 | 
					    Pick(surface_picker_x, surface_picker_y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Enable saving the converted pixmap to file
 | 
				
			||||||
 | 
					    save_surface->setEnabled(true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GraphicsSurfaceWidget::SaveSurface() {
 | 
				
			||||||
 | 
					    QString png_filter = tr("Portable Network Graphic (*.png)");
 | 
				
			||||||
 | 
					    QString bin_filter = tr("Binary data (*.bin)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString selectedFilter;
 | 
				
			||||||
 | 
					    QString filename = QFileDialog::getSaveFileName(
 | 
				
			||||||
 | 
					        this, tr("Save Surface"),
 | 
				
			||||||
 | 
					        QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
 | 
				
			||||||
 | 
					        QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (filename.isEmpty()) {
 | 
				
			||||||
 | 
					        // If the user canceled the dialog, don't save anything.
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (selectedFilter == png_filter) {
 | 
				
			||||||
 | 
					        const QPixmap* pixmap = surface_picture_label->pixmap();
 | 
				
			||||||
 | 
					        ASSERT_MSG(pixmap != nullptr, "No pixmap set");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QFile file(filename);
 | 
				
			||||||
 | 
					        file.open(QIODevice::WriteOnly);
 | 
				
			||||||
 | 
					        if (pixmap)
 | 
				
			||||||
 | 
					            pixmap->save(&file, "PNG");
 | 
				
			||||||
 | 
					    } else if (selectedFilter == bin_filter) {
 | 
				
			||||||
 | 
					        auto& gpu = Core::System::GetInstance().GPU();
 | 
				
			||||||
 | 
					        VAddr address = gpu.memory_manager->PhysicalToVirtualAddress(surface_address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const u8* buffer = Memory::GetPointer(address);
 | 
				
			||||||
 | 
					        ASSERT_MSG(buffer != nullptr, "Memory not accessible");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QFile file(filename);
 | 
				
			||||||
 | 
					        file.open(QIODevice::WriteOnly);
 | 
				
			||||||
 | 
					        int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
 | 
				
			||||||
 | 
					        QByteArray data(reinterpret_cast<const char*>(buffer), size);
 | 
				
			||||||
 | 
					        file.write(data);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        UNREACHABLE_MSG("Unhandled filter selected");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								src/yuzu/debugger/graphics/graphics_surface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/yuzu/debugger/graphics/graphics_surface.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					// Copyright 2014 Citra Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QLabel>
 | 
				
			||||||
 | 
					#include <QPushButton>
 | 
				
			||||||
 | 
					#include "video_core/memory_manager.h"
 | 
				
			||||||
 | 
					#include "video_core/textures/texture.h"
 | 
				
			||||||
 | 
					#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QComboBox;
 | 
				
			||||||
 | 
					class QSpinBox;
 | 
				
			||||||
 | 
					class CSpinBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GraphicsSurfaceWidget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurfacePicture : public QLabel {
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit SurfacePicture(QWidget* parent = nullptr,
 | 
				
			||||||
 | 
					                            GraphicsSurfaceWidget* surface_widget = nullptr);
 | 
				
			||||||
 | 
					    ~SurfacePicture();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected slots:
 | 
				
			||||||
 | 
					    virtual void mouseMoveEvent(QMouseEvent* event);
 | 
				
			||||||
 | 
					    virtual void mousePressEvent(QMouseEvent* event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    GraphicsSurfaceWidget* surface_widget;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GraphicsSurfaceWidget : public BreakPointObserverDock {
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using Event = Tegra::DebugContext::Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class Source {
 | 
				
			||||||
 | 
					        RenderTarget0 = 0,
 | 
				
			||||||
 | 
					        RenderTarget1 = 1,
 | 
				
			||||||
 | 
					        RenderTarget2 = 2,
 | 
				
			||||||
 | 
					        RenderTarget3 = 3,
 | 
				
			||||||
 | 
					        RenderTarget4 = 4,
 | 
				
			||||||
 | 
					        RenderTarget5 = 5,
 | 
				
			||||||
 | 
					        RenderTarget6 = 6,
 | 
				
			||||||
 | 
					        RenderTarget7 = 7,
 | 
				
			||||||
 | 
					        ZBuffer = 8,
 | 
				
			||||||
 | 
					        Custom = 9,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
 | 
				
			||||||
 | 
					                                   QWidget* parent = nullptr);
 | 
				
			||||||
 | 
					    void Pick(int x, int y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public slots:
 | 
				
			||||||
 | 
					    void OnSurfaceSourceChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnSurfaceAddressChanged(qint64 new_value);
 | 
				
			||||||
 | 
					    void OnSurfaceWidthChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnSurfaceHeightChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnSurfaceFormatChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnSurfacePickerXChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnSurfacePickerYChanged(int new_value);
 | 
				
			||||||
 | 
					    void OnUpdate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
					    void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
 | 
				
			||||||
 | 
					    void OnResumed() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SaveSurface();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
					    void Update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    QComboBox* surface_source_list;
 | 
				
			||||||
 | 
					    CSpinBox* surface_address_control;
 | 
				
			||||||
 | 
					    QSpinBox* surface_width_control;
 | 
				
			||||||
 | 
					    QSpinBox* surface_height_control;
 | 
				
			||||||
 | 
					    QComboBox* surface_format_control;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SurfacePicture* surface_picture_label;
 | 
				
			||||||
 | 
					    QSpinBox* surface_picker_x_control;
 | 
				
			||||||
 | 
					    QSpinBox* surface_picker_y_control;
 | 
				
			||||||
 | 
					    QLabel* surface_info_label;
 | 
				
			||||||
 | 
					    QPushButton* save_surface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Source surface_source;
 | 
				
			||||||
 | 
					    Tegra::GPUVAddr surface_address;
 | 
				
			||||||
 | 
					    unsigned surface_width;
 | 
				
			||||||
 | 
					    unsigned surface_height;
 | 
				
			||||||
 | 
					    Tegra::Texture::TextureFormat surface_format;
 | 
				
			||||||
 | 
					    int surface_picker_x = 0;
 | 
				
			||||||
 | 
					    int surface_picker_y = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -25,10 +25,13 @@
 | 
				
			|||||||
#include "core/gdbstub/gdbstub.h"
 | 
					#include "core/gdbstub/gdbstub.h"
 | 
				
			||||||
#include "core/loader/loader.h"
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
#include "core/settings.h"
 | 
					#include "core/settings.h"
 | 
				
			||||||
 | 
					#include "video_core/debug_utils/debug_utils.h"
 | 
				
			||||||
#include "yuzu/about_dialog.h"
 | 
					#include "yuzu/about_dialog.h"
 | 
				
			||||||
#include "yuzu/bootmanager.h"
 | 
					#include "yuzu/bootmanager.h"
 | 
				
			||||||
#include "yuzu/configuration/config.h"
 | 
					#include "yuzu/configuration/config.h"
 | 
				
			||||||
#include "yuzu/configuration/configure_dialog.h"
 | 
					#include "yuzu/configuration/configure_dialog.h"
 | 
				
			||||||
 | 
					#include "yuzu/debugger/graphics/graphics_breakpoints.h"
 | 
				
			||||||
 | 
					#include "yuzu/debugger/graphics/graphics_surface.h"
 | 
				
			||||||
#include "yuzu/debugger/profiler.h"
 | 
					#include "yuzu/debugger/profiler.h"
 | 
				
			||||||
#include "yuzu/debugger/registers.h"
 | 
					#include "yuzu/debugger/registers.h"
 | 
				
			||||||
#include "yuzu/debugger/wait_tree.h"
 | 
					#include "yuzu/debugger/wait_tree.h"
 | 
				
			||||||
@ -68,6 +71,9 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
 | 
				
			|||||||
void GMainWindow::ShowCallouts() {}
 | 
					void GMainWindow::ShowCallouts() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
 | 
					GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    debug_context = Tegra::DebugContext::Construct();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setAcceptDrops(true);
 | 
					    setAcceptDrops(true);
 | 
				
			||||||
    ui.setupUi(this);
 | 
					    ui.setupUi(this);
 | 
				
			||||||
    statusBar()->hide();
 | 
					    statusBar()->hide();
 | 
				
			||||||
@ -160,6 +166,16 @@ void GMainWindow::InitializeDebugWidgets() {
 | 
				
			|||||||
    connect(this, &GMainWindow::EmulationStopping, registersWidget,
 | 
					    connect(this, &GMainWindow::EmulationStopping, registersWidget,
 | 
				
			||||||
            &RegistersWidget::OnEmulationStopping);
 | 
					            &RegistersWidget::OnEmulationStopping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
 | 
				
			||||||
 | 
					    addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
 | 
				
			||||||
 | 
					    graphicsBreakpointsWidget->hide();
 | 
				
			||||||
 | 
					    debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
 | 
				
			||||||
 | 
					    addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
 | 
				
			||||||
 | 
					    graphicsSurfaceWidget->hide();
 | 
				
			||||||
 | 
					    debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    waitTreeWidget = new WaitTreeWidget(this);
 | 
					    waitTreeWidget = new WaitTreeWidget(this);
 | 
				
			||||||
    addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
 | 
					    addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
 | 
				
			||||||
    waitTreeWidget->hide();
 | 
					    waitTreeWidget->hide();
 | 
				
			||||||
@ -324,6 +340,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Core::System& system{Core::System::GetInstance()};
 | 
					    Core::System& system{Core::System::GetInstance()};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    system.SetGPUDebugContext(debug_context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
 | 
					    const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
 | 
					    Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
 | 
				
			||||||
 | 
				
			|||||||
@ -15,17 +15,18 @@ class Config;
 | 
				
			|||||||
class EmuThread;
 | 
					class EmuThread;
 | 
				
			||||||
class GameList;
 | 
					class GameList;
 | 
				
			||||||
class GImageInfo;
 | 
					class GImageInfo;
 | 
				
			||||||
class GPUCommandStreamWidget;
 | 
					 | 
				
			||||||
class GPUCommandListWidget;
 | 
					 | 
				
			||||||
class GraphicsBreakPointsWidget;
 | 
					class GraphicsBreakPointsWidget;
 | 
				
			||||||
class GraphicsTracingWidget;
 | 
					class GraphicsSurfaceWidget;
 | 
				
			||||||
class GraphicsVertexShaderWidget;
 | 
					 | 
				
			||||||
class GRenderWindow;
 | 
					class GRenderWindow;
 | 
				
			||||||
class MicroProfileDialog;
 | 
					class MicroProfileDialog;
 | 
				
			||||||
class ProfilerWidget;
 | 
					class ProfilerWidget;
 | 
				
			||||||
class RegistersWidget;
 | 
					class RegistersWidget;
 | 
				
			||||||
class WaitTreeWidget;
 | 
					class WaitTreeWidget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tegra {
 | 
				
			||||||
 | 
					class DebugContext;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GMainWindow : public QMainWindow {
 | 
					class GMainWindow : public QMainWindow {
 | 
				
			||||||
    Q_OBJECT
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -138,6 +139,8 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Ui::MainWindow ui;
 | 
					    Ui::MainWindow ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<Tegra::DebugContext> debug_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GRenderWindow* render_window;
 | 
					    GRenderWindow* render_window;
 | 
				
			||||||
    GameList* game_list;
 | 
					    GameList* game_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -158,6 +161,8 @@ private:
 | 
				
			|||||||
    ProfilerWidget* profilerWidget;
 | 
					    ProfilerWidget* profilerWidget;
 | 
				
			||||||
    MicroProfileDialog* microProfileDialog;
 | 
					    MicroProfileDialog* microProfileDialog;
 | 
				
			||||||
    RegistersWidget* registersWidget;
 | 
					    RegistersWidget* registersWidget;
 | 
				
			||||||
 | 
					    GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
 | 
				
			||||||
 | 
					    GraphicsSurfaceWidget* graphicsSurfaceWidget;
 | 
				
			||||||
    WaitTreeWidget* waitTreeWidget;
 | 
					    WaitTreeWidget* waitTreeWidget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QAction* actions_recent_files[max_recent_files_item];
 | 
					    QAction* actions_recent_files[max_recent_files_item];
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user