From 8bc177eb1bfb6818d046ce97e7dd6a4359c7267c Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Sat, 9 Aug 2025 15:19:30 -0700 Subject: [PATCH 1/3] wgpu: update to v25 of wgpu_native libraries --- gpu/buffer.go | 12 ++++++------ gpu/device.go | 21 ++++++++++----------- gpu/gpipeline.go | 2 +- gpu/gpu.go | 24 ++++++++++++------------ gpu/shader.go | 4 ++-- gpu/texture.go | 8 ++++---- gpu/types.go | 2 +- gpu/vars.go | 4 ++-- 8 files changed, 38 insertions(+), 39 deletions(-) diff --git a/gpu/buffer.go b/gpu/buffer.go index b7fbdc0d2f..1e31af1eac 100644 --- a/gpu/buffer.go +++ b/gpu/buffer.go @@ -14,8 +14,8 @@ import ( // note: WriteBuffer is the preferred method for writing, so we only need to manage Read // BufferMapAsyncError returns an error message if the status is not success. -func BufferMapAsyncError(status wgpu.BufferMapAsyncStatus) error { - if status != wgpu.BufferMapAsyncStatusSuccess { +func BufferMapAsyncError(status wgpu.MapAsyncStatus) error { + if status != wgpu.MapAsyncStatusSuccess { return errors.New("gpu BufferMapAsync was not successful") } return nil @@ -24,8 +24,8 @@ func BufferMapAsyncError(status wgpu.BufferMapAsyncStatus) error { // BufferReadSync does a MapAsync on given buffer, waiting on the device // until the sync is complete, and returning error if any issues. func BufferReadSync(device *Device, size int, buffer *wgpu.Buffer) error { - var status wgpu.BufferMapAsyncStatus - err := buffer.MapAsync(wgpu.MapModeRead, 0, uint64(size), func(s wgpu.BufferMapAsyncStatus) { + var status wgpu.MapAsyncStatus + err := buffer.MapAsync(wgpu.MapModeRead, 0, uint64(size), func(s wgpu.MapAsyncStatus) { status = s }) if errors.Log(err) != nil { @@ -44,9 +44,9 @@ func ValueReadSync(device *Device, values ...*Value) error { return nil } var errs []error - status := make([]wgpu.BufferMapAsyncStatus, nv) + status := make([]wgpu.MapAsyncStatus, nv) for i, vl := range values { - err := vl.readBuffer.MapAsync(wgpu.MapModeRead, 0, uint64(vl.AllocSize), func(s wgpu.BufferMapAsyncStatus) { + err := vl.readBuffer.MapAsync(wgpu.MapModeRead, 0, uint64(vl.AllocSize), func(s wgpu.MapAsyncStatus) { status[i] = s }) if err != nil { diff --git a/gpu/device.go b/gpu/device.go index 15cc30e004..51b29656d1 100644 --- a/gpu/device.go +++ b/gpu/device.go @@ -39,35 +39,34 @@ func NewDevice(gpu *GPU) (*Device, error) { // It gets the Queue for this device. func NewComputeDevice(gpu *GPU) (*Device, error) { // we only request max buffer sizes so compute can go as big as it needs to - limits := wgpu.DefaultLimits() + limits := wgpu.Limits{} + limits = wgpu.DefaultLimits() // Per https://github.com/cogentcore/core/issues/1362 -- this may cause issues on "downlevel" // hardware, so we may need to detect that. OTOH it probably won't be useful for compute anyway, // but we can just sort that out later // note: on web / chromium / dawn, limited to 10: https://issues.chromium.org/issues/366151398?pli=1 - limits.MaxStorageBuffersPerShaderStage = gpu.Limits.Limits.MaxStorageBuffersPerShaderStage - // fmt.Println("MaxStorageBuffersPerShaderStage:", gpu.Limits.Limits.MaxStorageBuffersPerShaderStage) + limits.MaxStorageBuffersPerShaderStage = gpu.Limits.MaxStorageBuffersPerShaderStage + // fmt.Println("MaxStorageBuffersPerShaderStage:", gpu.Limits.MaxStorageBuffersPerShaderStage) // note: these limits are being processed and allow the MaxBufferSize to be the // controlling factor -- if we don't set these, then the slrand example doesn't // work above a smaller limit. // TODO: converting these limits to int may cause issues on 32-bit systems - limits.MaxUniformBufferBindingSize = uint64(MemSizeAlignDown(int(gpu.Limits.Limits.MaxUniformBufferBindingSize), int(gpu.Limits.Limits.MinUniformBufferOffsetAlignment))) + limits.MaxUniformBufferBindingSize = uint64(MemSizeAlignDown(int(gpu.Limits.MaxUniformBufferBindingSize), int(gpu.Limits.MinUniformBufferOffsetAlignment))) - limits.MaxStorageBufferBindingSize = uint64(MemSizeAlignDown(int(gpu.Limits.Limits.MaxStorageBufferBindingSize), int(gpu.Limits.Limits.MinStorageBufferOffsetAlignment))) + limits.MaxStorageBufferBindingSize = uint64(MemSizeAlignDown(int(gpu.Limits.MaxStorageBufferBindingSize), int(gpu.Limits.MinStorageBufferOffsetAlignment))) // note: this limit is not working properly: g4 := uint64(0xFFFFFF00) - limits.MaxBufferSize = uint64(MemSizeAlignDown(int(min(gpu.Limits.Limits.MaxBufferSize, g4)), int(gpu.Limits.Limits.MinStorageBufferOffsetAlignment))) + limits.MaxBufferSize = uint64(MemSizeAlignDown(int(min(gpu.Limits.MaxBufferSize, g4)), int(gpu.Limits.MinStorageBufferOffsetAlignment))) if limits.MaxBufferSize == 0 { limits.MaxBufferSize = g4 } - // limits.MaxBindGroups = gpu.Limits.Limits.MaxBindGroups // note: no point in changing -- web constraint + // limits.MaxBindGroups = gpu.Limits.MaxBindGroups // note: no point in changing -- web constraint if Debug { fmt.Printf("Requesting sizes: MaxStorageBufferBindingSize: %X MaxBufferSize: %X\n", limits.MaxStorageBufferBindingSize, limits.MaxBufferSize) } desc := wgpu.DeviceDescriptor{ - RequiredLimits: &wgpu.RequiredLimits{ - Limits: limits, - }, + RequiredLimits: &limits, } wdev, err := gpu.GPU.RequestDevice(&desc) if errors.Log(err) != nil { @@ -107,5 +106,5 @@ func (dv *Device) WaitDone() { if dv.Device == nil { return } - dv.Device.Poll(true, nil) + dv.Device.Poll(true, 0) } diff --git a/gpu/gpipeline.go b/gpu/gpipeline.go index a0fefe9ccf..61106fe4f7 100644 --- a/gpu/gpipeline.go +++ b/gpu/gpipeline.go @@ -185,7 +185,7 @@ func (pl *GraphicsPipeline) Config(rebuild bool) error { if pl.System.Render().Depth.texture != nil { pd.DepthStencil = &wgpu.DepthStencilState{ Format: pl.System.Render().Depth.Format.Format, - DepthWriteEnabled: true, + DepthWriteEnabled: wgpu.OptionalBoolTrue, DepthCompare: wgpu.CompareFunctionLess, StencilFront: wgpu.StencilFaceState{ Compare: wgpu.CompareFunctionAlways, diff --git a/gpu/gpu.go b/gpu/gpu.go index 27bef9df28..87951e850d 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -88,7 +88,7 @@ type GPU struct { Properties wgpu.AdapterInfo // Limits are the limits of the current GPU adapter. - Limits wgpu.SupportedLimits + Limits wgpu.Limits // ComputeOnly indicates if this GPU is only used for compute, // which determines if it listens to GPU_COMPUTE_DEVICE @@ -181,7 +181,7 @@ func (gp *GPU) init(sf *wgpu.Surface) error { fmt.Println(gp.PropertiesString()) } - gp.MaxComputeWorkGroupCount1D = int(gp.Limits.Limits.MaxComputeWorkgroupsPerDimension) + gp.MaxComputeWorkGroupCount1D = int(gp.Limits.MaxComputeWorkgroupsPerDimension) dv := actualVendorName(&gp.Properties) if Debug || DebugAdapter { fmt.Println("GPU device vendor:", dv) @@ -201,7 +201,7 @@ func (gp *GPU) init(sf *wgpu.Surface) error { // string that the adapter VendorName contains, // or, failing that, from the description. func actualVendorName(ai *wgpu.AdapterInfo) string { - nm := strings.ToLower(ai.VendorName) + nm := strings.ToLower(ai.Vendor) // source: https://www.reddit.com/r/vulkan/comments/4ta9nj/is_there_a_comprehensive_list_of_the_names_and/ switch nm { case "0x10de": @@ -217,7 +217,7 @@ func actualVendorName(ai *wgpu.AdapterInfo) string { case "0x8086": return "intel" } - vd := strings.ToLower(ai.DriverDescription) + vd := strings.ToLower(ai.Description) if strings.Contains(vd, "apple") { return "apple" } @@ -225,13 +225,13 @@ func actualVendorName(ai *wgpu.AdapterInfo) string { } func adapterName(ai *wgpu.AdapterInfo) string { - if ai.Name != "" && !strings.HasPrefix(ai.Name, "0x") { - return ai.Name + if ai.Device != "" && !strings.HasPrefix(ai.Device, "0x") { + return ai.Device } - if ai.DriverDescription != "" && !strings.HasPrefix(ai.DriverDescription, "0x") { - return ai.DriverDescription + if ai.Description != "" && !strings.HasPrefix(ai.Description, "0x") { + return ai.Description } - return ai.VendorName + return ai.Vendor } func (gp *GPU) SelectGraphicsGPU(gpus []*wgpu.Adapter) int { @@ -259,7 +259,7 @@ func (gp *GPU) SelectGraphicsGPU(gpus []*wgpu.Adapter) int { } pnm := adapterName(&props) if strings.Contains(pnm, trgDevNm) { - devNm := props.Name + devNm := props.Device if Debug { log.Printf("gpu: selected device named: %s, specified in GPU_DEVICE or GPU_COMPUTE_DEVICE environment variable, index: %d\n", devNm, gi) } @@ -349,7 +349,7 @@ func (gp *GPU) SelectComputeGPU(gpus []*wgpu.Adapter) int { } pnm := adapterName(&props) if strings.Contains(pnm, trgDevNm) { - devNm := props.Name + devNm := props.Device if Debug { log.Printf("gpu: selected device named: %s, specified in GPU_DEVICE or GPU_COMPUTE_DEVICE environment variable, index: %d\n", devNm, gi) } @@ -408,7 +408,7 @@ func (gp *GPU) NewDevice() (*Device, error) { // PropertiesString returns a human-readable summary of the GPU properties. func (gp *GPU) PropertiesString() string { - return "\n######## GPU Properties\n" + reflectx.StringJSON(&gp.Properties) + reflectx.StringJSON(gp.Limits.Limits) + return "\n######## GPU Properties\n" + reflectx.StringJSON(&gp.Properties) + reflectx.StringJSON(gp.Limits) } // NoDisplayGPU Initializes WebGPU and returns that and a new diff --git a/gpu/shader.go b/gpu/shader.go index 22a767d400..9dad489459 100644 --- a/gpu/shader.go +++ b/gpu/shader.go @@ -57,8 +57,8 @@ func (sh *Shader) OpenFileFS(fsys fs.FS, fname string) error { // OpenCode loads given WGSL ".wgl" code for the Shader. func (sh *Shader) OpenCode(code string) error { module, err := sh.device.Device.CreateShaderModule(&wgpu.ShaderModuleDescriptor{ - Label: sh.Name, - WGSLDescriptor: &wgpu.ShaderModuleWGSLDescriptor{Code: code}, + Label: sh.Name, + WGSLSource: &wgpu.ShaderSourceWGSL{Code: code}, }) if errors.Log(err) != nil { return err diff --git a/gpu/texture.go b/gpu/texture.go index 9accabf181..2d4f32ed39 100644 --- a/gpu/texture.go +++ b/gpu/texture.go @@ -93,14 +93,14 @@ func (tx *Texture) SetFromGoImage(img image.Image, layer int) error { // https://www.w3.org/TR/webgpu/#gpuimagecopytexture tx.device.Queue.WriteTexture( - &wgpu.ImageCopyTexture{ + &wgpu.TexelCopyTextureInfo{ Aspect: wgpu.TextureAspectAll, Texture: tx.texture, MipLevel: 0, Origin: wgpu.Origin3D{X: 0, Y: 0, Z: 0}, }, rimg.Pix, - &wgpu.TextureDataLayout{ + &wgpu.TexelCopyBufferLayout{ Offset: 0, BytesPerRow: 4 * uint32(sz.X), RowsPerImage: uint32(sz.Y), @@ -259,9 +259,9 @@ func (tx *Texture) CopyToReadBuffer(cmd *wgpu.CommandEncoder) error { size := tx.Format.Extent3D() cmd.CopyTextureToBuffer( tx.texture.AsImageCopy(), - &wgpu.ImageCopyBuffer{ + &wgpu.TexelCopyBufferInfo{ Buffer: tx.readBuffer, - Layout: wgpu.TextureDataLayout{ + Layout: wgpu.TexelCopyBufferLayout{ Offset: 0, BytesPerRow: uint32(tx.ReadBufferDims.PaddedRowSize), RowsPerImage: wgpu.CopyStrideUndefined, diff --git a/gpu/types.go b/gpu/types.go index fd45d72e4e..0dcac20f5a 100644 --- a/gpu/types.go +++ b/gpu/types.go @@ -139,7 +139,7 @@ var TypeSizes = map[Types]int{ // TypeToVertexFormat maps gpu.Types to WebGPU VertexFormat var TypeToVertexFormat = map[Types]wgpu.VertexFormat{ - UndefinedType: wgpu.VertexFormatUndefined, + UndefinedType: 0, // Bool32: wgpu.VertexFormatUint32, // Int16: wgpu.VertexFormatR16Sint, // Uint16: wgpu.VertexFormatR16Uint, diff --git a/gpu/vars.go b/gpu/vars.go index 152308d77b..5966d162e1 100644 --- a/gpu/vars.go +++ b/gpu/vars.go @@ -101,12 +101,12 @@ func (vs *Vars) AddGroup(role VarRoles, name ...string) *VarGroup { } vg.alignBytes = 1 if role == Uniform { - vg.alignBytes = int(vs.sys.GPU().Limits.Limits.MinUniformBufferOffsetAlignment) + vg.alignBytes = int(vs.sys.GPU().Limits.MinUniformBufferOffsetAlignment) // note: wgpu-native reports alignment sizes of 64 // but then barfs when that is used. 256 seems to keep it happy vg.alignBytes = max(vg.alignBytes, 256) } else if role == Storage { - vg.alignBytes = int(vs.sys.GPU().Limits.Limits.MinStorageBufferOffsetAlignment) + vg.alignBytes = int(vs.sys.GPU().Limits.MinStorageBufferOffsetAlignment) vg.alignBytes = max(vg.alignBytes, 256) } vs.Groups[idx] = vg From 1f38e80b6923430b498da0530678153a59588699 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Mon, 11 Aug 2025 23:01:25 -0700 Subject: [PATCH 2/3] wgpu: updates --- gpu/device.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gpu/device.go b/gpu/device.go index 51b29656d1..709e14dd8f 100644 --- a/gpu/device.go +++ b/gpu/device.go @@ -39,8 +39,7 @@ func NewDevice(gpu *GPU) (*Device, error) { // It gets the Queue for this device. func NewComputeDevice(gpu *GPU) (*Device, error) { // we only request max buffer sizes so compute can go as big as it needs to - limits := wgpu.Limits{} - limits = wgpu.DefaultLimits() + limits := wgpu.DefaultLimits() // Per https://github.com/cogentcore/core/issues/1362 -- this may cause issues on "downlevel" // hardware, so we may need to detect that. OTOH it probably won't be useful for compute anyway, // but we can just sort that out later @@ -106,5 +105,5 @@ func (dv *Device) WaitDone() { if dv.Device == nil { return } - dv.Device.Poll(true, 0) + dv.Device.Poll(true, nil) } From c7607d647c95a1c23fe89253d7a6a0559722be96 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Tue, 19 Aug 2025 22:44:18 -0700 Subject: [PATCH 3/3] contimage: content image handling updates: scrollbars, double-click --- htmlcore/handler.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/htmlcore/handler.go b/htmlcore/handler.go index d245e496f0..822ddd71ae 100644 --- a/htmlcore/handler.go +++ b/htmlcore/handler.go @@ -17,7 +17,9 @@ import ( "cogentcore.org/core/base/iox/imagex" "cogentcore.org/core/colors" "cogentcore.org/core/core" + "cogentcore.org/core/events" "cogentcore.org/core/styles" + "cogentcore.org/core/styles/abilities" "cogentcore.org/core/styles/states" "cogentcore.org/core/styles/units" "cogentcore.org/core/text/lines" @@ -272,6 +274,15 @@ func handleElement(ctx *Context) { if pid != "" { img.SetName(pid) } + img.Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.Clickable, abilities.DoubleClickable) + s.Overflow.Set(styles.OverflowAuto) + }) + img.OnDoubleClick(func(e events.Event) { + d := core.NewBody("Image") + core.NewImage(d).SetImage(img.Image) + d.RunWindowDialog(img) + }) } go func() {