Merge pull request #1992 from degasus/asyncevents

Asyncevents
This commit is contained in:
skidau 2015-02-23 13:08:39 +11:00
commit 1c2977442f
11 changed files with 273 additions and 175 deletions

View File

@ -0,0 +1,114 @@
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/RenderBase.h"
AsyncRequests AsyncRequests::s_singleton;
AsyncRequests::AsyncRequests()
: m_enable(false), m_passthrough(true)
{
}
void AsyncRequests::PullEventsInternal()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_empty.store(true);
while (!m_queue.empty())
{
const Event& e = m_queue.front();
lock.unlock();
HandleEvent(e);
lock.lock();
m_queue.pop();
}
if (m_wake_me_up_again)
{
m_wake_me_up_again = false;
m_cond.notify_all();
}
}
void AsyncRequests::PushEvent(const AsyncRequests::Event& event, bool blocking)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_passthrough)
{
HandleEvent(event);
return;
}
m_empty.store(false);
m_wake_me_up_again |= blocking;
if (!m_enable)
return;
m_queue.push(event);
if (blocking)
{
m_cond.wait(lock, [this]{return m_queue.empty();});
}
}
void AsyncRequests::SetEnable(bool enable)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_enable = enable;
if (!enable)
{
// flush the queue on disabling
while (!m_queue.empty())
m_queue.pop();
if (m_wake_me_up_again)
m_cond.notify_all();
}
}
void AsyncRequests::HandleEvent(const AsyncRequests::Event& e)
{
EFBRectangle rc;
switch (e.type)
{
case Event::EFB_POKE_COLOR:
g_renderer->AccessEFB(POKE_COLOR, e.efb_poke.x, e.efb_poke.y, e.efb_poke.data);
break;
case Event::EFB_POKE_Z:
g_renderer->AccessEFB(POKE_Z, e.efb_poke.x, e.efb_poke.y, e.efb_poke.data);
break;
case Event::EFB_PEEK_COLOR:
*e.efb_peek.data = g_renderer->AccessEFB(PEEK_COLOR, e.efb_peek.x, e.efb_peek.y, 0);
break;
case Event::EFB_PEEK_Z:
*e.efb_peek.data = g_renderer->AccessEFB(PEEK_Z, e.efb_peek.x, e.efb_peek.y, 0);
break;
case Event::SWAP_EVENT:
Renderer::Swap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, e.swap_event.fbHeight, rc);
break;
case Event::BBOX_READ:
*e.bbox.data = g_renderer->BBoxRead(e.bbox.index);
break;
case Event::PERF_QUERY:
g_perf_query->FlushResults();
break;
}
}
void AsyncRequests::SetPassthrough(bool enable)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_passthrough = enable;
}

View File

@ -0,0 +1,94 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include "Common/CommonTypes.h"
class AsyncRequests
{
public:
struct Event
{
enum Type
{
EFB_POKE_COLOR,
EFB_POKE_Z,
EFB_PEEK_COLOR,
EFB_PEEK_Z,
SWAP_EVENT,
BBOX_READ,
PERF_QUERY,
} type;
u64 time;
union
{
struct
{
u16 x;
u16 y;
u32 data;
} efb_poke;
struct
{
u16 x;
u16 y;
u32* data;
} efb_peek;
struct
{
u32 xfbAddr;
u32 fbWidth;
u32 fbStride;
u32 fbHeight;
} swap_event;
struct
{
int index;
u16* data;
} bbox;
struct
{
} perf_query;
};
};
AsyncRequests();
void PullEvents()
{
if (!m_empty.load())
PullEventsInternal();
}
void PushEvent(const Event& event, bool blocking = false);
void SetEnable(bool enable);
void SetPassthrough(bool enable);
static AsyncRequests* GetInstance() { return &s_singleton; }
private:
void PullEventsInternal();
void HandleEvent(const Event& e);
static AsyncRequests s_singleton;
std::atomic<bool> m_empty;
std::queue<Event> m_queue;
std::mutex m_mutex;
std::condition_variable m_cond;
bool m_wake_me_up_again;
bool m_enable;
bool m_passthrough;
};

View File

