Merge pull request #9803 from Techjar/bbox-videocommon

VideoCommon: Abstract bounding box
This commit is contained in:
Léo Lam
2021-10-08 22:24:38 +02:00
committed by GitHub
41 changed files with 617 additions and 708 deletions

View File

@ -255,7 +255,7 @@ static void BPWritten(const BPCmd& bp)
if (PE_copy.copy_to_xfb == 1)
{
// Make sure we disable Bounding box to match the side effects of the non-failure path
BoundingBox::Disable();
g_renderer->BBoxDisable();
}
return;
@ -287,7 +287,7 @@ static void BPWritten(const BPCmd& bp)
// We should be able to get away with deactivating the current bbox tracking
// here. Not sure if there's a better spot to put this.
// the number of lines copied is determined by the y scale * source efb height
BoundingBox::Disable();
g_renderer->BBoxDisable();
float yScale;
if (PE_copy.scale_invert)
@ -452,7 +452,7 @@ static void BPWritten(const BPCmd& bp)
case BPMEM_CLEARBBOX2:
{
const u8 offset = bp.address & 2;
BoundingBox::Enable();
g_renderer->BBoxEnable();
g_renderer->BBoxWrite(offset, bp.newvalue & 0x3ff);
g_renderer->BBoxWrite(offset + 1, bp.newvalue >> 10);

View File

@ -3,73 +3,115 @@
#include "VideoCommon/BoundingBox.h"
#include <algorithm>
#include <array>
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/VideoConfig.h"
namespace BoundingBox
void BoundingBox::Enable()
{
namespace
{
// Whether or not bounding box is enabled.
bool s_is_active = false;
// Current bounding box coordinates.
std::array<u16, 4> s_coordinates{
0x80,
0xA0,
0x80,
0xA0,
};
} // Anonymous namespace
void Enable()
{
s_is_active = true;
PixelShaderManager::SetBoundingBoxActive(s_is_active);
m_is_active = true;
PixelShaderManager::SetBoundingBoxActive(m_is_active);
}
void Disable()
void BoundingBox::Disable()
{
s_is_active = false;
PixelShaderManager::SetBoundingBoxActive(s_is_active);
m_is_active = false;
PixelShaderManager::SetBoundingBoxActive(m_is_active);
}
bool IsEnabled()
void BoundingBox::Flush()
{
return s_is_active;
if (!g_ActiveConfig.backend_info.bSupportsBBox)
return;
m_is_valid = false;
if (std::none_of(m_dirty.begin(), m_dirty.end(), [](bool dirty) { return dirty; }))
return;
// TODO: Does this make any difference over just writing all the values?
// Games only ever seem to write all 4 values at once anyways.
for (u32 start = 0; start < NUM_BBOX_VALUES; ++start)
{
if (!m_dirty[start])
continue;
u32 end = start + 1;
while (end < NUM_BBOX_VALUES && m_dirty[end])
++end;
for (u32 i = start; i < end; ++i)
m_dirty[i] = false;
Write(start, std::vector<BBoxType>(m_values.begin() + start, m_values.begin() + end));
}
}
u16 GetCoordinate(Coordinate coordinate)
void BoundingBox::Readback()
{
return s_coordinates[static_cast<u32>(coordinate)];
if (!g_ActiveConfig.backend_info.bSupportsBBox)
return;
auto read_values = Read(0, NUM_BBOX_VALUES);
// Preserve dirty values, that way we don't need to sync.
for (u32 i = 0; i < NUM_BBOX_VALUES; i++)
{
if (!m_dirty[i])
m_values[i] = read_values[i];
}
m_is_valid = true;
}
void SetCoordinate(Coordinate coordinate, u16 value)
u16 BoundingBox::Get(u32 index)
{
s_coordinates[static_cast<u32>(coordinate)] = value;
ASSERT(index < NUM_BBOX_VALUES);
if (!m_is_valid)
Readback();
return static_cast<u16>(m_values[index]);
}
void Update(u16 left, u16 right, u16 top, u16 bottom)
void BoundingBox::Set(u32 index, u16 value)
{
const u16 new_left = std::min(left, GetCoordinate(Coordinate::Left));
const u16 new_right = std::max(right, GetCoordinate(Coordinate::Right));
const u16 new_top = std::min(top, GetCoordinate(Coordinate::Top));
const u16 new_bottom = std::max(bottom, GetCoordinate(Coordinate::Bottom));
ASSERT(index < NUM_BBOX_VALUES);
SetCoordinate(Coordinate::Left, new_left);
SetCoordinate(Coordinate::Right, new_right);
SetCoordinate(Coordinate::Top, new_top);
SetCoordinate(Coordinate::Bottom, new_bottom);
if (m_is_valid && m_values[index] == value)
return;
m_values[index] = value;
m_dirty[index] = true;
}
void DoState(PointerWrap& p)
// FIXME: This may not work correctly if we're in the middle of a draw.
// We should probably ensure that state saves only happen on frame boundaries.
// Nonetheless, it has been designed to be as safe as possible.
void BoundingBox::DoState(PointerWrap& p)
{
p.Do(s_is_active);
p.DoArray(s_coordinates);
}
p.Do(m_is_active);
p.DoArray(m_values);
p.DoArray(m_dirty);
p.Do(m_is_valid);
} // namespace BoundingBox
// We handle saving the backend values specially rather than using Readback() and Flush() so that
// we don't mess up the current cache state
std::vector<BBoxType> backend_values(NUM_BBOX_VALUES);
if (p.GetMode() == PointerWrap::MODE_READ)
{
p.Do(backend_values);
if (g_ActiveConfig.backend_info.bSupportsBBox)
Write(0, backend_values);
}
else
{
if (g_ActiveConfig.backend_info.bSupportsBBox)
backend_values = Read(0, NUM_BBOX_VALUES);
p.Do(backend_values);
}
}

