Skip to content

feat: gl 4.6 on metal - #10

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

aaf2tbz wants to merge 38 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. This is milestone M3 of the GL 4.x roadmap, to be released as winemetalgl-3.0.0 and 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.

  • Standalone Wine module (09e7fcb, a222f63):

    • Its own opengl32.dll forwarder plus winemetalgl.dll/.so, with WoW64 thunks.
    • Builds against the headers of any Wine 11.x; CI builds it.
    • Tested on stock Wine 11.0 (--with-opengl --with-vulkan) and on MetalSharp's Wine 11.17.
    • patches/wine is retired. Releases stage the module (install-wine-module.sh).
  • Optional extensions (G12):

    • Done: ARB_shader_viewport_layer_array, EXT_texture_shadow_lod, EXT_texture_sRGB_decode.
    • ASTC is deferred: block sizes other than 4×4 and an encoder.
    • ARB_texture_filter_minmax is not feasible: Metal's reduction mode has no effect on M4 / macOS 27.2.
    • Image uniforms past Metal's 8 read_write textures (7b47ed5).
  • wined3d FL 11_1 (G16):

    • wined3d reports feature level 11_1 through the module (9_3 on Apple's OpenGL).
    • Wine's d3d11 / d3d10core tests found 7 bugs, fixed in f76fd54; the review found the occlusion-query one (0b8f6ae).
    • Now 230 of 561,394 d3d11 and 209 of 194,651 d3d10core checks fail, the same for 64- and 32-bit programs; 32-bit d3d11 runs to the end since small buffers share pages below 4 GB.
    • Results and the remaining failures: docs/testing.md#direct3d-through-wined3d.
    • GL_AMD_pinned_memory (be2d1db) and the 32-bit buffer storage.
  • Corpus: scene4x at 4.4, 4.5 and 4.6 (fc3fe89), each matching the Apple-recorded 4.1 frame, with readbacks (persistent map, texture sub-image, statistics).

  • Performance (G14): benchmark A/B against 2.4.0, budgets. Development A/B on stock Wine 11.0: no entry more than 4% slower than 2.4.0 once the two flagged ones were rerun alternately; the evidence run follows the code freeze.

  • Evidence: KHR-GL46 (native cfg1, Wine cfg1 + cfg3 on stock 11.0), the MetalSharp 11.17 cfg1 check, and rechecks of KHR-GL33–43.

  • Reviews: seven independent reviews by area: shader compiler and SPIR-V, bindings and limits, direct state access, draws (indirect, tessellation, geometry), textures and formats, queries and transform feedback, the Wine module.

    • Each finding was checked against the code and the spec, and confirmed ones were fixed with a test that fails without the fix:
      • buffers, queries and the Wine module (0b8f6ae);
      • textures (0ba2044); direct state access (3da4020);
      • draws (eaad155); the shader compiler and SPIR-V (6211c21); a glslang fix the full run caught (7fba80f).
    • Among them: the wined3d occlusion query that lost one of 100 draws (Metal reset visibility slot 0 in a later pass), 32-bit multi-bind offsets, ERROR_BUSY for contexts current elsewhere, unvalidated application SPIR-V, and 3D BPTC readback.
    • Not changed: per-instance attribute conversion that grows with the base instance (it predates this branch, affects performance only, and needs per-draw rebasing of instance buffers).
  • DOOM (2016) confirmed by Avery.

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

Development runs (not evidence)

  • Full KHR-GL46 cfg1 on stock Wine 11.0 + module (09e7fcb): 15134 Pass, 4 Fail, 1 CompatibilityWarning, the same as native.
    • Since then the SPIR-V capabilities case passes.
    • The 3 remaining failures are known CTS defects or Metal limits.
  • Wine fixture gate: 71 fixtures × x86_64 + i386 (with opengl_module_edges), all pass on stock Wine 11.0.
  • GPU tests: 213, all pass.

DOOM[2016] Running Under WineMetalGL with OpenGL 4.5


image

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.
WineMetalGL as its own Wine module, built against any Wine 11.x install:
- winemetalgl.dll (builtin, x86_64 and i386): every GL command as a unix
  call (generated by tools/gen/wine_gen.py), WGL, pixel format selection
  and font lists adapted from Wine's opengl32 (LGPL-2.1+ notices kept);
- opengl32.dll: forwards every export to winemetalgl.dll, installed into a
  prefix with opengl32=native;
- winemetalgl.so: runs the core, owns contexts, pixel formats, window
  surfaces and pbuffers; window layers come from winemac's metal view when
  winemac exports macdrv_functions, else from a view of ours in the Cocoa
  window (stock WineHQ winemac exports nothing);
- wow64: 32-bit callers share the unix side; the core allocates buffer
  storage below 4 GB (new optional wmgl_set_buffer_memory), so mapped
  pointers reach them as they are; strings, sync objects and pointer arrays
  are converted by hand;
- debug callbacks are queued on the unix side and delivered by the PE side
  after the GL call that produced them.

scripts/build-wine-module.sh and scripts/install-wine-module.sh build and
install it; tests/wine/probe-opengl-wow64.sh runs the fixtures through it
with WINEMETALGL_MODULE=1 (and keeps a stock Wine's first boot from waiting
on the Mono/Gecko dialog).

Gate on stock Wine 11.0 (WineHQ tarball, unpatched): 69 fixtures, x86_64 and
i386 guests, all pass.
- Remove patches/wine and scripts/stage-wine.sh. The Wine module installs
  into any x86_64 Wine 11.x without patching or rebuilding Wine; the
  fixtures pass on stock Wine 11.0 and on MetalSharp's Wine 11.17.
- unix_window.m: Wine 11's struct macdrv_win_data is hwnd, cocoa_window,
  client_view; the module read the window rects as a view. The winemac
  metal view is used only for a window that already has a client view;
  WINEMETALGL_WINE_DEBUG logs which way each window takes.
- build-wine-module.sh takes a Wine source tree as well as an installation;
  CI builds the module against the Wine 11.0 release headers with a pinned
  llvm-mingw and checks the builtin marker and the WoW64 call table.
- Releases stage the module (stage-release.sh, verify-release.sh) with
  install-wine-module.sh, which works from a package too; the arm64 package
  is the core and the driver ABI header.
- Apple's OpenGL for references and benchmarks is Wine's own opengl32
  (WINEDLLOVERRIDES=opengl32=b) in replay.py, benchmark.py and run.py.
- The fixture probe always uses the module; opengl_later_entry_points no
  longer sets the patch's LaterEntryPoints key.
- Docs: building.md describes the module, release.md the new layout and
  what MetalSharp changes; README, CONTRIBUTING, DEPENDENCIES, SECURITY and
  the templates drop the patches.
…te limit

- inferStorageImageAccess decorates storage images a stage only reads
  NonWritable and those it only writes NonReadable, so SPIRV-Cross declares
  them access::read or access::write; atomics, function arguments and other
  uses stay read_write.
- A stage binds up to 16 images (textures 16..31), at most 8 read_write.
  GL_MAX_IMAGE_UNITS is 16 (binding points only); the per-stage image
  uniform limits stay 8, and GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES keeps
  counting 8 images.
- KHR-GL46.gl_spirv.spirv_validation_capabilities_test passes (natively and
  on stock Wine 11.0 with the module); the image, compute, SPIR-V,
  multi-bind, view, atomic counter, storage block, layout and DSA groups are
  unchanged otherwise.
- GPU test gpu_gl46_many_images: 13 images in one compute stage.
…ture

- GL_ARB_shader_viewport_layer_array (4.1+ contexts): vertex and
  tessellation evaluation shaders write gl_Layer and gl_ViewportIndex, as the
  geometry emulation's pass-through vertex function already did. Its 3 CTS
  cases pass; GPU test gpu_gl46_vertex_layer_viewport.
- GL_EXT_texture_shadow_lod (4.0+ contexts): glslang and SPIRV-Cross already
  translate textureLod and biased texture on array and cube shadow samplers.
  Its 15 CTS cases pass; GPU test gpu_gl46_texture_shadow_lod.
- Wine fixture opengl_buffer_storage (x86_64 and i386): a context above 4.3
  with GL_ARB_buffer_storage and GL_AMD_pinned_memory, a persistent coherent
  mapping that the next draw sees without a flush, and a pinned-memory
  buffer drawn from the program's pages after they change. 70 fixtures in
  the gate.
llvm-mingw's plain clang comes first on PATH in CI and has no macOS headers.
… lines

- scene4x core44: immutable buffer storage, the pixel count persistently
  mapped (reset and read through the mapping behind a fence), multi-bind,
  texture clears in place of glClear, enhanced layouts (normal and height
  packed by component, an explicit block member offset).
- core45: every object created with direct state access, a cull distance
  beyond the left clip plane, and the stored image's centre read back with
  glGetTextureSubImage and compared with the displayed pixel.
- core46: four indirect draws whose count comes from a parameter buffer,
  with gl_DrawID and gl_BaseInstance cancelling out in the vertex shader;
  statistics queries checking the submitted vertices and patches and the
  compute invocations; a vote in the compute shader; the display pass in
  SPIR-V. All three render scene4x-core41's frame (Apple's OpenGL reference).