@ -1,4 +1,5 @@
set(SRCS BoundingBox.cpp set(SRCS AsyncRequests.cpp
BoundingBox.cpp
BPFunctions.cpp BPFunctions.cpp
BPMemory.cpp BPMemory.cpp
BPStructs.cpp BPStructs.cpp

View File

@ -15,6 +15,7 @@
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/CommandProcessor.h" #include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/CPMemory.h" #include "VideoCommon/CPMemory.h"
#include "VideoCommon/DataReader.h" #include "VideoCommon/DataReader.h"
@ -282,11 +283,14 @@ void RunGpuLoop()
// This allows a system that we are maxing out in dual core mode to do other things // This allows a system that we are maxing out in dual core mode to do other things
bool yield_cpu = cpu_info.num_cores <= 2; bool yield_cpu = cpu_info.num_cores <= 2;
AsyncRequests::GetInstance()->SetEnable(true);
AsyncRequests::GetInstance()->SetPassthrough(false);
while (GpuRunningState) while (GpuRunningState)
{ {
g_video_backend->PeekMessages(); g_video_backend->PeekMessages();
VideoFifo_CheckAsyncRequest(); AsyncRequests::GetInstance()->PullEvents();
if (g_use_deterministic_gpu_thread) if (g_use_deterministic_gpu_thread)
{ {
// All the fifo/CP stuff is on the CPU. We just need to run the opcode decoder. // All the fifo/CP stuff is on the CPU. We just need to run the opcode decoder.
@ -348,7 +352,7 @@ void RunGpuLoop()
// This call is pretty important in DualCore mode and must be called in the FIFO Loop. // This call is pretty important in DualCore mode and must be called in the FIFO Loop.
// If we don't, s_swapRequested or s_efbAccessRequested won't be set to false // If we don't, s_swapRequested or s_efbAccessRequested won't be set to false
// leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing things down. // leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing things down.
VideoFifo_CheckAsyncRequest(); AsyncRequests::GetInstance()->PullEvents();
CommandProcessor::isPossibleWaitingSetDrawDone = false; CommandProcessor::isPossibleWaitingSetDrawDone = false;
} }
@ -377,6 +381,8 @@ void RunGpuLoop()
} }
// wake up SyncGPU if we were interrupted // wake up SyncGPU if we were interrupted
s_video_buffer_cond.notify_all(); s_video_buffer_cond.notify_all();
AsyncRequests::GetInstance()->SetEnable(false);
AsyncRequests::GetInstance()->SetPassthrough(true);
} }

View File

@ -53,7 +53,3 @@ void EmulatorState(bool running);
bool AtBreakpoint(); bool AtBreakpoint();
void ResetVideoBuffer(); void ResetVideoBuffer();
void Fifo_SetRendering(bool bEnabled); void Fifo_SetRendering(bool bEnabled);
// Implemented by the Video Backend
void VideoFifo_CheckAsyncRequest();

View File

