1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-18 02:52:10 +01:00

[d3d9] Account for vertex declaration size for UP draws

The stride may not give us the full picture here as the stride
may not encompass the vertex declaration entirely.

Consider a vertex declaration of size 20, and a stride of 12,
we may not have covered the whole range of space the draw wants with
VertexCount * Stride.

Some games such as FF13 Lightning Returns have two float3s in the vertex decl
and draw two triangles with the last float being out of bounds. This causes
the whole vertex element to be set to 0 on NVIDIA which breaks their fullscreen passes.

Instead, take (VertexCount - 1) * Stride + VertexDeclSize for the buffer size
and pad with 0s outside of the VertexCount * Stride range.

Closes: #2046
Closes: #1908
This commit is contained in:
Joshua Ashton 2021-05-01 09:56:34 +01:00 committed by Joshie
parent 1ab2565521
commit 0f52c85d21
2 changed files with 35 additions and 9 deletions

View File

@ -2399,10 +2399,11 @@ namespace dxvk {
auto drawInfo = GenerateDrawInfo(PrimitiveType, PrimitiveCount, 0);
const uint32_t upSize = drawInfo.vertexCount * VertexStreamZeroStride;
const uint32_t dataSize = GetUPDataSize(drawInfo.vertexCount, VertexStreamZeroStride);
const uint32_t bufferSize = GetUPBufferSize(drawInfo.vertexCount, VertexStreamZeroStride);
auto upSlice = AllocTempBuffer<true>(upSize);
std::memcpy(upSlice.mapPtr, pVertexStreamZeroData, upSize);
auto upSlice = AllocTempBuffer<true>(bufferSize);
FillUPVertexBuffer(upSlice.mapPtr, pVertexStreamZeroData, dataSize, bufferSize);
EmitCs([this,
cBufferSlice = std::move(upSlice.slice),
@ -2445,21 +2446,21 @@ namespace dxvk {
auto drawInfo = GenerateDrawInfo(PrimitiveType, PrimitiveCount, 0);
const uint32_t vertexSize = (MinVertexIndex + NumVertices) * VertexStreamZeroStride;
const uint32_t vertexDataSize = GetUPDataSize(MinVertexIndex + NumVertices, VertexStreamZeroStride);
const uint32_t vertexBufferSize = GetUPBufferSize(MinVertexIndex + NumVertices, VertexStreamZeroStride);
const uint32_t indexSize = IndexDataFormat == D3DFMT_INDEX16 ? 2 : 4;
const uint32_t indicesSize = drawInfo.vertexCount * indexSize;
const uint32_t upSize = vertexSize + indicesSize;
const uint32_t upSize = vertexBufferSize + indicesSize;
auto upSlice = AllocTempBuffer<true>(upSize);
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr);
std::memcpy(data, pVertexStreamZeroData, vertexSize);
std::memcpy(data + vertexSize, pIndexData, indicesSize);
FillUPVertexBuffer(data, pVertexStreamZeroData, vertexDataSize, vertexBufferSize);
std::memcpy(data + vertexBufferSize, pIndexData, indicesSize);
EmitCs([this,
cVertexSize = vertexSize,
cVertexSize = vertexBufferSize,
cBufferSlice = std::move(upSlice.slice),
cPrimType = PrimitiveType,
cPrimCount = PrimitiveCount,

View File

@ -1085,6 +1085,31 @@ namespace dxvk {
const DWORD* pShaderBytecode,
const DxsoModuleInfo* pModuleInfo);
inline uint32_t GetUPDataSize(uint32_t vertexCount, uint32_t stride) {
return vertexCount * stride;
}
inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) {
return (vertexCount - 1) * stride + m_state.vertexDecl->GetSize();
}
inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) {
uint8_t* data = reinterpret_cast<uint8_t*>(buffer);
// Don't copy excess data if we don't end up needing it.
dataSize = std::min(dataSize, bufferSize);
std::memcpy(data, userData, dataSize);
// Pad out with 0 to make buffer range checks happy
// Some games have components out of range in the vertex decl
// that they don't read from the shader.
// My tests show that these are read back as 0 always if out of range of
// the dataSize.
//
// So... make the actual buffer the range that satisfies the range of the vertex
// declaration and pad with 0s outside of it.
if (dataSize < bufferSize)
std::memset(data + dataSize, 0, bufferSize - dataSize);
}
// So we don't do OOB.
template <DxsoProgramType ProgramType,
D3D9ConstantType ConstantType>