Repository navigation
Expand file tree
/
Copy pathmesh_output.py
More file actions
156 lines (118 loc) · 5.35 KB
/
Copy pathmesh_output.py
File metadata and controls
156 lines (118 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# these imports are not strictly necessary, but are convenient
import renderdoc
import qrenderdoc
# this is here to give autocomplete when editing the example
# in VS Code where it doesn't know about this global
from typing import TYPE_CHECKING
if TYPE_CHECKING:
pyrenderdoc = qrenderdoc.CaptureContext()
if not pyrenderdoc.IsCaptureLoaded():
raise RuntimeError("Run example with capture open and a vertex draw selected")
import struct
from typing import List, cast
# verify the current drawcall
pipe = pyrenderdoc.CurPipelineState()
avoid_stages = [
renderdoc.ShaderStage.Mesh,
renderdoc.ShaderStage.Geometry,
renderdoc.ShaderStage.Hull,
]
if any([pipe.GetShader(x) != renderdoc.ResourceId() for x in avoid_stages]):
raise RuntimeError("Can't run example on this draw")
refl = pipe.GetShaderReflection(renderdoc.ShaderStage.Vertex)
if refl is None:
raise RuntimeError("Can't run example on this draw")
controller = pyrenderdoc.GetBlockingController()
meshdata = controller.GetPostVSData(0, 0, renderdoc.MeshDataStage.VSOut)
print(f"Mesh data contains {meshdata.numIndices} indices in {str(meshdata.topology)}")
if meshdata.indexResourceId != renderdoc.ResourceId():
print(" (indexed)")
else:
print(" (non-indexed)")
if meshdata.unproject:
far = f"{meshdata.farPlane:.2f}"
if meshdata.farPlane > 3.0e+38:
far = "inf"
print(f" Rasterized data: {meshdata.nearPlane:.2f}-{far}")
# default to just indices, but if this does use an index buffer then fetch that data
idxs = [i for i in range(meshdata.numIndices)]
if meshdata.indexResourceId != renderdoc.ResourceId():
bufdata = controller.GetBufferData(
meshdata.indexResourceId, meshdata.indexByteOffset, meshdata.indexByteSize
)
# pick the appropriate format character for 1-byte, 2-byte, or 4-byte indices
# 01234
struct_type = " BH I"[meshdata.indexByteStride]
# use struct.unpack to interpret the bytes as a series of integers
idxs = cast(
List[int], struct.unpack_from(f"={meshdata.numIndices}{struct_type}", bufdata)
)
def fmt_vec(vec):
return ", ".join([f"{x:.3f}" for x in vec])
# print the first 4 triangles
for tri in range(min(4, meshdata.numIndices // 3)):
tri_idxs = idxs[tri * 3 : tri * 3 + 3]
print()
print(f"Triangle {tri}:")
for idx in tri_idxs:
print(f" [{idx}]:")
# we expect baseVertex to be 0 - this does NOT come from the
# original draw, but we still include it
idx += meshdata.baseVertex
offset = meshdata.vertexByteOffset + meshdata.vertexByteStride * idx
# it would definitely be better to cache this data and look it up locally,
# but we do this to demonstrate how GetBufferData can be used
vert_data = controller.GetBufferData(
meshdata.vertexResourceId, offset, meshdata.vertexByteStride
)
# this could again be cached outside the per-vertex loop
offset = 0
# RenderDoc always outputs the position at the beginning of the vertex
# data, so that the mesh data can be re-used for rendering without needing
# any offsets.
posidx = [
o.systemValue == renderdoc.ShaderBuiltin.Position
for o in refl.outputSignature
].index(True)
if posidx >= 0:
pos = refl.outputSignature[posidx]
# simple case, we assume float output and don't have to worry about alignment
posdata = fmt_vec(struct.unpack_from(f"={pos.compCount}f", vert_data))
print(f" <pos>: {posdata}")
offset += pos.compCount * 4
for output in refl.outputSignature:
# position was handled above, so skip it here
if output.systemValue == renderdoc.ShaderBuiltin.Position:
continue
# some APIs align postvs data, to traditional 'wide' alignment:
# elements rounded up to 4 bytes and 3-wide vectors aligned to 4-wide
# in all other cases data is tightly packed
if pipe.HasAlignedPostVSData(renderdoc.MeshDataStage.VSOut):
align = max(4, renderdoc.VarTypeByteSize(output.varType))
if output.compCount == 3:
align *= 4
else:
align *= output.compCount
if offset % align != 0:
offset = align - (offset % align)
data_offs = offset
offset += output.compCount * renderdoc.VarTypeByteSize(output.varType)
# for simplicity we don't handle many different variable types here, only
# simple ones. You can use the varType and more complex struct formats to
# decode other types of data
fmtchar = ""
if output.varType == renderdoc.VarType.Float:
fmtchar = "f"
elif output.varType == renderdoc.VarType.UInt:
fmtchar = "I"
elif output.varType == renderdoc.VarType.SInt:
fmtchar = "i"
if fmtchar != "":
fmt = f"={output.compCount}{fmtchar}"
data = fmt_vec(struct.unpack_from(fmt, vert_data, data_offs))
else:
data = "<non-decoded data>"
name = output.varName
if name == "":
name = output.semanticIdxName
print(f" {name}: {data}")