@ -1,6 +1,7 @@
#include "Common/Event.h" #include "Common/Event.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/BoundingBox.h" #include "VideoCommon/BoundingBox.h"
#include "VideoCommon/BPStructs.h" #include "VideoCommon/BPStructs.h"
#include "VideoCommon/CommandProcessor.h" #include "VideoCommon/CommandProcessor.h"
@ -18,18 +19,7 @@
bool s_BackendInitialized = false; bool s_BackendInitialized = false;
Common::Flag s_swapRequested;
static Common::Flag s_FifoShuttingDown; static Common::Flag s_FifoShuttingDown;
static Common::Flag s_efbAccessRequested;
static Common::Event s_efbAccessReadyEvent;
static Common::Flag s_perfQueryRequested;
static Common::Event s_perfQueryReadyEvent;
static Common::Flag s_BBoxRequested;
static Common::Event s_BBoxReadyEvent;
static int s_BBoxIndex;
static u16 s_BBoxResult;
static volatile struct static volatile struct
{ {
@ -39,16 +29,6 @@ static volatile struct
u32 fbHeight; u32 fbHeight;
} s_beginFieldArgs; } s_beginFieldArgs;
static struct
{
EFBAccessType type;
u32 x;
u32 y;
u32 Data;
} s_accessEFBArgs;
static u32 s_AccessEFBResult = 0;
void VideoBackendHardware::EmuStateChange(EMUSTATE_CHANGE newState) void VideoBackendHardware::EmuStateChange(EMUSTATE_CHANGE newState)
{ {
EmulatorState((newState == EMUSTATE_CHANGE_PLAY) ? true : false); EmulatorState((newState == EMUSTATE_CHANGE_PLAY) ? true : false);
@ -64,8 +44,6 @@ void VideoBackendHardware::Video_ExitLoop()
{ {
ExitGpuLoop(); ExitGpuLoop();
s_FifoShuttingDown.Set(); s_FifoShuttingDown.Set();
s_efbAccessReadyEvent.Set();
s_perfQueryReadyEvent.Set();
} }
void VideoBackendHardware::Video_SetRendering(bool bEnabled) void VideoBackendHardware::Video_SetRendering(bool bEnabled)
@ -73,45 +51,11 @@ void VideoBackendHardware::Video_SetRendering(bool bEnabled)
Fifo_SetRendering(bEnabled); Fifo_SetRendering(bEnabled);
} }
// Run from the graphics thread (from Fifo.cpp)
static void VideoFifo_CheckSwapRequest()
{
if (g_ActiveConfig.bUseXFB)
{
if (s_swapRequested.IsSet())
{
EFBRectangle rc;
Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbStride, s_beginFieldArgs.fbHeight, rc);
s_swapRequested.Clear();
}
}
}
// Run from the graphics thread (from Fifo.cpp)
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
if (g_ActiveConfig.bUseXFB)
{
if (s_swapRequested.IsSet())
{
u32 aLower = xfbAddr;
u32 aUpper = xfbAddr + 2 * fbWidth * fbHeight;
u32 bLower = s_beginFieldArgs.xfbAddr;
u32 bUpper = s_beginFieldArgs.xfbAddr + 2 * s_beginFieldArgs.fbStride * s_beginFieldArgs.fbHeight;
if (AddressRangesOverlap(aLower, aUpper, bLower, bUpper))
VideoFifo_CheckSwapRequest();
}
}
}
// Run from the CPU thread (from VideoInterface.cpp) // Run from the CPU thread (from VideoInterface.cpp)
void VideoBackendHardware::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight) void VideoBackendHardware::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight)
{ {
if (s_BackendInitialized && g_ActiveConfig.bUseXFB) if (s_BackendInitialized && g_ActiveConfig.bUseXFB)
{ {
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread)
VideoFifo_CheckSwapRequest();
s_beginFieldArgs.xfbAddr = xfbAddr; s_beginFieldArgs.xfbAddr = xfbAddr;
s_beginFieldArgs.fbWidth = fbWidth; s_beginFieldArgs.fbWidth = fbWidth;
s_beginFieldArgs.fbStride = fbStride; s_beginFieldArgs.fbStride = fbStride;
@ -122,16 +66,19 @@ void VideoBackendHardware::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStri
// Run from the CPU thread (from VideoInterface.cpp) // Run from the CPU thread (from VideoInterface.cpp)
void VideoBackendHardware::Video_EndField() void VideoBackendHardware::Video_EndField()
{ {
if (s_BackendInitialized) if (s_BackendInitialized && g_ActiveConfig.bUseXFB && g_renderer)
{ {
SyncGPU(SYNC_GPU_SWAP); SyncGPU(SYNC_GPU_SWAP);
// Wait until the GPU thread has swapped. Prevents FIFO overflows. AsyncRequests::Event e;
while (g_ActiveConfig.bUseXFB && SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread && s_swapRequested.IsSet()) e.time = 0;
{ e.type = AsyncRequests::Event::SWAP_EVENT;
Common::YieldCPU();
} e.swap_event.xfbAddr = s_beginFieldArgs.xfbAddr;
s_swapRequested.Set(); e.swap_event.fbWidth = s_beginFieldArgs.fbWidth;
e.swap_event.fbStride = s_beginFieldArgs.fbStride;
e.swap_event.fbHeight = s_beginFieldArgs.fbHeight;
AsyncRequests::GetInstance()->PushEvent(e, false);
} }
} }
@ -152,53 +99,35 @@ bool VideoBackendHardware::Video_Screenshot(const std::string& filename)
return true; return true;
} }
void VideoFifo_CheckEFBAccess()
{
if (s_efbAccessRequested.IsSet())
{
s_AccessEFBResult = g_renderer->AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y, s_accessEFBArgs.Data);
s_efbAccessRequested.Clear();
s_efbAccessReadyEvent.Set();
}
}
u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData)
{ {
if (s_BackendInitialized && g_ActiveConfig.bEFBAccessEnable) if (!g_ActiveConfig.bEFBAccessEnable)
{ {
SyncGPU(SYNC_GPU_EFB_POKE);
s_accessEFBArgs.type = type;
s_accessEFBArgs.x = x;
s_accessEFBArgs.y = y;
s_accessEFBArgs.Data = InputData;
s_efbAccessRequested.Set();
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread)
{
s_efbAccessReadyEvent.Reset();
if (s_FifoShuttingDown.IsSet())
return 0; return 0;
s_efbAccessRequested.Set(); }
s_efbAccessReadyEvent.Wait();
if (type == POKE_COLOR || type == POKE_Z)
{
AsyncRequests::Event e;
e.type = type == POKE_COLOR ? AsyncRequests::Event::EFB_POKE_COLOR : AsyncRequests::Event::EFB_POKE_Z;
e.time = 0;
e.efb_poke.data = InputData;
e.efb_poke.x = x;
e.efb_poke.y = y;
AsyncRequests::GetInstance()->PushEvent(e, false);
return 0;
} }
else else
VideoFifo_CheckEFBAccess();
return s_AccessEFBResult;
}
return 0;
}
static void VideoFifo_CheckPerfQueryRequest()
{
if (s_perfQueryRequested.IsSet())
{ {
g_perf_query->FlushResults(); AsyncRequests::Event e;
s_perfQueryRequested.Clear(); u32 result;
s_perfQueryReadyEvent.Set(); e.type = type == PEEK_COLOR ? AsyncRequests::Event::EFB_PEEK_COLOR : AsyncRequests::Event::EFB_PEEK_Z;
e.time = 0;
e.efb_peek.x = x;
e.efb_peek.y = y;
e.efb_peek.data = &result;
AsyncRequests::GetInstance()->PushEvent(e, true);
return result;
} }
} }
@ -211,20 +140,12 @@ u32 VideoBackendHardware::Video_GetQueryResult(PerfQueryType type)
SyncGPU(SYNC_GPU_PERFQUERY); SyncGPU(SYNC_GPU_PERFQUERY);
// TODO: Is this check sane? AsyncRequests::Event e;
e.time = 0;
e.type = AsyncRequests::Event::PERF_QUERY;
if (!g_perf_query->IsFlushed()) if (!g_perf_query->IsFlushed())
{ AsyncRequests::GetInstance()->PushEvent(e, true);
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread)
{
s_perfQueryReadyEvent.Reset();
if (s_FifoShuttingDown.IsSet())
return 0;
s_perfQueryRequested.Set();
s_perfQueryReadyEvent.Wait();
}
else
g_perf_query->FlushResults();
}
return g_perf_query->GetQueryResult(type); return g_perf_query->GetQueryResult(type);
} }
@ -236,43 +157,23 @@ u16 VideoBackendHardware::Video_GetBoundingBox(int index)
SyncGPU(SYNC_GPU_BBOX); SyncGPU(SYNC_GPU_BBOX);
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread) AsyncRequests::Event e;
{ u16 result;
s_BBoxReadyEvent.Reset(); e.time = 0;
if (s_FifoShuttingDown.IsSet()) e.type = AsyncRequests::Event::BBOX_READ;
return 0; e.bbox.index = index;
s_BBoxIndex = index; e.bbox.data = &result;
s_BBoxRequested.Set(); AsyncRequests::GetInstance()->PushEvent(e, true);
s_BBoxReadyEvent.Wait();
return s_BBoxResult;
}
else
{
return g_renderer->BBoxRead(index);
}
}
static void VideoFifo_CheckBBoxRequest() return result;
{
if (s_BBoxRequested.IsSet())
{
s_BBoxResult = g_renderer->BBoxRead(s_BBoxIndex);
s_BBoxRequested.Clear();
s_BBoxReadyEvent.Set();
}
} }
void VideoBackendHardware::InitializeShared() void VideoBackendHardware::InitializeShared()
{ {
VideoCommon_Init(); VideoCommon_Init();
s_swapRequested.Clear();
s_efbAccessRequested.Clear();
s_perfQueryRequested.Clear();
s_FifoShuttingDown.Clear(); s_FifoShuttingDown.Clear();
memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs));
memset(&s_accessEFBArgs, 0, sizeof(s_accessEFBArgs));
s_AccessEFBResult = 0;
m_invalid = false; m_invalid = false;
} }
@ -291,11 +192,7 @@ void VideoBackendHardware::DoState(PointerWrap& p)
VideoCommon_DoState(p); VideoCommon_DoState(p);
p.DoMarker("VideoCommon"); p.DoMarker("VideoCommon");
p.Do(s_swapRequested);
p.Do(s_efbAccessRequested);
p.Do(s_beginFieldArgs); p.Do(s_beginFieldArgs);
p.Do(s_accessEFBArgs);
p.Do(s_AccessEFBResult);
p.DoMarker("VideoBackendHardware"); p.DoMarker("VideoBackendHardware");
// Refresh state. // Refresh state.
@ -326,20 +223,11 @@ void VideoBackendHardware::PauseAndLock(bool doLock, bool unpauseOnUnlock)
Fifo_PauseAndLock(doLock, unpauseOnUnlock); Fifo_PauseAndLock(doLock, unpauseOnUnlock);
} }
void VideoBackendHardware::RunLoop(bool enable) void VideoBackendHardware::RunLoop(bool enable)
{ {
VideoCommon_RunLoop(enable); VideoCommon_RunLoop(enable);
} }
void VideoFifo_CheckAsyncRequest()
{
VideoFifo_CheckSwapRequest();
VideoFifo_CheckEFBAccess();
VideoFifo_CheckPerfQueryRequest();
VideoFifo_CheckBBoxRequest();
}
void VideoBackendHardware::Video_GatherPipeBursted() void VideoBackendHardware::Video_GatherPipeBursted()
{ {
CommandProcessor::GatherPipeBursted(); CommandProcessor::GatherPipeBursted();

View File

@ -4,7 +4,3 @@
#include "Common/Flag.h" #include "Common/Flag.h"
extern bool s_BackendInitialized; extern bool s_BackendInitialized;
extern Common::Flag s_swapRequested;
void VideoFifo_CheckEFBAccess();
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight);

View File

@ -116,8 +116,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidt
if (!fbWidth || !fbHeight) if (!fbWidth || !fbHeight)
return; return;
VideoFifo_CheckEFBAccess();
VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
XFBWrited = true; XFBWrited = true;
if (g_ActiveConfig.bUseXFB) if (g_ActiveConfig.bUseXFB)
@ -127,7 +125,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidt
else else
{ {
Swap(xfbAddr, fbWidth, fbWidth, fbHeight, sourceRc, Gamma); Swap(xfbAddr, fbWidth, fbWidth, fbHeight, sourceRc, Gamma);
s_swapRequested.Clear();
} }
} }