View File

@ -3,40 +3,48 @@
#pragma once
#include <array>
#include <vector>
#include "Common/CommonTypes.h"
class PointerWrap;
// Bounding Box manager
namespace BoundingBox
using BBoxType = s32;
constexpr u32 NUM_BBOX_VALUES = 4;
class BoundingBox
{
// Indicates a coordinate of the bounding box.
enum class Coordinate
{
Left, // The X coordinate of the left side of the bounding box.
Right, // The X coordinate of the right side of the bounding box.
Top, // The Y coordinate of the top of the bounding box.
Bottom, // The Y coordinate of the bottom of the bounding box.
public:
explicit BoundingBox() = default;
virtual ~BoundingBox() = default;
bool IsEnabled() const { return m_is_active; }
void Enable();
void Disable();
void Flush();
u16 Get(u32 index);
void Set(u32 index, u16 value);
void DoState(PointerWrap& p);
// Initialize, Read, and Write are only safe to call if the backend supports bounding box,
// otherwise unexpected exceptions can occur
virtual bool Initialize() = 0;
protected:
virtual std::vector<BBoxType> Read(u32 index, u32 length) = 0;
// TODO: This can likely use std::span once we're on C++20
virtual void Write(u32 index, const std::vector<BBoxType>& values) = 0;
private:
void Readback();
bool m_is_active = false;
std::array<BBoxType, NUM_BBOX_VALUES> m_values = {};
std::array<bool, NUM_BBOX_VALUES> m_dirty = {};
bool m_is_valid = true;
};
// Enables bounding box.
void Enable();
// Disables bounding box.
void Disable();
// Determines if bounding box is enabled.
bool IsEnabled();
// Gets a particular coordinate for the bounding box.
u16 GetCoordinate(Coordinate coordinate);
// Sets a particular coordinate for the bounding box.
void SetCoordinate(Coordinate coordinate, u16 value);
// Updates all bounding box coordinates.
void Update(u16 left, u16 right, u16 top, u16 bottom);
// Save state
void DoState(PointerWrap& p);
} // namespace BoundingBox

View File

