Qt: Redo frame inspector using video logs

This commit is contained in:
Vicki Pfau 2019-06-01 14:31:51 -07:00
parent 59d2e58bbb
commit ef3cc7bd9f
3 changed files with 178 additions and 83 deletions

View File

@ -14,6 +14,8 @@
#include "CoreController.h"
#include <mgba/core/core.h>
#include <mgba/feature/video-logger.h>
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
@ -37,7 +39,6 @@ FrameView::FrameView(std::shared_ptr<CoreController> controller, QWidget* parent
++m_glowFrame;
invalidateQueue();
});
m_glowTimer.start();
m_ui.renderedView->installEventFilter(this);
m_ui.compositedView->installEventFilter(this);
@ -66,6 +67,15 @@ FrameView::FrameView(std::shared_ptr<CoreController> controller, QWidget* parent
QPixmap rendered = m_rendered.scaledToHeight(m_rendered.height() * m_ui.magnification->value());
m_ui.renderedView->setPixmap(rendered);
});
m_controller->addFrameAction(std::bind(&FrameView::frameCallback, this, m_callbackLocker));
}
FrameView::~FrameView() {
QMutexLocker locker(&m_mutex);
*m_callbackLocker = false;
if (m_vl) {
m_vl->deinit(m_vl);
}
}
bool FrameView::lookupLayer(const QPointF& coord, Layer*& out) {
@ -122,24 +132,26 @@ void FrameView::disableLayer(const QPointF& coord) {
m_disabled.insert(layer->id);
}
#ifdef M_CORE_GBA
void FrameView::updateTilesGBA(bool force) {
if (m_ui.freeze->checkState() == Qt::Checked) {
return;
}
QMutexLocker locker(&m_mutex);
m_queue.clear();
{
CoreController::Interrupter interrupter(m_controller);
updateRendered();
uint16_t* io = static_cast<GBA*>(m_controller->thread()->core->board)->memory.io;
QRgb backdrop = M_RGB5_TO_RGB8(static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[0]);
int mode = GBARegisterDISPCNTGetMode(io[REG_DISPCNT >> 1]);
m_gbaDispcnt = io[REG_DISPCNT >> 1];
int mode = GBARegisterDISPCNTGetMode(m_gbaDispcnt);
std::array<bool, 4> enabled{
bool(GBARegisterDISPCNTIsBg0Enable(io[REG_DISPCNT >> 1])),
bool(GBARegisterDISPCNTIsBg1Enable(io[REG_DISPCNT >> 1])),
bool(GBARegisterDISPCNTIsBg2Enable(io[REG_DISPCNT >> 1])),
bool(GBARegisterDISPCNTIsBg3Enable(io[REG_DISPCNT >> 1])),
bool(GBARegisterDISPCNTIsBg0Enable(m_gbaDispcnt)),
bool(GBARegisterDISPCNTIsBg1Enable(m_gbaDispcnt)),
bool(GBARegisterDISPCNTIsBg2Enable(m_gbaDispcnt)),
bool(GBARegisterDISPCNTIsBg3Enable(m_gbaDispcnt)),
};
for (int priority = 0; priority < 4; ++priority) {
@ -204,10 +216,53 @@ void FrameView::updateTilesGBA(bool force) {
QPixmap::fromImage(backdropImage),
{}, {0, 0}, false
});
updateRendered();
}
invalidateQueue(QSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS));
}
void FrameView::injectGBA() {
mVideoLogger* logger = m_vl->videoLogger;
mVideoLoggerInjectionPoint(logger, LOGGER_INJECTION_FIRST_SCANLINE);
GBA* gba = static_cast<GBA*>(m_vl->board);
gba->video.renderer->highlightBG[0] = false;
gba->video.renderer->highlightBG[1] = false;
gba->video.renderer->highlightBG[2] = false;
gba->video.renderer->highlightBG[3] = false;
for (int i = 0; i < 128; ++i) {
gba->video.renderer->highlightOBJ[i] = false;
}
QPalette palette;
gba->video.renderer->highlightColor = palette.color(QPalette::HighlightedText).rgb();
gba->video.renderer->highlightAmount = sin(m_glowFrame * M_PI / 30) * 64 + 64;
for (const Layer& layer : m_queue) {
switch (layer.id.type) {
case LayerId::SPRITE:
if (!layer.enabled) {
mVideoLoggerInjectOAM(logger, layer.id.index << 2, 0x200);
}
if (layer.id == m_active) {
gba->video.renderer->highlightOBJ[layer.id.index] = true;
}
break;
case LayerId::BACKGROUND:
m_vl->enableVideoLayer(m_vl, layer.id.index, layer.enabled);
if (layer.id == m_active) {
gba->video.renderer->highlightBG[layer.id.index] = true;
}
break;
}
}
if (m_ui.disableScanline->checkState() == Qt::Checked) {
mVideoLoggerIgnoreAfterInjection(logger, (1 << DIRTY_PALETTE) | (1 << DIRTY_OAM) | (1 << DIRTY_REGISTER));
} else {
mVideoLoggerIgnoreAfterInjection(logger, 0);
}
}
#endif
#ifdef M_CORE_GB
void FrameView::updateTilesGB(bool force) {
if (m_ui.freeze->checkState() == Qt::Checked) {
return;
@ -220,23 +275,33 @@ void FrameView::updateTilesGB(bool force) {
invalidateQueue(m_controller->screenDimensions());
}
void FrameView::injectGB() {
for (const Layer& layer : m_queue) {
}
}
#endif
void FrameView::invalidateQueue(const QSize& dims) {
if (dims.isValid()) {
m_dims = dims;
}
bool blockSignals = m_ui.queue->blockSignals(true);
QPixmap composited(m_dims);
QMutexLocker locker(&m_mutex);
if (m_vl) {
m_vl->reset(m_vl);
switch (m_controller->platform()) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
injectGBA();
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
injectGB();
#endif
}
m_vl->runFrame(m_vl);
}
QPainter painter(&composited);
QPalette palette;
QColor activeColor = palette.color(QPalette::HighlightedText);
activeColor.setAlpha(sin(m_glowFrame * M_PI / 60) * 16 + 96);
QRectF rect(0, 0, m_dims.width(), m_dims.height());
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect, QColor(0, 0, 0, 0));
painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
for (int i = 0; i < m_queue.count(); ++i) {
const Layer& layer = m_queue[i];
QListWidgetItem* item;
@ -251,61 +316,20 @@ void FrameView::invalidateQueue(const QSize& dims) {
item->setCheckState(layer.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, i);
item->setSelected(layer.id == m_active);
if (!layer.enabled) {
continue;
}
QPointF location = layer.location;
QSizeF layerDims(layer.image.width(), layer.image.height());
QRegion region;
if (layer.repeats) {
if (location.x() + layerDims.width() < 0) {
location.setX(std::fmod(location.x(), layerDims.width()));
}
if (location.y() + layerDims.height() < 0) {
location.setY(std::fmod(location.y(), layerDims.height()));
}
if (layer.id == m_active) {
region = layer.mask.translated(location.x(), location.y());
region += layer.mask.translated(location.x() + layerDims.width(), location.y());
region += layer.mask.translated(location.x(), location.y() + layerDims.height());
region += layer.mask.translated(location.x() + layerDims.width(), location.y() + layerDims.height());
}
} else {
QRectF layerRect(location, layerDims);
if (!rect.intersects(layerRect)) {
continue;
}
if (layer.id == m_active) {
region = layer.mask.translated(location.x(), location.y());
}
}
if (layer.id == m_active) {
painter.setClipping(true);
painter.setClipRegion(region);
painter.fillRect(rect, activeColor);
painter.setClipping(false);
}
if (layer.repeats) {
painter.drawPixmap(location, layer.image);
painter.drawPixmap(location + QPointF(layerDims.width(), 0), layer.image);
painter.drawPixmap(location + QPointF(0, layerDims.height()), layer.image);
painter.drawPixmap(location + QPointF(layerDims.width(), layerDims.height()), layer.image);
} else {
painter.drawPixmap(location, layer.image);
}
}
painter.end();
while (m_ui.queue->count() > m_queue.count()) {
delete m_ui.queue->takeItem(m_queue.count());
}
m_ui.queue->blockSignals(blockSignals);
QPixmap composited;
if (m_framebuffer.isNull()) {
updateRendered();
composited = m_rendered;
} else {
composited.convertFromImage(m_framebuffer);
}
m_composited = composited.scaled(m_dims * m_ui.magnification->value());
m_ui.compositedView->setPixmap(m_composited);
}
@ -336,6 +360,53 @@ bool FrameView::eventFilter(QObject* obj, QEvent* event) {
return false;
}
void FrameView::refreshVl() {
QMutexLocker locker(&m_mutex);
m_currentFrame = m_nextFrame;
m_nextFrame = VFileMemChunk(nullptr, 0);
if (m_currentFrame) {
m_controller->endVideoLog(false);
VFile* currentFrame = VFileMemChunk(nullptr, m_currentFrame->size(m_currentFrame));
void* buffer = currentFrame->map(currentFrame, m_currentFrame->size(m_currentFrame), MAP_WRITE);
m_currentFrame->seek(m_currentFrame, 0, SEEK_SET);
m_currentFrame->read(m_currentFrame, buffer, m_currentFrame->size(m_currentFrame));
currentFrame->unmap(currentFrame, buffer, m_currentFrame->size(m_currentFrame));
m_currentFrame = currentFrame;
QMetaObject::invokeMethod(this, "newVl");
}
m_controller->endVideoLog();
m_controller->startVideoLog(m_nextFrame, false);
}
void FrameView::newVl() {
if (!m_glowTimer.isActive()) {
m_glowTimer.start();
}
QMutexLocker locker(&m_mutex);
if (m_vl) {
m_vl->deinit(m_vl);
}
m_vl = mCoreFindVF(m_currentFrame);
m_vl->init(m_vl);
m_vl->loadROM(m_vl, m_currentFrame);
mCoreInitConfig(m_vl, nullptr);
unsigned width, height;
m_vl->desiredVideoDimensions(m_vl, &width, &height);
m_framebuffer = QImage(width, height, QImage::Format_RGBX8888);
m_vl->setVideoBuffer(m_vl, reinterpret_cast<color_t*>(m_framebuffer.bits()), width);
m_vl->reset(m_vl);
}
void FrameView::frameCallback(FrameView* viewer, std::shared_ptr<bool> lock) {
if (!*lock) {
return;
}
CoreController::Interrupter interrupter(viewer->m_controller, true);
viewer->refreshVl();
viewer->m_controller->addFrameAction(std::bind(&FrameView::frameCallback, viewer, lock));
}
QString FrameView::LayerId::readable() const {
QString typeStr;
switch (type) {

View File

@ -10,14 +10,19 @@
#include <QBitmap>
#include <QImage>
#include <QList>
#include <QMutex>
#include <QPixmap>
#include <QSet>
#include <QTimer>
#include "AssetView.h"
#include <mgba-util/vfs.h>
#include <memory>
struct VFile;
namespace QGBA {
class CoreController;
@ -27,6 +32,7 @@ Q_OBJECT
public:
FrameView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
~FrameView();
public slots:
void selectLayer(const QPointF& coord);
@ -35,16 +41,20 @@ public slots:
protected:
#ifdef M_CORE_GBA
void updateTilesGBA(bool force) override;
void injectGBA();
#endif
#ifdef M_CORE_GB
void updateTilesGB(bool force) override;
void injectGB();
#endif
bool eventFilter(QObject* obj, QEvent* event) override;
private slots:
void invalidateQueue(const QSize& dims = QSize());
void invalidateQueue(const QSize& = {});
void updateRendered();
void refreshVl();
void newVl();
private:
struct LayerId {
@ -57,7 +67,7 @@ private:
} type = NONE;
int index = -1;
bool operator==(const LayerId& other) const { return other.type == type && other.index == index; }
bool operator!=(const LayerId& other) const { return other.type != type || other.index != index; }
operator uint() const { return (type << 8) | index; }
QString readable() const;
};
@ -73,6 +83,8 @@ private:
bool lookupLayer(const QPointF& coord, Layer*&);
static void frameCallback(FrameView*, std::shared_ptr<bool>);
Ui::FrameView m_ui;
LayerId m_active{};
@ -80,12 +92,24 @@ private:
int m_glowFrame;
QTimer m_glowTimer;
QMutex m_mutex{QMutex::Recursive};
VFile* m_currentFrame = nullptr;
VFile* m_nextFrame = nullptr;
mCore* m_vl = nullptr;
QImage m_framebuffer;
QSize m_dims;
QList<Layer> m_queue;
QSet<LayerId> m_disabled;
QPixmap m_composited;
QPixmap m_rendered;
mMapCacheEntry m_mapStatus[4][128 * 128] = {}; // TODO: Correct size
#ifdef M_CORE_GBA
uint16_t m_gbaDispcnt;
#endif
std::shared_ptr<bool> m_callbackLocker{std::make_shared<bool>(true)};
};
}

View File

@ -13,7 +13,7 @@
<property name="windowTitle">
<string>Inspect frame</string>
</property>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,1,0,1,0">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,1,1,0" columnstretch="0,1">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
@ -51,7 +51,7 @@
</property>
</widget>
</item>
<item row="4" column="1" rowspan="2">
<item row="5" column="1" rowspan="2">
<widget class="QScrollArea" name="compositedArea">
<property name="widgetResizable">
<bool>true</bool>
@ -61,8 +61,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>591</width>
<height>403</height>
<width>567</width>
<height>382</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -90,7 +90,7 @@
</widget>
</widget>
</item>
<item row="0" column="1" rowspan="4">
<item row="0" column="1" rowspan="5">
<widget class="QScrollArea" name="renderedArea">
<property name="widgetResizable">
<bool>true</bool>
@ -100,8 +100,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>591</width>
<height>446</height>
<width>567</width>
<height>467</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -129,7 +129,10 @@
</widget>
</widget>
</item>
<item row="5" column="0">
<item row="3" column="0" rowspan="3">
<widget class="QListWidget" name="queue"/>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="exportButton">
<property name="enabled">
<bool>false</bool>
@ -139,13 +142,10 @@
</property>
</widget>
</item>
<item row="2" column="0" rowspan="3">
<widget class="QListWidget" name="queue">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="2" column="0">
<widget class="QCheckBox" name="disableScanline">
<property name="text">
<string>Disable scanline effects</string>
</property>
</widget>
</item>