View File

@ -166,8 +166,6 @@ void VertexManager::Flush()
// loading a state will invalidate BP, so check for it // loading a state will invalidate BP, so check for it
g_video_backend->CheckInvalidState(); g_video_backend->CheckInvalidState();
VideoFifo_CheckEFBAccess();
#if defined(_DEBUG) || defined(DEBUGFAST) #if defined(_DEBUG) || defined(DEBUGFAST)
PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfmem.numTexGen.numTexGens, PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfmem.numTexGen.numTexGens,
xfmem.numChan.numColorChans, xfmem.dualTexTrans.enabled, bpmem.ztex2.op, xfmem.numChan.numColorChans, xfmem.dualTexTrans.enabled, bpmem.ztex2.op,

View File

@ -35,6 +35,7 @@
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<ItemGroup> <ItemGroup>
<ClCompile Include="AsyncRequests.cpp" />
<ClCompile Include="AVIDump.cpp" /> <ClCompile Include="AVIDump.cpp" />
<ClCompile Include="BoundingBox.cpp" /> <ClCompile Include="BoundingBox.cpp" />
<ClCompile Include="BPFunctions.cpp" /> <ClCompile Include="BPFunctions.cpp" />
@ -84,6 +85,7 @@
<ClCompile Include="XFStructs.cpp" /> <ClCompile Include="XFStructs.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="AsyncRequests.h" />
<ClInclude Include="AVIDump.h" /> <ClInclude Include="AVIDump.h" />
<ClInclude Include="BoundingBox.h" /> <ClInclude Include="BoundingBox.h" />
<ClInclude Include="BPFunctions.h" /> <ClInclude Include="BPFunctions.h" />

View File

@ -146,6 +146,9 @@
<ClCompile Include="TextureDecoder_x64.cpp"> <ClCompile Include="TextureDecoder_x64.cpp">
<Filter>Decoding</Filter> <Filter>Decoding</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="AsyncRequests.cpp">
<Filter>Util</Filter>
</ClCompile>
<ClCompile Include="BoundingBox.cpp"> <ClCompile Include="BoundingBox.cpp">
<Filter>Util</Filter> <Filter>Util</Filter>
</ClCompile> </ClCompile>
@ -290,6 +293,9 @@
<ClInclude Include="VertexLoaderUtils.h"> <ClInclude Include="VertexLoaderUtils.h">
<Filter>Vertex Loading</Filter> <Filter>Vertex Loading</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="AsyncRequests.h">
<Filter>Util</Filter>
</ClInclude>
<ClInclude Include="BoundingBox.h"> <ClInclude Include="BoundingBox.h">
<Filter>Util</Filter> <Filter>Util</Filter>
</ClInclude> </ClInclude>