Create 6 / Flywheel 1.0.6 now renders correctly with both flywheel:instancing
and flywheel:indirect on DirectGLES and DirectVulkan (verified in-game on
Adreno 830: waterwheels and cogwheels solid, animated, correct pairing, no
crashes across all four combinations).
- MG_State/MG_Impl: sync explicitly-ranged SSBO bindings of FLUSH_EXPLICIT
persistent maps to the backend before compute dispatches. Flywheel writes
its scatter-copy descriptors into the staging ring's persistent map and
never flushes that span (UB per spec, works on drivers whose maps alias
GPU-visible memory); our maps alias the CPU shadow, so the descriptors
never reached the GPU: the scatter compute copied nothing (GLES: empty
draw commands) or stale garbage (Vulkan: wild indirect commands ending in
VK_ERROR_DEVICE_LOST).
- MG_Impl/MG_Backend: real glFenceSync objects backed by backend fences
(GLES: native ES syncs guarded by context generation and owner thread;
Vulkan: buffer-manager frame serials), replacing always-signaled stubs
that let Flywheel reclaim staging memory the GPU still reads.
- MG_Backend/DirectGLES: compute dispatches now run the same per-program
resource sync as draws (uniform-block bindings and sampler units must be
re-established through the API because layout(binding) is stripped from
transpiled ESSL) and rebind texture units afterwards; the cull shader
used to read a stale _FlwFrameUniforms binding and the depth-pyramid
downsample sampled a stale unit-0 texture, zeroing the Hi-Z pyramid and
occlusion-culling all Flywheel geometry. Image uniforms are excluded from
glUniform1i (ES bakes their unit via layout(binding)); image-unit sync is
clamped to the device limit; eliminated/SSBO-classified uniform blocks
are skipped.
- MG_Backend/DirectGLES: gl_BaseInstance in native indirect draws reads the
GPU-written command buffer through an injected mg_IndirectParams SSBO
view addressed per draw instead of the zero CPU shadow; layout(binding)
is preserved for SSBO/image declarations (ES has no API rebinding for
them); the ES context ownership claim moved to a global atomic owner
thread with an EGL ground-truth check, and deferred buffer op state is
mutex-guarded, so ops cannot silently no-op after context migration.
- MG_Backend/DirectVulkan: new RebaseInstanceIndexPass rewrites vertex
InstanceIndex loads to (InstanceIndex - BaseInstance). glslang's relaxed
Vulkan mode aliases gl_InstanceID to InstanceIndex, which includes
firstInstance, but GL's gl_InstanceID is zero-based - draws with nonzero
baseInstance paired meshes with wrong instance data (cogwheel drawn as a
waterwheel, another wheel collapsed invisible). Gated on the
shaderDrawParameters device feature. Sampled-read barriers additionally
cover the compute stage (the Hi-Z downsample samples the depth
attachment from compute), and short uniform-buffer ranges keep the
existing zero-padding.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Advertise ARB_gpu_shader5 / ARB_multi_bind / ARB_shading_language_420pack /
ARB_vertex_attrib_binding / ARB_shader_image_size so LWJGL reports
SUPPORTS_INDIRECT.
- New LowerDrawParametersPass demotes DrawIndex/BaseInstance/BaseVertex
builtins to Private globals (mg_DrawID/mg_BaseInstance/mg_BaseVertex) for
the ESSL transpile; SPIRV-Cross otherwise throws for ES profiles. The
program manager promotes the emitted globals to uniforms and feeds them
per (sub-)draw.
- Indirect draws now execute natively on the GPU (glDrawElementsIndirect /
glDrawArraysIndirect per command) when an indirect buffer is bound, so
compute-written command fields (Flywheel culling updates instanceCount)
are honored; detects GL_EXT_base_instance and falls back to the CPU loop
when the command's baseInstance cannot be consumed natively.
- Sync SSBO binding points for graphics draws, not just compute (Flywheel
vertex shaders read instance data from SSBOs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the application-specific PackPhotonSharedVec3Memory GLSL regex
patch with a general DecomposeWorkgroupVec3Pass SPIR-V optimization pass.
The new pass decomposes vec3/ivec3/uvec3/bvec3 Workgroup (shared) memory
variables into scalar arrays (e.g. shared vec3 arr[N][M] -> shared float
arr[N][M][3]), rewriting whole-vector loads/stores into per-component
scalar loads/stores. Component-level accesses (e.g. arr[i].x) are
unchanged since a trailing component index into a float[3] yields the
same scalar pointer as it did for a vec3.
Unlike the regex hack, the pass is application-agnostic: it does not
match on variable names, array dimensions, or shader pack identity, and
runs at the SPIR-V level before SPIRV-Cross decompilation.
Registered in SanitizeAndOptimizeBinary after AggressiveDCE so dead
workgroup accesses are already eliminated. Asserts on unsupported
OpAtomic*/OpCopyMemory targeting vec3 workgroup pointers.
Adds ProgramUtilTest.DecomposeWorkgroupVec3InSpirvPass covering array
declaration, +=, whole load/store, component access, and row-copy loop.