Skip to content

feat: gl 4.6 on metal - #10

Draft
aaf2tbz wants to merge 18 commits into
mainfrom
feat/gl46
Draft

aaf2tbz wants to merge 18 commits into
mainfrom
feat/gl46

Conversation

@aaf2tbz

@aaf2tbz aaf2tbz commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

OpenGL 4.4, 4.5 and 4.6 (core and compatibility) on Metal: milestone M3 of the GL 4.x roadmap, to be released as winemetalgl-3.0.0, shipped as a standalone Wine module that needs no Wine patches.

This is a draft; the description is the live progress record.

Progress

  • GL 4.4 (G09): clear texture, multi-bind, query buffer objects, enhanced layouts, mirror clamp to edge, stencil textures, 10F_11F_11F_REV vertices, GLSL 4.40, arbitrary border colours. 4.4 flip at be2d1db.
  • GL 4.5 (G10): DSA, cull distance, inverted conditional render, ES 3.1 compatibility, texture sub-image reads, derivative control, texture/image samples, context release behaviour, GLSL 4.50. 4.5 flip at 7bf4873.
  • GL 4.6 (G11): SPIR-V shaders, pipeline statistics, indirect parameters, draw parameters, group vote, atomic counter ops, transform feedback overflow queries, no-error contexts, parallel shader compile, GLSL 4.60. 4.6 flip at a451696; the 10 GL 4.4–4.6 fixtures joined the gate.
  • Optional extensions (G12), wined3d FL 11_1 (G16), performance budgets (G14). GL_AMD_pinned_memory is done (be2d1db): Wine's wow64 opengl32 needs it to give 32-bit guests more than 4.3 and ARB_buffer_storage.
  • Standalone module: own opengl32.dll + winemetalgl.dll/.so, wow64 thunks, tested on stock Wine 11.0
  • Corpus and benchmark entries that exercise 4.4–4.6
  • Evidence: KHR-GL46 (native cfg1, Wine cfg1 + cfg3 on stock 11.0) and rechecks of KHR-GL33–43
  • Reviews of the GL 4.0–4.6 implementation, findings resolved
  • DOOM (2016) confirmed by Avery

Design notes for the 4.x pieces that work around Metal (DSA, cull distances, border colours, draw parameters, pipeline statistics, SPIR-V) are in docs/decisions/0002-gl4-architecture.md.

