diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index 9d553c2..3901878 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -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(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) diff --git a/DDrawCompat/D3dDdi/Device.h b/DDrawCompat/D3dDdi/Device.h index 871b9c5..75ba4f1 100644 --- a/DDrawCompat/D3dDdi/Device.h +++ b/DDrawCompat/D3dDdi/Device.h @@ -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); diff --git a/DDrawCompat/D3dDdi/DeviceFuncs.cpp b/DDrawCompat/D3dDdi/DeviceFuncs.cpp index 275ea5b..45fa948 100644 --- a/DDrawCompat/D3dDdi/DeviceFuncs.cpp +++ b/DDrawCompat/D3dDdi/DeviceFuncs.cpp @@ -14,6 +14,13 @@ namespace D3dDdi::Device::remove(hDevice); return D3dDdi::DeviceFuncs::s_origVtablePtr->pfnDestroyDevice(hDevice); } + + template + 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 @@ -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 + 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 } } diff --git a/DDrawCompat/D3dDdi/DrawPrimitive.cpp b/DDrawCompat/D3dDdi/DrawPrimitive.cpp index af8f020..b17d4ce 100644 --- a/DDrawCompat/D3dDdi/DrawPrimitive.cpp +++ b/DDrawCompat/D3dDdi/DrawPrimitive.cpp @@ -1,50 +1,13 @@ -#include -#include - #include #include #include #include -#include 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(const_cast(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(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(getBatchedVertexCount()); + for (UINT i = 0; i < count; ++i) + { + const UINT16 zeroBasedIndex = static_cast(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(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(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(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(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(getBatchedVertexCount())); + } + else if (indices) + { + m_batched.indices.push_back(static_cast(baseVertexIndex + indices[0])); + } + else + { + m_batched.indices.push_back(static_cast(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(m_batched.baseVertexIndex); + UINT i = baseVertexIndex; + for (; i < baseVertexIndex + m_batched.primitiveCount - 1; i += 2) + { + m_batched.indices.push_back(static_cast(i)); + m_batched.indices.push_back(static_cast(i + 1)); + m_batched.indices.push_back(static_cast(i + 2)); + m_batched.indices.push_back(static_cast(i + 1)); + m_batched.indices.push_back(static_cast(i + 3)); + m_batched.indices.push_back(static_cast(i + 2)); + } + if (i < baseVertexIndex + m_batched.primitiveCount) + { + m_batched.indices.push_back(static_cast(i)); + m_batched.indices.push_back(static_cast(i + 1)); + m_batched.indices.push_back(static_cast(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(i + 1)); + m_batched.indices.push_back(static_cast(i + 2)); + m_batched.indices.push_back(static_cast(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(static_cast(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(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(m_streamSource.stride); + if (m_streamSource.vertices) { - baseVertexIndex -= data.MinIndex; - data.BaseVertexOffset = baseVertexIndex * static_cast(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(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(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(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(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(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(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)) { diff --git a/DDrawCompat/D3dDdi/DrawPrimitive.h b/DDrawCompat/D3dDdi/DrawPrimitive.h index 741802c..e99a42e 100644 --- a/DDrawCompat/D3dDdi/DrawPrimitive.h +++ b/DDrawCompat/D3dDdi/DrawPrimitive.h @@ -1,8 +1,6 @@ #pragma once -#include #include -#include #include #include @@ -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 vertices; + std::vector 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 m_sysMemVertexBuffers; + BatchedPrimitives m_batched; }; }