1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Improved rendering performance for indexed and system memory primitives

This commit is contained in:
narzoul 2020-05-26 00:28:17 +02:00
parent ca9404ec6f
commit bff7253ecd
9 changed files with 439 additions and 112 deletions

View File

@ -2,11 +2,11 @@
#include <winternl.h>
#include <../km/d3dkmthk.h>
#include "Common/HResultException.h"
#include "D3dDdi/Adapter.h"
#include "D3dDdi/Device.h"
#include "D3dDdi/DeviceFuncs.h"
#include "D3dDdi/Resource.h"
#include <Common/HResultException.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/DeviceFuncs.h>
#include <D3dDdi/Resource.h>
namespace
{
@ -24,8 +24,7 @@ namespace D3dDdi
, m_renderTarget(nullptr)
, m_renderTargetSubResourceIndex(0)
, m_sharedPrimary(nullptr)
, m_streamSourceData{}
, m_streamSource(nullptr)
, m_drawPrimitive(*this)
{
}
@ -66,6 +65,13 @@ namespace D3dDdi
{
Resource resource(*this, data);
m_resources.emplace(resource, std::move(resource));
if (data.Flags.VertexBuffer &&
D3DDDIPOOL_SYSTEMMEM == data.Pool &&
data.pSurfList[0].pSysMem)
{
m_drawPrimitive.addSysMemVertexBuffer(data.hResource,
static_cast<BYTE*>(const_cast<void*>(data.pSurfList[0].pSysMem)), data.Fvf);
}
return S_OK;
}
catch (const HResultException& e)
@ -115,57 +121,27 @@ namespace D3dDdi
g_gdiResourceHandle = nullptr;
g_gdiResource = nullptr;
}
if (resource == m_streamSource)
{
m_streamSource = nullptr;
}
m_drawPrimitive.removeSysMemVertexBuffer(resource);
}
return result;
}
HRESULT Device::drawIndexedPrimitive(const D3DDDIARG_DRAWINDEXEDPRIMITIVE* data)
{
prepareForRendering();
return m_origVtable.pfnDrawIndexedPrimitive(m_device, data);
}
HRESULT Device::drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2* data,
UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer)
{
if (2 != indicesSize)
{
return E_INVALIDARG;
}
prepareForRendering();
return m_origVtable.pfnDrawIndexedPrimitive2(m_device, data, indicesSize, indexBuffer, flagBuffer);
return m_drawPrimitive.drawIndexed(*data, indexBuffer, flagBuffer);
}
HRESULT Device::drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE* data, const UINT* flagBuffer)
{
if (m_streamSource && 0 != m_streamSourceData.Stride)
{
m_streamSource->fixVertexData(m_streamSourceData.Offset + data->VStart * m_streamSourceData.Stride,
data->PrimitiveCount, m_streamSourceData.Stride);
}
prepareForRendering();
return m_origVtable.pfnDrawPrimitive(m_device, data, flagBuffer);
}
HRESULT Device::drawPrimitive2(const D3DDDIARG_DRAWPRIMITIVE2* data)
{
prepareForRendering();
return m_origVtable.pfnDrawPrimitive2(m_device, data);
}
HRESULT Device::drawRectPatch(const D3DDDIARG_DRAWRECTPATCH* data, const D3DDDIRECTPATCH_INFO* info,
const FLOAT* patch)
{
prepareForRendering();
return m_origVtable.pfnDrawRectPatch(m_device, data, info, patch);
}
HRESULT Device::drawTriPatch(const D3DDDIARG_DRAWTRIPATCH* data, const D3DDDITRIPATCH_INFO* info,
const FLOAT* patch)
{
prepareForRendering();
return m_origVtable.pfnDrawTriPatch(m_device, data, info, patch);
return m_drawPrimitive.draw(*data, flagBuffer);
}
HRESULT Device::flush()
@ -234,24 +210,12 @@ namespace D3dDdi
HRESULT Device::setStreamSource(const D3DDDIARG_SETSTREAMSOURCE* data)
{
HRESULT result = m_origVtable.pfnSetStreamSource(m_device, data);
if (SUCCEEDED(result) && 0 == data->Stream)
{
m_streamSourceData = *data;
m_streamSource = getResource(data->hVertexBuffer);
}
return result;
return m_drawPrimitive.setStreamSource(*data);
}
HRESULT Device::setStreamSourceUm(const D3DDDIARG_SETSTREAMSOURCEUM* data, const void* umBuffer)
{
HRESULT result = m_origVtable.pfnSetStreamSourceUm(m_device, data, umBuffer);
if (SUCCEEDED(result) && 0 == data->Stream)
{
m_streamSourceData = {};
m_streamSource = nullptr;
}
return result;
return m_drawPrimitive.setStreamSourceUm(*data, umBuffer);
}
HRESULT Device::unlock(const D3DDDIARG_UNLOCK* data)