CTS (development runs, native cfg1, not evidence)

  • KHR-GL45.direct_state_access: 371 of 371. cull_distance, conditional_render_inverted, get_texture_sub_image, texture_border_clamp, gpu_shader5 (clamp-to-border gathers), buffer_storage, incomplete_texture_access, get_uniform: all pass.
  • KHR-GL44.clear_tex_image: all but the generic compressed case, a known CTS defect.
  • KHR-Single-GL46 (enhanced_layouts 4250 and arrays_of_arrays_gl 806 cases, which the plain lists mostly skip): 5048 of 5056. Open: struct varyings with doubles, struct outputs captured through the geometry/tessellation emulations, xfb_vertex_streams, and two multi-dimensional interface-array cases.
  • GL 4.6 groups: draw parameters 13/13, indirect parameters 3/3, pipeline statistics 10/10, transform feedback overflow 13/13, atomic counter ops 4/4, group vote 5/5, parallel shader compile 3/3, spirv_extensions 1/1, gl_spirv 8/9 (the capabilities case needs 10 image uniforms in a compute stage; GL's minimum and ours is 8, as Metal allows 8 read_write textures per function).
  • Wine fixture gate: 69 fixtures × x86_64 + i386, all pass at 4.6.

glClearTexImage and glClearTexSubImage (GL 4.4, GL_ARB_clear_texture):
one texel converted as by TexSubImage without the pixel store state, or
zeros, repeated over a region of a level, layer by layer; cube maps clear
their faces as layers and 1D arrays their rows. Multisample images are not
cleared yet. KHR-GL44.clear_tex_image: 75 of 78 pass; the buffer-texture
case needs DSA, the STENCIL_INDEX case stencil textures, and the
generic-compressed case is a CTS defect (known-failures.json).
glBindBuffersBase/Range, glBindTextures, glBindSamplers,
glBindImageTextures and glBindVertexBuffers (GL 4.4, GL_ARB_multi_bind):
a range past the last binding changes nothing; an error in one binding
leaves it alone and the others are made; the generic buffer binding is not
changed. glGetIntegeri_v answers the texture and sampler bindings of a
unit. KHR-GL44.multi_bind: 17 of 17.
With a buffer bound to GL_QUERY_BUFFER (GL 4.4,
GL_ARB_query_buffer_object), glGetQueryObject* writes the result (32 or
64 bits, or the availability flag) at the given offset through an ordered
GPU copy; GL_QUERY_RESULT still waits for the result on the CPU.
GL_QUERY_BUFFER_BINDING can be queried. KHR-GL44.map_buffer_alignment
passes in 4.4 contexts.
GL 4.4 (GL_ARB_texture_stencil8): STENCIL_INDEX and STENCIL_INDEX8 are
texture formats, uploaded with STENCIL_INDEX data and sampled as unsigned
integers. Stencil-only multisample blits of one format and sample count
copy every sample. GL_MIRROR_CLAMP_TO_EDGE is a wrap mode in 4.4
contexts (GL_ARB_texture_mirror_clamp_to_edge). KHR-GL46.texture_stencil8
passes (development override).
Transform feedback declared in the last vertex processing stage (GL 4.4,
GL_ARB_enhanced_layouts: xfb_buffer, xfb_offset, xfb_stride on outputs and
block members) replaces glTransformFeedbackVaryings: varyings are captured
at their byte offsets with the declared or implicit strides. Block
layout(offset/align) comes from glslang's layout like the other block
rules. KHR-GL46 transform feedback, geometry and tessellation groups show
no regressions.
GL_MAX_UNIFORM_BUFFER_BINDINGS is 84, the GL 4.5 minimum (14 blocks in
each of six stages). gl_MaxVertexUniformVectors and
gl_MaxFragmentUniformVectors match GL_MAX_*_UNIFORM_VECTORS
(KHR-GL46.limits).
GL 4.5 (GL_ARB_direct_state_access): glCreate* for every object type and
the named commands for buffers, textures, framebuffers, renderbuffers,
vertex arrays, transform feedback objects and queries (97 entry points).
Each named command runs the bind-to-edit implementation with the object
in a binding for the call, restored before it returns, so both share
their validation. Named commands add the GL 4.5 rules the bind-to-edit
ones do not have: INVALID_OPERATION for a name that is not an object of
the kind or a target the command does not accept, draw/read buffer
enums, colour attachments past the limit and sample counts.

Also: created queries answer GL_QUERY_TARGET and their defaults before
first use; glGetFramebufferParameteriv answers the framebuffer-dependent
state (GL 4.5); the default framebuffer reports its right buffers as
absent; glFramebufferTextureLayer checks the layer against the target's
limit; glVertexAttribFormat requires size 3 for
UNSIGNED_INT_10F_11F_11F_REV; glMemoryBarrierByRegion.
KHR-GL46.direct_state_access: 371 of 371 (one CompatibilityWarning).
GL 4.5 / ARB_cull_distance on Metal, which has clip distances only.
The vertex stage's gl_CullDistance (masked to a local array by
SPIRV-Cross) becomes the [[user(cullN)]] varyings plus an indicator
per distance (0 where negative, 1e30 elsewhere). Interpolated, the
indicator is exactly 0 only inside primitives with the distance
negative at every vertex, and a fragment variant discards those
fragments. The geometry and tessellation emulations carry
gl_CullDistance like gl_ClipDistance (including redeclared output
sizes), GL_MAX_CULL_DISTANCES and GL_MAX_COMBINED_CLIP_AND_CULL_
DISTANCES report 8, and #extension GL_ARB_cull_distance compiles as
GLSL 4.50 with the macro predefined.

KHR-GL46.cull_distance: 202 Pass, 16 NotSupported (by design) native
and under Wine; KHR-GL46.limits 140/140.
… strings

- glGetTextureSubImage and glGetCompressedTextureSubImage (GL 4.5 /
  ARB_get_texture_sub_image): regions of every target, cube faces as z,
  compressed images decoded, whole blocks for the compressed variant.
- The *_INVERTED conditional rendering modes (ARB_conditional_render_
  inverted).
- GL_CONTEXT_RELEASE_BEHAVIOR reports FLUSH (KHR_context_flush_control;
  releasing a context flushes it).
- Buffer sub-data, clears, copies and reads fail only where they overlap
  a non-persistent mapping (GL 4.5 sections 6.2.2, 6.3, 6.6); a mapped
  buffer is not renamed by a sub-data update.
- glEnable/DisableVertexAttribArray work without a bound VAO in 4.5 core
  contexts, as the CTS (get_uniform) and drivers expect.
- Shadow lookups of incomplete textures return 0.
- 4.4 and 4.5 contexts advertise the extensions those versions made core.

KHR-GL46 at dev 4.6: get_texture_sub_image 2/2, conditional_render_
inverted 2/2, incomplete_texture_access, get_uniform_tests,
buffer_storage 34/34 with the DSA buffer groups.
Metal's samplers give three border colours. Clamp-to-border is now
emulated in the shader for the draws that need more:
- Every stage is translated by BorderMsl (shader/border.cpp), which wraps
  each sample and gather of a non-arrayed 1D, 2D, 3D or rectangle
  texture in a per-texture macro; the default definition is the plain
  value, so ordinary draws are unchanged.
