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

Aggregate Draw(Indexed)Primitive calls

This commit is contained in:
narzoul 2020-06-16 22:08:50 +02:00
parent 8e07040750
commit bb8ce374e2
5 changed files with 697 additions and 121 deletions

View File

@ -32,6 +32,7 @@ namespace D3dDdi
HRESULT Device::blt(const D3DDDIARG_BLT* data)
{
flushPrimitives();
auto it = m_resources.find(data->hDstResource);
if (it != m_resources.end())
{
@ -43,6 +44,7 @@ namespace D3dDdi
HRESULT Device::clear(const D3DDDIARG_CLEAR* data, UINT numRect, const RECT* rect)
{
flushPrimitives();
if (data->Flags & D3DCLEAR_TARGET)
{
prepareForRendering();
@ -52,6 +54,7 @@ namespace D3dDdi
HRESULT Device::colorFill(const D3DDDIARG_COLORFILL* data)
{
flushPrimitives();
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
@ -94,6 +97,7 @@ namespace D3dDdi
HRESULT Device::destroyResource(HANDLE resource)
{
flushPrimitives();
if (g_gdiResource && resource == *g_gdiResource)
{
D3DDDIARG_LOCK lock = {};
@ -130,14 +134,10 @@ namespace D3dDdi
}
HRESULT Device::drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2* data,
UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer)
UINT /*indicesSize*/, const void* indexBuffer, const UINT* flagBuffer)
{
if (2 != indicesSize)
{
return E_INVALIDARG;
}
prepareForRendering();
return m_drawPrimitive.drawIndexed(*data, indexBuffer, flagBuffer);
return m_drawPrimitive.drawIndexed(*data, static_cast<const UINT16*>(indexBuffer), flagBuffer);
}
HRESULT Device::drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE* data, const UINT* flagBuffer)
@ -152,6 +152,7 @@ namespace D3dDdi
{
return S_OK;
}
flushPrimitives();
return m_origVtable.pfnFlush(m_device);
}
@ -161,11 +162,13 @@ namespace D3dDdi
{
return S_OK;
}
flushPrimitives();
return m_origVtable.pfnFlush1(m_device, FlushFlags);
}
HRESULT Device::lock(D3DDDIARG_LOCK* data)
{
flushPrimitives();
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
@ -186,12 +189,14 @@ namespace D3dDdi
HRESULT Device::present(const D3DDDIARG_PRESENT* data)
{
flushPrimitives();
prepareForRendering(data->hSrcResource, data->SrcSubResourceIndex, true);
return m_origVtable.pfnPresent(m_device, data);
}
HRESULT Device::present1(D3DDDIARG_PRESENT1* data)
{
flushPrimitives();
for (UINT i = 0; i < data->SrcResources; ++i)
{
prepareForRendering(data->phSrcResources[i].hResource, data->phSrcResources[i].SubResourceIndex, true);
@ -201,6 +206,7 @@ namespace D3dDdi
HRESULT Device::setRenderTarget(const D3DDDIARG_SETRENDERTARGET* data)
{
flushPrimitives();
HRESULT result = m_origVtable.pfnSetRenderTarget(m_device, data);
if (SUCCEEDED(result) && 0 == data->RenderTargetIndex)
{
@ -229,6 +235,7 @@ namespace D3dDdi
return S_OK;
}
flushPrimitives();
HRESULT result = m_origVtable.pfnSetTexture(m_device, stage, texture);
if (SUCCEEDED(result) && stage < m_textures.size())
{
@ -237,6 +244,7 @@ namespace D3dDdi
return result;
}
flushPrimitives();
return m_origVtable.pfnSetTexture(m_device, stage, texture);
}
@ -247,6 +255,7 @@ namespace D3dDdi
return S_OK;
}
flushPrimitives();
HRESULT result = m_origVtable.pfnSetVertexShaderDecl(m_device, shader);
if (SUCCEEDED(result))
{
@ -257,6 +266,7 @@ namespace D3dDdi
HRESULT Device::unlock(const D3DDDIARG_UNLOCK* data)
{
flushPrimitives();
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
@ -267,6 +277,7 @@ namespace D3dDdi
HRESULT Device::updateWInfo(const D3DDDIARG_WINFO* data)
{
flushPrimitives();
if (1.0f == data->WNear && 1.0f == data->WFar)
{
D3DDDIARG_WINFO wInfo = {};
@ -326,11 +337,6 @@ namespace D3dDdi
return it != m_resources.end() ? &it->second : nullptr;
}
void Device::enableFlush(bool enable)
{
s_isFlushEnabled = enable;
}
Resource* Device::findResource(HANDLE resource)
{
for (auto& device : s_devices)

View File

@ -47,6 +47,7 @@ namespace D3dDdi
const D3DDDI_DEVICEFUNCS& getOrigVtable() const { return m_origVtable; }
Resource* getResource(HANDLE resource);
void flushPrimitives() { m_drawPrimitive.flushPrimitives(); }
void prepareForRendering(HANDLE resource, UINT subResourceIndex, bool isReadOnly);
void prepareForRendering();
@ -54,7 +55,7 @@ namespace D3dDdi
static Device& get(HANDLE device);
static void remove(HANDLE device);
static void enableFlush(bool enable);
static void enableFlush(bool enable) { s_isFlushEnabled = enable; }
static Resource* findResource(HANDLE resource);
static Resource* getGdiResource();
static void setGdiResourceHandle(HANDLE resource);

View File

@ -14,6 +14,13 @@ namespace
D3dDdi::Device::remove(hDevice);
return D3dDdi::DeviceFuncs::s_origVtablePtr->pfnDestroyDevice(hDevice);
}
template <typename DeviceMethodPtr, DeviceMethodPtr deviceMethod, typename... Params>
HRESULT APIENTRY flushPrimitives(HANDLE hDevice, Params... params)
{
D3dDdi::Device::get(hDevice).flushPrimitives();
return (D3dDdi::DeviceFuncs::s_origVtablePtr->*deviceMethod)(hDevice, params...);
}
}
#define DEVICE_FUNC(func) deviceFunc<decltype(&Device::func), &Device::func>
@ -49,5 +56,36 @@ namespace D3dDdi
vtable.pfnSetVertexShaderDecl = &DEVICE_FUNC(setVertexShaderDecl);
vtable.pfnUnlock = &DEVICE_FUNC(unlock);
vtable.pfnUpdateWInfo = &DEVICE_FUNC(updateWInfo);
#define FLUSH_PRIMITIVES(func) vtable.func = &flushPrimitives<decltype(&D3DDDI_DEVICEFUNCS::func), &D3DDDI_DEVICEFUNCS::func>
FLUSH_PRIMITIVES(pfnBufBlt);
FLUSH_PRIMITIVES(pfnBufBlt1);
FLUSH_PRIMITIVES(pfnDeletePixelShader);
FLUSH_PRIMITIVES(pfnDeleteVertexShaderDecl);
FLUSH_PRIMITIVES(pfnDeleteVertexShaderFunc);
FLUSH_PRIMITIVES(pfnDepthFill);
FLUSH_PRIMITIVES(pfnDiscard);
FLUSH_PRIMITIVES(pfnGenerateMipSubLevels);
FLUSH_PRIMITIVES(pfnSetClipPlane);
FLUSH_PRIMITIVES(pfnSetDepthStencil);
FLUSH_PRIMITIVES(pfnSetPalette);
FLUSH_PRIMITIVES(pfnSetPixelShader);
FLUSH_PRIMITIVES(pfnSetPixelShaderConst);
FLUSH_PRIMITIVES(pfnSetPixelShaderConstB);
FLUSH_PRIMITIVES(pfnSetPixelShaderConstI);
FLUSH_PRIMITIVES(pfnSetRenderState);
FLUSH_PRIMITIVES(pfnSetScissorRect);
FLUSH_PRIMITIVES(pfnSetTextureStageState);
FLUSH_PRIMITIVES(pfnSetVertexShaderConst);
FLUSH_PRIMITIVES(pfnSetVertexShaderConstB);
FLUSH_PRIMITIVES(pfnSetVertexShaderConstI);
FLUSH_PRIMITIVES(pfnSetVertexShaderFunc);
FLUSH_PRIMITIVES(pfnSetViewport);
FLUSH_PRIMITIVES(pfnStateSet);
FLUSH_PRIMITIVES(pfnTexBlt);
FLUSH_PRIMITIVES(pfnTexBlt1);
FLUSH_PRIMITIVES(pfnUpdatePalette);
FLUSH_PRIMITIVES(pfnSetZRange);
#undef FLUSH_PRIMITIVES
}
}

View File

@ -1,50 +1,13 @@
#include <algorithm>
#include <set>
#include <Common/Log.h>
#include <D3dDdi/DrawPrimitive.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/ScopedCriticalSection.h>
namespace
{
const UINT INDEX_BUFFER_SIZE = 256 * 1024;
const UINT VERTEX_BUFFER_SIZE = 1024 * 1024;
class VertexRhwFixer
{
public:
VertexRhwFixer(D3DTLVERTEX* vertex)
{
if (vertex && 0.0f == vertex->rhw)
{
vertex->rhw = 1.0f;
m_vertex = vertex;
}
else
{
m_vertex = nullptr;
}
}
VertexRhwFixer(const void* vertex)
: VertexRhwFixer(static_cast<D3DTLVERTEX*>(const_cast<void*>(vertex)))
{
}
~VertexRhwFixer()
{
if (m_vertex)
{
m_vertex->rhw = 0.0f;
}
}
private:
D3DTLVERTEX* m_vertex;
};
UINT getVertexCount(D3DPRIMITIVETYPE primitiveType, UINT primitiveCount)
{
switch (primitiveType)
@ -63,6 +26,22 @@ namespace
}
return 0;
}
void updateMax(UINT& max, UINT value)
{
if (value > max)
{
max = value;
}
}
void updateMin(UINT& min, UINT value)
{
if (value < min)
{
min = value;
}
}
}
namespace D3dDdi
@ -73,6 +52,7 @@ namespace D3dDdi
, m_vertexBuffer(device, VERTEX_BUFFER_SIZE)
, m_indexBuffer(device, m_vertexBuffer ? INDEX_BUFFER_SIZE : 0)
, m_streamSource{}
, m_batched{}
{
LOG_ONCE("Dynamic vertex buffers are " << (m_vertexBuffer ? "" : "not ") << "available");
LOG_ONCE("Dynamic index buffers are " << (m_indexBuffer ? "" : "not ") << "available");
@ -91,65 +71,539 @@ namespace D3dDdi
m_sysMemVertexBuffers[resource] = { vertices, fvf };
}
HRESULT DrawPrimitive::draw(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer)
void DrawPrimitive::appendIndexedVertices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex)
{
auto firstVertexPtr = m_streamSource.vertices + data.VStart * m_streamSource.stride;
VertexRhwFixer fixer((m_streamSource.fvf & D3DFVF_XYZRHW) ? firstVertexPtr : nullptr);
rebaseIndices();
appendIndexedVerticesWithoutRebase(indices, count, baseVertexIndex, minIndex, maxIndex);
}
if (m_streamSource.vertices && m_vertexBuffer)
void DrawPrimitive::appendIndexedVerticesWithoutRebase(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex)
{
UINT vertexCount = maxIndex - minIndex + 1;
if (vertexCount <= count)
{
auto vertexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount);
auto baseVertexIndex = loadVertices(firstVertexPtr, vertexCount);
if (baseVertexIndex >= 0)
INT delta = getBatchedVertexCount() - minIndex;
for (UINT i = 0; i < count; ++i)
{
D3DDDIARG_DRAWPRIMITIVE dp = data;
dp.VStart = baseVertexIndex;
return m_origVtable.pfnDrawPrimitive(m_device, &dp, flagBuffer);
m_batched.indices.push_back(static_cast<UINT16>(indices[i] + delta));
}
appendVertices(baseVertexIndex + minIndex, vertexCount);
return;
}
static UINT16 indexMap[D3DMAXNUMVERTICES] = {};
static BYTE indexCycles[D3DMAXNUMVERTICES] = {};
static BYTE currentCycle = 0;
static UINT maxVertexCount = 0;
++currentCycle;
if (0 == currentCycle)
{
memset(indexCycles, 0, maxVertexCount);
maxVertexCount = vertexCount;
++currentCycle;
}
else
{
updateMax(maxVertexCount, vertexCount);
}
UINT16 newIndex = static_cast<UINT16>(getBatchedVertexCount());
for (UINT i = 0; i < count; ++i)
{
const UINT16 zeroBasedIndex = static_cast<UINT16>(indices[i] - minIndex);
if (currentCycle != indexCycles[zeroBasedIndex])
{
appendVertices(baseVertexIndex + indices[i], 1);
indexMap[zeroBasedIndex] = newIndex;
indexCycles[zeroBasedIndex] = currentCycle;
m_batched.indices.push_back(newIndex);
++newIndex;
}
else
{
m_batched.indices.push_back(indexMap[zeroBasedIndex]);
}
}
}
void DrawPrimitive::appendIndexRange(UINT base, UINT count)
{
rebaseIndices();
appendIndexRangeWithoutRebase(base, count);
}
void DrawPrimitive::appendIndexRangeWithoutRebase(UINT base, UINT count)
{
for (UINT i = base; i < base + count; ++i)
{
m_batched.indices.push_back(static_cast<UINT16>(i));
}
updateMin(m_batched.minIndex, base);
updateMax(m_batched.maxIndex, base + count - 1);
}
void DrawPrimitive::appendIndices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex)
{
rebaseIndices();
for (UINT i = 0; i < count; ++i)
{
m_batched.indices.push_back(static_cast<UINT16>(baseVertexIndex + indices[i]));
}
updateMin(m_batched.minIndex, baseVertexIndex + minIndex);
updateMax(m_batched.maxIndex, baseVertexIndex + maxIndex);
}
void DrawPrimitive::appendIndicesAndVertices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex)
{
if (m_streamSource.vertices)
{
if (indices)
{
appendIndexedVertices(indices, count, baseVertexIndex, minIndex, maxIndex);
}
else
{
if (!m_batched.indices.empty())
{
appendIndexRange(getBatchedVertexCount(), count);
}
appendVertices(baseVertexIndex + minIndex, count);
}
}
else if (indices)
{
appendIndices(indices, count, baseVertexIndex, minIndex, maxIndex);
}
else
{
appendIndexRange(baseVertexIndex, count);
}
}
void DrawPrimitive::appendLineOrTriangleList(INT baseVertexIndex, UINT primitiveCount, UINT vpp,
const UINT16* indices, UINT minIndex, UINT maxIndex)
{
if (m_streamSource.vertices ||
indices ||
!m_batched.indices.empty() ||
m_batched.baseVertexIndex + static_cast<INT>(m_batched.primitiveCount * vpp) != baseVertexIndex)
{
appendIndicesAndVertices(indices, primitiveCount * vpp, baseVertexIndex, minIndex, maxIndex);
}
}
bool DrawPrimitive::appendPrimitives(D3DPRIMITIVETYPE primitiveType, INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex)
{
if ((m_batched.primitiveCount + primitiveCount) * 3 > D3DMAXNUMVERTICES)
{
return false;
}
switch (primitiveType)
{
case D3DPT_POINTLIST:
if (D3DPT_POINTLIST != m_batched.primitiveType ||
!m_streamSource.vertices &&
m_batched.baseVertexIndex + static_cast<INT>(m_batched.primitiveCount) != baseVertexIndex)
{
return false;
}
if (m_streamSource.vertices)
{
appendVertices(baseVertexIndex, primitiveCount);
}
break;
case D3DPT_LINESTRIP:
return false;
case D3DPT_LINELIST:
if (D3DPT_LINELIST != m_batched.primitiveType)
{
return false;
}
appendLineOrTriangleList(baseVertexIndex, primitiveCount, 2, indices, minIndex, maxIndex);
break;
case D3DPT_TRIANGLELIST:
if (m_batched.primitiveType < D3DPT_TRIANGLELIST)
{
return false;
}
convertToTriangleList();
appendLineOrTriangleList(baseVertexIndex, primitiveCount, 3, indices, minIndex, maxIndex);
break;
case D3DPT_TRIANGLESTRIP:
if (m_batched.primitiveType < D3DPT_TRIANGLELIST)
{
return false;
}
appendTriangleStrip(baseVertexIndex, primitiveCount, indices, minIndex, maxIndex);
break;
case D3DPT_TRIANGLEFAN:
if (m_batched.primitiveType < D3DPT_TRIANGLELIST)
{
return false;
}
appendTriangleFan(baseVertexIndex, primitiveCount, indices, minIndex, maxIndex);
break;
}
m_batched.primitiveCount += primitiveCount;
return true;
}
void DrawPrimitive::appendTriangleFan(INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex)
{
convertToTriangleList();
rebaseIndices();
appendIndicesAndVertices(indices, primitiveCount + 2, baseVertexIndex, minIndex, maxIndex);
convertIndexedTriangleFanToList(m_batched.primitiveCount, primitiveCount);
}
void DrawPrimitive::appendTriangleStrip(INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex)
{
if (D3DPT_TRIANGLESTRIP != m_batched.primitiveType)
{
convertToTriangleList();
rebaseIndices();
appendIndicesAndVertices(indices, primitiveCount + 2, baseVertexIndex, minIndex, maxIndex);
convertIndexedTriangleStripToList(m_batched.primitiveCount, primitiveCount);
return;
}
if (!m_streamSource.vertices || !m_batched.indices.empty())
{
rebaseIndices();
}
for (UINT i = 1 + m_batched.primitiveCount % 2; i != 0; --i)
{
repeatLastBatchedVertex();
m_batched.primitiveCount++;
}
if (m_batched.indices.empty())
{
appendVertices(baseVertexIndex, 1);
}
else
{
if (m_streamSource.vertices)
{
m_batched.indices.push_back(static_cast<UINT16>(getBatchedVertexCount()));
}
else if (indices)
{
m_batched.indices.push_back(static_cast<UINT16>(baseVertexIndex + indices[0]));
}
else
{
m_batched.indices.push_back(static_cast<UINT16>(baseVertexIndex));
}
}
m_batched.primitiveCount += 3;
appendIndicesAndVertices(indices, primitiveCount + 2, baseVertexIndex, minIndex, maxIndex);
}
void DrawPrimitive::appendVertices(UINT base, UINT count)
{
auto vertices = m_streamSource.vertices + base * m_streamSource.stride;
m_batched.vertices.insert(m_batched.vertices.end(), vertices, vertices + count * m_streamSource.stride);
}
void DrawPrimitive::clearBatchedPrimitives()
{
m_batched.primitiveCount = 0;
m_batched.vertices.clear();
m_batched.indices.clear();
}
void DrawPrimitive::convertIndexedTriangleFanToList(UINT startPrimitive, UINT primitiveCount)
{
const UINT totalPrimitiveCount = startPrimitive + primitiveCount;
m_batched.indices.resize(totalPrimitiveCount * 3);
INT startIndexPos = startPrimitive * 3;
INT oldIndexPos = startIndexPos + primitiveCount - 1;
INT newIndexPos = (totalPrimitiveCount - 1) * 3;
const UINT16 startIndex = m_batched.indices[startIndexPos];
while (newIndexPos > startIndexPos)
{
m_batched.indices[newIndexPos + 2] = startIndex;
m_batched.indices[newIndexPos + 1] = m_batched.indices[oldIndexPos + 2];
m_batched.indices[newIndexPos] = m_batched.indices[oldIndexPos + 1];
newIndexPos -= 3;
oldIndexPos--;
}
m_batched.indices[newIndexPos] = m_batched.indices[oldIndexPos + 1];
m_batched.indices[newIndexPos + 1] = m_batched.indices[oldIndexPos + 2];
m_batched.indices[newIndexPos + 2] = startIndex;
}
void DrawPrimitive::convertIndexedTriangleStripToList(UINT startPrimitive, UINT primitiveCount)
{
const UINT totalPrimitiveCount = startPrimitive + primitiveCount;
m_batched.indices.resize(totalPrimitiveCount * 3);
INT oldIndexPos = startPrimitive * 3 + primitiveCount - 2;
INT newIndexPos = (totalPrimitiveCount - 2) * 3;
if (0 != primitiveCount % 2)
{
m_batched.indices[newIndexPos + 5] = m_batched.indices[oldIndexPos + 3];
m_batched.indices[newIndexPos + 4] = m_batched.indices[oldIndexPos + 2];
m_batched.indices[newIndexPos + 3] = m_batched.indices[oldIndexPos + 1];
newIndexPos -= 3;
oldIndexPos--;
}
while (newIndexPos >= oldIndexPos)
{
m_batched.indices[newIndexPos + 5] = m_batched.indices[oldIndexPos + 2];
m_batched.indices[newIndexPos + 4] = m_batched.indices[oldIndexPos + 3];
m_batched.indices[newIndexPos + 3] = m_batched.indices[oldIndexPos + 1];
m_batched.indices[newIndexPos + 2] = m_batched.indices[oldIndexPos + 2];
m_batched.indices[newIndexPos + 1] = m_batched.indices[oldIndexPos + 1];
m_batched.indices[newIndexPos] = m_batched.indices[oldIndexPos];
newIndexPos -= 6;
oldIndexPos -= 2;
}
}
void DrawPrimitive::convertToTriangleList()
{
const bool alreadyIndexed = !m_batched.indices.empty();
switch (m_batched.primitiveType)
{
case D3DPT_TRIANGLELIST:
return;
case D3DPT_TRIANGLESTRIP:
if (alreadyIndexed)
{
rebaseIndices();
convertIndexedTriangleStripToList(0, m_batched.primitiveCount);
}
else
{
const UINT baseVertexIndex = static_cast<UINT>(m_batched.baseVertexIndex);
UINT i = baseVertexIndex;
for (; i < baseVertexIndex + m_batched.primitiveCount - 1; i += 2)
{
m_batched.indices.push_back(static_cast<UINT16>(i));
m_batched.indices.push_back(static_cast<UINT16>(i + 1));
m_batched.indices.push_back(static_cast<UINT16>(i + 2));
m_batched.indices.push_back(static_cast<UINT16>(i + 1));
m_batched.indices.push_back(static_cast<UINT16>(i + 3));
m_batched.indices.push_back(static_cast<UINT16>(i + 2));
}
if (i < baseVertexIndex + m_batched.primitiveCount)
{
m_batched.indices.push_back(static_cast<UINT16>(i));
m_batched.indices.push_back(static_cast<UINT16>(i + 1));
m_batched.indices.push_back(static_cast<UINT16>(i + 2));
}
}
break;
case D3DPT_TRIANGLEFAN:
if (alreadyIndexed)
{
rebaseIndices();
convertIndexedTriangleFanToList(0, m_batched.primitiveCount);
}
else
{
for (UINT i = m_batched.baseVertexIndex; i < m_batched.baseVertexIndex + m_batched.primitiveCount; ++i)
{
m_batched.indices.push_back(static_cast<UINT16>(i + 1));
m_batched.indices.push_back(static_cast<UINT16>(i + 2));
m_batched.indices.push_back(static_cast<UINT16>(m_batched.baseVertexIndex));
}
}
break;
}
m_batched.primitiveType = D3DPT_TRIANGLELIST;
if (!alreadyIndexed)
{
m_batched.minIndex = m_batched.baseVertexIndex;
m_batched.maxIndex = m_batched.baseVertexIndex + m_batched.primitiveCount + 1;
m_batched.baseVertexIndex = 0;
}
}
HRESULT DrawPrimitive::draw(D3DDDIARG_DRAWPRIMITIVE data, const UINT* flagBuffer)
{
if (0 == m_batched.primitiveCount || flagBuffer ||
!appendPrimitives(data.PrimitiveType, data.VStart, data.PrimitiveCount, nullptr, 0, 0))
{
flushPrimitives();
auto vertexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount);
if (m_streamSource.vertices)
{
appendVertices(data.VStart, vertexCount);
m_batched.baseVertexIndex = 0;
}
else
{
m_batched.baseVertexIndex = data.VStart;
m_batched.minIndex = D3DMAXNUMVERTICES;
m_batched.maxIndex = 0;
}
m_batched.primitiveType = data.PrimitiveType;
m_batched.primitiveCount = data.PrimitiveCount;
if (flagBuffer)
{
flushPrimitives(flagBuffer);
}
}
return m_origVtable.pfnDrawPrimitive(m_device, &data, flagBuffer);
return S_OK;
}
HRESULT DrawPrimitive::drawIndexed(
D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const void* indices, const UINT* flagBuffer)
D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const UINT16* 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);
auto [min, max] = std::minmax_element(indices, indices + 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)
if (0 == m_batched.primitiveCount || flagBuffer ||
!appendPrimitives(data.PrimitiveType, data.BaseVertexOffset / static_cast<INT>(m_streamSource.stride),
data.PrimitiveCount, indices, *min, *max))
{
auto baseVertexIndex = loadVertices(firstVertexPtr, data.NumVertices);
if (baseVertexIndex >= 0)
flushPrimitives();
m_batched.baseVertexIndex = data.BaseVertexOffset / static_cast<INT>(m_streamSource.stride);
if (m_streamSource.vertices)
{
baseVertexIndex -= data.MinIndex;
data.BaseVertexOffset = baseVertexIndex * static_cast<INT>(m_streamSource.stride);
appendIndexedVerticesWithoutRebase(indices, indexCount, m_batched.baseVertexIndex, *min, *max);
m_batched.baseVertexIndex = 0;
}
else
{
m_batched.indices.assign(indices, indices + indexCount);
m_batched.minIndex = *min;
m_batched.maxIndex = *max;
}
m_batched.primitiveType = data.PrimitiveType;
m_batched.primitiveCount = data.PrimitiveCount;
if (flagBuffer)
{
flushPrimitives(flagBuffer);
}
}
return S_OK;
}
void DrawPrimitive::fixFirstVertexRhw()
{
auto firstVertex = reinterpret_cast<D3DTLVERTEX*>(m_batched.vertices.data());
if ((m_streamSource.fvf & D3DFVF_XYZRHW) && 0.0f == firstVertex->rhw)
{
firstVertex->rhw = 1.0f;
}
}
HRESULT DrawPrimitive::flush(const UINT* flagBuffer)
{
D3DDDIARG_DRAWPRIMITIVE data = {};
data.PrimitiveType = m_batched.primitiveType;
data.VStart = m_batched.baseVertexIndex;
data.PrimitiveCount = m_batched.primitiveCount;
if (m_streamSource.vertices)
{
fixFirstVertexRhw();
data.VStart = loadVertices(m_batched.vertices.data(), getBatchedVertexCount());
}
clearBatchedPrimitives();
return m_origVtable.pfnDrawPrimitive(m_device, &data, flagBuffer);
}
HRESULT DrawPrimitive::flushIndexed(const UINT* flagBuffer)
{
D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data = {};
data.PrimitiveType = m_batched.primitiveType;
data.BaseVertexOffset = m_batched.baseVertexIndex * static_cast<INT>(m_streamSource.stride);
if (m_streamSource.vertices)
{
data.MinIndex = -m_batched.baseVertexIndex;
data.NumVertices = getBatchedVertexCount();
}
else
{
data.MinIndex = m_batched.minIndex;
data.NumVertices = m_batched.maxIndex - m_batched.minIndex + 1;
}
data.PrimitiveCount = m_batched.primitiveCount;
if (m_streamSource.vertices)
{
fixFirstVertexRhw();
INT baseVertexIndex = loadVertices(m_batched.vertices.data(), data.NumVertices) - data.MinIndex;
data.BaseVertexOffset = baseVertexIndex * static_cast<INT>(m_streamSource.stride);
}
INT startIndex = -1;
if ((!m_streamSource.vertices || m_vertexBuffer) && m_indexBuffer && !flagBuffer)
{
auto startIndex = loadIndices(firstIndexPtr, indexCount);
if (startIndex >= 0)
{
D3DDDIARG_DRAWINDEXEDPRIMITIVE dp = {};
dp.PrimitiveType = data.PrimitiveType;
dp.BaseVertexIndex = data.BaseVertexOffset / static_cast<INT>(m_streamSource.stride);
dp.MinIndex = data.MinIndex;
dp.NumVertices = data.NumVertices;
dp.StartIndex = startIndex;
dp.PrimitiveCount = data.PrimitiveCount;
return m_origVtable.pfnDrawIndexedPrimitive(m_device, &dp);
}
startIndex = loadIndices(m_batched.indices.data(), m_batched.indices.size());
}
return m_origVtable.pfnDrawIndexedPrimitive2(m_device, &data, 2, indices, flagBuffer);
HRESULT result = S_OK;
if (startIndex >= 0)
{
D3DDDIARG_DRAWINDEXEDPRIMITIVE dp = {};
dp.PrimitiveType = data.PrimitiveType;
dp.BaseVertexIndex = data.BaseVertexOffset / static_cast<INT>(m_streamSource.stride);
dp.MinIndex = data.MinIndex;
dp.NumVertices = data.NumVertices;
dp.StartIndex = startIndex;
dp.PrimitiveCount = data.PrimitiveCount;
result = m_origVtable.pfnDrawIndexedPrimitive(m_device, &dp);
}
else
{
result = m_origVtable.pfnDrawIndexedPrimitive2(m_device, &data, 2, m_batched.indices.data(), flagBuffer);
}
clearBatchedPrimitives();
return result;
}
HRESULT DrawPrimitive::flushPrimitives(const UINT* flagBuffer)
{
if (0 == m_batched.primitiveCount)
{
return S_OK;
}
return m_batched.indices.empty() ? flush(flagBuffer) : flushIndexed(flagBuffer);
}
UINT DrawPrimitive::getBatchedVertexCount() const
{
return m_batched.vertices.size() / m_streamSource.stride;
}
INT DrawPrimitive::loadIndices(const void* indices, UINT count)
@ -167,40 +621,79 @@ namespace D3dDdi
INT DrawPrimitive::loadVertices(const void* vertices, UINT count)
{
UINT size = count * m_streamSource.stride;
if (size > m_vertexBuffer.getSize())
{
m_vertexBuffer.resize((size + VERTEX_BUFFER_SIZE - 1) / VERTEX_BUFFER_SIZE * VERTEX_BUFFER_SIZE);
if (m_vertexBuffer)
{
D3DDDIARG_SETSTREAMSOURCE ss = {};
ss.hVertexBuffer = m_vertexBuffer;
ss.Stride = m_streamSource.stride;
m_origVtable.pfnSetStreamSource(m_device, &ss);
}
else
{
LOG_ONCE("WARN: Dynamic vertex buffer resize failed");
}
}
if (m_vertexBuffer)
{
INT baseVertexIndex = m_vertexBuffer.load(vertices, count);
if (baseVertexIndex >= 0)
UINT size = count * m_streamSource.stride;
if (size > m_vertexBuffer.getSize())
{
return baseVertexIndex;
m_vertexBuffer.resize((size + VERTEX_BUFFER_SIZE - 1) / VERTEX_BUFFER_SIZE * VERTEX_BUFFER_SIZE);
if (m_vertexBuffer)
{
D3DDDIARG_SETSTREAMSOURCE ss = {};
ss.hVertexBuffer = m_vertexBuffer;
ss.Stride = m_streamSource.stride;
m_origVtable.pfnSetStreamSource(m_device, &ss);
}
else
{
LOG_ONCE("WARN: Dynamic vertex buffer resize failed");
}
}
LOG_ONCE("WARN: Dynamic vertex buffer lock failed");
if (m_vertexBuffer)
{
INT baseVertexIndex = m_vertexBuffer.load(vertices, count);
if (baseVertexIndex >= 0)
{
return baseVertexIndex;
}
LOG_ONCE("WARN: Dynamic vertex buffer lock failed");
}
m_vertexBuffer.resize(0);
m_indexBuffer.resize(0);
}
D3DDDIARG_SETSTREAMSOURCEUM ss = {};
ss.Stride = m_streamSource.stride;
m_origVtable.pfnSetStreamSourceUm(m_device, &ss, m_streamSource.vertices);
m_origVtable.pfnSetStreamSourceUm(m_device, &ss, vertices);
return 0;
}
m_vertexBuffer.resize(0);
m_indexBuffer.resize(0);
return -1;
void DrawPrimitive::rebaseIndices()
{
if (0 != m_batched.baseVertexIndex || m_batched.indices.empty())
{
if (m_batched.indices.empty())
{
auto vertexCount = getVertexCount(m_batched.primitiveType, m_batched.primitiveCount);
appendIndexRangeWithoutRebase(m_batched.baseVertexIndex, vertexCount);
}
else
{
for (auto& index : m_batched.indices)
{
index = static_cast<UINT16>(m_batched.baseVertexIndex + index);
}
m_batched.minIndex += m_batched.baseVertexIndex;
m_batched.maxIndex += m_batched.baseVertexIndex;
}
m_batched.baseVertexIndex = 0;
}
}
void DrawPrimitive::repeatLastBatchedVertex()
{
if (m_batched.indices.empty())
{
m_batched.vertices.reserve(m_batched.vertices.size() + m_streamSource.stride);
m_batched.vertices.insert(m_batched.vertices.end(),
m_batched.vertices.end() - m_streamSource.stride, m_batched.vertices.end());
}
else
{
m_batched.indices.push_back(m_batched.indices.back());
}
}
void DrawPrimitive::removeSysMemVertexBuffer(HANDLE resource)
@ -216,6 +709,7 @@ namespace D3dDdi
return setSysMemStreamSource(it->second.vertices, data.Stride, it->second.fvf);
}
flushPrimitives();
HRESULT result = m_origVtable.pfnSetStreamSource(m_device, &data);
if (SUCCEEDED(result))
{
@ -232,9 +726,10 @@ namespace D3dDdi
HRESULT DrawPrimitive::setSysMemStreamSource(const BYTE* vertices, UINT stride, UINT fvf)
{
HRESULT result = S_OK;
if (m_vertexBuffer)
if (!m_streamSource.vertices || stride != m_streamSource.stride)
{
if (!m_streamSource.vertices || stride != m_streamSource.stride)
flushPrimitives();
if (m_vertexBuffer)
{
D3DDDIARG_SETSTREAMSOURCE ss = {};
ss.hVertexBuffer = m_vertexBuffer;
@ -246,12 +741,6 @@ namespace D3dDdi
}
}
}
else if (vertices != m_streamSource.vertices || stride != m_streamSource.stride)
{
D3DDDIARG_SETSTREAMSOURCEUM ss = {};
ss.Stride = stride;
result = m_origVtable.pfnSetStreamSourceUm(m_device, &ss, vertices);
}
if (SUCCEEDED(result))
{

View File

@ -1,8 +1,6 @@
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include <d3d.h>
@ -22,12 +20,25 @@ namespace D3dDdi
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 flushPrimitives(const UINT* flagBuffer = nullptr);
HRESULT draw(D3DDDIARG_DRAWPRIMITIVE data, const UINT* flagBuffer);
HRESULT drawIndexed(D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const UINT16* indices, const UINT* flagBuffer);
HRESULT setStreamSource(const D3DDDIARG_SETSTREAMSOURCE& data);
HRESULT setStreamSourceUm(const D3DDDIARG_SETSTREAMSOURCEUM& data, const void* umBuffer);
private:
struct BatchedPrimitives
{
D3DPRIMITIVETYPE primitiveType;
UINT primitiveCount;
INT baseVertexIndex;
UINT minIndex;
UINT maxIndex;
std::vector<BYTE> vertices;
std::vector<UINT16> indices;
};
struct StreamSource
{
const BYTE* vertices;
@ -41,8 +52,38 @@ namespace D3dDdi
UINT fvf;
};
void appendIndexedVertices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex);
void appendIndexedVerticesWithoutRebase(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex);
void appendIndexRange(UINT base, UINT count);
void appendIndexRangeWithoutRebase(UINT base, UINT count);
void appendIndices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex);
void appendIndicesAndVertices(const UINT16* indices, UINT count,
INT baseVertexIndex, UINT minIndex, UINT maxIndex);
void appendLineOrTriangleList(INT baseVertexIndex, UINT primitiveCount, UINT vpp,
const UINT16* indices, UINT minIndex, UINT maxIndex);
bool appendPrimitives(D3DPRIMITIVETYPE primitiveType, INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex);
void appendTriangleFan(INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex);
void appendTriangleStrip(INT baseVertexIndex, UINT primitiveCount,
const UINT16* indices, UINT minIndex, UINT maxIndex);
void appendVertices(UINT base, UINT count);
void clearBatchedPrimitives();
void convertIndexedTriangleFanToList(UINT startPrimitive, UINT primitiveCount);
void convertIndexedTriangleStripToList(UINT startPrimitive, UINT primitiveCount);
void convertToTriangleList();
void fixFirstVertexRhw();
HRESULT flush(const UINT* flagBuffer);
HRESULT flushIndexed(const UINT* flagBuffer);
INT loadIndices(const void* indices, UINT count);
INT loadVertices(const void* vertices, UINT count);
UINT getBatchedVertexCount() const;
void rebaseIndices();
void repeatLastBatchedVertex();
HRESULT setSysMemStreamSource(const BYTE* vertices, UINT stride, UINT fvf);
HANDLE m_device;
@ -51,5 +92,6 @@ namespace D3dDdi
DynamicIndexBuffer m_indexBuffer;
StreamSource m_streamSource;
std::map<HANDLE, SysMemVertexBuffer> m_sysMemVertexBuffers;
BatchedPrimitives m_batched;
};
}