View File

@ -7,6 +7,8 @@
#include <d3dnthal.h>
#include <d3dumddi.h>
#include <D3dDdi/DrawPrimitive.h>
namespace D3dDdi
{
class Adapter;
@ -23,15 +25,9 @@ namespace D3dDdi
HRESULT createResource(D3DDDIARG_CREATERESOURCE* data);
HRESULT createResource2(D3DDDIARG_CREATERESOURCE2* data);
HRESULT destroyResource(HANDLE resource);
HRESULT drawIndexedPrimitive(const D3DDDIARG_DRAWINDEXEDPRIMITIVE* data);
HRESULT drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2* data,
UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer);
HRESULT drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE* data, const UINT* flagBuffer);
HRESULT drawPrimitive2(const D3DDDIARG_DRAWPRIMITIVE2* data);
HRESULT drawRectPatch(const D3DDDIARG_DRAWRECTPATCH* data, const D3DDDIRECTPATCH_INFO* info,
const FLOAT* patch);
HRESULT drawTriPatch(const D3DDDIARG_DRAWTRIPATCH* data, const D3DDDITRIPATCH_INFO* info,
const FLOAT* patch);
HRESULT flush();
HRESULT flush1(UINT FlushFlags);
HRESULT lock(D3DDDIARG_LOCK* data);
@ -74,8 +70,7 @@ namespace D3dDdi
Resource* m_renderTarget;
UINT m_renderTargetSubResourceIndex;
HANDLE m_sharedPrimary;
D3DDDIARG_SETSTREAMSOURCE m_streamSourceData;
Resource* m_streamSource;
DrawPrimitive m_drawPrimitive;
static std::map<HANDLE, Device> s_devices;
static bool s_isFlushEnabled;

View File

@ -11,12 +11,8 @@ namespace
HRESULT APIENTRY destroyDevice(HANDLE hDevice)
{
HRESULT result = D3dDdi::DeviceFuncs::s_origVtablePtr->pfnDestroyDevice(hDevice);
if (SUCCEEDED(result))
{
D3dDdi::Device::remove(hDevice);
}
return result;
D3dDdi::Device::remove(hDevice);
return D3dDdi::DeviceFuncs::s_origVtablePtr->pfnDestroyDevice(hDevice);
}
}
@ -38,12 +34,8 @@ namespace D3dDdi
vtable.pfnCreateResource2 = &DEVICE_FUNC(createResource2);
vtable.pfnDestroyDevice = &destroyDevice;
vtable.pfnDestroyResource = &DEVICE_FUNC(destroyResource);
vtable.pfnDrawIndexedPrimitive = &DEVICE_FUNC(drawIndexedPrimitive);
vtable.pfnDrawIndexedPrimitive2 = &DEVICE_FUNC(drawIndexedPrimitive2);
vtable.pfnDrawPrimitive = &DEVICE_FUNC(drawPrimitive);
vtable.pfnDrawPrimitive2 = &DEVICE_FUNC(drawPrimitive2);
vtable.pfnDrawRectPatch = &DEVICE_FUNC(drawRectPatch);
vtable.pfnDrawTriPatch = &DEVICE_FUNC(drawTriPatch);
vtable.pfnFlush = &DEVICE_FUNC(flush);
vtable.pfnFlush1 = &DEVICE_FUNC(flush1);
vtable.pfnLock = &DEVICE_FUNC(lock);

View File

