summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2021-10-08 22:24:38 +0200
committerGitHub <noreply@github.com>2021-10-08 22:24:38 +0200
commitff1cb5a1c08d5a4a2a0449b8b23a4977ccc20f8a (patch)
treea584d167e0c71c08d4d885f60aa071f5a577692e /Source/Core/VideoCommon
parentd90b30ca251146c478759190d9b206f38c3b70d3 (diff)
parent1161af80594c403233e3d21589af004ee2d75594 (diff)
Merge pull request #9803 from Techjar/bbox-videocommon
VideoCommon: Abstract bounding box
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/BPStructs.cpp6
-rw-r--r--Source/Core/VideoCommon/BoundingBox.cpp134
-rw-r--r--Source/Core/VideoCommon/BoundingBox.h58
-rw-r--r--Source/Core/VideoCommon/PixelEngine.cpp3
-rw-r--r--Source/Core/VideoCommon/PixelShaderGen.cpp3
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp39
-rw-r--r--Source/Core/VideoCommon/RenderBase.h14
-rw-r--r--Source/Core/VideoCommon/VertexManagerBase.cpp2
-rw-r--r--Source/Core/VideoCommon/VideoState.cpp4
9 files changed, 172 insertions, 91 deletions
diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp
index 1ea2cbb5c2..fb6372f09c 100644
--- a/Source/Core/VideoCommon/BPStructs.cpp
+++ b/Source/Core/VideoCommon/BPStructs.cpp
@@ -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);
diff --git a/Source/Core/VideoCommon/BoundingBox.cpp b/Source/Core/VideoCommon/BoundingBox.cpp
index d4c8c57bf2..d056efa167 100644
--- a/Source/Core/VideoCommon/BoundingBox.cpp
+++ b/Source/Core/VideoCommon/BoundingBox.cpp
@@ -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
-{
-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()
+void BoundingBox::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));
-
- SetCoordinate(Coordinate::Left, new_left);
- SetCoordinate(Coordinate::Right, new_right);
- SetCoordinate(Coordinate::Top, new_top);
- SetCoordinate(Coordinate::Bottom, new_bottom);
+ ASSERT(index < NUM_BBOX_VALUES);
+
+ 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);
+
+ // 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);
-} // namespace BoundingBox
+ 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);
+ }
+}
diff --git a/Source/Core/VideoCommon/BoundingBox.h b/Source/Core/VideoCommon/BoundingBox.h
index caf123a13e..8583986a36 100644
--- a/Source/Core/VideoCommon/BoundingBox.h
+++ b/Source/Core/VideoCommon/BoundingBox.h
@@ -3,40 +3,48 @@
#pragma once
+#include <array>
+#include <vector>
+
#include "Common/CommonTypes.h"
class PointerWrap;
-// Bounding Box manager
-namespace BoundingBox
-{
-// Indicates a coordinate of the bounding box.
-enum class Coordinate
+using BBoxType = s32;
+constexpr u32 NUM_BBOX_VALUES = 4;
+
+class BoundingBox
{
- 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;
-// Enables bounding box.
-void Enable();
+ bool IsEnabled() const { return m_is_active; }
+ void Enable();
+ void Disable();
-// Disables bounding box.
-void Disable();
+ void Flush();
-// Determines if bounding box is enabled.
-bool IsEnabled();
+ u16 Get(u32 index);
+ void Set(u32 index, u16 value);
-// Gets a particular coordinate for the bounding box.
-u16 GetCoordinate(Coordinate coordinate);
+ void DoState(PointerWrap& p);
-// Sets a particular coordinate for the bounding box.
-void SetCoordinate(Coordinate coordinate, u16 value);
+ // Initialize, Read, and Write are only safe to call if the backend supports bounding box,
+ // otherwise unexpected exceptions can occur
+ virtual bool Initialize() = 0;
-// Updates all bounding box coordinates.
-void Update(u16 left, u16 right, u16 top, u16 bottom);
+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;
-// Save state
-void DoState(PointerWrap& p);
-} // namespace BoundingBox
+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;
+};
diff --git a/Source/Core/VideoCommon/PixelEngine.cpp b/Source/Core/VideoCommon/PixelEngine.cpp
index e73cb936e9..87d58eb010 100644
--- a/Source/Core/VideoCommon/PixelEngine.cpp
+++ b/Source/Core/VideoCommon/PixelEngine.cpp
@@ -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>());
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 8827c47868..5f5b41e081 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -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;
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 6771df83a3..45e42e7931 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -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.
diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h
index ddc86cd3b3..380225b133 100644
--- a/Source/Core/VideoCommon/RenderBase.h
+++ b/Source/Core/VideoCommon/RenderBase.h
@@ -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
diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp
index f78ec9fca2..73ab9af3c2 100644
--- a/Source/Core/VideoCommon/VertexManagerBase.cpp
+++ b/Source/Core/VideoCommon/VertexManagerBase.cpp
@@ -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();
diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp
index bef1afe888..f954ca7c30 100644
--- a/Source/Core/VideoCommon/VideoState.cpp
+++ b/Source/Core/VideoCommon/VideoState.cpp
@@ -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");