- A draw whose bound texture and sampler wrap CLAMP_TO_BORDER with a
  border Metal cannot give (other colours, integer and shadow borders,
  swizzled or non-RGBA formats, rectangle textures) uses a function
  variant (pipeline key borders) with the border baked in: the hardware
  value inside the image, the border for nearest samples outside it,
  hand filtering from texel reads for linear samples across the edge,
  per-texel substitution for gathers, and the border's depth for
  comparisons. Vertex (including transform feedback capture) and
  fragment functions, compute dispatches, and the geometry and
  tessellation control kernels take variants.

Cull distance fixes (from 2bafa02): gl_PerVertex declares
gl_CullDistance whether or not a shader writes it, so cull varyings
and the fragment cull test now need an actual write (the geometry
emulation's unwritten, uninitialised distances culled points in
shader_image_load_store.early-fragment-tests), and the fragment cull
variant inserts its input struct before an [[early_fragment_tests]]
function.

KHR-GL46 at dev 4.6: texture_border_clamp 32/32, gpu_shader5 22/22;
texture_*, shaders, shader_image_load_store and 19 other shader groups
(8,700 cases) with no new failures; GPU test gpu_gl44_border_colors.
glClearTex(Sub)Image of a multisample image, which Metal cannot upload
to: a render pass per layer sets every sample of the region, as a load
action for whole layers and a scissored draw otherwise. Colour texels
are written bit for bit through an unsigned integer view of the
storage; depth and stencil through the depth and stencil attachments.

GPU test gpu_gl44_clear_texture_multisample; KHR-GL46 clear_tex_image,
texture_multisample, sample_variables and copy_image unchanged.
The single-configuration CTS lists (khronos_mustpass_single: KHR-Single-
GL4x enhanced_layouts and arrays_of_arrays_gl) were never run; run.py now
takes them (gl46-khr-single.txt). enhanced_layouts goes from 3,940 to
4,244 of 4,250:
- Interface locations past the stage's GL_MAX_*_COMPONENTS / 4 (and
  vertex inputs past GL_MAX_VERTEX_ATTRIBS) fail to compile.
- GL_LOCATION_COMPONENT, and the GL_TRANSFORM_FEEDBACK_BUFFER interface
  (BUFFER_BINDING, active variables, TRANSFORM_FEEDBACK_BUFFER_STRIDE;
  OFFSET and TRANSFORM_FEEDBACK_BUFFER_INDEX of captured varyings).
- Transform feedback: only the buffers varyings are captured into must be
  bound; the interleaved limit applies per buffer; arrays of blocks and
  struct outputs are captured element by element.
- Vertex inputs may alias locations (glslang patch, as OpenGL allows),
  and attributes packed by component share their locations in the
  GL_MAX_VERTEX_ATTRIBS count; doubles packed by component read their
  part of the attribute.
- Geometry emulation: arrays of output blocks, and inputs that are
  arrays per vertex. Tessellation emulation of separable programs strips
  hashed locations; inputs (and input blocks) the previous stage does not
  write are allowed while unread (glslang patch extended to blocks).
- fp64: arrays of doubles loaded from buffers, and double members of
  interface blocks, move element by element (MSL native arrays).
- Internal emulation programs and the capture relink print their sources
  under WINEMETALGL_DUMP_SHADERS=1 when they fail.

KHR-GL46 transform_feedback, gpu_shader_fp64, vertex_attrib, program_
interface_query, geometry/tessellation_shader, shaders and 8 more groups:
no new failures. GPU tests: vertex input aliasing, xfb double matrices.
GL 4.3 arrays of arrays, which the single-configuration CTS list checks
(803 of 806 now):
- Arrays of arrays of samplers and of stage inputs and outputs, which MSL
  cannot declare, are flattened in SPIR-V before translation
  (flattenArraysOfArrays): access chains through every dimension index
  the one-dimensional array at ((i0 * d1 + i1) * d2 + ...).
- A sampler array of arrays is reflected as a uniform per innermost array
  ("s[i][0]", GL 4.6 section 7.3.1.1) in consecutive slots.
- Every element of an atomic counter array of arrays counts against
  GL_MAX_*_ATOMIC_COUNTERS.
- A uniform array of arrays with an initializer takes every unsized
  dimension from it (glslang patch extended).

Open: arrays of arrays as varyings through the geometry or tessellation
emulation (InteractionInterfaceArrays2/4). KHR-GL46 shaders, texture_*,
program_interface_query and 7 more groups: no new failures.
Contexts now get version 4.4 (GLSL 4.40) without the development
override.