@ -0,0 +1,329 @@
#include <algorithm>
#include <set>
#include <D3dDdi/DrawPrimitive.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/ScopedCriticalSection.h>
namespace
{
class VertexRhwFixer
{
public:
VertexRhwFixer(D3DTLVERTEX* vertex)
{
if (vertex && 0.0f == vertex->rhw)
{
vertex->rhw = 1.0f;
m_vertex = vertex;
}
else
{
m_vertex = nullptr;
}
}
VertexRhwFixer(void* vertex)
: VertexRhwFixer(static_cast<D3DTLVERTEX*>(vertex))
{
}
~VertexRhwFixer()
{
if (m_vertex)
{
m_vertex->rhw = 0.0f;
}
}
private:
D3DTLVERTEX* m_vertex;
};
UINT getVertexCount(D3DPRIMITIVETYPE primitiveType, UINT primitiveCount)
{
switch (primitiveType)
{
case D3DPT_POINTLIST:
return primitiveCount;
case D3DPT_LINELIST:
return primitiveCount * 2;
case D3DPT_LINESTRIP:
return primitiveCount + 1;
case D3DPT_TRIANGLELIST:
return primitiveCount * 3;
case D3DPT_TRIANGLESTRIP:
case D3DPT_TRIANGLEFAN:
return primitiveCount + 2;
}
return 0;
}
UINT roundUpToNearestMultiple(UINT num, UINT mul)
{
return (num + mul - 1) / mul * mul;
}
}
namespace D3dDdi
{
DrawPrimitive::Buffer::Buffer(HANDLE device, const D3DDDI_DEVICEFUNCS& origVtable, D3DDDIFORMAT format, UINT size)
: m_device(device)
, m_origVtable(origVtable)
, m_resource(nullptr, [=](HANDLE vb) { origVtable.pfnDestroyResource(device, vb); })
, m_format(format)
, m_initialSize(size)
, m_size(0)
, m_stride(0)
, m_pos(0)
{
resize(size);
}
HANDLE DrawPrimitive::Buffer::getHandle() const
{
return m_resource.get();
}
void* DrawPrimitive::Buffer::lock(UINT size)
{
D3DDDIARG_LOCK lock = {};
lock.hResource = m_resource.get();
lock.Range.Offset = m_pos;
lock.Range.Size = size;
lock.Flags.RangeValid = 1;
if (0 == m_pos)
{
lock.Flags.Discard = 1;
}
else
{
lock.Flags.WriteOnly = 1;
lock.Flags.NoOverwrite = 1;
}
m_origVtable.pfnLock(m_device, &lock);
return lock.pSurfData;
}
UINT DrawPrimitive::Buffer::load(const void* src, UINT count, UINT stride)
{
if (stride != m_stride)
{
m_stride = stride;
m_pos = roundUpToNearestMultiple(m_pos, stride);
}
UINT size = count * m_stride;
if (m_pos + size > m_size)
{
m_pos = 0;
if (size > m_size)
{
if (!resize(roundUpToNearestMultiple(size, m_initialSize)))
{
return UINT_MAX;
}
}
}
auto dst = lock(size);
if (!dst)
{
return UINT_MAX;
}
memcpy(dst, src, size);
unlock();
UINT pos = m_pos;
m_pos += size;
return pos;
}
bool DrawPrimitive::Buffer::resize(UINT size)
{
if (0 == size)
{
m_resource.reset();
m_size = 0;
return true;
}
D3DDDI_SURFACEINFO surfaceInfo = {};
surfaceInfo.Width = size;
surfaceInfo.Height = 1;
D3DDDIARG_CREATERESOURCE2 cr = {};
cr.Format = m_format;
cr.Pool = D3DDDIPOOL_VIDEOMEMORY;
cr.pSurfList = &surfaceInfo;
cr.SurfCount = 1;
cr.Flags.Dynamic = 1;
cr.Flags.WriteOnly = 1;
if (D3DDDIFMT_VERTEXDATA == m_format)
{
cr.Flags.VertexBuffer = 1;
}
else
{
cr.Flags.IndexBuffer = 1;
}
if (FAILED(m_origVtable.pfnCreateResource2
? m_origVtable.pfnCreateResource2(m_device, &cr)
: m_origVtable.pfnCreateResource(m_device, reinterpret_cast<D3DDDIARG_CREATERESOURCE*>(&cr))))
{
return false;
}
m_resource.reset(cr.hResource);
m_size = size;
return true;
}
void DrawPrimitive::Buffer::unlock()
{
D3DDDIARG_UNLOCK unlock = {};
unlock.hResource = m_resource.get();
m_origVtable.pfnUnlock(m_device, &unlock);
}
DrawPrimitive::DrawPrimitive(Device& device)
: m_device(device)
, m_origVtable(device.getOrigVtable())
, m_vertexBuffer(m_device, m_origVtable, D3DDDIFMT_VERTEXDATA, 1024 * 1024)
, m_indexBuffer(m_device, m_origVtable, D3DDDIFMT_INDEX16, 256 * 1024)
, m_streamSource{}
{
if (m_indexBuffer.getHandle())
{
D3DDDIARG_SETINDICES si = {};
si.hIndexBuffer = m_indexBuffer.getHandle();
si.Stride = 2;
m_origVtable.pfnSetIndices(m_device, &si);
}
}
void DrawPrimitive::addSysMemVertexBuffer(HANDLE resource, BYTE* vertices, UINT fvf)
{
m_sysMemVertexBuffers[resource] = { vertices, fvf };
}
HRESULT DrawPrimitive::draw(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer)
{
auto firstVertexPtr = m_streamSource.vertices + data.VStart * m_streamSource.stride;
VertexRhwFixer fixer((m_streamSource.fvf & D3DFVF_XYZRHW) ? firstVertexPtr : nullptr);
if (m_streamSource.vertices && m_vertexBuffer.getHandle())
{
auto vertexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount);
auto vbOffset = m_vertexBuffer.load(firstVertexPtr, vertexCount, m_streamSource.stride);
if (UINT_MAX == vbOffset)
{
return E_OUTOFMEMORY;
}
D3DDDIARG_DRAWPRIMITIVE dp = data;
dp.VStart = vbOffset / m_streamSource.stride;
return m_origVtable.pfnDrawPrimitive(m_device, &dp, flagBuffer);
}
return m_origVtable.pfnDrawPrimitive(m_device, &data, flagBuffer);
}
HRESULT DrawPrimitive::drawIndexed(
D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const void* indices, const UINT* flagBuffer)
{
auto firstIndexPtr = reinterpret_cast<const UINT16*>(static_cast<const BYTE*>(indices) + data.StartIndexOffset);
auto indexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount);
auto [min, max] = std::minmax_element(firstIndexPtr, firstIndexPtr + indexCount);
data.MinIndex = *min;
data.NumVertices = *max - *min + 1;
auto firstVertexPtr = m_streamSource.vertices + data.BaseVertexOffset + data.MinIndex * m_streamSource.stride;
VertexRhwFixer fixer((m_streamSource.fvf & D3DFVF_XYZRHW) ? firstVertexPtr : nullptr);
if (m_streamSource.vertices && m_vertexBuffer.getHandle())
{
data.BaseVertexOffset = m_vertexBuffer.load(firstVertexPtr, data.NumVertices, m_streamSource.stride);
if (UINT_MAX == data.BaseVertexOffset)
{
return E_OUTOFMEMORY;
}
data.BaseVertexOffset -= data.MinIndex * m_streamSource.stride;
}
if (m_indexBuffer.getHandle() && !flagBuffer)
{
D3DDDIARG_DRAWINDEXEDPRIMITIVE dp = {};
dp.PrimitiveType = data.PrimitiveType;
dp.BaseVertexIndex = data.BaseVertexOffset / m_streamSource.stride;
dp.MinIndex = data.MinIndex;
dp.NumVertices = data.NumVertices;
dp.StartIndex = m_indexBuffer.load(firstIndexPtr, indexCount, 2) / 2;
dp.PrimitiveCount = data.PrimitiveCount;
return m_origVtable.pfnDrawIndexedPrimitive(m_device, &dp);
}
return m_origVtable.pfnDrawIndexedPrimitive2(m_device, &data, 2, indices, flagBuffer);
}
void DrawPrimitive::removeSysMemVertexBuffer(HANDLE resource)
{
m_sysMemVertexBuffers.erase(resource);
}
HRESULT DrawPrimitive::setStreamSource(BYTE* vertices, UINT stride, UINT fvf)
{
HRESULT result = S_OK;
if (m_vertexBuffer.getHandle())
{
if (!m_streamSource.vertices || stride != m_streamSource.stride)
{
D3DDDIARG_SETSTREAMSOURCE ss = {};
ss.hVertexBuffer = m_vertexBuffer.getHandle();
ss.Stride = stride;
result = m_origVtable.pfnSetStreamSource(m_device, &ss);
}
}
else if (vertices != m_streamSource.vertices || stride != m_streamSource.stride)
{
D3DDDIARG_SETSTREAMSOURCEUM ss = {};
ss.Stride = stride;
result = m_origVtable.pfnSetStreamSourceUm(m_device, &ss, vertices);
}
m_streamSource.vertices = vertices;
m_streamSource.stride = stride;
m_streamSource.fvf = fvf;
return result;
}
HRESULT DrawPrimitive::setStreamSource(const D3DDDIARG_SETSTREAMSOURCE& data)
{
if (0 != data.Stride)
{
auto it = m_sysMemVertexBuffers.find(data.hVertexBuffer);
if (it != m_sysMemVertexBuffers.end())
{
return setStreamSource(it->second.vertices + data.Offset, data.Stride, it->second.fvf);
}
}
HRESULT result = m_origVtable.pfnSetStreamSource(m_device, &data);
if (SUCCEEDED(result))
{
m_streamSource = {};
m_streamSource.stride = data.Stride;
}
return result;
}
HRESULT DrawPrimitive::setStreamSourceUm(const D3DDDIARG_SETSTREAMSOURCEUM& data, const void* umBuffer)
{
return setStreamSource(static_cast<BYTE*>(const_cast<void*>(umBuffer)), data.Stride, 0);
}
}

