diff --git a/Source/Core/VideoBackends/D3D/BoundingBox.cpp b/Source/Core/VideoBackends/D3D/BoundingBox.cpp
new file mode 100644
index 0000000000..727d5a9fac
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/BoundingBox.cpp
@@ -0,0 +1,95 @@
+// Copyright 2014 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "VideoBackends/D3D/BoundingBox.h"
+
+#include "VideoCommon/VideoConfig.h"
+
+namespace DX11
+{
+
+static ID3D11Buffer* s_bbox_buffer;
+static ID3D11Buffer* s_bbox_Readbuffer;
+static ID3D11UnorderedAccessView* s_bbox_uav;
+
+ID3D11UnorderedAccessView* &BBox::GetUAV()
+{
+ return s_bbox_uav;
+}
+
+void BBox::Init()
+{
+ if (g_ActiveConfig.backend_info.bSupportsBBox)
+ {
+ // create the pool texture here
+ auto desc = CD3D11_BUFFER_DESC(4 * sizeof(s32), D3D11_BIND_UNORDERED_ACCESS, D3D11_USAGE_DEFAULT, 0, 0, 4);
+ int initial_values[4] = { 0, 0, 0, 0 };
+ D3D11_SUBRESOURCE_DATA data;
+ data.pSysMem = initial_values;
+ data.SysMemPitch = 4 * sizeof(s32);
+ data.SysMemSlicePitch = 0;
+ HRESULT hr;
+ hr = D3D::device->CreateBuffer(&desc, &data, &s_bbox_buffer);
+ CHECK(SUCCEEDED(hr), "create bbox buffer");
+ D3D::SetDebugObjectName((ID3D11DeviceChild*)s_bbox_buffer, "boundingbox buffer");
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.BindFlags = 0;
+ hr = D3D::device->CreateBuffer(&desc, nullptr, &s_bbox_Readbuffer);
+ CHECK(SUCCEEDED(hr), "create bbox staging buffer");
+ D3D::SetDebugObjectName((ID3D11DeviceChild*)s_bbox_Readbuffer, "boundingbox staging buffer");
+
+ D3D11_UNORDERED_ACCESS_VIEW_DESC UAVdesc;
+ memset(&UAVdesc, 0, sizeof(UAVdesc));
+ UAVdesc.Format = DXGI_FORMAT_R32_SINT;
+ UAVdesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
+ UAVdesc.Buffer.FirstElement = 0;
+ UAVdesc.Buffer.Flags = 0;
+ UAVdesc.Buffer.NumElements = 4;
+ hr = D3D::device->CreateUnorderedAccessView(s_bbox_buffer, &UAVdesc, &s_bbox_uav);
+ CHECK(SUCCEEDED(hr), "create bbox UAV");
+ D3D::SetDebugObjectName((ID3D11DeviceChild*)s_bbox_uav, "boundingbox UAV");
+ }
+}
+
+void BBox::Shutdown()
+{
+ if (s_bbox_buffer != nullptr)
+ {
+ s_bbox_buffer->Release();
+ s_bbox_buffer = nullptr;
+ }
+ if (s_bbox_Readbuffer != nullptr)
+ {
+ s_bbox_Readbuffer->Release();
+ s_bbox_Readbuffer = nullptr;
+ }
+ if (s_bbox_uav != nullptr)
+ {
+ s_bbox_uav->Release();
+ s_bbox_uav = nullptr;
+ }
+}
+
+void BBox::Set(int index, int value)
+{
+ D3D11_BOX box{ index * sizeof(s32), 0, 0, (index + 1) * sizeof(s32), 1, 1 };
+ D3D::context->UpdateSubresource(s_bbox_buffer, 0, &box, &value, 0, 0);
+}
+
+int BBox::Get(int index)
+{
+ int data = 0;
+ D3D::context->CopyResource(s_bbox_Readbuffer, s_bbox_buffer);
+ D3D11_MAPPED_SUBRESOURCE map;
+ HRESULT hr = D3D::context->Map(s_bbox_Readbuffer, 0, D3D11_MAP_READ, 0, &map);
+ if (SUCCEEDED(hr))
+ {
+ data = ((s32*)map.pData)[index];
+ }
+ D3D::context->Unmap(s_bbox_Readbuffer, 0);
+ return data;
+}
+
+};
diff --git a/Source/Core/VideoBackends/D3D/BoundingBox.h b/Source/Core/VideoBackends/D3D/BoundingBox.h
new file mode 100644
index 0000000000..8fd359a356
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D/BoundingBox.h
@@ -0,0 +1,22 @@
+// Copyright 2014 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+#include "VideoBackends/D3D/D3DBase.h"
+
+namespace DX11
+{
+
+class BBox
+{
+public:
+ static ID3D11UnorderedAccessView* &GetUAV();
+ static void Init();
+ static void Shutdown();
+
+ static void Set(int index, int value);
+ static int Get(int index);
+};
+
+};
diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj b/Source/Core/VideoBackends/D3D/D3D.vcxproj
index 41e1c18529..7ea9e09d07 100644
--- a/Source/Core/VideoBackends/D3D/D3D.vcxproj
+++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj
@@ -35,6 +35,7 @@
+
@@ -57,6 +58,7 @@
+
diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
index a552f47216..c0158038b2 100644
--- a/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
+++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -67,6 +67,9 @@
Render
+
+ Render
+
@@ -129,5 +132,8 @@
+
+ Render
+
\ No newline at end of file
diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp
index 6adf3ac52f..c393afeb50 100644
--- a/Source/Core/VideoBackends/D3D/Render.cpp
+++ b/Source/Core/VideoBackends/D3D/Render.cpp
@@ -16,6 +16,7 @@
#include "Core/Host.h"
#include "Core/Movie.h"
+#include "VideoBackends/D3D/BoundingBox.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h"
@@ -1213,4 +1214,43 @@ int Renderer::GetMaxTextureSize()
return DX11::D3D::GetMaxTextureSize();
}
+u16 Renderer::BBoxRead(int index)
+{
+ // Here we get the min/max value of the truncated position of the upscaled framebuffer.
+ // So we have to correct them to the unscaled EFB sizes.
+ int value = BBox::Get(index);
+
+ if (index < 2)
+ {
+ // left/right
+ value = value * EFB_WIDTH / s_target_width;
+ }
+ else
+ {
+ // up/down
+ value = value * EFB_HEIGHT / s_target_height;
+ }
+ if (index & 1)
+ value++; // fix max values to describe the outer border
+
+ return value;
+}
+
+void Renderer::BBoxWrite(int index, u16 _value)
+{
+ int value = _value; // u16 isn't enough to multiply by the efb width
+ if (index & 1)
+ value--;
+ if (index < 2)
+ {
+ value = value * s_target_width / EFB_WIDTH;
+ }
+ else
+ {
+ value = value * s_target_height / EFB_HEIGHT;
+ }
+
+ BBox::Set(index, value);
+}
+
} // namespace DX11
diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h
index 8b9fe9d87f..ac03cace98 100644
--- a/Source/Core/VideoBackends/D3D/Render.h
+++ b/Source/Core/VideoBackends/D3D/Render.h
@@ -34,8 +34,8 @@ public:
void RenderText(const std::string& text, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
- u16 BBoxRead(int index) override { return 0; };
- void BBoxWrite(int index, u16 value) override {};
+ u16 BBoxRead(int index) override;
+ void BBoxWrite(int index, u16 value) override;
void ResetAPIState() override;
void RestoreAPIState() override;
diff --git a/Source/Core/VideoBackends/D3D/VertexManager.cpp b/Source/Core/VideoBackends/D3D/VertexManager.cpp
index a59ea63dea..9e1b5ea17f 100644
--- a/Source/Core/VideoBackends/D3D/VertexManager.cpp
+++ b/Source/Core/VideoBackends/D3D/VertexManager.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include "VideoBackends/D3D/BoundingBox.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/PixelShaderCache.h"
#include "VideoBackends/D3D/Render.h"
#include "VideoBackends/D3D/VertexManager.h"
#include "VideoBackends/D3D/VertexShaderCache.h"
+#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/Debugger.h"
#include "VideoCommon/IndexGenerator.h"
@@ -206,7 +208,10 @@ void VertexManager::vFlush(bool useDstAlpha)
GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR,true,{printf("Fail to set pixel shader\n");});
return;
}
-
+ if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active)
+ {
+ D3D::context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, nullptr, nullptr, 2, 1, &BBox::GetUAV(), nullptr);
+ }
u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride();
PrepareDrawBuffers(stride);
diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp
index ed5868c91b..9d53f974f6 100644
--- a/Source/Core/VideoBackends/D3D/main.cpp
+++ b/Source/Core/VideoBackends/D3D/main.cpp
@@ -13,6 +13,7 @@
#include "Core/Core.h"
#include "Core/Host.h"
+#include "VideoBackends/D3D/BoundingBox.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DUtil.h"
#include "VideoBackends/D3D/Globals.h"
@@ -77,7 +78,7 @@ void InitBackendInfo()
g_Config.backend_info.bSupportsDualSourceBlend = true;
g_Config.backend_info.bSupportsPrimitiveRestart = true;
g_Config.backend_info.bSupportsOversizedViewports = false;
- g_Config.backend_info.bSupportsBBox = false; // TODO: not implemented
+ g_Config.backend_info.bSupportsBBox = true;
g_Config.backend_info.bSupportsStereoscopy = false; // TODO: not implemented
IDXGIFactory* factory;
@@ -180,6 +181,7 @@ void VideoBackend::Video_Prepare()
PixelShaderManager::Init();
CommandProcessor::Init();
PixelEngine::Init();
+ BBox::Init();
// Tell the host that the window is ready
Host_Message(WM_USER_CREATE);
@@ -204,6 +206,8 @@ void VideoBackend::Shutdown()
D3D::ShutdownUtils();
PixelShaderCache::Shutdown();
VertexShaderCache::Shutdown();
+ BBox::Shutdown();
+
delete g_perf_query;
delete g_vertex_manager;
delete g_texture_cache;
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index ae27c3f586..8b58ee00e8 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -261,11 +261,20 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
if (g_ActiveConfig.backend_info.bSupportsBBox)
{
- out.Write(
- "layout(std140, binding = 3) buffer BBox {\n"
- "\tint4 bbox_data;\n"
- "};\n"
- );
+ if (ApiType == API_OPENGL)
+ {
+ out.Write(
+ "layout(std140, binding = 3) buffer BBox {\n"
+ "\tint4 bbox_data;\n"
+ "};\n"
+ );
+ }
+ else
+ {
+ out.Write(
+ "globallycoherent RWBuffer bbox_data : register(u2);\n"
+ );
+ }
}
GenerateVSOutputStruct(out, ApiType);
@@ -582,12 +591,24 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active)
{
uid_data->bounding_box = true;
- out.Write(
- "\tif(bbox_data.x > int(gl_FragCoord.x)) atomicMin(bbox_data.x, int(gl_FragCoord.x));\n"
- "\tif(bbox_data.y < int(gl_FragCoord.x)) atomicMax(bbox_data.y, int(gl_FragCoord.x));\n"
- "\tif(bbox_data.z > int(gl_FragCoord.y)) atomicMin(bbox_data.z, int(gl_FragCoord.y));\n"
- "\tif(bbox_data.w < int(gl_FragCoord.y)) atomicMax(bbox_data.w, int(gl_FragCoord.y));\n"
- );
+ if (ApiType == API_OPENGL)
+ {
+ out.Write(
+ "\tif(bbox_data.x > int(gl_FragCoord.x)) atomicMin(bbox_data.x, int(gl_FragCoord.x));\n"
+ "\tif(bbox_data.y < int(gl_FragCoord.x)) atomicMax(bbox_data.y, int(gl_FragCoord.x));\n"
+ "\tif(bbox_data.z > int(gl_FragCoord.y)) atomicMin(bbox_data.z, int(gl_FragCoord.y));\n"
+ "\tif(bbox_data.w < int(gl_FragCoord.y)) atomicMax(bbox_data.w, int(gl_FragCoord.y));\n"
+ );
+ }
+ else
+ {
+ out.Write(
+ "\tif(bbox_data[0] > int(rawpos.x)) InterlockedMin(bbox_data[0], int(rawpos.x));\n"
+ "\tif(bbox_data[1] < int(rawpos.x)) InterlockedMax(bbox_data[1], int(rawpos.x));\n"
+ "\tif(bbox_data[2] > int(rawpos.y)) InterlockedMin(bbox_data[2], int(rawpos.y));\n"
+ "\tif(bbox_data[3] < int(rawpos.y)) InterlockedMax(bbox_data[3], int(rawpos.y));\n"
+ );
+ }
}
out.Write("}\n");