GL_AMD_pinned_memory: glBufferData on GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_
AMD makes page-aligned application memory the buffer's storage
(newBufferWithBytesNoCopy), never replaced by orphaning or renaming.
Wine's opengl32 limits 32-bit (WoW64) guests to GL 4.3 and hides
GL_ARB_buffer_storage unless the host has it; with it, 32-bit guests get
4.4 and persistent mappings in 32-bit memory.

Wine fixtures: the version a context reports is in fixture_version.h
(raised with each flip); opengl_gl33_resources accepts Wine's mutable
pinned-memory storage for 32-bit persistent buffers. Gate: 59 fixtures,
x86_64 and i386, all pass. GPU test gpu_gl44_amd_pinned_memory.
Contexts now get version 4.5 (GLSL 4.50, and GLSL ES 3.10 in the
shading language list) without the development override. Every GL 4.5
extension is implemented: direct state access, clip control, cull
distances, conditional render inverted, derivative control, ES 3.1
compatibility, texture sub-image reads, shader texture image samples,
texture barrier, KHR_context_flush_control and KHR_robustness.

Wine fixtures: fixture_version.h reports 4.5; opengl_later_entry_points
uses glSpecializeShader (GL 4.6) as the later entry point;
opengl_gl33_texture's direct state access path (taken now that the entry
points exist) gives glTextureStorage2D and glTextureView sized formats,
as GL requires. Gate: 59 fixtures, x86_64 and i386, all pass. GPU test
gpu_gl45_derivative_control.
…w queries

- ARB_shader_draw_parameters: gl_BaseVertex, gl_BaseInstance and gl_DrawID
  (and the ARB names) become hidden default uniforms the draw fills from
  the GL command; multi-draws set the draw index around each draw.
- ARB_indirect_parameters: PARAMETER_BUFFER and glMultiDraw*IndirectCount.
  Multi-draw indirect commands are validated against the indirect buffer
  before anything draws.
- ARB_pipeline_statistics_query: the eleven counters, counted on the CPU
  from the draw paths; fragment shader invocations by a counting fragment
  function variant (pipeline key countFragments).
- ARB_transform_feedback_overflow_query: overflow and per-stream overflow
  queries, usable for conditional rendering.
- ARB_shader_atomic_counter_ops, ARB_shader_group_vote advertised in 4.6
  contexts. A vertex function's atomic counter buffers move into its
  storage block argument buffer: MAX_VERTEX_ATOMIC_COUNTER_BUFFERS is 8.
- Atomic counter buffers are referenced only by stages that use them.
- Transform feedback: a discarding draw no longer runs the vertex stage
  after the capture pass, and a rasterizing draw whose vertex stage writes
  memory captures while drawing, so the stage runs once.
- Geometry emulation: stream qualifiers are stripped only inside layout(),
  not from names such as out_stream0.

CTS (KHR-GL46, native cfg1, dev 4.6): the 4.6 groups pass except gl_spirv,
spirv_extensions and parallel_shader_compile; a 1385-case sweep of the
touched groups passes. GPU tests gpu_gl46_*; gate 59 fixtures x86_64 and
i386 pass.
glShaderBinary takes SPIR-V modules (SHADER_BINARY_FORMAT_SPIR_V);
glSpecializeShader checks the entry point and specialization constants and
translates the module to GLSL 4.60 with SPIRV-Cross, which the existing
compiler, emulations and program binaries take as application source
(src/core/shader/spirv_input.cpp):
- inter-stage variables are named by location and linked to the previous
  stage's outputs; unnamed resources by location or binding;
- program queries report generated names, and every name of a module
  without debug names, as empty;
- GL atomic counters and gl_PatchVerticesIn, which SPIRV-Cross's GLSL lacks;
- SPIR_V_BINARY, the SPIR-V/GLSL link check, GL_SPIR_V_EXTENSIONS.

Also: GL_MAX_COMBINED_IMAGE_UNIFORMS is checked at link.

CTS (KHR-GL46, native, dev 4.6): gl_spirv 8/9, spirv_extensions 1/1; the
capabilities case needs more than 8 image uniforms in a stage (next). A
4396-case sweep of program and shader groups shows no other failure.
Contexts report 4.6 and GLSL 4.60, with ARB_parallel_shader_compile and
KHR_parallel_shader_compile (compiles and links finish before the call
returns: COMPLETION_STATUS is TRUE; the thread limit is kept) and
KHR_no_error (the context flag was already honoured).

The 10 fixtures that needed GL 4.4 to 4.6 join the gate. Two fixtures
followed the version: opengl_wgl_modern now expects 4.6 requests to succeed
and 4.7 to fail; opengl_later_entry_points expects glSpecializeShader without
the LaterEntryPoints setting, as no core entry point is above a 4.6
context's version.

Gate: 69 fixtures, x86_64 and i386, all pass. GPU tests 186, host 46.
CTS (native, KHR-GL46): parallel_shader_compile 3/3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant