Repository navigation
release: v0.34.9 - #386
Merged
Merged
release: v0.34.9#386
Conversation
SetVertexBufferCommand configured every attribute with glVertexAttribPointer, which converts the values to float, so a uvec4 or ivec4 shader input read the float's bits. Integer formats now go through glVertexAttribIPointer, as Rust wgpu-hal does by VertexAttribKind (src/gles/queue.rs SetVertexAttribute). glVertexAttribIPointer is core in GL 3.0 and ES 3.0 and joins the required entry points on Windows and Linux. vertexFormatToGL returns the integer flag with the size, type and normalization, and maps Float16x2, Float16x4 and Unorm1010102, which fell to the Float32x4 default before (src/gles/conv.rs describe_vertex_format). Found by a flat integer varying readback: GLES returned 0x4f4afebb for 0x89abcdef (P1-GLES-026).
WGSL's @interpolate(flat) means @interpolate(flat, first), and integer varyings are always flat, but GL gives flat varyings the last vertex by default. Device open now sets glProvokingVertex(GL_FIRST_VERTEX_CONVENTION), core in desktop GL 3.2. OpenGL ES has it only with GL_ANGLE_provoking_vertex; an ES context without it keeps the last vertex, which only @interpolate(flat, either) allows. Rust wgpu-hal never sets the provoking vertex, so this follows the WGSL spec (Interpolation) rather than Rust (P1-GLES-026).
…reation The software backend dropped every failure between the shader source and the interpreter: naga's lowering and SPIR-V errors in CreateShaderModule, and the ParseModule error in ParsedModule. A render pipeline built from such a module was created and drew nothing; a compute pipeline failed with ErrComputeRequiresSPIRV, which did not say why. The module now parses its SPIR-V once at creation and keeps the error, and both pipeline creators return it with the stage and entry point, as Rust wgpu reports backend translation failures at pipeline creation. The root wraps HAL failures of CreateShaderModule, CreateRenderPipeline and CreateComputePipeline in the typed core errors (kind HAL), so callers can match them with errors.As. ADR-078 step 0 (P1-SW-019).
…struction counter ADR-078 step 1. Adds tests that pin what the interpreter computes today for integer and boolean vectors, comparisons, OpAll/OpAny, image queries, atomics and uniform uvec4 loads, each asserting the current wrong value and the step that fixes it, plus plain tests for the paths that are right today and gg shader controls. Adds integer-vector and loop benchmarks and an instruction counter behind the swinstrcount build tag, and a dynamic-offset compute test that loads a whole uvec2 uniform.
…ult shapes (ADR-078 step 2) Adds TagUVec2..4, TagIVec2..4 and TagBVec2..4 with raw 32-bit lanes, the lane kind and shape tables, a precomputed result shape per instruction, and makes every boundary function (buffer writes, sizes, composite index and insert, debugger printing, image coordinates, fragment outputs) handle the new tags. No producer emits them yet.
… ops (ADR-078 steps 3-4) Integer and bool vectors now come out of every producer with their own tags and exact 32-bit lanes: constants, OpCompositeConstruct, OpVectorShuffle, OpUndef, buffer loads, bitcasts, conversions, image fetches and size queries, the GLSL.std.450 integer functions, frexp exponents, unpackDouble2x32 and the compute builtins. The integer, comparison, logical and select ops work lane by lane on those words. Behavior changes, each to the WGSL definition: - division by zero gives the dividend and x % 0 gives 0 (was 0 for both); INT_MIN / -1 is INT_MIN and INT_MIN % -1 is 0; - OpSMod takes the divisor's sign (it had OpSRem's semantics); - OpBitFieldInsert inserts every lane (only lane 0 before). The expected-failure rows of steps 3 and 4 are flipped to plain checks, and the 0xDEADBEEF uniform case in hal/allbackends now passes on software.
…mats and flat varyings (ADR-078 step 5) Integer and bool varyings and vertex attributes cross the rasterizer as the bit patterns of their 32-bit words, and the rasterizer copies flat and integer varyings from the provoking vertex instead of interpolating them. Integer vertex formats (Uint8x2 to Sint32x4) are read zero- or sign-extended to their words; they returned zeros before. Scalar integer varyings no longer drop to 0, the second triangle of a strip pair takes its first vertex as the provoking one, and integer stores into storage buffers take the raw word.
…tising ray query (ADR-078 step 6) The interpreter returned zero for SPIR-V that naga emits from valid WGSL: OpVectorTimesMatrix (v * m), OpAtomicAnd/Or/Xor, OpIsNan/OpIsInf (naga's isnan/isinf), OpQuantizeToF16, OpSDotKHR/OpUDotKHR (dot4I8Packed and dot4U8Packed), OpImageGather, OpImageDrefGather and both OpImageSampleDref* forms. They now execute: gather reads the base level's linear footprint in Vulkan lane order with the sampler's address modes, and the depth compares test ref against each texel (WebGPU order) before filtering, so linear filtering is PCF. Sampler.Compare carries the comparison function from the HAL sampler. The software adapter no longer advertises FeatureRayQuery: the interpreter does not execute OpRayQuery*, so a ray_query shader could not run. The CPU BVH build stays reachable through the HAL. Tests: naga-compiled shader tests for every new opcode, shown to fail on the previous commit (all zeros), and an allbackends readback of gather, gatherCompare, sampleCompare and sampleCompareLevel that agrees on Vulkan, DX12 (FXC and DXIL), GLES and software. Range loops over the 128-byte raster.Triangle index instead of copying (gocritic).
ADR-078 step 7 (P1-SW-019; the interpreter half of P1-SW-017). One table, supportedOps, lists every function-body opcode the interpreter executes, with its name and whether it carries a result type and id. decodeInstruction takes its layout from the table, ParseModule rejects any other opcode in a function body, the Float16 and ray query capabilities, f16 and the ray query and acceleration structure types, and run()'s default returns the same error instead of skipping the instruction. The error names the opcode and wraps the new shader.ErrUnsupported, and it reaches CreateComputePipeline and CreateRenderPipeline as a HAL error. OpAll and OpAny were skipped before, so all() and any() returned false whatever the input. They now reduce bool vectors of every width. A table test checks the table against run()'s case labels and the opcode constants, and an emitter test compiles nine WGSL builtin families through the software device so a naga change that emits a new opcode fails there.
ADR-078 step 8 (P1-SW-019). Image loads now use naga's Restrict bounds check policy, as Rust wgpu-hal does on Vulkan without robust image access: the level, the sample, the array layer and each coordinate are clamped by an unsigned min against the queried count or size minus one. That clamp is integer vector arithmetic, which the interpreter now holds exactly, so the Unchecked stopgap is no longer needed. fetchTexelLevel's own clamp stays as a second line of defense. The visible change is at a negative coordinate, level or layer: it now reads the last texel, level or layer, as on Vulkan, instead of the first. Both are inside the texture, which is what WGSL allows. The new test covers 1D, 2D with mip levels, 2D arrays and 3D, in and out of bounds; its five negative-value rows fail on the Unchecked policy.
ADR-078 step 9 (P1-SW-019). Integer and bool vectors are word vectors everywhere since steps 3-8, so the conversions kept for the old forms go: laneBits and its saturating float-to-integer path, the TagArray branches of laneWords, lanePool, uint32Pair, expComponent, imageCoord3 and texelComponents. A float scalar in an integer lane now gives its bits, never its value; valid SPIR-V never puts one there. The typed value rows lose their expected-failure form (each keeps the step that fixed it), the unit tests use typed integer vectors, and a test pins the 48-byte Value size its doc states.
The software rasterizer applied stencilFront to every triangle and ran the stencil test only when stencilFront was not the default, so a pipeline with a stencil state only on the back face drew without one (P2-SW-030). The raster pipeline now keeps a front and a back stencil state and picks one per triangle by IsBackFacing and the pipeline's frontFace (index.bs "stencil test"). The test runs when either face is not the default, as core's isStencilEnabled, Vulkan's stencilTestEnable and Dawn's UsesStencil decide; an Undefined face member counts as its default there too. TestStencilTwoSided draws two triangles of opposite winding with front increment and back decrement (ccw and cw) and with a back-only replace, then probes each triangle's stencil with compare equal. It failed on software and passes on Vulkan, DX12 (FXC and DXIL), GLES and software.
…implicit-LOD sampling dpdx, dpdy and fwidth (plain, fine and coarse) returned 0 on the software backend, and implicit-LOD samples always read the base level, because every fragment ran alone. A fragment entry point that reaches a derivative or an implicit-LOD sample (shader.Module.NeedsQuads, through function calls) now runs one 2x2 quad at a time: the interpreter suspends each lane at such an instruction, the quad executor computes it from the lanes' values and resumes them. Fine and plain derivatives take the lane's row or column, coarse ones the top row and left column; an implicit-LOD sample takes its level from the coarse derivatives of its coordinate, one level per quad, with the bias added. Lanes that are uncovered or fail the early depth/stencil test run as helpers, whose buffer, atomic and storage-texture writes are dropped and whose outputs are not written. raster.QuadRasterizer produces the quads with the same coverage and interpolation as Rasterize and reuses its storage; other shaders keep the per-fragment path. P2-SW-020.
The software backend executed encoder commands when they were recorded, so a command buffer that was never submitted still changed resources, and a Queue.WriteBuffer or WriteTexture made between recording a copy and submitting it landed after the copy instead of before it. The command encoder now records each command as a closure holding its resources into the command buffer, and Queue.Submit runs the command buffers in order, as the GL backend of Rust wgpu replays its command list at submit (wgpu-hal/src/gles/queue.rs). Render and compute passes record their begin, state, draw/dispatch and end commands the same way; render pass Stats are gathered at record time. Submit refuses a command buffer the software backend did not record. Queue writes stay immediate, so they keep their place on the queue timeline. New tests cover an unsubmitted command buffer and a WriteBuffer between record and submit on every backend, and the software skip in TestStencilUploadQueueOrder is removed. P1-SW-031.
…igned WebGPU's texture descriptor validation, as Rust wgpu-core's create_texture applies it, allows a block-compressed format only on a 2D texture whose width and height are multiples of the block. Core accepted a BC1 5x5, an ETC2 6x4, a 1D BC1 and a 3D BC1 texture, so the input reached the driver on Vulkan. A 3D BC or ASTC texture needs the sliced-3D features, which gputypes does not have yet, so it is refused, as Rust refuses it without them. The errors are CreateTextureErrorInvalidCompressedDimension and CreateTextureErrorNotMultipleOfBlockSize (P2-VAL-017).
naga v0.21.4 casts a u32 image-load level to int in GLSL under Restrict and ReadZeroSkipWrite, which the GLES backend uses on GL 4.3+, so a WGSL textureLoad with a u32 level no longer fails to compile there; writes uint(gl_PrimitiveID) on OpenGL ES; and accepts compound assignment through a pointer parameter. The stencil blit goes back to a u32 mip level, and the software emitter test to *p += x.
Add hal/software/internal/texcompress, which decodes one texel of a BC1-BC7 block from that block alone: BC1-BC3 (S3TC), BC4/BC5 (RGTC, unorm and snorm), BC6H (ufloat and sfloat) and BC7. BC6H and BC7 are ported by algorithm from Mesa's texcompress_bptc_tmp.h (27e63a95). The palette arithmetic is the GPUs', measured by readback: BC1-BC3 colors and BC3 alpha use weights of 256 (round(256k/n)) with rounding, where Mesa divides exactly and truncates; BC4/BC5 use per-endpoint weights of 65536 to an 8.8 value widened by bit replication, and choose the snorm mode after clamping -128 to -127. testdata/gpu_readback.txt holds every texel of 112 blocks as textureLoad returned them on Vulkan, DX12 and GLES (bit-identical); the test checks each bit for bit. Its generator is the allbackends decode test, wired with the software sampler.
Extend texcompress to ETC2 RGB8, RGB8A1 and RGBA8 (each with its sRGB variant) and EAC R11/RG11 unorm and snorm, per the Khronos Data Format Specification 1.3 ETC2 section: individual, differential, T, H and planar modes, punch-through alpha, and the EAC 11-bit formats (unorm of 2047, snorm of 1023 with a base codeword of -128 read as -127, multiplier 0). The golden readback gains 72 ETC2/EAC blocks covering every mode; all decode bit for bit.
WebGPU's GPUTexelCopyBufferLayout and hal.ImageDataLayout count rowsPerImage in texel block rows. bufferRowOffset took it for texel rows and divided it by the block height, so a WriteTexture or a buffer copy of several layers of a block-compressed texture placed every image after the first at the wrong offset. Uncompressed formats, whose block is one texel, were not affected.
Texel fetches of the BC1-BC7, ETC2 and EAC formats decode the texel from its own 4x4 block through internal/texcompress, then filtering, mip selection, layers and wrapping work as for any other format; the RGB of an sRGB format decodes to linear after the block decode. The software adapter now offers texture-compression-bc and texture-compression-etc2. TestCompressedTextureDecode reads every texel of a set of blocks per format with textureLoad on every backend: the software backend must return the committed GPU readback, and Vulkan, DX12 and GLES must return what the software backend returns, bit for bit (sRGB color channels as 8-bit codes, since GPUs approximate the sRGB curve).
The software backend recorded SetViewport and never read it: every draw mapped NDC to the whole render target with depth 0..1. One ViewportTransform now maps clip space to framebuffer coordinates and depth for every draw path (per-fragment, quad, MRT, indexed, indirect, vertex buffers), as the WebGPU spec's rasterization step defines, and rasterization is limited to the pixels whose centers lie in the viewport, intersected with the target and the scissor. The fullscreen blit fast path no longer stands in for a draw whose viewport is not the whole target. TestViewportMapping and TestViewportMappingIndirect read the result back on every backend; software failed all of them before. BenchmarkSmallTrianglesDraw measures the per-triangle vertex path. P1-SW-036
The software backend divided every vertex by its w and rasterized the result. A vertex behind the eye (w < 0) was mirrored through the center of projection, and a vertex beyond the ±2^21 pixels the rasterizer snaps to made the whole triangle vanish. A Clipper now clips every triangle in clip space, as the WebGPU spec's primitive clipping step does: exactly against 0 <= z <= w and w > 0, and in x and y against a guard band 8192 pixels past the render target, since the viewport and the scissor already bound the pixels drawn. New vertices interpolate the position and every attribute in clip space and copy flat attributes from the provoking vertex; the clipped polygon keeps the winding and is drawn as a fan. A triangle that needs no clipping keeps its attribute slices and allocates nothing. The old ClipTriangle, ClipTriangleNearFar, ClipTriangleAgainstPlane(s) and ClipTriangleFast were never called and flipped the winding of one of the two triangles they split a quad into; they are removed with their tests. TestClipTriangleBehindEye and TestClipTriangleFarVertex read the result back on every backend; software failed both before. P1-SW-027
Varyings were interpolated linearly in screen space, so textures and colors swam on any primitive whose vertices had different w. The rasterizer now keeps 1/w per vertex and weights each perspective lane by b_i/w_i, normalized, as the WebGPU spec requires (P1-SW-037). - Project keeps W = 1/w; the position builtin's w is the interpolated 1/w, as WGSL defines it. - Depth stays linear in screen space through one helper shared by the per-fragment and quad paths. - @interpolate(linear) inputs carry a NoPerspective lane mask, read from the SPIR-V NoPerspective decoration, and use screen barycentrics. A vertex clipping creates computes them from the triangle's vertices at its clip-space barycentrics, Σ b·w·A / Σ b·w, so they stay finite when an intermediate vertex of the clipper rounds to w = 0. - Attributes are interpolated as A0 + p1(A1-A0) + p2(A2-A0), so an attribute equal at all vertices comes back exactly. New readback tests on every backend cover a perspective quad with perspective and linear varyings, a texture, depth, position.z/w, a depth test against a plane, the far-plane clip of a perspective quad, and a linear varying on a triangle with a vertex behind the eye. On dx12 the position.w check is left out: naga's HLSL and DXIL backends pass SV_Position.w through instead of 1/w (naga P1-HLSL-037).
The software backend section of the README and docs/ARCHITECTURE.md describe what v0.34.9 does: commands at Queue.Submit, clip-space clipping, the viewport transform, perspective-correct interpolation, quad derivatives, two-sided stencil and BC/ETC2 sampling; the known gaps left are ASTC, depth-clip-control/frag_depth and helper invocations after discard. The GLES section names the vertex attribute and provoking vertex handling. ROADMAP and AGENTS.md move to v0.34.9 and naga v0.21.4.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Raw SPIR-V passed to CreateShaderModule went straight to the software interpreter, which sizes its value slice by the header Bound and indexes it by every result and operand ID. A module with an ID at or above Bound, or a Bound of 0, parsed fine and panicked with index out of range at the first draw or dispatch inside Queue.Submit (P1-SW-039). ParseModule now validates what the interpreter relies on, as the SPIR-V validator and naga's SPIR-V frontend (which Rust wgpu runs at module creation) do: - the header Bound is in [1, 4194303], the SPIR-V universal limit; - every result ID is in (0, Bound); - every ID operand of a function-body instruction, every result type, entry point function and interface, and variable initializer names an ID the module defines, found per opcode from the SPIR-V grammar so literal operands (switch cases, composite indexes, memory and image operand masks) are not mistaken for IDs; - a type uses only types declared before it, so a struct cannot contain itself, and vectors and matrices have 2 to 4 components or columns. The value slice is sized by the largest defined ID instead of the header Bound, which may legally be far larger. A test module declared a pointer before its pointee array, which SPIR-V forbids; it now declares them in order.
The Go spec lets a compiler fuse x*y + z into one fused multiply-add; the arm64 backend does, amd64 does not. In the clipper that moved a vertex the near plane creates from z = 0 to z = -1.5e-8 on arm64, so the clipped geometry, and the pixels it covers, differed between architectures, and two clipper tests failed on darwin/arm64 only. Plane distances and the interpolation in intersect now round each product with an explicit float32 conversion, which the spec defines to prevent fusion. The arm64 assembly of the clipper has no fused multiply-adds left (it had 16).
Every Metal draw passed MTLPrimitiveTypeTriangle whatever the pipeline's topology: Metal takes the primitive type per draw call, not from the pipeline state, and the topology was never carried to the encoder. A triangle strip of four vertices drew one triangle instead of two, and line and point pipelines drew triangles. TestFlatIntegerVaryings/strip found it on the macOS runner. The render pipeline now keeps the MTLPrimitiveType of its topology and Draw, DrawIndexed, DrawIndirect and DrawIndexedIndirect pass it, as Rust wgpu-hal does (RenderPipeline.raw_primitive_type, set_render_pipeline). A draw without a pipeline is not encoded. The indirect command buffer path stays triangle-list only.
TestCompressedTextureDecode demanded every GPU decode bit for bit what the
software backend returns. That held on Intel Iris Xe, whose readback is the
golden, and failed on the CI runners: WARP and Apple's virtual GPU round the
interpolated BC1-BC5 palette entries differently, by one 8-bit code.
BC1-BC5 texels now compare within one code of the format (1/255 unorm, the
sRGB code for sRGB channels, 1/127 snorm), with -1, 0 and 1 exact. The
Direct3D 11.3 specification (19.5) bounds BC1-BC3 by 1/255 against the
ideal; its 1/65535 and 1/32767 for BC4/BC5 against the ideal are not met by
Intel Iris Xe itself, so they cannot be the test's bound. The texels are
classified from the Khronos Data Format Specification formulas.
Two decodes the Khronos spec leaves to the implementation accept each
value it allows: a signed BC4/BC5 block with red0 = -127 and red1 = -128
(the mode comparison "may vary by implementation"; WARP returns -1 where
Intel returns 1), and a BC7 block of the reserved mode, whose alpha may be
0 or 1.
The virtual GPU of GitHub's macOS runners ("Apple Paravirtual device")
decodes interpolated BC1-BC5 entries up to two codes off; on that adapter
only, an interpolated value between its endpoints is accepted and logged.
BC6H, BC7, ETC2 and EAC still compare bit for bit.
CreateComputePipeline checked the workgroup size against the device limits only for WGSL modules: a SPIR-V module has no IR, and nothing read its LocalSize. On the software backend LocalSize 1024 1024 1024 started 2^30 invocations at the dispatch and ran out of memory, and the 32-bit product of 65536 65536 1 wrapped to zero invocations. A SPIR-V module now keeps the LocalSize of each GLCompute entry point, and pipeline creation applies the same checks as for WGSL (zero, per dimension, total invocations), as Rust wgpu-core does through naga's SPIR-V frontend. The software backend counts invocations in 64 bits.
A raw SPIR-V module states array lengths with any constant, and the interpreter materializes every constant composite and every Private, Function and Workgroup variable as values, one 48-byte value per scalar or vector. An OpConstantNull of array<f32, 0xFFFFFFFF> asked for 206 GB inside ParseModule, at CreateShaderModule, and ended the process with a fatal out of memory (P1-SW-040). ParseModule now gives every type its natural byte size as it is declared, with saturating arithmetic, and rejects the module when a type exceeds the device's maxBufferSize (the largest thing WebGPU lets a shader address), or when its Private variables exceed 64 KiB, the Function variables of one function 64 KiB, its Workgroup variables (used or not, all allocated per dispatch) 1 MiB, or its composite and null constants 1 MiB. WebGPU sets no limit for these, so the software backend names its own (shader.MaxPrivateBytes, MaxFunctionBytes, MaxWorkgroupBytes, MaxConstantBytes): Private and Function are eight times the WGSL guarantees of 8192 bytes, Workgroup 64 times the default maxComputeWorkgroupStorageSize, which still bounds what an entry point uses. ParseModuleLimits takes the limits; the software device passes its adapter's. The naga snapshot corpus (149 modules, two SPIR-V option sets) parses as before.
…s and Metal draws
…pter Replace the adapter-name exception for GitHub's macOS VM GPU with one tolerance for every adapter: an interpolated BC1-BC5 palette entry may differ from the software decode by two codes, endpoints by one, and -1, 0 and 1 are exact; a deviation above one code is logged with the adapter's name. The specification's value is always accepted: Metal returns BC4/BC5 snorm endpoints as exactly k/127, which the software decoder (exact with Intel Iris Xe) misses by 4e-6, and WARP returns -1 for the block whose mode the Khronos spec leaves open. WARP and Intel return snorm endpoints up to 2e-5 off k/127, so endpoints are not compared exactly. "Between its endpoints", the old exception, admitted up to 255 codes on that adapter, so a palette-order bug would have passed; and the virtual GPU likely forwards to the host's Apple GPU, so real Apple hardware could have failed what CI passed. The upload, copy and format bugs the Metal comparison is for move endpoint texels, by tens of codes.
…bounds Two more deep reviews of the bounds found modules they let through: - an array of images, samplers, pointers, empty structs, void or function types counted 0 bytes per element and still allocated one value each (array<image, 2^32>, array<void, 2^32-1>); - a type ID declared twice kept its old size in the composites sized from it while the interpreter used the new one; - Workgroup memory is laid out by ArrayStride and Offset decorations, not by the budgeted natural size: a stride of 2^32-1 asked for 4 GB per dispatch, and a decoration may come after the variable; - what an invocation holds live was not bounded at all: a value slot per ID up to the largest, the result of every typed instruction (OpUndef or OpLoad of a 256 MiB type), Uniform data, and Function variables along a call chain, times every invocation of a workgroup live at a barrier; - two entry points of one name: the core's workgroup-size check could take the other one. Every type now counts at least one 4-byte slot; a result ID defined twice, two entry points of one name and recursion (SPIR-V 2.16.1) are rejected; Workgroup memory is bounded after the parse by its laid-out size in 64 bits, so the ArrayStride Rust naga emits on workgroup arrays still parses; one invocation may hold at most 256 MiB live, and compute pipeline creation rejects a workgroup above 1 GiB. The core keeps the larger workgroup when two compute entry points share a name. The naga snapshot corpus and the tracked gg, g3d and gogpu shaders parse as before. README, CHANGELOG and the compute backend docs list the limits and say the software backend is not a sandbox for hostile SPIR-V: use WGSL for untrusted shaders.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A software backend correctness release: the CPU backend now renders and samples the way the GPU backends do, checked by readback against Vulkan, DX12 and GLES.
Fixed
Queue.Submit, as on the GPU backends (P1-SW-031). They ran when recorded, so a command buffer that was never submitted still took effect, and aQueue.WriteBuffer/WriteTexturemade between recording a copy and submitting it was overwritten in the wrong order.dpdx,dpdyandfwidth(fine, coarse and plain) return real differences instead of 0, andtextureSample,textureSampleBiasandtextureSampleComparepick the mip level from the coordinate's screen-space derivatives instead of always the base level. A discarded invocation stops instead of becoming a helper (see Known issues).@interpolate(linear)is honoured, depth is linear in screen space, and the fragment position's w is 1/w as WGSL defines it.SetViewportapplies to every draw, x, y, size and depth range (P1-SW-036). Every draw used the whole render target and depth 0..1.all(),any(), vector-times-matrix,isnan/isinf,quantizeToF16, packeddot4,textureGather,textureGatherCompare,textureSampleCompare(Level)and the remaining atomics returned zeros before. Integer vertex formats (uint32/sint32x1-x4) read as zero before.CreateRenderPipelineandCreateComputePipeline.textureLoadreads an in-bounds texel; a negative coordinate reads the last texel, as on Vulkan.texture-compression-bcandtexture-compression-etc2. ASTC is not offered yet.RowsPerImageof an upload or buffer copy counts texel block rows, as WebGPU defines it. Copies of several layers of a block-compressed texture put every layer after the first at the wrong offset.FeatureRayQuery: its interpreter does not execute ray queries (P1-SW-028). Executing them is planned (WebGPU RayQuery Support #369).glVertexAttribPointer, which converts to float, souint/sintvertex inputs read the float's bits. They now useglVertexAttribIPointer, as Rust wgpu-hal does.float16x2,float16x4andunorm10-10-10-2vertex formats were read asfloat32x4and are now mapped.@interpolate(flat)requires (glProvokingVertexon desktop GL; on OpenGL ES only withGL_ANGLE_provoking_vertex). GL's default is the last vertex.CreateTexture, as WebGPU and Rust wgpu-core require (P2-VAL-017): a width or height that is not a multiple of the block, a 1D compressed texture, a 3D compressed texture (the sliced-3D features are not offered yet). Before, such input reached the driver on Vulkan.Changed
textureLoad,textureDimensionsortextureNumLevelswith a u32 level on desktop GL 4.3+;primitive_indexcompiles on OpenGL ES; compound assignment through a pointer parameter (*p += x) is accepted.CreateTextureErrorInvalidCompressedDimensionandCreateTextureErrorNotMultipleOfBlockSize(newcore.CreateTextureErrorKindvalues) andCreateTextureError.Dimension.hal/software:Queue.Submitreturns an error for a nil command buffer or one not created by the software backend, before running any of the batch.hal/software/shader:Module.Executereads an integer input given as a float Value as its bits, no longer by value. Pass integer inputs as integer Values, as its documentation says.Known issues
discard, the discarded invocation ends instead of becoming a helper (naga emitsOpKill), so a non-linear derivative such asdpdxFine(x*y)in that quad differs from the GPU backends, and an alpha test followed by a perspective-warpedtextureSamplecan pick a slightly different mip level.@builtin(position).wreads w instead of 1/w (a naga HLSL/DXIL issue, to be fixed in naga).GL_ANGLE_provoking_vertex(Mesa ES, most Android) still give flat varyings the last vertex.-tags rustwith go-webgpu/webgpu v0.5.5 remains unusable for real rendering (0.34.6 known issues); in addition, programs abort at adapter request on Linux amd64 and Intel macOS. These are fixed in go-webgpu/webgpu's next release.