Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.20.1] - 2026-03-11

### Fixed

- **Metal: missing stencil attachment in render pass** — `BeginRenderPass` configured
only the depth attachment, completely skipping the stencil attachment. On Apple Silicon
TBDR GPUs, this left the stencil load action as `MTLLoadActionDontCare`, causing
undefined stencil values and progressive rendering artifacts on Retina displays.
Now configures `rpDesc.stencilAttachment` with texture, load/store actions, and clear
value — matching the Vulkan and DX12 backends.
([#171](https://github.com/gogpu/gg/issues/171))

- **Metal: missing `setClearDepth:` call** — depth clear value was never explicitly set,
relying on Metal's default of 1.0. Now calls `setClearDepth:` when `DepthLoadOp` is
`LoadOpClear` for correctness.

## [0.20.0] - 2026-03-10

### Added
Expand Down
2 changes: 2 additions & 0 deletions hal/backends_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func TestProbeBackendRegistered(t *testing.T) {
}
if info == nil {
t.Fatal("ProbeBackend returned nil info")
return
}
if info.Variant != gputypes.BackendEmpty {
t.Errorf("variant = %v, want BackendEmpty", info.Variant)
Expand All @@ -118,6 +119,7 @@ func TestProbeBackendViaFactory(t *testing.T) {
}
if info == nil {
t.Fatal("ProbeBackend returned nil info")
return
}
if info.Variant != testFactoryVariant3 {
t.Errorf("variant = %v, want %v", info.Variant, testFactoryVariant3)
Expand Down
23 changes: 23 additions & 0 deletions hal/metal/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,35 @@ func (e *CommandEncoder) BeginRenderPass(desc *hal.RenderPassDescriptor) hal.Ren
}
if desc.DepthStencilAttachment != nil {
dsa := desc.DepthStencilAttachment

// Depth attachment
depthAttachment := MsgSend(rpDesc, Sel("depthAttachment"))
if tv, ok := dsa.View.(*TextureView); ok && tv != nil {
_ = MsgSend(depthAttachment, Sel("setTexture:"), uintptr(tv.raw))
}
_ = MsgSend(depthAttachment, Sel("setLoadAction:"), uintptr(loadOpToMTL(dsa.DepthLoadOp)))
if dsa.DepthLoadOp == gputypes.LoadOpClear {
msgSendVoid(depthAttachment, Sel("setClearDepth:"), argFloat64(float64(dsa.DepthClearValue)))
}
_ = MsgSend(depthAttachment, Sel("setStoreAction:"), uintptr(storeOpToMTL(dsa.DepthStoreOp)))

// Stencil attachment — same texture, separate load/store/clear.
// Metal requires both depth and stencil attachments to be configured
// independently when using combined depth-stencil formats (e.g.
// Depth32FloatStencil8). Without this, the stencil load action
// defaults to MTLLoadActionDontCare, leaving stencil values
// undefined and causing progressive rendering artifacts on Apple
// Silicon TBDR GPUs.
// Reference: Rust wgpu-hal metal/command.rs:705-727.
stencilAttachment := MsgSend(rpDesc, Sel("stencilAttachment"))
if tv, ok := dsa.View.(*TextureView); ok && tv != nil {
_ = MsgSend(stencilAttachment, Sel("setTexture:"), uintptr(tv.raw))
}
_ = MsgSend(stencilAttachment, Sel("setLoadAction:"), uintptr(loadOpToMTL(dsa.StencilLoadOp)))
if dsa.StencilLoadOp == gputypes.LoadOpClear {
_ = MsgSend(stencilAttachment, Sel("setClearStencil:"), uintptr(dsa.StencilClearValue))
}
_ = MsgSend(stencilAttachment, Sel("setStoreAction:"), uintptr(storeOpToMTL(dsa.StencilStoreOp)))
}
encoder := MsgSend(e.cmdBuffer, Sel("renderCommandEncoderWithDescriptor:"), uintptr(rpDesc))
if encoder == 0 {
Expand Down
2 changes: 2 additions & 0 deletions hal/software/software_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,9 +1059,11 @@ func TestSurfaceAcquireTexture(t *testing.T) {
}
if acquired == nil {
t.Fatal("acquired is nil")
return
}
if acquired.Texture == nil {
t.Fatal("acquired.Texture is nil")
return
}
if acquired.Suboptimal {
t.Error("expected Suboptimal=false")
Expand Down
Loading