- lowerBuiltinUniforms declared the hidden draw-parameter uniforms inside
  the first #define line when one preceded the code, breaking the shader;
  declarations now go before the first token outside a directive. Host test
  shader_builtin_uniforms_after_defines.
wined3d reports feature level 11_1 on WineMetalGL (9_3 on Apple's OpenGL).
Its d3d11 and d3d10core conformance tests found:

- Geometry shader emulation copied "invariant gl_Position;" (wined3d's
  geometry shaders) into its compute kernel, so the program failed to link:
  layered rendering, stream output from geometry shaders and tessellation,
  viewport arrays. Qualifier-only redeclarations of built-ins are dropped.
- GL_EXT_texture_sRGB_decode: TEXTURE_SRGB_DECODE_EXT on textures and
  samplers, sampled through the format's linear view. wined3d disables its
  sRGB render target support (ARB_framebuffer_sRGB) without it; sRGB
  multisample resolves and swapchain views now come out linear.
- wined3d writes literal constants as global consts initialized from bit
  casts; glslang does not fold them and rejected the shader (feature level
  9_x draws). The const goes; the bits are kept exactly.
- Converted per-instance attributes with divisor UINT_MAX (D3D step rate 0)
  overflowed 32 bits and converted no elements.
- S3TC texture view classes (DXT1 RGB/RGBA, DXT3, DXT5), so sRGB views of
  typeless BC1-3 textures are allowed; BPTC formats on GL_TEXTURE_3D (GL 4.6
  table 8.14).
- Occlusion queries over more than 2^32 samples in one pass wrapped: Apple
  GPUs count a visibility slot in 32 bits, so a pass moves to a new slot
  before its draws could cover 2^31 samples.
- WINEMETALGL_DUMP_SHADERS=1 also prints application shaders that fail to
  compile.

GPU tests for each. d3d11 failures 370 -> 231 and d3d10core 353 -> 210 of
561,394 and 194,651 checks, 205 of each from D24 depth stored as
Depth32Float (depth bias units).
…ported reasons for KHR-GL46

- Buffer storage for 32-bit programs is allocated top-down, away from the
  program's own heap, and a failed allocation is reported with the size and
  the storage in use (WINEMETALGL_WINE_DEBUG traces each one).
- Occlusion queries trace their slot count and any slot that counted
  nothing (WINEMETALGL_LOG=3).
- tests/cts/notsupported.json: reasons for the KHR-GL46 NotSupported groups
  (sparse texture 2 and clamp, post-depth coverage, 8 samples, image
  samples, the tessellation in/out limits, ES-only cube map array cases).
- gpu_gl46_occlusion_many_draws creates and clears its target inside the
  query, through a second framebuffer, as wined3d does.
wined3d reports feature level 11_1 on WineMetalGL. docs/testing.md describes
how Wine's d3d11 and d3d10core tests are built and run, their results on
stock Wine 11.0 with the module, and what the remaining failures are.
Buffers:
- Re-specifying a GL_AMD_pinned_memory buffer gives it storage of its own
  instead of writing into and keeping the application's memory.
- Host memory Metal releases (32-bit programs) goes back on an application
  thread, before the next allocation: Metal may release on its own thread.
- glGetBufferSubData and glCopyBufferSubData fail on any non-persistent
  mapping (GL 4.6 sections 6.6, 6.7), not only an overlapping one.
- Range checks compare size with limit - offset, so offset + size cannot
  wrap past them.

Queries:
- Visibility results accumulate, and slot 0 is never a query's: a later
  pass in the command buffer reset what the first query draw had counted
  there (wined3d's test_occlusion_query lost one of 100 draws).
- Framebuffer blits and texture clears drawn in an open pass do not count
  towards occlusion queries.
- Occlusion slots and FRAGMENT_SHADER_INVOCATIONS counters move on before
  what their draws can count (instances included) passes 2^31.
- glQueryCounter refuses a query glCreateQueries made for another target.
- TIME_ELAPSED subtracts GPU times as reported; a waited timer result waits
  for its completion handler instead of ending as 0.
- Query-buffer results: a negative offset is INVALID_VALUE; unaligned
  offsets are written once the buffer is idle.

Wine module:
- 32-bit glBindBuffersRange, glBindVertexBuffers and
  glVertexArrayVertexBuffers widen their GLintptr/GLsizeiptr arrays.
- A context current on another thread can be neither made current nor
  deleted (ERROR_BUSY). Contexts are allocated one by one, and the debug
  callbacks (KHR and AMD, separately) are read through the thread's current
  context.
- Below 4 GB, small buffers and the string copies share 1 MiB chunks in
  16 KiB pages instead of a 64 KiB reservation each.
- Destroyed windows' drawables go (the PE side notices with IsWindow); the
  fallback view hides with its window, clips to its ancestors' client
  areas, and is placed again when its Cocoa window was missing or closed.
- A pbuffer keeps one DC until released; wglQueryPbufferARB with an unknown
  attribute is ERROR_INVALID_DATA; a DC without a pixel format is
  ERROR_INVALID_PIXEL_FORMAT; 32-bit glGetPointerv narrows checked.
- install-wine-module.sh reports a failed codesign.

Tests: gpu_gl46_occlusion_many_draws breaks the pass between draws,
gpu_gl46_occlusion_ignores_blit, gpu_cr_buffer_range_checks, the pinned
memory test re-specifies its buffer, and the opengl_module_edges fixture
(multi-bind ranges, ERROR_BUSY, pbuffer DCs, child window lifecycle) joins
the gate.
- 3D compressed (BPTC) textures read back each depth: glGetCompressedTexImage
  copies by depth, not by a slice the texture does not have, and decoded
  reads (glGetTexImage, glGetTextureSubImage) copy the image into a 2D
  texture first (Metal makes only 3D views of a 3D texture).
- glClearTexImage without data converts zeros like data, so RGB formats
  stored as RGBA keep alpha 1; a multisample GL_DEPTH_COMPONENT16 clear
  reads its 16-bit depth; glClearTexSubImage bounds cannot wrap.
- Emulated clamp-to-border: projective shadow lookups divide the reference
  by q; NEAREST_MIPMAP_LINEAR blends two nearest lookups instead of
  filtering linearly.
- Stencil textures (STENCIL_INDEX8, or depth-stencil in STENCIL_INDEX mode)
  are incomplete with a linear filter (GL 4.6 section 8.17).
- Immutable textures can be render targets, for renderable views of a
  format that is not (GL_RGB9_E5 seen as GL_R32UI).

Tests: gpu_gl46_bptc_3d reads back and re-uploads each depth,
gpu_gl46_clear_texture_values, gpu_gl46_border_proj_mip_nearest,
gpu_gl46_view_render_stencil_complete. KHR-GL46 texture_view,
stencil_texturing, clear_texture, get_texture_sub_image,
texture_border_clamp, texture_cube_map_array, copy_image, texture_gather,
buffer_storage and the DSA texture and buffer groups (dev, native cfg1):
777 Pass, 2 NotSupported, 1 known failure (texture_view.view_sampling).
- glFramebufferTextureLayer and glNamedFramebufferTextureLayer attach a
  cube map face by layer (GL 4.5).
- glGetTexParameter*/glGetTextureParameter* report GL_TEXTURE_TARGET (4.5).
- In compatibility contexts the DSA vertex array commands take 0 for the
  default vertex array.
- glGetTextureImage of a cube map packs the six faces as one 3D image (pack
  parameters, image skipping); bufSize bounds client memory only, and a
  negative one is INVALID_VALUE. glGetCompressedTextureImage likewise.
- glTextureSubImage3D/glCompressedTextureSubImage3D of a cube map: bounds
  that cannot wrap, UNPACK_SKIP_IMAGES honoured, a compressed depth of 0
  writes nothing.
- glVertexArrayVertexBuffer takes names from glGenBuffers not yet bound;
  glTransformFeedbackBuffer* with a buffer that does not exist is
  INVALID_VALUE (GL 4.6 section 13.2.2).
- Pixel buffer ranges (pack, unpack, glReadPixels, glBitmap, glDrawPixels)
  are checked without offset + size wrapping.

Tests: gpu_gl46_dsa_corners, compat_dsa_default_vertex_array. KHR-GL46
direct_state_access, layered framebuffers, packed_pixels,
pixelstoragemodes, transform_feedback, texture_cube_map_array and
vertex_attrib_binding (dev, native cfg1): 6023 Pass, 82 NotSupported, the
known framebuffers_check_status CompatibilityWarning.
- Transform feedback with a vertex stage that writes memory draws its
  captured list in one pass under both provoking-vertex conventions: the
  list is drawn through indices that put each primitive's provoking vertex
  first (a rotation, keeping the winding). The vertex stage no longer runs
  twice under the default last-vertex convention, and strips and fans
  flat-shade from GL's provoking vertex under the first.
- Geometry and tessellation emulation: an allocation the device cannot
  make skips the draw (logged) instead of writing through nil buffers; a
  tessellation control border variant that did not compile is not bound;
  chunks made for one huge allocation are let go once their work is done.
- Indirect draws: command and count offsets near the top of the address
  space cannot wrap the bounds checks; the indirect and parameter buffers
  are checked again after the unlocked wait for GPU writes.
- glMultiDraw*Indirect*: mode, type, vertex array, indirect buffer and
  alignment errors are generated once before any command is read, also
  when no command draws.

Tests: gpu_gl46_xfb_memory_flat_provoking. KHR-GL46 draw_indirect,
indirect_parameters, shader_draw_parameters, transform_feedback,
transform_feedback_overflow_query, tessellation_shader,
pipeline_statistics_query, vertex_attrib_64bit and texture_border_clamp
(dev, native cfg1): 447 Pass, 1 NotSupported (listed).
MTLRenderPassDescriptor.visibilityResultType is macOS 26 API, which the
macOS 15 SDK (CI) does not have. Reserving slot 0 is what fixes the lost
draw (gpu_gl46_occlusion_many_draws passes with it alone), on every macOS.
- Arrays of arrays of images are reflected as a uniform per innermost
  array with consecutive slots, as samplers are; the reflection no longer
  writes past its image slots (image2D img[2][3]).
- Application SPIR-V is checked before SPIRV-Cross parses it: every
  instruction fits the module, every <id> operand is below the header's
  bound (at most 2^22), and opcodes are known. Operand kinds come from
  glslang's SPIR-V tables (spirv_validate.cpp).
- SPIR-V atomic counter decrements return the value before the decrement,
  as OpAtomicIDecrement does (GLSL's function returns the value after).
- SPIR-V stages match default-block uniforms by location (ARB_gl_spirv):
  stages naming a location differently are renamed alike at link; structure
  types are named by layout whether or not the module names them.
- An array of transform feedback output blocks is captured by consecutive
  buffers, element k into xfb_buffer + k (GLSL 4.60 section 4.4.2.1).
- Arrays of arrays of atomic counters are a uniform per innermost array.
- A #version or varying index too long for an int no longer throws out of
  glCompileShader or glLinkProgram.

Tests: gpu_gl46_image_arrays_of_arrays, gpu_gl46_spirv_malformed,
gpu_gl46_spirv_counter_decrement, gpu_gl46_long_numbers,
gpu_gl46_spirv_uniform_locations, gpu_gl46_xfb_block_array_buffers,
gpu_gl46_counter_arrays_of_arrays. KHR-GL46 gl_spirv, shader_atomic_counters,
transform_feedback*, program_interface_query, shader_image_load_store,
shader_draw_parameters and shader_subroutine (dev, native cfg1): 211 Pass,
2 NotSupported (listed). KHR-Single-GL46 enhanced_layouts and
arrays_of_arrays_gl: 5048 Pass, the same 8 failures as before (cbf2208).
glslang declared the bias forms (texture of sampler2DArrayShadow and
samplerCubeArrayShadow, textureOffset of sampler2DArrayShadow) for every
stage, so a vertex shader using one compiled. Since the extension is
advertised (f93d243), KHR-GL46.negative_texture_lookup_functions_with_bias
runs those cases instead of skipping them, and they failed.

KHR-GL46 negative_texture_lookup_functions_with_bias, ext_texture_shadow_lod
and texture_lod_bias (dev, native cfg1): 109 Pass, 27 NotSupported (sparse).
Full KHR-GL46 at 6211c21 (dev, native cfg1): 15153 Pass; the failures are
these three and the known four.
d3d11 230 and d3d10core 209 failures of 561,394 and 194,651 checks, the
same as 64-bit and 32-bit programs: test_occlusion_query passes, and
32-bit d3d11 now runs to the end (wined3d's 64 MB persistent chunks find
room below 4 GB since small buffers share pages).
DOOM (2016) attaches its render targets on one context and draws with
another (its render thread's). GL makes framebuffers per-context container
objects, so on the drawing context the names were new, empty framebuffers
(binding an unused name creates one in a compatibility context), every
scene draw failed with INVALID_FRAMEBUFFER_OPERATION, and the game showed
a black screen after loading. Mesa keeps framebuffers in the shared state,
and NVIDIA and AMD share them too; framebuffer names now live in the share
group.

Silent failures are reported (at WINEMETALGL_LOG=1 and above):
- a framebuffer that cannot be drawn to, once per framebuffer and
  attachment point, with what is wrong with the attachment;
- each GL error once per command and error, with the command.

Tests: gpu_shared_framebuffers (fails without the change). KHR-GL46 DSA
framebuffers, framebuffer_blit, draw_buffers, api, CommonBugs,
layout_binding, packed_depth_stencil, texture_view, layered FBOs (dev,
native cfg1): 94 Pass, the known CompatibilityWarning and view_sampling.
DOOM (2016) ran at 10 fps: its menu video uploads three planes a frame
(1920x1088 and 960x544, GL_RED/GL_UNSIGNED_BYTE into R8), and each texel
went through the generic converter (four doubles, clamped, re-encoded):
about 20 ms a plane under Rosetta, with the share group's lock held, so
its other thread waited about 50 ms a frame. Frames now take 12-13 ms
(0.2-0.6 ms of it in GL calls).

- unpackPixels copies whole rows when the client bytes are the stored
  bytes: RED/RG/RGBA (and their integer forms) and ALPHA/LUMINANCE/
  LUMINANCE_ALPHA of matching 8, 16 and 32-bit components, and the
  existing raw formats with one-byte types. BGRA bytes into RGBA8 swap two
  bytes per texel.
- Uploads convert straight into the staging memory the GPU copies from,
  instead of a zero-filled vector copied again.
- WINEMETALGL_STATS also profiles every GL command across threads (time,
  calls and share-group lock waits per frame, the 15 costliest commands and
  each thread's share), printed every 60 frames.

KHR-GL46 packed_pixels, pixelstoragemodes, texture_swizzle,
texture_storage, texture_size_promotion, clear_tex_image, texture_stencil8,
texture_view, internalformat, get_texture_sub_image (dev, native cfg1):
6192 Pass, the 3 known failures.
- Keep CPU conversions of vertex attributes per buffer until its
  contents change, and decode runs of elements in typed loops.
- Read pixels into a pack buffer with a blit, without waiting.
- Cache index-range scans in a hash map and scan without restart
  in a vectorizable loop.
- Reuse the last pipeline, and skip encoder state, textures and
  samplers the encoder already has; bind the samplers a draw
  prepared instead of looking them up again.
- Upload large default uniforms once for both stages.
- Hash pipeline keys a word at a time.
- Stats: converted vertices and causes, CPU index rewrites and
  uniform bytes; WMGL_BENCH draw-overhead benchmark.
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