View File

@ -0,0 +1,74 @@
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include <d3d.h>
#include <d3dumddi.h>
namespace D3dDdi
{
class Device;
class DrawPrimitive
{
public:
DrawPrimitive(Device& device);
void addSysMemVertexBuffer(HANDLE resource, BYTE* vertices, UINT fvf);
void removeSysMemVertexBuffer(HANDLE resource);
HRESULT draw(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer);
HRESULT drawIndexed(D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const void* indices, const UINT* flagBuffer);
HRESULT setStreamSource(const D3DDDIARG_SETSTREAMSOURCE& data);
HRESULT setStreamSourceUm(const D3DDDIARG_SETSTREAMSOURCEUM& data, const void* umBuffer);
private:
class Buffer
{
public:
Buffer(HANDLE device, const D3DDDI_DEVICEFUNCS& origVtable, D3DDDIFORMAT format, UINT size);
HANDLE getHandle() const;
UINT load(const void* src, UINT count, UINT stride);
private:
void* lock(UINT size);
bool resize(UINT size);
void unlock();
HANDLE m_device = nullptr;
const D3DDDI_DEVICEFUNCS& m_origVtable;
std::unique_ptr<void, std::function<void(HANDLE)>> m_resource;
D3DDDIFORMAT m_format;
UINT m_initialSize;
UINT m_size;
UINT m_stride;
UINT m_pos;
};
struct StreamSource
{
BYTE* vertices;
UINT stride;
UINT fvf;
};
struct SysMemVertexBuffer
{
BYTE* vertices;
UINT fvf;
};
HRESULT setStreamSource(BYTE* vertices, UINT stride, UINT fvf);
HANDLE m_device;
const D3DDDI_DEVICEFUNCS& m_origVtable;
Buffer m_vertexBuffer;
Buffer m_indexBuffer;
StreamSource m_streamSource;
std::map<HANDLE, SysMemVertexBuffer> m_sysMemVertexBuffers;
};
}

