Fix support for triangle fans on GL/Vulkan

This commit is contained in:
baldurk
2019-08-20 15:33:12 +01:00
parent 27dcb72b93
commit 07158ad24c
19 changed files with 629 additions and 158 deletions
+1
View File
@@ -62,6 +62,7 @@ Maths & Utilities
.. autofunction:: renderdoc.VertexOffset
.. autofunction:: renderdoc.PatchList_Count
.. autofunction:: renderdoc.PatchList_Topology
.. autofunction:: renderdoc.SupportsRestart
.. autofunction:: renderdoc.IsStrip
.. autofunction:: renderdoc.IsD3D
.. autofunction:: renderdoc.MaskForStage
+11 -4
View File
@@ -2071,7 +2071,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
ClearModels();
if(m_Ctx.CurPipelineState().IsStripRestartEnabled() && draw &&
(draw->flags & DrawFlags::Indexed) && IsStrip(draw->topology))
(draw->flags & DrawFlags::Indexed) && SupportsRestart(draw->topology))
{
bufdata->vsinConfig.primRestart = m_Ctx.CurPipelineState().GetStripRestartIndex();
@@ -2091,9 +2091,6 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
float vpWidth = qAbs(vp.width);
float vpHeight = qAbs(vp.height);
m_Config.position.allowRestart = m_Ctx.CurPipelineState().IsStripRestartEnabled();
m_Config.position.restartIndex = m_Ctx.CurPipelineState().GetStripRestartIndex();
m_Config.fov = ui->fovGuess->value();
m_Config.aspect = (vpWidth > 0.0f && vpHeight > 0.0f) ? (vpWidth / vpHeight) : 1.0f;
m_Config.highlightVert = 0;
@@ -2526,6 +2523,11 @@ void BufferViewer::UI_CalculateMeshFormats()
m_VSInPosition = MeshFormat();
m_VSInSecondary = MeshFormat();
m_VSInPosition.allowRestart = m_Ctx.CurPipelineState().IsStripRestartEnabled() &&
(draw->flags & DrawFlags::Indexed) &&
SupportsRestart(draw->topology);
m_VSInPosition.restartIndex = m_Ctx.CurPipelineState().GetStripRestartIndex();
const BufferConfiguration &vsinConfig = m_ModelVSIn->getConfig();
if(!vsinConfig.columns.empty())
@@ -2629,6 +2631,9 @@ void BufferViewer::UI_CalculateMeshFormats()
}
}
m_PostVSPosition.allowRestart = m_VSInPosition.allowRestart;
m_PostVSPosition.restartIndex = m_VSInPosition.restartIndex;
const BufferConfiguration &gsoutConfig = m_ModelGSOut->getConfig();
m_PostGSPosition = MeshFormat();
@@ -2654,6 +2659,8 @@ void BufferViewer::UI_CalculateMeshFormats()
}
}
m_PostGSPosition.allowRestart = false;
m_PostGSPosition.indexByteStride = 0;
if(!(draw->flags & DrawFlags::Indexed))
+16
View File
@@ -1812,6 +1812,22 @@ constexpr inline uint32_t PatchList_Count(Topology topology)
: uint32_t(topology) - uint32_t(Topology::PatchList_1CPs) + 1;
}
DOCUMENT(R"(Check whether or not this topology supports primitive restart.
.. note:: This is almost but not quite the same as being a line/triangle strip - triangle fans
also support restart. See also :func:`IsStrip`.
:param Topology t: The topology to check.
:return: ``True`` if it describes a topology that allows restart, ``False`` for a list.
:rtype: ``bool``
)");
constexpr inline bool SupportsRestart(Topology topology)
{
return topology == Topology::LineStrip || topology == Topology::TriangleStrip ||
topology == Topology::LineStrip_Adj || topology == Topology::TriangleStrip_Adj ||
topology == Topology::TriangleFan;
}
DOCUMENT(R"(Check whether or not this is a strip-type topology.
:param Topology t: The topology to check.
+12 -11
View File
@@ -65,6 +65,9 @@ bool TriangleRayIntersect(vec3 A, vec3 B, vec3 C, vec3 RayPosition, vec3 RayDire
{
bool Result = false;
if(A == B || A == C || B == C)
return false;
vec3 v0v1 = B - A;
vec3 v0v2 = C - A;
vec3 pvec = cross(RayDirection, v0v2);
@@ -161,23 +164,21 @@ void trianglePath(uint threadID)
bool hit;
if(meshpick.unproject == 1u)
{
hit = TriangleRayIntersect(pos0.xyz / pos0.w, pos1.xyz / pos1.w, pos2.xyz / pos2.w,
meshpick.rayPos, meshpick.rayDir,
/*out*/ hitPosition);
}
else
{
hit = TriangleRayIntersect(pos0.xyz, pos1.xyz, pos2.xyz, meshpick.rayPos, meshpick.rayDir,
/*out*/ hitPosition);
pos0.xyz /= pos0.w;
pos1.xyz /= pos1.w;
pos2.xyz /= pos2.w;
}
hit = TriangleRayIntersect(pos0.xyz, pos1.xyz, pos2.xyz, meshpick.rayPos, meshpick.rayDir,
/*out*/ hitPosition);
// ray hit a triangle, so return the vertex that was closest
// to the triangle/ray intersection point
if(hit)
{
float dist0 = distance(pos0.xyz / pos0.w, hitPosition);
float dist1 = distance(pos1.xyz / pos1.w, hitPosition);
float dist2 = distance(pos2.xyz / pos2.w, hitPosition);
float dist0 = distance(pos0.xyz, hitPosition);
float dist1 = distance(pos1.xyz, hitPosition);
float dist2 = distance(pos2.xyz, hitPosition);
uint result_idx = atomicAdd(pickresult.counter, 1u);
+12 -11
View File
@@ -144,6 +144,9 @@ bool TriangleRayIntersect(float3 A, float3 B, float3 C, float3 RayPosition, floa
{
bool Result = false;
if(all(A == B) || all(A == C) || all(B == C))
return false;
float3 v0v1 = B - A;
float3 v0v2 = C - A;
float3 pvec = cross(RayDirection, v0v2);
@@ -224,23 +227,21 @@ void trianglePath(uint threadID)
bool hit;
if(PickUnproject == 1)
{
hit = TriangleRayIntersect(pos0.xyz / pos0.w, pos1.xyz / pos1.w, pos2.xyz / pos2.w, PickRayPos,
PickRayDir,
/*out*/ hitPosition);
}
else
{
hit = TriangleRayIntersect(pos0.xyz, pos1.xyz, pos2.xyz, PickRayPos, PickRayDir,
/*out*/ hitPosition);
pos0.xyz /= pos0.w;
pos1.xyz /= pos1.w;
pos2.xyz /= pos2.w;
}
hit = TriangleRayIntersect(pos0.xyz, pos1.xyz, pos2.xyz, PickRayPos, PickRayDir,
/*out*/ hitPosition);
// ray hit a triangle, so return the vertex that was closest
// to the triangle/ray intersection point
if(hit)
{
float dist0 = distance(pos0.xyz / pos0.w, hitPosition);
float dist1 = distance(pos1.xyz / pos1.w, hitPosition);
float dist2 = distance(pos2.xyz / pos2.w, hitPosition);
float dist0 = distance(pos0.xyz, hitPosition);
float dist1 = distance(pos1.xyz, hitPosition);
float dist2 = distance(pos2.xyz, hitPosition);
uint meshVert = vertid0;
if(dist1 < dist0 && dist1 < dist2)
@@ -80,6 +80,10 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm
key |= 1ULL << bit;
bit++;
if(primary.allowRestart)
key |= 1ULL << bit;
bit++;
// only 64 bits, make sure they all fit
RDCASSERT(bit < 64);
@@ -100,6 +104,15 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm
pipeDesc.SampleMask = 0xFFFFFFFF;
pipeDesc.SampleDesc.Count = D3D12_MSAA_SAMPLECOUNT;
pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED;
if(primary.allowRestart)
{
if(primary.indexByteStride == 2)
pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF;
else
pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF;
}
D3D_PRIMITIVE_TOPOLOGY topo = MakeD3DPrimitiveTopology(primary.topology);
if(topo == D3D_PRIMITIVE_TOPOLOGY_POINTLIST ||
+115 -73
View File
@@ -1943,58 +1943,6 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
}
}
drv.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]);
MeshPickUBOData *cdata =
(MeshPickUBOData *)drv.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(MeshPickUBOData),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
cdata->rayPos = rayPos;
cdata->rayDir = rayDir;
cdata->use_indices = cfg.position.indexByteStride ? 1U : 0U;
cdata->numVerts = cfg.position.numIndices;
bool isTriangleMesh = true;
switch(cfg.position.topology)
{
case Topology::TriangleList:
{
cdata->meshMode = MESH_TRIANGLE_LIST;
break;
}
case Topology::TriangleStrip:
{
cdata->meshMode = MESH_TRIANGLE_STRIP;
break;
}
case Topology::TriangleFan:
{
cdata->meshMode = MESH_TRIANGLE_FAN;
break;
}
case Topology::TriangleList_Adj:
{
cdata->meshMode = MESH_TRIANGLE_LIST_ADJ;
break;
}
case Topology::TriangleStrip_Adj:
{
cdata->meshMode = MESH_TRIANGLE_STRIP_ADJ;
break;
}
default: // points, lines, patchlists, unknown
{
cdata->meshMode = MESH_OTHER;
isTriangleMesh = false;
}
}
// line/point data
cdata->unproject = cfg.position.unproject;
cdata->mvp = cfg.position.unproject ? pickMVPProj : pickMVP;
cdata->coords = Vec2f((float)x, (float)y);
cdata->viewport = Vec2f((float)width, (float)height);
drv.glUnmapBuffer(eGL_UNIFORM_BUFFER);
GLuint ib = 0;
uint32_t minIndex = 0;
@@ -2007,29 +1955,45 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
if(cfg.position.indexByteStride && cfg.position.indexResourceId != ResourceId())
ib = m_pDriver->GetResourceManager()->GetCurrentResource(cfg.position.indexResourceId).name;
const bool fandecode =
(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart);
uint32_t numIndices = cfg.position.numIndices;
// We copy into our own buffers to promote to the target type (uint32) that the shader expects.
// Most IBs will be 16-bit indices, most VBs will not be float4. We also apply baseVertex here
if(ib)
{
std::vector<uint32_t> idxtmp;
// if it's a triangle fan that allows restart, we'll have to unpack it.
// Allocate enough space for the list on the GPU, and enough temporary space to upcast into
// first
if(fandecode)
{
idxtmp.resize(numIndices);
numIndices *= 3;
}
// resize up on demand
if(DebugData.pickIBBuf == 0 || DebugData.pickIBSize < cfg.position.numIndices * sizeof(uint32_t))
if(DebugData.pickIBBuf == 0 || DebugData.pickIBSize < numIndices * sizeof(uint32_t))
{
drv.glDeleteBuffers(1, &DebugData.pickIBBuf);
drv.glGenBuffers(1, &DebugData.pickIBBuf);
drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickIBBuf);
drv.glNamedBufferDataEXT(DebugData.pickIBBuf, cfg.position.numIndices * sizeof(uint32_t),
NULL, eGL_STREAM_DRAW);
drv.glNamedBufferDataEXT(DebugData.pickIBBuf, numIndices * sizeof(uint32_t), NULL,
eGL_STREAM_DRAW);
DebugData.pickIBSize = cfg.position.numIndices * sizeof(uint32_t);
DebugData.pickIBSize = numIndices * sizeof(uint32_t);
}
byte *idxs = new byte[cfg.position.numIndices * cfg.position.indexByteStride];
memset(idxs, 0, cfg.position.numIndices * cfg.position.indexByteStride);
byte *idxs = new byte[numIndices * cfg.position.indexByteStride];
memset(idxs, 0, numIndices * cfg.position.indexByteStride);
std::vector<uint32_t> outidxs;
outidxs.resize(cfg.position.numIndices);
outidxs.resize(numIndices);
drv.glBindBuffer(eGL_COPY_READ_BUFFER, ib);
@@ -2045,6 +2009,8 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
uint16_t *idxs16 = (uint16_t *)idxs;
uint32_t *idxs32 = (uint32_t *)idxs;
size_t idxcount = 0;
if(cfg.position.indexByteStride == 1)
{
for(uint32_t i = 0; i < cfg.position.numIndices; i++)
@@ -2069,11 +2035,8 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
}
outidxs[i] = idx;
idxcount++;
}
drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickIBBuf);
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, cfg.position.numIndices * sizeof(uint32_t),
outidxs.data());
}
else if(cfg.position.indexByteStride == 2)
{
@@ -2099,11 +2062,8 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
}
outidxs[i] = idx;
idxcount++;
}
drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickIBBuf);
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, cfg.position.numIndices * sizeof(uint32_t),
outidxs.data());
}
else
{
@@ -2129,12 +2089,30 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
}
outidxs[i] = idx;
idxcount++;
}
}
// if it's a triangle fan that allows restart, unpack it
if(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart)
{
// resize to how many indices were actually read
outidxs.resize(idxcount);
// patch the index buffer
PatchTriangleFanRestartIndexBufer(outidxs, cfg.position.restartIndex);
for(uint32_t &idx : idxtmp)
{
if(idx == cfg.position.restartIndex)
idx = 0;
}
drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickIBBuf);
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, cfg.position.numIndices * sizeof(uint32_t),
outidxs.data());
numIndices = (uint32_t)outidxs.size();
}
drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickIBBuf);
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, numIndices * sizeof(uint32_t), outidxs.data());
}
// unpack and linearise the data
@@ -2177,6 +2155,61 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, (maxIndex + 1) * sizeof(Vec4f), vbData.data());
}
drv.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]);
MeshPickUBOData *cdata =
(MeshPickUBOData *)drv.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(MeshPickUBOData),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
cdata->rayPos = rayPos;
cdata->rayDir = rayDir;
cdata->use_indices = cfg.position.indexByteStride ? 1U : 0U;
cdata->numVerts = numIndices;
bool isTriangleMesh = true;
switch(cfg.position.topology)
{
case Topology::TriangleList:
{
cdata->meshMode = MESH_TRIANGLE_LIST;
break;
}
case Topology::TriangleStrip:
{
cdata->meshMode = MESH_TRIANGLE_STRIP;
break;
}
case Topology::TriangleFan:
{
if(fandecode)
cdata->meshMode = MESH_TRIANGLE_LIST;
else
cdata->meshMode = MESH_TRIANGLE_FAN;
break;
}
case Topology::TriangleList_Adj:
{
cdata->meshMode = MESH_TRIANGLE_LIST_ADJ;
break;
}
case Topology::TriangleStrip_Adj:
{
cdata->meshMode = MESH_TRIANGLE_STRIP_ADJ;
break;
}
default: // points, lines, patchlists, unknown
{
cdata->meshMode = MESH_OTHER;
isTriangleMesh = false;
}
}
// line/point data
cdata->unproject = cfg.position.unproject;
cdata->mvp = cfg.position.unproject ? pickMVPProj : pickMVP;
cdata->coords = Vec2f((float)x, (float)y);
cdata->viewport = Vec2f((float)width, (float)height);
drv.glUnmapBuffer(eGL_UNIFORM_BUFFER);
uint32_t reset[4] = {};
drv.glBindBufferBase(eGL_SHADER_STORAGE_BUFFER, 0, DebugData.pickResultBuf);
drv.glBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, sizeof(uint32_t) * 4, &reset);
@@ -2194,6 +2227,8 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
drv.glBindBuffer(eGL_COPY_READ_BUFFER, DebugData.pickResultBuf);
drv.glGetBufferSubData(eGL_COPY_READ_BUFFER, 0, sizeof(uint32_t), &numResults);
uint32_t ret = ~0U;
if(numResults > 0)
{
if(isTriangleMesh)
@@ -2226,7 +2261,7 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
drv.glUnmapNamedBufferEXT(DebugData.pickResultBuf);
return closest->vertid;
ret = closest->vertid;
}
else
{
@@ -2265,11 +2300,18 @@ uint32_t GLReplay::PickVertex(uint32_t eventId, int32_t width, int32_t height,
drv.glUnmapNamedBufferEXT(DebugData.pickResultBuf);
return closest->vertid;
ret = closest->vertid;
}
}
return ~0U;
if(fandecode)
{
// undo the triangle list expansion
if(ret > 2)
ret = (ret + 3) / 3 + 1;
}
return ret;
}
void GLReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace,
+1 -1
View File
@@ -730,7 +730,7 @@ void GLReplay::InitPostVSBuffers(uint32_t eventId)
uint32_t stripRestartValue32 = 0;
if(IsStrip(drawcall->topology) && rs.Enabled[GLRenderState::eEnabled_PrimitiveRestart])
if(SupportsRestart(drawcall->topology) && rs.Enabled[GLRenderState::eEnabled_PrimitiveRestart])
{
stripRestartValue32 = rs.Enabled[GLRenderState::eEnabled_PrimitiveRestartFixedIndex]
? ~0U
+107 -50
View File
@@ -1049,55 +1049,10 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
}
}
MeshPickUBOData *ubo = (MeshPickUBOData *)m_VertexPick.UBO.Map();
const bool fandecode =
(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart);
ubo->rayPos = rayPos;
ubo->rayDir = rayDir;
ubo->use_indices = cfg.position.indexByteStride ? 1U : 0U;
ubo->numVerts = cfg.position.numIndices;
bool isTriangleMesh = true;
switch(cfg.position.topology)
{
case Topology::TriangleList:
{
ubo->meshMode = MESH_TRIANGLE_LIST;
break;
};
case Topology::TriangleStrip:
{
ubo->meshMode = MESH_TRIANGLE_STRIP;
break;
};
case Topology::TriangleFan:
{
ubo->meshMode = MESH_TRIANGLE_FAN;
break;
};
case Topology::TriangleList_Adj:
{
ubo->meshMode = MESH_TRIANGLE_LIST_ADJ;
break;
};
case Topology::TriangleStrip_Adj:
{
ubo->meshMode = MESH_TRIANGLE_STRIP_ADJ;
break;
};
default: // points, lines, patchlists, unknown
{
ubo->meshMode = MESH_OTHER;
isTriangleMesh = false;
};
}
// line/point data
ubo->unproject = cfg.position.unproject;
ubo->mvp = cfg.position.unproject ? pickMVPProj : pickMVP;
ubo->coords = Vec2f((float)x, (float)y);
ubo->viewport = Vec2f((float)w, (float)h);
m_VertexPick.UBO.Unmap();
uint32_t numIndices = cfg.position.numIndices;
bytebuf idxs;
@@ -1116,8 +1071,20 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
if(!idxs.empty())
{
std::vector<uint32_t> idxtmp;
// if it's a triangle fan that allows restart, we'll have to unpack it.
// Allocate enough space for the list on the GPU, and enough temporary space to upcast into
// first
if(fandecode)
{
idxtmp.resize(numIndices);
numIndices *= 3;
}
// resize up on demand
if(m_VertexPick.IBSize < cfg.position.numIndices * sizeof(uint32_t))
if(m_VertexPick.IBSize < numIndices * sizeof(uint32_t))
{
if(m_VertexPick.IBSize > 0)
{
@@ -1125,7 +1092,7 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
m_VertexPick.IBUpload.Destroy();
}
m_VertexPick.IBSize = cfg.position.numIndices * sizeof(uint32_t);
m_VertexPick.IBSize = numIndices * sizeof(uint32_t);
m_VertexPick.IB.Create(m_pDriver, dev, m_VertexPick.IBSize, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
@@ -1133,12 +1100,19 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
}
uint32_t *outidxs = (uint32_t *)m_VertexPick.IBUpload.Map();
uint32_t *mappedPtr = outidxs;
memset(outidxs, 0, m_VertexPick.IBSize);
// if we're decoding a fan, we write into our temporary vector first
if(fandecode)
outidxs = idxtmp.data();
uint16_t *idxs16 = (uint16_t *)&idxs[0];
uint32_t *idxs32 = (uint32_t *)&idxs[0];
size_t idxcount = 0;
if(cfg.position.indexByteStride == 2)
{
size_t bufsize = idxs.size() / 2;
@@ -1165,6 +1139,7 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
}
outidxs[i] = idx;
idxcount++;
}
}
else
@@ -1188,9 +1163,31 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
maxIndex = RDCMAX(idx, maxIndex);
outidxs[i] = idx;
idxcount++;
}
}
// if it's a triangle fan that allows restart, unpack it
if(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart)
{
// resize to how many indices were actually read
idxtmp.resize(idxcount);
// patch the index buffer
PatchTriangleFanRestartIndexBufer(idxtmp, cfg.position.restartIndex);
for(uint32_t &idx : idxtmp)
{
if(idx == cfg.position.restartIndex)
idx = 0;
}
numIndices = (uint32_t)idxtmp.size();
// now copy the decoded list to the GPU
memcpy(mappedPtr, idxtmp.data(), idxtmp.size() * sizeof(uint32_t));
}
m_VertexPick.IBUpload.Unmap();
}
@@ -1235,6 +1232,59 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
m_VertexPick.VBUpload.Unmap();
}
MeshPickUBOData *ubo = (MeshPickUBOData *)m_VertexPick.UBO.Map();
ubo->rayPos = rayPos;
ubo->rayDir = rayDir;
ubo->use_indices = cfg.position.indexByteStride ? 1U : 0U;
ubo->numVerts = numIndices;
bool isTriangleMesh = true;
switch(cfg.position.topology)
{
case Topology::TriangleList:
{
ubo->meshMode = MESH_TRIANGLE_LIST;
break;
};
case Topology::TriangleStrip:
{
ubo->meshMode = MESH_TRIANGLE_STRIP;
break;
};
case Topology::TriangleFan:
{
if(fandecode)
ubo->meshMode = MESH_TRIANGLE_LIST;
else
ubo->meshMode = MESH_TRIANGLE_FAN;
break;
};
case Topology::TriangleList_Adj:
{
ubo->meshMode = MESH_TRIANGLE_LIST_ADJ;
break;
};
case Topology::TriangleStrip_Adj:
{
ubo->meshMode = MESH_TRIANGLE_STRIP_ADJ;
break;
};
default: // points, lines, patchlists, unknown
{
ubo->meshMode = MESH_OTHER;
isTriangleMesh = false;
};
}
// line/point data
ubo->unproject = cfg.position.unproject;
ubo->mvp = cfg.position.unproject ? pickMVPProj : pickMVP;
ubo->coords = Vec2f((float)x, (float)y);
ubo->viewport = Vec2f((float)w, (float)h);
m_VertexPick.UBO.Unmap();
VkDescriptorBufferInfo ibInfo = {};
VkDescriptorBufferInfo vbInfo = {};
@@ -1425,6 +1475,13 @@ uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const
VkMarkerRegion::End();
if(fandecode)
{
// undo the triangle list expansion
if(ret > 2)
ret = (ret + 3) / 3 + 1;
}
return ret;
}
+14
View File
@@ -2114,8 +2114,22 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, CompType typeHint, Floa
VkPipelineInputAssemblyStateCreateInfo ia = {
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
// most topologies are decomposed into triangle lists on output
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
ia.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
else if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST ||
pipeCreateInfo.pInputAssemblyState->topology ==
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY ||
pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP ||
pipeCreateInfo.pInputAssemblyState->topology ==
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY)
ia.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
else if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
VkVertexInputBindingDescription binds[] = {
// primary
{0, 0, VK_VERTEX_INPUT_RATE_VERTEX},
+2 -2
View File
@@ -1608,8 +1608,8 @@ void VulkanReplay::FetchVSOut(uint32_t eventId)
if(drawcall->flags & DrawFlags::Indexed)
{
const bool restart =
pipeCreateInfo.pInputAssemblyState->primitiveRestartEnable && IsStrip(drawcall->topology);
const bool restart = pipeCreateInfo.pInputAssemblyState->primitiveRestartEnable &&
SupportsRestart(drawcall->topology);
bytebuf idxdata;
std::vector<uint32_t> indices;
uint8_t *idx8 = NULL;
+5 -1
View File
@@ -83,6 +83,10 @@ MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLay
key |= 1ULL << bit;
bit++;
if(primary.allowRestart)
key |= 1ULL << bit;
bit++;
// only 64 bits, make sure they all fit
RDCASSERT(bit < 64);
@@ -138,7 +142,7 @@ MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLay
false,
};
ia.primitiveRestartEnable = primary.allowRestart && IsStrip(primary.topology);
ia.primitiveRestartEnable = primary.allowRestart && SupportsRestart(primary.topology);
VkRect2D scissor = {{0, 0}, {16384, 16384}};
+1 -1
View File
@@ -143,13 +143,13 @@ extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_VertexOffset(Topology t
case Topology::LineLoop:
case Topology::TriangleStrip:
case Topology::LineStrip_Adj:
case Topology::TriangleFan:
// for strips, each new vertex creates a new primitive
return primitive;
case Topology::TriangleStrip_Adj:
// triangle strip with adjacency is a special case as every other
// vert is purely for adjacency so it's doubled
return primitive * 2;
case Topology::TriangleFan: RDCERR("Cannot get VertexOffset for triangle fan!"); break;
}
return primitive * RENDERDOC_NumVerticesPerPrimitive(topology);
+117 -4
View File
@@ -259,6 +259,86 @@ void PatchLineStripIndexBuffer(const DrawcallDescription *draw, uint8_t *idx8, u
#undef IDX_VALUE
}
void PatchTriangleFanRestartIndexBufer(std::vector<uint32_t> &patchedIndices, uint32_t restartIndex)
{
if(patchedIndices.empty())
return;
std::vector<uint32_t> newIndices;
uint32_t firstIndex = patchedIndices[0];
size_t i = 1;
// while we have at least two indices left
while(i + 1 < patchedIndices.size())
{
uint32_t a = patchedIndices[i];
uint32_t b = patchedIndices[i + 1];
if(a != restartIndex && b != restartIndex)
{
// no restart, add primitive
newIndices.push_back(firstIndex);
newIndices.push_back(a);
newIndices.push_back(b);
i++;
continue;
}
else if(b == restartIndex)
{
// we've already added the last triangle before the restart in the previous iteration, just
// continue so we hit the a == restartIndex case below
i++;
}
else if(a == restartIndex)
{
// new first index is b
firstIndex = b;
// skip both the restartIndex value and the first index, and begin at the next real index (if
// it exists)
i += 2;
uint32_t next[2] = {b, b};
// if this is the last vertex, the triangle will be degenerate
if(i < patchedIndices.size())
next[0] = patchedIndices[i];
if(i + 1 < patchedIndices.size())
next[1] = patchedIndices[i + 1];
// output 3 dummy degenerate triangles so vertex ID mapping is easy
// we rotate the triangles so the important vertex is last in each.
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
if(1)
{
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
newIndices.push_back(restartIndex);
}
else
{
newIndices.push_back(next[0]);
newIndices.push_back(next[1]);
newIndices.push_back(firstIndex);
newIndices.push_back(next[1]);
newIndices.push_back(firstIndex);
newIndices.push_back(next[0]);
}
}
}
newIndices.swap(patchedIndices);
}
void StandardFillCBufferVariable(uint32_t dataOffset, const bytebuf &data, ShaderVariable &outvar,
uint32_t matStride)
{
@@ -509,7 +589,8 @@ FloatVector HighlightCache::InterpretVertex(const byte *data, uint32_t vert, con
vert = indices[vert];
if(IsStrip(cfg.position.topology))
if(SupportsRestart(cfg.position.topology) && cfg.position.topology != Topology::TriangleFan &&
cfg.position.allowRestart)
{
if((cfg.position.indexByteStride == 1 && vert == 0xff) ||
(cfg.position.indexByteStride == 2 && vert == 0xffff) ||
@@ -625,6 +706,8 @@ void HighlightCache::CacheHighlightingData(uint32_t eventId, const MeshDisplay &
newKey = inthash(cfg.position.vertexByteStride, newKey);
newKey = inthash(cfg.position.indexResourceId, newKey);
newKey = inthash(cfg.position.vertexResourceId, newKey);
newKey = inthash((uint64_t)cfg.position.allowRestart, newKey);
newKey = inthash((uint64_t)cfg.position.restartIndex, newKey);
if(cacheKey != newKey)
{
@@ -687,7 +770,7 @@ void HighlightCache::CacheHighlightingData(uint32_t eventId, const MeshDisplay &
maxIndex += add;
uint32_t primRestart = 0;
if(IsStrip(cfg.position.topology))
if(SupportsRestart(cfg.position.topology) && cfg.position.allowRestart)
{
if(cfg.position.indexByteStride == 1)
primRestart = 0xff;
@@ -719,6 +802,13 @@ void HighlightCache::CacheHighlightingData(uint32_t eventId, const MeshDisplay &
driver->GetBufferData(cfg.position.vertexResourceId, cfg.position.vertexByteOffset,
(maxIndex + 1) * cfg.position.vertexByteStride, vertexData);
// if it's a fan AND it uses primitive restart, decompose it into a triangle list because the
// restart changes the central vertex
if(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart)
{
PatchTriangleFanRestartIndexBufer(indices, cfg.position.restartIndex);
}
}
}
@@ -735,10 +825,20 @@ bool HighlightCache::FetchHighlightPositions(const MeshDisplay &cfg, FloatVector
uint32_t idx = cfg.highlightVert;
Topology meshtopo = cfg.position.topology;
// if it's a fan AND it uses primitive restart, it was decomposed into a triangle list
if(meshtopo == Topology::TriangleFan && cfg.position.allowRestart)
{
meshtopo = Topology::TriangleList;
// due to triangle fan expansion the index we need to look up is adjusted
if(idx > 2)
idx = (idx - 1) * 3 - 1;
}
activeVertex = InterpretVertex(data, idx, cfg, dataEnd, true, valid);
uint32_t primRestart = 0;
if(IsStrip(meshtopo))
if(SupportsRestart(meshtopo) && cfg.position.allowRestart)
{
if(cfg.position.indexByteStride == 1)
primRestart = 0xff;
@@ -834,6 +934,19 @@ bool HighlightCache::FetchHighlightPositions(const MeshDisplay &cfg, FloatVector
activePrim.push_back(InterpretVertex(data, v + 0, cfg, dataEnd, true, valid));
activePrim.push_back(InterpretVertex(data, v + 1, cfg, dataEnd, true, valid));
}
else if(meshtopo == Topology::TriangleFan)
{
// find first vert in primitive. In fans a vert isn't
// in only one primitive, so we pick the first primitive
// it's in. This means the first N points are in the first
// primitive, and thereafter each point is in the next primitive
uint32_t v = RDCMAX(idx, 2U) - 1;
// first vert in the whole fan
activePrim.push_back(InterpretVertex(data, 0, cfg, dataEnd, true, valid));
activePrim.push_back(InterpretVertex(data, v + 0, cfg, dataEnd, true, valid));
activePrim.push_back(InterpretVertex(data, v + 1, cfg, dataEnd, true, valid));
}
else if(meshtopo == Topology::TriangleStrip)
{
// find first vert in primitive. In strips a vert isn't
@@ -1029,7 +1142,7 @@ bool HighlightCache::FetchHighlightPositions(const MeshDisplay &cfg, FloatVector
}
else if(meshtopo >= Topology::PatchList)
{
uint32_t dim = PatchList_Count(cfg.position.topology);
uint32_t dim = PatchList_Count(meshtopo);
uint32_t v0 = uint32_t(idx / dim) * dim;
+2
View File
@@ -244,6 +244,8 @@ void SetupDrawcallPointers(std::vector<DrawcallDescription *> &drawcallTable,
void PatchLineStripIndexBuffer(const DrawcallDescription *draw, uint8_t *idx8, uint16_t *idx16,
uint32_t *idx32, std::vector<uint32_t> &patchedIndices);
void PatchTriangleFanRestartIndexBufer(std::vector<uint32_t> &patchedIndices, uint32_t restartIndex);
uint64_t CalcMeshOutputSize(uint64_t curSize, uint64_t requiredOutput);
void StandardFillCBufferVariable(uint32_t dataOffset, const bytebuf &data, ShaderVariable &outvar,
+1
View File
@@ -25,6 +25,7 @@ set(VULKAN_SRC
vk/vk_simple_triangle.cpp
vk/vk_spec_constants.cpp
vk/vk_structured_buffer_nested.cpp
vk/vk_triangle_fan.cpp
vk/vk_vertex_attr_zoo.cpp
vk/vk_video_textures.cpp
vk/vk_vs_max_desc_set.cpp)
+1
View File
@@ -214,6 +214,7 @@
<ClCompile Include="vk\vk_line_raster.cpp" />
<ClCompile Include="vk\vk_multi_thread_windows.cpp" />
<ClCompile Include="vk\vk_spec_constants.cpp" />
<ClCompile Include="vk\vk_triangle_fan.cpp" />
<ClCompile Include="vk\vk_vertex_attr_zoo.cpp" />
<ClCompile Include="vk\vk_buffer_address.cpp" />
<ClCompile Include="vk\vk_cbuffer_zoo.cpp" />
+3
View File
@@ -321,6 +321,9 @@
<ClCompile Include="vk\vk_spec_constants.cpp">
<Filter>Vulkan\demos</Filter>
</ClCompile>
<ClCompile Include="vk\vk_triangle_fan.cpp">
<Filter>Vulkan\demos</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="D3D11">
+195
View File
@@ -0,0 +1,195 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2018-2019 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "vk_test.h"
TEST(VK_Triangle_Fan, VulkanGraphicsTest)
{
static constexpr const char *Description =
"Draws a triangle fan with primitive restart to test different edge cases.";
std::string common = R"EOSHADER(
#version 420 core
struct v2f
{
vec4 pos;
vec4 col;
vec4 uv;
};
)EOSHADER";
const std::string vertex = R"EOSHADER(
layout(location = 0) in vec3 Position;
layout(location = 1) in vec4 Color;
layout(location = 2) in vec2 UV;
layout(location = 0) out v2f vertOut;
void main()
{
vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1);
gl_Position = vertOut.pos;
vertOut.col = Color;
vertOut.uv = vec4(UV.xy, 0, 1);
}
)EOSHADER";
const std::string pixel = R"EOSHADER(
layout(location = 0) in v2f vertIn;
layout(location = 0, index = 0) out vec4 Color;
void main()
{
Color = vertIn.col;
}
)EOSHADER";
int main()
{
// initialise, create window, create context, etc
if(!Init())
return 3;
VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo());
vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
pipeCreateInfo.layout = layout;
pipeCreateInfo.renderPass = mainWindow->rp;
pipeCreateInfo.inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
pipeCreateInfo.inputAssemblyState.primitiveRestartEnable = VK_FALSE;
pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col),
vkh::vertexAttr(2, 0, DefaultA2V, uv),
};
pipeCreateInfo.stages = {
CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo);
pipeCreateInfo.inputAssemblyState.primitiveRestartEnable = VK_TRUE;
VkPipeline restartpipe = createGraphicsPipeline(pipeCreateInfo);
const DefaultA2V verts[] = {
// centre A
{Vec3f(0.0f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
// fan A
{Vec3f(-0.8f, 0.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(-0.5f, 0.3f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.0f, 0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.5f, 0.3f, 0.0f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.8f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)},
// centre B
{Vec3f(0.0f, -0.75f, 0.0f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)},
// fan B
{Vec3f(-0.8f, 0.0f - 0.75f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(-0.5f, 0.3f - 0.75f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.0f, 0.5f - 0.75f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.5f, 0.3f - 0.75f, 0.0f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.8f, 0.0f - 0.75f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)},
};
AllocatedBuffer vb(allocator,
vkh::BufferCreateInfo(sizeof(verts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
vb.upload(verts);
uint32_t indices[13] = {0, 1, 2, 3, 4, 5, ~0U, 6, 7, 8, 9, 10, 11};
AllocatedBuffer ib(allocator,
vkh::BufferCreateInfo(sizeof(indices), VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
ib.upload(indices);
while(Running())
{
VkCommandBuffer cmd = GetCommandBuffer();
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
VkImage swapimg =
StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL,
vkh::ClearColorValue(0.4f, 0.5f, 0.6f, 1.0f), 1,
vkh::ImageSubresourceRange());
vkCmdBeginRenderPass(
cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor),
VK_SUBPASS_CONTENTS_INLINE);
VkViewport v = mainWindow->viewport;
v.width /= 2;
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, restartpipe);
vkCmdSetViewport(cmd, 0, 1, &v);
vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
vkCmdBindIndexBuffer(cmd, ib.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmd, 13, 1, 0, 0, 0);
v.x += v.width;
vkCmdSetViewport(cmd, 0, 1, &v);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0);
vkCmdEndRenderPass(cmd);
FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
vkEndCommandBuffer(cmd);
Submit(0, 1, {cmd});
Present();
}
return 0;
}
};
REGISTER_TEST();