mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 19:20:15 +00:00
Search bar for GPU Commands
This commit is contained in:
parent
cc7f1155a8
commit
8aa28825db
@ -13,6 +13,7 @@
|
|||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
#include "citra_qt/debugger/graphics_cmdlists.h"
|
#include "citra_qt/debugger/graphics_cmdlists.h"
|
||||||
#include "citra_qt/util/spinbox.h"
|
#include "citra_qt/util/spinbox.h"
|
||||||
#include "citra_qt/util/util.h"
|
#include "citra_qt/util/util.h"
|
||||||
@ -20,6 +21,8 @@
|
|||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
#include "video_core/pica.h"
|
#include "video_core/pica.h"
|
||||||
#include "video_core/pica_state.h"
|
#include "video_core/pica_state.h"
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
|
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
|
||||||
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
|
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
|
||||||
@ -51,7 +54,7 @@ public:
|
|||||||
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {}
|
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||||
|
|
||||||
int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
|
int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
|
||||||
return static_cast<int>(pica_trace.writes.size());
|
return static_cast<int>((pica_trace.isSearchCorrected ? pica_trace.search_corrected_writes : pica_trace.writes).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
|
int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
|
||||||
@ -62,7 +65,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
|
|||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
const auto& write = pica_trace.writes[index.row()];
|
const auto& write = (pica_trace.isSearchCorrected ? pica_trace.search_corrected_writes[index.row()] : pica_trace.writes[index.row()]);
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
QString content;
|
QString content;
|
||||||
@ -112,6 +115,16 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPUCommandListModel::UpdatePicaTrace(const QString& str) {
|
||||||
|
|
||||||
|
pica_trace.CorrectSearchWrites(str.toStdString());
|
||||||
|
cout << "Updated writes";
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
#define COMMAND_IN_RANGE(cmd_id, reg_name) \
|
#define COMMAND_IN_RANGE(cmd_id, reg_name) \
|
||||||
(cmd_id >= PICA_REG_INDEX(reg_name) && \
|
(cmd_id >= PICA_REG_INDEX(reg_name) && \
|
||||||
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
|
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
|
||||||
@ -218,7 +231,14 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
|
|||||||
sub_layout->addWidget(copy_all);
|
sub_layout->addWidget(copy_all);
|
||||||
main_layout->addLayout(sub_layout);
|
main_layout->addLayout(sub_layout);
|
||||||
}
|
}
|
||||||
main_widget->setLayout(main_layout);
|
|
||||||
|
search_bar = new QLineEdit();
|
||||||
|
search_bar->setPlaceholderText("Search Bar");
|
||||||
|
main_layout->addWidget(search_bar);
|
||||||
|
|
||||||
|
connect(search_bar, SIGNAL(textChanged(const QString &)), model, SLOT(UpdatePicaTrace(const QString &)));
|
||||||
|
|
||||||
|
main_widget->setLayout(main_layout);
|
||||||
|
|
||||||
setWidget(main_widget);
|
setWidget(main_widget);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
|
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
|
||||||
|
void UpdatePicaTrace(const QString &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pica::DebugUtils::PicaTrace pica_trace;
|
Pica::DebugUtils::PicaTrace pica_trace;
|
||||||
@ -41,13 +42,14 @@ class GPUCommandListWidget : public QDockWidget {
|
|||||||
public:
|
public:
|
||||||
GPUCommandListWidget(QWidget* parent = nullptr);
|
GPUCommandListWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
QLineEdit* search_bar;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnToggleTracing();
|
void OnToggleTracing();
|
||||||
void OnCommandDoubleClicked(const QModelIndex&);
|
void OnCommandDoubleClicked(const QModelIndex&);
|
||||||
|
|
||||||
void SetCommandInfo(const QModelIndex&);
|
void SetCommandInfo(const QModelIndex&);
|
||||||
|
|
||||||
void CopyAllToClipboard();
|
void CopyAllToClipboard();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TracingFinished(const Pica::DebugUtils::PicaTrace&);
|
void TracingFinished(const Pica::DebugUtils::PicaTrace&);
|
||||||
|
@ -20,251 +20,283 @@
|
|||||||
#include "video_core/pica.h"
|
#include "video_core/pica.h"
|
||||||
|
|
||||||
namespace CiTrace {
|
namespace CiTrace {
|
||||||
class Recorder;
|
class Recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Pica {
|
namespace Pica {
|
||||||
|
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
struct ShaderSetup;
|
struct ShaderSetup;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DebugContext {
|
class DebugContext {
|
||||||
public:
|
public:
|
||||||
enum class Event {
|
enum class Event {
|
||||||
FirstEvent = 0,
|
FirstEvent = 0,
|
||||||
|
|
||||||
PicaCommandLoaded = FirstEvent,
|
PicaCommandLoaded = FirstEvent,
|
||||||
PicaCommandProcessed,
|
PicaCommandProcessed,
|
||||||
IncomingPrimitiveBatch,
|
IncomingPrimitiveBatch,
|
||||||
FinishedPrimitiveBatch,
|
FinishedPrimitiveBatch,
|
||||||
VertexShaderInvocation,
|
VertexShaderInvocation,
|
||||||
IncomingDisplayTransfer,
|
IncomingDisplayTransfer,
|
||||||
GSPCommandProcessed,
|
GSPCommandProcessed,
|
||||||
BufferSwapped,
|
BufferSwapped,
|
||||||
|
|
||||||
NumEvents
|
NumEvents
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inherit from this class to be notified of events registered to some debug context.
|
* Inherit from this class to be notified of events registered to some debug context.
|
||||||
* Most importantly this is used for our debugger GUI.
|
* Most importantly this is used for our debugger GUI.
|
||||||
*
|
*
|
||||||
* To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
|
* To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
|
||||||
* @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
|
* @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
|
||||||
* access
|
* access
|
||||||
* @todo Evaluate an alternative interface, in which there is only one managing observer and
|
* @todo Evaluate an alternative interface, in which there is only one managing observer and
|
||||||
* multiple child observers running (by design) on the same thread.
|
* multiple child observers running (by design) on the same thread.
|
||||||
*/
|
*/
|
||||||
class BreakPointObserver {
|
class BreakPointObserver {
|
||||||
public:
|
public:
|
||||||
/// Constructs the object such that it observes events of the given DebugContext.
|
/// Constructs the object such that it observes events of the given DebugContext.
|
||||||
BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
|
BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
|
||||||
: context_weak(debug_context) {
|
: context_weak(debug_context) {
|
||||||
std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
|
std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
|
||||||
debug_context->breakpoint_observers.push_back(this);
|
debug_context->breakpoint_observers.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~BreakPointObserver() {
|
virtual ~BreakPointObserver() {
|
||||||
auto context = context_weak.lock();
|
auto context = context_weak.lock();
|
||||||
if (context) {
|
if (context) {
|
||||||
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
|
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
|
||||||
context->breakpoint_observers.remove(this);
|
context->breakpoint_observers.remove(this);
|
||||||
|
|
||||||
// If we are the last observer to be destroyed, tell the debugger context that
|
// 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 Citra
|
// it is free to continue. In particular, this is required for a proper Citra
|
||||||
// shutdown, when the emulation thread is waiting at a breakpoint.
|
// shutdown, when the emulation thread is waiting at a breakpoint.
|
||||||
if (context->breakpoint_observers.empty())
|
if (context->breakpoint_observers.empty())
|
||||||
context->Resume();
|
context->Resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action to perform when a breakpoint was reached.
|
* Action to perform when a breakpoint was reached.
|
||||||
* @param event Type of event which triggered the breakpoint
|
* @param event Type of event which triggered the breakpoint
|
||||||
* @param data Optional data pointer (if unused, this is a nullptr)
|
* @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.
|
* @note This function will perform nothing unless it is overridden in the child class.
|
||||||
*/
|
*/
|
||||||
virtual void OnPicaBreakPointHit(Event, void*) {}
|
virtual void OnPicaBreakPointHit(Event, void*) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action to perform when emulation is resumed from a breakpoint.
|
* Action to perform when emulation is resumed from a breakpoint.
|
||||||
* @note This function will perform nothing unless it is overridden in the child class.
|
* @note This function will perform nothing unless it is overridden in the child class.
|
||||||
*/
|
*/
|
||||||
virtual void OnPicaResume() {}
|
virtual void OnPicaResume() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Weak context pointer. This need not be valid, so when requesting a shared_ptr via
|
* Weak context pointer. This need not be valid, so when requesting a shared_ptr via
|
||||||
* context_weak.lock(), always compare the result against nullptr.
|
* context_weak.lock(), always compare the result against nullptr.
|
||||||
*/
|
*/
|
||||||
std::weak_ptr<DebugContext> context_weak;
|
std::weak_ptr<DebugContext> context_weak;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple structure defining a breakpoint state
|
* Simple structure defining a breakpoint state
|
||||||
*/
|
*/
|
||||||
struct BreakPoint {
|
struct BreakPoint {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static constructor used to create a shared_ptr of a DebugContext.
|
* Static constructor used to create a shared_ptr of a DebugContext.
|
||||||
*/
|
*/
|
||||||
static std::shared_ptr<DebugContext> Construct() {
|
static std::shared_ptr<DebugContext> Construct() {
|
||||||
return std::shared_ptr<DebugContext>(new DebugContext);
|
return std::shared_ptr<DebugContext>(new DebugContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the emulation core when a given event has happened. If a breakpoint has been set
|
* 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.
|
* 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
|
* The current thread then is halted until Resume() is called from another thread (or until
|
||||||
* emulation is stopped).
|
* emulation is stopped).
|
||||||
* @param event Event which has happened
|
* @param event Event which has happened
|
||||||
* @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
|
* @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
|
||||||
* Resume() is called.
|
* Resume() is called.
|
||||||
*/
|
*/
|
||||||
void OnEvent(Event event, void* data) {
|
void OnEvent(Event event, void* data) {
|
||||||
// This check is left in the header to allow the compiler to inline it.
|
// This check is left in the header to allow the compiler to inline it.
|
||||||
if (!breakpoints[(int)event].enabled)
|
if (!breakpoints[(int)event].enabled)
|
||||||
return;
|
return;
|
||||||
// For the rest of event handling, call a separate function.
|
// For the rest of event handling, call a separate function.
|
||||||
DoOnEvent(event, data);
|
DoOnEvent(event, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoOnEvent(Event event, void* data);
|
void DoOnEvent(Event event, void* data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resume from the current breakpoint.
|
* Resume from the current breakpoint.
|
||||||
* @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
|
* @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
|
||||||
* Calling from any other thread is safe.
|
* Calling from any other thread is safe.
|
||||||
*/
|
*/
|
||||||
void Resume();
|
void Resume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all set breakpoints and resume emulation.
|
* Delete all set breakpoints and resume emulation.
|
||||||
*/
|
*/
|
||||||
void ClearBreakpoints() {
|
void ClearBreakpoints() {
|
||||||
for (auto& bp : breakpoints) {
|
for (auto& bp : breakpoints) {
|
||||||
bp.enabled = false;
|
bp.enabled = false;
|
||||||
}
|
}
|
||||||
Resume();
|
Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Evaluate if access to these members should be hidden behind a public interface.
|
// TODO: Evaluate if access to these members should be hidden behind a public interface.
|
||||||
std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
|
std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
|
||||||
Event active_breakpoint;
|
Event active_breakpoint;
|
||||||
bool at_breakpoint = false;
|
bool at_breakpoint = false;
|
||||||
|
|
||||||
std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
|
std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Private default constructor to make sure people always construct this through Construct()
|
* Private default constructor to make sure people always construct this through Construct()
|
||||||
* instead.
|
* instead.
|
||||||
*/
|
*/
|
||||||
DebugContext() = default;
|
DebugContext() = default;
|
||||||
|
|
||||||
/// Mutex protecting current breakpoint state and the observer list.
|
/// Mutex protecting current breakpoint state and the observer list.
|
||||||
std::mutex breakpoint_mutex;
|
std::mutex breakpoint_mutex;
|
||||||
|
|
||||||
/// Used by OnEvent to wait for resumption.
|
/// Used by OnEvent to wait for resumption.
|
||||||
std::condition_variable resume_from_breakpoint;
|
std::condition_variable resume_from_breakpoint;
|
||||||
|
|
||||||
/// List of registered observers
|
/// List of registered observers
|
||||||
std::list<BreakPointObserver*> breakpoint_observers;
|
std::list<BreakPointObserver*> breakpoint_observers;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
|
extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
|
||||||
|
|
||||||
namespace DebugUtils {
|
namespace DebugUtils {
|
||||||
|
|
||||||
#define PICA_DUMP_TEXTURES 0
|
#define PICA_DUMP_TEXTURES 0
|
||||||
#define PICA_LOG_TEV 0
|
#define PICA_LOG_TEV 0
|
||||||
|
|
||||||
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
|
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
|
||||||
const Shader::ShaderSetup& setup,
|
const Shader::ShaderSetup& setup,
|
||||||
const Regs::VSOutputAttributes* output_attributes);
|
const Regs::VSOutputAttributes* output_attributes);
|
||||||
|
|
||||||
// Utility class to log Pica commands.
|
// Utility class to log Pica commands.
|
||||||
struct PicaTrace {
|
struct PicaTrace {
|
||||||
struct Write {
|
struct Write {
|
||||||
u16 cmd_id;
|
u16 cmd_id;
|
||||||
u16 mask;
|
u16 mask;
|
||||||
u32 value;
|
u32 value;
|
||||||
};
|
};
|
||||||
std::vector<Write> writes;
|
std::vector<Write> writes;
|
||||||
};
|
std::vector<Write> search_corrected_writes;
|
||||||
|
bool isSearchCorrected = false;
|
||||||
|
void CorrectSearchWrites(std::string search_string) {
|
||||||
|
|
||||||
void StartPicaTracing();
|
search_corrected_writes.clear();
|
||||||
bool IsPicaTracing();
|
|
||||||
void OnPicaRegWrite(PicaTrace::Write write);
|
|
||||||
std::unique_ptr<PicaTrace> FinishPicaTracing();
|
|
||||||
|
|
||||||
struct TextureInfo {
|
if (search_string.size() == 0)
|
||||||
PAddr physical_address;
|
isSearchCorrected = false;
|
||||||
int width;
|
else isSearchCorrected = true;
|
||||||
int height;
|
|
||||||
int stride;
|
|
||||||
Pica::Regs::TextureFormat format;
|
|
||||||
|
|
||||||
static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
|
for (int i = 0; i < writes.size(); i++) {
|
||||||
const Pica::Regs::TextureFormat& format);
|
bool isValid = true;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
std::string cmdName = Pica::Regs::GetCommandName(writes[i].cmd_id);
|
||||||
* Lookup texel located at the given coordinates and return an RGBA vector of its color.
|
|
||||||
* @param source Source pointer to read data from
|
|
||||||
* @param s,t Texture coordinates to read from
|
|
||||||
* @param info TextureInfo object describing the texture setup
|
|
||||||
* @param disable_alpha This is used for debug widgets which use this method to display textures
|
|
||||||
* without providing a good way to visualize alpha by themselves. If true, this will return 255 for
|
|
||||||
* the alpha component, and either drop the information entirely or store it in an "unused" color
|
|
||||||
* channel.
|
|
||||||
* @todo Eventually we should get rid of the disable_alpha parameter.
|
|
||||||
*/
|
|
||||||
const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
|
|
||||||
bool disable_alpha = false);
|
|
||||||
|
|
||||||
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
|
if (cmdName.size() < search_string.size())
|
||||||
|
continue;
|
||||||
|
|
||||||
std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
|
for (int j = 0; j < search_string.size(); j++) {
|
||||||
std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
|
if (cmdName.at(j) != search_string.at(j)) {
|
||||||
|
isValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dumps the Tev stage config to log at trace level
|
if (!isValid)
|
||||||
void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
|
continue;
|
||||||
|
|
||||||
/**
|
search_corrected_writes.push_back(writes[i]);
|
||||||
* Used in the vertex loader to merge access records. TODO: Investigate if actually useful.
|
}
|
||||||
*/
|
|
||||||
class MemoryAccessTracker {
|
|
||||||
/// Combine overlapping and close ranges
|
|
||||||
void SimplifyRanges() {
|
|
||||||
for (auto it = ranges.begin(); it != ranges.end(); ++it) {
|
|
||||||
// NOTE: We add 32 to the range end address to make sure "close" ranges are combined,
|
|
||||||
// too
|
|
||||||
auto it2 = std::next(it);
|
|
||||||
while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) {
|
|
||||||
it->second = std::max(it->second, it2->first + it2->second - it->first);
|
|
||||||
it2 = ranges.erase(it2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
}
|
||||||
/// Record a particular memory access in the list
|
};
|
||||||
void AddAccess(u32 paddr, u32 size) {
|
|
||||||
// Create new range or extend existing one
|
|
||||||
ranges[paddr] = std::max(ranges[paddr], size);
|
|
||||||
|
|
||||||
// Simplify ranges...
|
void StartPicaTracing();
|
||||||
SimplifyRanges();
|
bool IsPicaTracing();
|
||||||
}
|
void OnPicaRegWrite(PicaTrace::Write write);
|
||||||
|
std::unique_ptr<PicaTrace> FinishPicaTracing();
|
||||||
|
|
||||||
/// Map of accessed ranges (mapping start address to range size)
|
struct TextureInfo {
|
||||||
std::map<u32, u32> ranges;
|
PAddr physical_address;
|
||||||
};
|
int width;
|
||||||
|
int height;
|
||||||
} // namespace
|
int stride;
|
||||||
|
Pica::Regs::TextureFormat format;
|
||||||
|
|
||||||
|
static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
|
||||||
|
const Pica::Regs::TextureFormat& format);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup texel located at the given coordinates and return an RGBA vector of its color.
|
||||||
|
* @param source Source pointer to read data from
|
||||||
|
* @param s,t Texture coordinates to read from
|
||||||
|
* @param info TextureInfo object describing the texture setup
|
||||||
|
* @param disable_alpha This is used for debug widgets which use this method to display textures
|
||||||
|
* without providing a good way to visualize alpha by themselves. If true, this will return 255 for
|
||||||
|
* the alpha component, and either drop the information entirely or store it in an "unused" color
|
||||||
|
* channel.
|
||||||
|
* @todo Eventually we should get rid of the disable_alpha parameter.
|
||||||
|
*/
|
||||||
|
const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
|
||||||
|
bool disable_alpha = false);
|
||||||
|
|
||||||
|
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
|
||||||
|
|
||||||
|
std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
|
||||||
|
std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
|
||||||
|
|
||||||
|
/// Dumps the Tev stage config to log at trace level
|
||||||
|
void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in the vertex loader to merge access records. TODO: Investigate if actually useful.
|
||||||
|
*/
|
||||||
|
class MemoryAccessTracker {
|
||||||
|
/// Combine overlapping and close ranges
|
||||||
|
void SimplifyRanges() {
|
||||||
|
for (auto it = ranges.begin(); it != ranges.end(); ++it) {
|
||||||
|
// NOTE: We add 32 to the range end address to make sure "close" ranges are combined,
|
||||||
|
// too
|
||||||
|
auto it2 = std::next(it);
|
||||||
|
while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) {
|
||||||
|
it->second = std::max(it->second, it2->first + it2->second - it->first);
|
||||||
|
it2 = ranges.erase(it2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Record a particular memory access in the list
|
||||||
|
void AddAccess(u32 paddr, u32 size) {
|
||||||
|
// Create new range or extend existing one
|
||||||
|
ranges[paddr] = std::max(ranges[paddr], size);
|
||||||
|
|
||||||
|
// Simplify ranges...
|
||||||
|
SimplifyRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map of accessed ranges (mapping start address to range size)
|
||||||
|
std::map<u32, u32> ranges;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
Loading…
Reference in New Issue
Block a user