View File

@ -143,15 +143,6 @@ namespace D3dDdi
, m_lockBuffer(nullptr, &heapFree)
, m_lockResource(nullptr, ResourceDeleter(device))
{
if (D3DDDIFMT_VERTEXDATA == data.Format &&
data.Flags.VertexBuffer &&
data.Flags.MightDrawFromLocked &&
D3DDDIPOOL_SYSTEMMEM != data.Pool)
{
const HRESULT D3DERR_NOTAVAILABLE = 0x8876086A;
throw HResultException(D3DERR_NOTAVAILABLE);
}
fixResourceData(device, reinterpret_cast<D3DDDIARG_CREATERESOURCE&>(m_fixedData));
m_formatInfo = getFormatInfo(m_fixedData.Format);
@ -477,31 +468,6 @@ namespace D3dDdi
}
}
void Resource::fixVertexData(UINT offset, UINT count, UINT stride)
{
if (!m_fixedData.Flags.MightDrawFromLocked ||
!m_fixedData.pSurfList[0].pSysMem ||
!(m_fixedData.Fvf & D3DFVF_XYZRHW))
{
return;
}
unsigned char* data = static_cast<unsigned char*>(const_cast<void*>(m_fixedData.pSurfList[0].pSysMem)) + offset;
if (0.0f != reinterpret_cast<D3DTLVERTEX*>(data)->rhw)
{
return;
}
for (UINT i = 0; i < count; ++i)
{
if (0.0f == reinterpret_cast<D3DTLVERTEX*>(data)->rhw)
{
reinterpret_cast<D3DTLVERTEX*>(data)->rhw = 1.0f;
}
data += stride;
}
}
void* Resource::getLockPtr(UINT subResourceIndex)
{
return m_lockData.empty() ? nullptr : m_lockData[subResourceIndex].data;

View File

@ -6,7 +6,7 @@
#include <d3d.h>
#include <d3dumddi.h>
#include "D3dDdi/FormatInfo.h"
#include <D3dDdi/FormatInfo.h>
namespace D3dDdi
{
@ -30,7 +30,6 @@ namespace D3dDdi
HRESULT blt(D3DDDIARG_BLT data);
HRESULT colorFill(D3DDDIARG_COLORFILL data);
void endGdiAccess(bool isReadOnly);
void fixVertexData(UINT offset, UINT count, UINT stride);
void* getLockPtr(UINT subResourceIndex);
HRESULT lock(D3DDDIARG_LOCK& data);
void prepareForRendering(UINT subResourceIndex, bool isReadOnly);

View File

@ -154,6 +154,7 @@
<ClInclude Include="D3dDdi\Device.h" />
<ClInclude Include="D3dDdi\DeviceCallbacks.h" />
<ClInclude Include="D3dDdi\DeviceFuncs.h" />
<ClInclude Include="D3dDdi\DrawPrimitive.h" />
<ClInclude Include="D3dDdi\FormatInfo.h" />
<ClInclude Include="D3dDdi\Hooks.h" />
<ClInclude Include="D3dDdi\KernelModeThunks.h" />
@ -243,6 +244,7 @@
<ClCompile Include="D3dDdi\Device.cpp" />
<ClCompile Include="D3dDdi\DeviceCallbacks.cpp" />
<ClCompile Include="D3dDdi\DeviceFuncs.cpp" />
<ClCompile Include="D3dDdi\DrawPrimitive.cpp" />
<ClCompile Include="D3dDdi\FormatInfo.cpp" />
<ClCompile Include="D3dDdi\Hooks.cpp" />
<ClCompile Include="D3dDdi\KernelModeThunks.cpp" />

View File

@ -387,6 +387,9 @@
<ClInclude Include="D3dDdi\Log\CommonLog.h">
<Filter>Header Files\D3dDdi\Log</Filter>
</ClInclude>
<ClInclude Include="D3dDdi\DrawPrimitive.h">
<Filter>Header Files\D3dDdi</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -593,5 +596,8 @@
<ClCompile Include="D3dDdi\Log\CommonLog.cpp">
<Filter>Source Files\D3dDdi\Log</Filter>
</ClCompile>
<ClCompile Include="D3dDdi\DrawPrimitive.cpp">
<Filter>Source Files\D3dDdi</Filter>
</ClCompile>
</ItemGroup>
</Project>