@ -18,6 +18,7 @@
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/PerfQueryBase.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoBackendBase.h"
namespace PixelEngine
@ -231,7 +232,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
for (int i = 0; i < 4; ++i)
{
mmio->Register(base | (PE_BBOX_LEFT + 2 * i), MMIO::ComplexRead<u16>([i](u32) {
BoundingBox::Disable();
g_renderer->BBoxDisable();
return g_video_backend->Video_GetBoundingBox(i);
}),
MMIO::InvalidWrite<u16>());

View File

@ -14,6 +14,7 @@
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/LightingShaderGen.h"
#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VideoCommon.h"
@ -178,7 +179,7 @@ PixelShaderUid GetPixelShaderUid()
uid_data->genMode_numindstages = bpmem.genMode.numindstages;
uid_data->genMode_numtevstages = bpmem.genMode.numtevstages;
uid_data->genMode_numtexgens = bpmem.genMode.numtexgens;
uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && BoundingBox::IsEnabled();
uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && g_renderer->IsBBoxEnabled();
uid_data->rgba6_format =
bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor;
uid_data->dither = bpmem.blendmode.dither && uid_data->rgba6_format;

View File

@ -57,6 +57,7 @@
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/FPSCounter.h"
@ -124,6 +125,13 @@ bool Renderer::Initialize()
if (!m_post_processor->Initialize(m_backbuffer_format))
return false;
m_bounding_box = CreateBoundingBox();
if (g_ActiveConfig.backend_info.bSupportsBBox && !m_bounding_box->Initialize())
{
PanicAlertFmt("Failed to initialize bounding box.");
return false;
}
return true;
}
@ -137,6 +145,7 @@ void Renderer::Shutdown()
ShutdownFrameDumping();
ShutdownImGui();
m_post_processor.reset();
m_bounding_box.reset();
}
void Renderer::BeginUtilityDrawing()
@ -184,15 +193,30 @@ void Renderer::ReinterpretPixelData(EFBReinterpretType convtype)
g_framebuffer_manager->ReinterpretPixelData(convtype);
}
u16 Renderer::BBoxRead(int index)
bool Renderer::IsBBoxEnabled() const
{
return m_bounding_box->IsEnabled();
}
void Renderer::BBoxEnable()
{
m_bounding_box->Enable();
}
void Renderer::BBoxDisable()
{
m_bounding_box->Disable();
}
u16 Renderer::BBoxRead(u32 index)
{
if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox)
return m_bounding_box_fallback[index];
return BBoxReadImpl(index);
return m_bounding_box->Get(index);
}
void Renderer::BBoxWrite(int index, u16 value)
void Renderer::BBoxWrite(u32 index, u16 value)
{
if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox)
{
@ -200,12 +224,15 @@ void Renderer::BBoxWrite(int index, u16 value)
return;
}
BBoxWriteImpl(index, value);
m_bounding_box->Set(index, value);
}
void Renderer::BBoxFlush()
{
BBoxFlushImpl();
if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox)
return;
m_bounding_box->Flush();
}
u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
@ -1761,6 +1788,8 @@ void Renderer::DoState(PointerWrap& p)
p.Do(m_last_xfb_height);
p.DoArray(m_bounding_box_fallback);
m_bounding_box->DoState(p);
if (p.GetMode() == PointerWrap::MODE_READ)
{
// Force the next xfb to be displayed.

View File

@ -38,6 +38,7 @@ class AbstractPipeline;
class AbstractShader;
class AbstractTexture;
class AbstractStagingTexture;
class BoundingBox;
class NativeVertexFormat;
class NetPlayChatUI;
class PointerWrap;
@ -213,8 +214,11 @@ public:
virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data);
virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points);
u16 BBoxRead(int index);
void BBoxWrite(int index, u16 value);
bool IsBBoxEnabled() const;
void BBoxEnable();
void BBoxDisable();
u16 BBoxRead(u32 index);
void BBoxWrite(u32 index, u16 value);
void BBoxFlush();
virtual void Flush() {}
@ -303,9 +307,7 @@ protected:
// Should be called with the ImGui lock held.
void DrawImGui();
virtual u16 BBoxReadImpl(int index) = 0;
virtual void BBoxWriteImpl(int index, u16 value) = 0;
virtual void BBoxFlushImpl() {}
virtual std::unique_ptr<BoundingBox> CreateBoundingBox() const = 0;
AbstractFramebuffer* m_current_framebuffer = nullptr;
const AbstractPipeline* m_current_pipeline = nullptr;
@ -396,6 +398,8 @@ private:
u32 m_last_xfb_stride = 0;
u32 m_last_xfb_height = 0;
std::unique_ptr<BoundingBox> m_bounding_box;
// Nintendo's SDK seems to write "default" bounding box values before every draw (1023 0 1023 0
// are the only values encountered so far, which happen to be the extents allowed by the BP
// registers) to reset the registers for comparison in the pixel engine, and presumably to detect

View File

@ -269,7 +269,7 @@ void VertexManagerBase::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 nu
void VertexManagerBase::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex)
{
// If bounding box is enabled, we need to flush any changes first, then invalidate what we have.
if (BoundingBox::IsEnabled() && g_ActiveConfig.bBBoxEnable &&
if (g_renderer->IsBBoxEnabled() && g_ActiveConfig.bBBoxEnable &&
g_ActiveConfig.backend_info.bSupportsBBox)
{
g_renderer->BBoxFlush();

View File

@ -5,7 +5,6 @@
#include "Common/ChunkFile.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Fifo.h"
@ -71,9 +70,6 @@ void VideoCommon_DoState(PointerWrap& p)
g_vertex_manager->DoState(p);
p.DoMarker("VertexManager");
BoundingBox::DoState(p);
p.DoMarker("BoundingBox");
g_framebuffer_manager->DoState(p);
p.DoMarker("FramebufferManager");