mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
A coherent persistent mapping's writes reach the host by the range each
reader needs; with its explicit flushes now left to the readers
(f03b31e2), the indirect dispatch was the one reader of buffer memory that
settled no range for what it reads itself: the three group counts in
GL_DISPATCH_INDIRECT_BUFFER. (tools/gl_buffer_writers.py, regenerated.)
glpersist step 10: the counts written into a coherent, explicitly flushed
mapping, flushed, dispatched indirectly; the shader's mark read back. 34/34
natively; without this change step 10 reads mark 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_40f26e8988a74ea098d8bc80ff68287c
118 lines
6.3 KiB
Python
118 lines
6.3 KiB
Python
"""Commands that may change a buffer object's contents other than through
|
|
glBufferData/glBufferSubData: GPU writes (copies, clears, transform feedback,
|
|
compute, pixel and query readbacks into a bound buffer), the barriers that
|
|
publish shader writes, and the direct-state-access uploads and maps the guest
|
|
does not track. The guest calls buffer_contents_unknown() (guest/gl/gexgl.c)
|
|
before each, which drops its copies of mapped buffers' contents.
|
|
(glBufferStorage replaces only the bound buffer's: buffer_new_storage.) Used by
|
|
tools/gen_gl44_transport.py and tools/gen_gl_compat.py."""
|
|
|
|
import re
|
|
|
|
BUFFER_WRITERS = re.compile(
|
|
r"^gl(NamedBufferStorage|CopyBufferSubData|CopyNamedBufferSubData|NamedCopyBufferSubDataEXT"
|
|
r"|ClearBuffer(Sub)?Data|ClearNamedBuffer(Sub)?Data|(Begin|End)TransformFeedback"
|
|
r"|DispatchCompute|MemoryBarrier|Readn?Pixels|Getn?TexImage|GetTexture(Sub)?Image"
|
|
r"|Getn?CompressedTexImage|GetCompressedTexture(Sub)?Image|GetQueryObject"
|
|
r"|GetQueryBufferObject|MapNamedBuffer|UnmapNamedBuffer|FlushMappedNamedBuffer"
|
|
r"|NamedBuffer(Sub)?Data|NamedBufferPageCommitment|BufferPageCommitment"
|
|
r"|NamedBufferAttachMemory|BufferAttachMemory)")
|
|
|
|
PACK_READS = re.compile(
|
|
r"^gl(Readn?Pixels|Getn?TexImage|GetTexture(Sub)?Image|Getn?CompressedTexImage"
|
|
r"|GetCompressedTexture(Sub)?Image)")
|
|
|
|
|
|
def mark_buffer_writer(name: str, function: str) -> str:
|
|
"""FUNCTION (C source of NAME) with the call added as its first statement."""
|
|
if not BUFFER_WRITERS.match(name):
|
|
return function
|
|
# Readbacks write a buffer only into a bound pack or query buffer, and
|
|
# they are frequent (occlusion queries every frame).
|
|
call = "buffer_contents_unknown();"
|
|
if name == "glCopyBufferSubData":
|
|
call = "buffer_copy(readTarget, writeTarget, readOffset, writeOffset, size);"
|
|
if PACK_READS.match(name):
|
|
call = "if (bound_buffer(0x88EBu)) " + call # GL_PIXEL_PACK_BUFFER
|
|
elif name.startswith("glGetQueryObject"):
|
|
call = "if (bound_buffer(0x9192u)) " + call # GL_QUERY_BUFFER
|
|
# A barrier also publishes the application's writes to persistent
|
|
# mappings (persistent_barrier, guest/gl/gexgl.c).
|
|
if name == "glMemoryBarrier":
|
|
call += " persistent_barrier(barriers);"
|
|
at = function.index("{\n") + 2
|
|
return function[:at] + " " + call + "\n" + function[at:]
|
|
|
|
|
|
# Uploads gexgl.c splits when larger than one command can carry: each such
|
|
# command first calls split_<name>(its arguments), which returns 1 when it
|
|
# sent the upload itself (guest/gl/gexgl.c).
|
|
SPLIT_UPLOADS = {"glTexSubImage3D", "glCompressedTexImage2D", "glCompressedTexImage3D",
|
|
"glCompressedTexSubImage2D", "glCompressedTexSubImage3D"}
|
|
|
|
|
|
def mark_split_upload(name: str, function: str) -> str:
|
|
"""FUNCTION with the split_<NAME> call added as its first statement."""
|
|
if name not in SPLIT_UPLOADS:
|
|
return function
|
|
head = function[:function.index("{\n")]
|
|
params = head[head.index("(") + 1:head.rindex(")")]
|
|
args = ", ".join(re.findall(r"(\w+)\s*(?:,|$)", params.replace("\n", " ")))
|
|
at = function.index("{\n") + 2
|
|
return function[:at] + f" if (split_{name}({args})) return;\n" + function[at:]
|
|
|
|
|
|
# Commands that read buffer memory a coherent persistent mapping may have
|
|
# changed: each first sends what it can read (persistent_draw_elements and
|
|
# the rest, guest/gl/gexgl.c), or every mapping when that is unknown.
|
|
PERSISTENT_READS = {
|
|
"glDrawArraysInstancedBaseInstance": "persistent_draw_arrays(first, count, instancecount, baseinstance);",
|
|
"glDrawElementsBaseVertex": "persistent_draw_elements(count, type, indices, basevertex, 1, 0, 0, 0, 0);",
|
|
"glDrawElementsInstancedBaseInstance":
|
|
"persistent_draw_elements(count, type, indices, 0, instancecount, baseinstance, 0, 0, 0);",
|
|
"glDrawElementsInstancedBaseVertex":
|
|
"persistent_draw_elements(count, type, indices, basevertex, instancecount, 0, 0, 0, 0);",
|
|
"glDrawElementsInstancedBaseVertexBaseInstance":
|
|
"persistent_draw_elements(count, type, indices, basevertex, instancecount, baseinstance, 0, 0, 0);",
|
|
"glDrawRangeElements": "persistent_draw_elements(count, type, indices, 0, 1, 0, 1, start, end);",
|
|
"glDrawRangeElementsBaseVertex":
|
|
"persistent_draw_elements(count, type, indices, basevertex, 1, 0, 1, start, end);",
|
|
"glCopyBufferSubData": "persistent_read_target(readTarget, readOffset, size);",
|
|
"glGetBufferSubData": "persistent_read_target(target, offset, size);",
|
|
"glBindBufferRange": "persistent_indexed(target, index, buffer, offset, size);",
|
|
"glTexBuffer": "persistent_texbuffer(buffer, 0, 0);",
|
|
"glTexBufferRange": "persistent_texbuffer(buffer, offset, size);",
|
|
# The bindings, and the three group counts in GL_DISPATCH_INDIRECT_BUFFER.
|
|
"glDispatchComputeIndirect": "persistent_dispatch(); persistent_read_target(0x90EEu, indirect, 12);",
|
|
}
|
|
|
|
PERSISTENT_UNKNOWN_READS = re.compile(
|
|
r"^gl(MultiDraw\w*|Draw\w*Indirect\w*|DrawTransformFeedback\w*|Draw(Range)?ElementArray\w*|DrawMeshArraysSUN"
|
|
r"|CopyNamedBufferSubData|NamedCopyBufferSubDataEXT|GetNamedBufferSubData\w*)$")
|
|
PERSISTENT_DISPATCH = re.compile(r"^gl(DispatchCompute\w*|DrawMeshTasks(NV|EXT))$")
|
|
PERSISTENT_VAO_STATE = re.compile(
|
|
r"^gl(BindVertexBuffers?|VertexAttribBinding|VertexAttrib[IL]?Format(NV)?|VertexBindingDivisor"
|
|
r"|VertexArray\w+|VertexAttribLPointer)$")
|
|
PERSISTENT_BINDINGS = re.compile(r"^gl(BindBuffers(Base|Range)|TextureBuffer\w*)$")
|
|
PERSISTENT_UNPACK = re.compile(r"^gl((Compressed)?Tex(ture)?(Sub)?Image[123]D(EXT)?|DrawPixels)$")
|
|
|
|
|
|
def mark_persistent_reader(name: str, function: str) -> str:
|
|
"""FUNCTION with its persistent-mapping call added as its first statement."""
|
|
call = PERSISTENT_READS.get(name)
|
|
if call is None:
|
|
if PERSISTENT_UNKNOWN_READS.match(name):
|
|
call = "persistent_read_unknown();"
|
|
elif PERSISTENT_DISPATCH.match(name):
|
|
call = "persistent_dispatch();"
|
|
elif PERSISTENT_VAO_STATE.match(name):
|
|
call = "persistent_vao_unknown();"
|
|
elif PERSISTENT_BINDINGS.match(name):
|
|
call = "persistent_indexed_unknown();"
|
|
elif PERSISTENT_UNPACK.match(name):
|
|
call = "if (bound_buffer(0x88ECu)) persistent_unpack(NULL, 0);" # GL_PIXEL_UNPACK_BUFFER
|
|
else:
|
|
return function
|
|
at = function.index("{\n") + 2
|
|
return function[:at] + " " + call + "\n" + function[at:]
|