├── .gitignore ├── .goki └── config.toml ├── LICENSE ├── MACOS.md ├── MoltenVK.md ├── README.md ├── cmds.go ├── compute.go ├── consts.go ├── debug.go ├── device.go ├── doc.go ├── enumgen.go ├── errors.go ├── examples ├── compute1 │ ├── Makefile │ ├── compute.go │ ├── sqvecel.hlsl │ └── sqvecel.spv ├── drawidx │ ├── Makefile │ ├── drawidx.go │ ├── indexed.spv │ ├── indexed.vert │ ├── vtxcolor.frag │ └── vtxcolor.spv ├── drawtri │ ├── Makefile │ ├── drawtri.go │ ├── trianglelit.spv │ ├── trianglelit.vert │ ├── vtxcolor.frag │ └── vtxcolor.spv ├── exptest │ ├── Makefile │ ├── README.md │ ├── exptest.go │ ├── gpu_exptest.hlsl │ └── gpu_exptest.spv ├── images │ ├── ground.png │ ├── sound1.png │ ├── teximg.jpg │ ├── text.png │ ├── up.png │ ├── wood.png │ └── world1.png ├── memtest │ ├── Makefile │ ├── gpu_memtest.hlsl │ ├── gpu_memtest.spv │ └── memtest.go ├── phong │ └── phong.go ├── renderframe │ ├── Makefile │ ├── gtigen.go │ ├── indexed.spv │ ├── indexed.vert │ ├── renderframe.go │ ├── vtxcolor.frag │ └── vtxcolor.spv ├── texture │ ├── Makefile │ ├── gtigen.go │ ├── texture.go │ ├── texture_frag.frag │ ├── texture_frag.spv │ ├── texture_vert.spv │ └── texture_vert.vert └── vdraw │ └── vdraw.go ├── framebuffer.go ├── glfw.go ├── go.mod ├── go.sum ├── gosl ├── README.md ├── alignsl │ ├── README.md │ └── alignsl.go ├── diff │ └── diff.go ├── doc.go ├── examples │ ├── basic │ │ ├── README.md │ │ ├── compute.go │ │ ├── main.go │ │ └── shaders │ │ │ ├── Makefile │ │ │ └── basic_nouse.glsl │ └── rand │ │ ├── Makefile │ │ ├── README.md │ │ ├── main.go │ │ ├── rand.go │ │ ├── rand.hlsl │ │ └── shaders │ │ └── Makefile ├── extract.go ├── files.go ├── gosl.go ├── gosl_test.go ├── process.go ├── shaders │ ├── Makefile │ ├── basic.hlsl │ └── basic.spv ├── slbool │ ├── README.md │ ├── slbool.go │ └── slboolcore │ │ └── slboolcore.go ├── sledits.go ├── slprint │ ├── gobuild.go │ ├── nodes.go │ └── printer.go ├── slrand │ ├── Makefile │ ├── README.md │ ├── slrand.go │ ├── slrand.hlsl │ └── slrand_test.go ├── sltype │ ├── README.md │ ├── float.go │ └── int.go ├── testdata │ ├── basic.go │ ├── basic.go.gosl │ └── basic.golden └── threading │ └── threading.go ├── gpu.go ├── gpu_android.go ├── gpu_darwin.go ├── gpu_linux.go ├── gpu_windows.go ├── image.go ├── membuff.go ├── memory.go ├── memory_32.go ├── memory_64.go ├── opts.go ├── pipeline.go ├── render.go ├── renderframe.go ├── roles.go ├── shader.go ├── strings.go ├── surface.go ├── system.go ├── szalloc ├── gtigen.go ├── idxs.go ├── szalloc.go ├── szalloc_test.go └── uniqints.go ├── texture.go ├── types.go ├── vals.go ├── var.go ├── vars.go ├── varset.go ├── vdraw ├── README.md ├── config.go ├── draw.go ├── fill.go ├── gtigen.go ├── shaders │ ├── Makefile │ ├── draw_depma_frag.frag │ ├── draw_depma_frag.spv │ ├── draw_frag.frag │ ├── draw_frag.spv │ ├── draw_vert.spv │ ├── draw_vert.vert │ ├── fill_frag.frag │ ├── fill_frag.spv │ ├── fill_vert.spv │ └── fill_vert.vert └── vdraw.go ├── vgpu_test.go ├── vkinit ├── vkinit.go ├── vkinit_bsd.go ├── vkinit_darwin.go ├── vkinit_lin.go └── vkinit_windows.go ├── vphong ├── README.md ├── camera.go ├── color.go ├── enumgen.go ├── gtigen.go ├── lights.go ├── mesh.go ├── mtxs.go ├── sets.go ├── shaders │ ├── Makefile │ ├── onecolor_frag.frag │ ├── onecolor_frag.spv │ ├── onecolor_vert.spv │ ├── onecolor_vert.vert │ ├── pervertex_frag.frag │ ├── pervertex_frag.spv │ ├── pervertex_vert.spv │ ├── pervertex_vert.vert │ ├── phong_frag.frag │ ├── texture_frag.frag │ ├── texture_frag.spv │ ├── texture_vert.spv │ └── texture_vert.vert ├── system.go ├── texture.go └── vphong.go └── vshape ├── README.md ├── box.go ├── capsule.go ├── cylinder.go ├── doc.go ├── group.go ├── gtigen.go ├── lines.go ├── plane.go ├── shape.go ├── sphere.go ├── torus.go └── triangle.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /.goki/config.toml: -------------------------------------------------------------------------------- 1 | Name = "vgpu" 2 | Desc = "" 3 | Version = "v2.0.0-dev0.0.30" 4 | Type = "Library" 5 | 6 | [Build] 7 | Package = "." 8 | Output = "" 9 | ID = "com.org.todo.vgpu" 10 | Debug = false 11 | Rebuild = false 12 | Install = false 13 | PrintOnly = false 14 | Print = false 15 | Trimpath = false 16 | Work = false 17 | IOSVersion = "13.0" 18 | AndroidMinSDK = 23 19 | AndroidTargetSDK = 29 20 | 21 | [Web] 22 | Port = "8080" 23 | RandomVersion = false 24 | Gzip = false 25 | BackgroundColor = "#2d2c2c" 26 | ThemeColor = "#2d2c2c" 27 | LoadingLabel = "" 28 | Lang = "en" 29 | Title = "" 30 | Description = "" 31 | Author = "" 32 | Image = "" 33 | AutoUpdateInterval = "10s" 34 | WasmContentLengthHeader = "" 35 | ServiceWorkerTemplate = "" 36 | 37 | [Setup] 38 | [Setup.Platform] 39 | OS = "" 40 | Arch = "" 41 | 42 | [Log] 43 | Target = "android" 44 | Keep = false 45 | All = "F" 46 | 47 | [Release] 48 | VersionFile = "vgpu/version.go" 49 | Package = "vgpu" 50 | 51 | [Generate] 52 | Dir = "." 53 | Output = "gokigen.go" 54 | [Generate.Enumgen] 55 | Dir = "." 56 | Output = "enumgen.go" 57 | Transform = "" 58 | TrimPrefix = "" 59 | AddPrefix = "" 60 | LineComment = false 61 | AcceptLower = true 62 | Text = true 63 | JSON = false 64 | YAML = false 65 | SQL = false 66 | GQL = false 67 | Extend = true 68 | [Generate.Gtigen] 69 | Dir = "." 70 | Output = "gtigen.go" 71 | AddTypes = true 72 | AddMethods = false 73 | AddFuncs = false 74 | Instance = false 75 | TypeVar = false 76 | Setters = false 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, emergent 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MoltenVK.md: -------------------------------------------------------------------------------- 1 | # How to build and use a custom MoltenVK library 2 | 3 | There are a lot of hoops to jump through, thanks to lots of Apple security things that result in things like the app just failing to run with `Killed: 9` and no further explanation. 4 | 5 | * Download and install the latest SDK as a starting point from: https://vulkan.lunarg.com 6 | 7 | * Follow all the README.md from https://github.com/KhronosGroup/MoltenVK -- in particular: 8 | + actually *start* XCode -- not enough to update it -- it does some updates when launched 9 | 10 | * Example for how to use a modified dependency: 11 | + clone the dependency (e.g., https://github.com/KhronosGroup/SPIRV-Cross) 12 | + specify its path during build: 13 | 14 | ```bash 15 | $ ./fetchDependencies --macos --spirv-cross-root /Users/oreilly/github/SPIRV-Cross 16 | ```` 17 | 18 | * `make macos MVK_CONFIG_LOG_LEVEL=1` builds `.dylib` in: `Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib` -- if you don't specify the log level, you'll get all the info messages. 19 | 20 | * Critically you need to *remove* the existing file before copying over the new one to `/usr/local/lib`, where the SDK puts its library: 21 | 22 | ```bash 23 | $ sudo rm /usr/local/lib/libMoltenVK.dylib 24 | $ sudo cp Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib /usr/local/lib 25 | ``` 26 | 27 | * If you don't do this, the program will die immediately with `Killed: 9` -- all the other unsuccessful stuff below was failed attempts to fix this. 28 | 29 | * Also, there is this mysterious `.icd` file that needs to refer to the .dylib -- it won't load the library properly if you don't get this one right. The one that is installed by Vulkan SDK is good and the one in the package is **NOT** -- it specifies a path in the same dir. 30 | 31 | The default has a `library_path` that is relative -- can also have it point to the full correct path: 32 | 33 | ```json 34 | { 35 | "file_format_version" : "1.0.0", 36 | "ICD": { 37 | "library_path": "/usr/local/lib/libMoltenVK.dylib", 38 | "api_version" : "1.2.0", 39 | "is_portability_driver" : true 40 | } 41 | } 42 | ``` 43 | 44 | * Metal library location is: `/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/macos/lib/clang/31001.667/include/metal` 45 | 46 | * Compile a metal file directly to see what is going on: 47 | + do `make` in SPIRV-Cross top level, and copy `spirv-cross` exe to ~/bin 48 | 49 | ```bash 50 | $ spirv-cross gpu_sendspike.spv --metal --msl-version 30000 >ss.metal 51 | $ xcrun -sdk macosx metal ss.metal -std=metal3.0 52 | ``` 53 | 54 | 55 | I've got it working finally -- it took me way too long to figure out that I just needed to disable gatekeeper to not have to reboot every iteration! I tried a bunch of useless stuff about code signing certificates that never worked. 56 | 57 | #if MVK_XCODE_14 58 | if ( mvkOSVersionIsAtLeast(12.0) ) { 59 | _metalFeatures.mslVersionEnum = MTLLanguageVersion3_0; 60 | } 61 | #endif 62 | 63 | 64 | # XCode developer certificates, gatekeeper 65 | 66 | * To disable gatekeeper -- didn't help with rebooth issue: 67 | 68 | ```bash 69 | $ sudo /usr/sbin/spctl --master-disable 70 | ``` 71 | 72 | Here's info on code signing --might be useful someday but definitely didn't help with rebooting. 73 | 74 | * Or sign the thing somehow: https://ioscodesigning.com -- in XCode, can sign in with apple id and get a certificate. 75 | + Xcode, Preferences, Accounts, + to add 76 | + enter apple id 77 | + Do `Download Manual Profiles` -- doesn't work without this 78 | + Open `Keychain Access` app and find certificate, click on `Trust` and set to `Always Trust` 79 | + May need to reboot at this point. 80 | 81 | Verify: 82 | ```bash 83 | $ security find-identity -v -p codesigning 84 | ``` 85 | 86 | Sign: 87 | ```bash 88 | $ codesign -f -s rcoreilly@me.com Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib 89 | Warning: unable to build chain to self-signed root for signer "Apple Development: rcoreilly@me.com (86223M5MVQ)" 90 | Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib: errSecInternalComponent 91 | ``` 92 | 93 | This seems to fail, as confirmed by: 94 | ```bash 95 | $ codesign -v Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib 96 | Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib: code object is not signed at all 97 | ``` 98 | and: 99 | ```bash 100 | $ /usr/sbin/spctl -a -t exec -vv Package/Release/MoltenVK/dylib/macOS/libMoltenVK.dylib 101 | ``` 102 | 103 | Bunch of stuff that didn't work: https://developer.apple.com/forums/thread/86161 104 | 105 | Try installing: https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer 106 | 107 | 108 | -------------------------------------------------------------------------------- /consts.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vgpu 6 | 7 | import ( 8 | vk "github.com/goki/vulkan" 9 | ) 10 | 11 | // todo: this does not parse in enumgen 12 | 13 | // Topologies are the different vertex topology 14 | type Topologies int32 15 | 16 | const ( 17 | PointList = Topologies(vk.PrimitiveTopologyPointList) 18 | LineList = Topologies(vk.PrimitiveTopologyLineList) 19 | LineStrip = Topologies(vk.PrimitiveTopologyLineStrip) 20 | TriangleList = Topologies(vk.PrimitiveTopologyTriangleList) 21 | TriangleStrip = Topologies(vk.PrimitiveTopologyTriangleStrip) 22 | TriangleFan = Topologies(vk.PrimitiveTopologyTriangleFan) 23 | LineListWithAdjacency = Topologies(vk.PrimitiveTopologyLineListWithAdjacency) 24 | LineStripWithAdjacency = Topologies(vk.PrimitiveTopologyLineStripWithAdjacency) 25 | TriangleListWithAdjacency = Topologies(vk.PrimitiveTopologyTriangleListWithAdjacency) 26 | TriangleStripWithAdjacency = Topologies(vk.PrimitiveTopologyTriangleStripWithAdjacency) 27 | PatchList = Topologies(vk.PrimitiveTopologyPatchList) 28 | ) 29 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | package vgpu 9 | 10 | import ( 11 | "bytes" 12 | "fmt" 13 | "os" 14 | "runtime" 15 | "strings" 16 | ) 17 | 18 | // A StackFrame contains all necessary information about to generate a line 19 | // in a callstack. 20 | type StackFrame struct { 21 | File string 22 | LineNumber int 23 | Name string 24 | Package string 25 | ProgramCounter uintptr 26 | } 27 | 28 | // newStackFrame populates a stack frame object from the program counter. 29 | func newStackFrame(pc uintptr) (frame StackFrame) { 30 | 31 | frame = StackFrame{ProgramCounter: pc} 32 | if frame.Func() == nil { 33 | return 34 | } 35 | frame.Package, frame.Name = packageAndName(frame.Func()) 36 | 37 | // pc -1 because the program counters we use are usually return addresses, 38 | // and we want to show the line that corresponds to the function call 39 | frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1) 40 | return 41 | 42 | } 43 | 44 | // Func returns the function that this stackframe corresponds to 45 | func (frame *StackFrame) Func() *runtime.Func { 46 | if frame.ProgramCounter == 0 { 47 | return nil 48 | } 49 | return runtime.FuncForPC(frame.ProgramCounter) 50 | } 51 | 52 | // String returns the stackframe formatted in the same way as go does 53 | // in runtime/debug.Stack() 54 | func (frame *StackFrame) String() string { 55 | str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter) 56 | 57 | source, err := frame.SourceLine() 58 | if err != nil { 59 | return str 60 | } 61 | 62 | return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source) 63 | } 64 | 65 | // SourceLine gets the line of code (from File and Line) of the original source if possible 66 | func (frame *StackFrame) SourceLine() (string, error) { 67 | data, err := os.ReadFile(frame.File) 68 | 69 | if err != nil { 70 | return "", err 71 | } 72 | 73 | lines := bytes.Split(data, []byte{'\n'}) 74 | if frame.LineNumber <= 0 || frame.LineNumber >= len(lines) { 75 | return "???", nil 76 | } 77 | // -1 because line-numbers are 1 based, but our array is 0 based 78 | return string(bytes.Trim(lines[frame.LineNumber-1], " \t")), nil 79 | } 80 | 81 | func packageAndName(fn *runtime.Func) (string, string) { 82 | name := fn.Name() 83 | pkg := "" 84 | 85 | // The name includes the path name to the package, which is unnecessary 86 | // since the file name is already included. Plus, it has center dots. 87 | // That is, we see 88 | // runtime/debug.*T·ptrmethod 89 | // and want 90 | // *T.ptrmethod 91 | // Since the package path might contains dots (e.g. code.google.com/...), 92 | // we first remove the path prefix if there is one. 93 | if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 { 94 | pkg += name[:lastslash] + "/" 95 | name = name[lastslash+1:] 96 | } 97 | if period := strings.Index(name, "."); period >= 0 { 98 | pkg += name[:period] 99 | name = name[period+1:] 100 | } 101 | 102 | name = strings.Replace(name, "·", ".", -1) 103 | return pkg, name 104 | } 105 | -------------------------------------------------------------------------------- /device.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vgpu 6 | 7 | import ( 8 | "errors" 9 | "unsafe" 10 | 11 | vk "github.com/goki/vulkan" 12 | ) 13 | 14 | // Device holds Device and associated Queue info 15 | type Device struct { 16 | 17 | // logical device 18 | Device vk.Device 19 | 20 | // queue index for device 21 | QueueIndex uint32 22 | 23 | // queue for device 24 | Queue vk.Queue 25 | } 26 | 27 | // Init initializes a device based on QueueFlagBits 28 | func (dv *Device) Init(gp *GPU, flags vk.QueueFlagBits) error { 29 | err := dv.FindQueue(gp, flags) 30 | if err != nil { 31 | return err 32 | } 33 | dv.MakeDevice(gp) 34 | return nil 35 | } 36 | 37 | // FindQueue finds queue for given flag bits, sets in QueueIndex 38 | // returns error if not found. 39 | func (dv *Device) FindQueue(gp *GPU, flags vk.QueueFlagBits) error { 40 | // Get queue family properties 41 | var queueCount uint32 42 | vk.GetPhysicalDeviceQueueFamilyProperties(gp.GPU, &queueCount, nil) 43 | queueProperties := make([]vk.QueueFamilyProperties, queueCount) 44 | vk.GetPhysicalDeviceQueueFamilyProperties(gp.GPU, &queueCount, queueProperties) 45 | if queueCount == 0 { // probably should try another GPU 46 | return errors.New("vulkan error: no queue families found on GPU 0") 47 | } 48 | 49 | // Find a suitable queue family for the target Vulkan mode 50 | found := false 51 | required := vk.QueueFlags(flags) 52 | for i := uint32(0); i < queueCount; i++ { 53 | queueProperties[i].Deref() 54 | if queueProperties[i].QueueFlags&required != 0 { 55 | dv.QueueIndex = i 56 | found = true 57 | break 58 | } 59 | } 60 | if !found { 61 | err := errors.New("GPU vulkan error: could not found queue with graphics capabilities") 62 | return err 63 | } 64 | return nil 65 | } 66 | 67 | // MakeDevice and Queue based on QueueIndex 68 | func (dv *Device) MakeDevice(gp *GPU) { 69 | queueInfos := []vk.DeviceQueueCreateInfo{{ 70 | SType: vk.StructureTypeDeviceQueueCreateInfo, 71 | QueueFamilyIndex: dv.QueueIndex, 72 | QueueCount: 1, 73 | PQueuePriorities: []float32{1.0}, 74 | }} 75 | 76 | feats := vk.PhysicalDeviceFeatures{ 77 | SamplerAnisotropy: vk.True, // used in Sampler.Config 78 | ShaderSampledImageArrayDynamicIndexing: vk.True, 79 | ShaderUniformBufferArrayDynamicIndexing: vk.True, 80 | ShaderStorageBufferArrayDynamicIndexing: vk.True, 81 | } 82 | gp.SetGPUOpts(&feats, gp.EnabledOpts) 83 | 84 | // log.Printf("features: %#v\n", feats) 85 | 86 | var device vk.Device 87 | ret := vk.CreateDevice(gp.GPU, &vk.DeviceCreateInfo{ 88 | SType: vk.StructureTypeDeviceCreateInfo, 89 | QueueCreateInfoCount: uint32(len(queueInfos)), 90 | PQueueCreateInfos: queueInfos, 91 | EnabledExtensionCount: uint32(len(gp.DeviceExts)), 92 | PpEnabledExtensionNames: gp.DeviceExts, 93 | EnabledLayerCount: uint32(len(gp.ValidationLayers)), 94 | PpEnabledLayerNames: gp.ValidationLayers, 95 | PEnabledFeatures: []vk.PhysicalDeviceFeatures{feats}, 96 | PNext: unsafe.Pointer(gp.DeviceFeaturesNeeded), 97 | }, nil, &device) 98 | IfPanic(NewError(ret)) 99 | 100 | /* note: not using this for PNext: 101 | unsafe.Pointer(&vk.PhysicalDeviceShaderAtomicFloatFeatures{ 102 | SType: vk.StructureTypePhysicalDeviceShaderAtomicFloatFeatures, 103 | ShaderBufferFloat32AtomicAdd: vk.True, 104 | }), 105 | */ 106 | 107 | // _ = ret 108 | dv.Device = device 109 | 110 | var queue vk.Queue 111 | vk.GetDeviceQueue(dv.Device, dv.QueueIndex, 0, &queue) 112 | dv.Queue = queue 113 | } 114 | 115 | func (dv *Device) Destroy() { 116 | if dv.Device == nil { 117 | return 118 | } 119 | vk.DeviceWaitIdle(dv.Device) 120 | vk.DestroyDevice(dv.Device, nil) 121 | dv.Device = nil 122 | } 123 | 124 | // DeviceWaitIdle waits until the device is idle and ready 125 | // for commands -- maybe useful to call if getting not ready 126 | // errors in particular situations 127 | func (dv *Device) DeviceWaitIdle() { 128 | vk.DeviceWaitIdle(dv.Device) 129 | } 130 | 131 | // NewGraphicsDevice returns a new Graphics Device, on given GPU. 132 | // This is suitable for no display offscreen rendering. 133 | // Typically use the Surface Device for rendering to a display window. 134 | func NewGraphicsDevice(gp *GPU) (*Device, error) { 135 | dev := &Device{} 136 | if err := dev.Init(gp, vk.QueueGraphicsBit); err != nil { 137 | return nil, err 138 | } 139 | return dev, nil 140 | } 141 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package vgpu implements a convenient interface to the Vulkan GPU-based 7 | graphics and compute framework, in Go, using the 8 | https://github.com/goki/vulkan Go bindings. 9 | 10 | The Cogent Core GUI framework runs on top if this, replacing the previous 11 | OpenGL-based framework, and the compute engine is used for the 12 | emergent neural network simulation framework. 13 | */ 14 | package vgpu 15 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | package vgpu 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "runtime/debug" 14 | 15 | vk "github.com/goki/vulkan" 16 | ) 17 | 18 | func IsError(ret vk.Result) bool { 19 | return ret != vk.Success 20 | } 21 | 22 | func NewError(ret vk.Result) error { 23 | if ret != vk.Success { 24 | err := fmt.Errorf("vulkan error: %s (%d)", vk.Error(ret).Error(), ret) 25 | if Debug { 26 | log.Println(err) 27 | debug.PrintStack() 28 | } 29 | return err 30 | } 31 | return nil 32 | } 33 | 34 | func IfPanic(err error, finalizers ...func()) { 35 | if err != nil { 36 | for _, fn := range finalizers { 37 | fn() 38 | } 39 | panic(err) 40 | } 41 | } 42 | 43 | func CheckErr(err *error) { 44 | if v := recover(); v != nil { 45 | *err = fmt.Errorf("%+v", v) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/compute1/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of HLSL files for compute 2 | 3 | all: sqvecel.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | -------------------------------------------------------------------------------- /examples/compute1/compute.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "math/rand" 10 | "runtime" 11 | 12 | "cogentcore.org/core/math32" 13 | "cogentcore.org/core/vgpu" 14 | ) 15 | 16 | func init() { 17 | // must lock main thread for gpu! 18 | runtime.LockOSThread() 19 | } 20 | 21 | func main() { 22 | if vgpu.InitNoDisplay() != nil { 23 | return 24 | } 25 | 26 | gp := vgpu.NewComputeGPU() 27 | vgpu.Debug = true 28 | gp.Config("compute1") 29 | fmt.Printf("Running on GPU: %s\n", gp.DeviceName) 30 | 31 | // gp.PropertiesString(true) // print 32 | 33 | sy := gp.NewComputeSystem("compute1") 34 | pl := sy.NewPipeline("compute1") 35 | pl.AddShaderFile("sqvecel", vgpu.ComputeShader, "sqvecel.spv") 36 | 37 | vars := sy.Vars() 38 | set := vars.AddSet() 39 | 40 | n := 20 // note: not necc to spec up-front, but easier if so 41 | 42 | threads := 64 43 | nInt := math32.IntMultiple(float32(n), float32(threads)) 44 | n = int(nInt) // enforce optimal n's -- otherwise requires range checking 45 | nGps := n / threads // dispatch n 46 | fmt.Printf("n: %d\n", n) 47 | 48 | inv := set.Add("In", vgpu.Float32Vector4, n, vgpu.Storage, vgpu.ComputeShader) 49 | outv := set.Add("Out", vgpu.Float32Vector4, n, vgpu.Storage, vgpu.ComputeShader) 50 | _ = outv 51 | 52 | set.ConfigValues(1) // one val per var 53 | sy.Config() // configures vars, allocates vals, configs pipelines.. 54 | 55 | ivl, _ := inv.Values.ValueByIndexTry(0) 56 | idat := ivl.Floats32() 57 | for i := 0; i < n; i++ { 58 | idat[i*4+0] = rand.Float32() 59 | idat[i*4+1] = rand.Float32() 60 | idat[i*4+2] = rand.Float32() 61 | idat[i*4+3] = rand.Float32() 62 | } 63 | ivl.SetMod() 64 | 65 | sy.Mem.SyncToGPU() 66 | 67 | vars.BindDynValuesAllIndex(0) 68 | 69 | cmd := sy.ComputeCmdBuff() 70 | sy.ComputeResetBindVars(cmd, 0) 71 | pl.ComputeDispatch(cmd, nGps, 1, 1) 72 | sy.ComputeCmdEnd(cmd) 73 | sy.ComputeSubmitWait(cmd) // if no wait, faster, but validation complains 74 | 75 | sy.Mem.SyncValueIndexFromGPU(0, "Out", 0) 76 | _, ovl, _ := vars.ValueByIndexTry(0, "Out", 0) 77 | 78 | odat := ovl.Floats32() 79 | for i := 0; i < n; i++ { 80 | fmt.Printf("In: %d\tr: %g\tg: %g\tb: %g\ta: %g\n", i, idat[i*4+0], idat[i*4+1], idat[i*4+2], idat[i*4+3]) 81 | fmt.Printf("Out: %d\tr: %g\tg: %g\tb: %g\ta: %g\n", i, odat[i*4+0], odat[i*4+1], odat[i*4+2], odat[i*4+3]) 82 | } 83 | fmt.Printf("\n") 84 | 85 | sy.Destroy() 86 | gp.Destroy() 87 | vgpu.Terminate() 88 | } 89 | -------------------------------------------------------------------------------- /examples/compute1/sqvecel.hlsl: -------------------------------------------------------------------------------- 1 | // HLSL compute example 2 | 3 | [[vk::binding(0, 0)]] RWStructuredBuffer In; 4 | [[vk::binding(1, 0)]] RWStructuredBuffer Out; 5 | 6 | [numthreads(64, 1, 1)] 7 | 8 | void main(uint3 idx : SV_DispatchThreadID) { 9 | Out[idx.x] = In[idx.x] * In[idx.x]; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /examples/compute1/sqvecel.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/compute1/sqvecel.spv -------------------------------------------------------------------------------- /examples/drawidx/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of HLSL files for compute 2 | 3 | all: indexed.spv vtxcolor.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | %.spv : %.vert 9 | glslc -fshader-stage=vertex -o $@ $< 10 | 11 | %.spv : %.frag 12 | glslc -fshader-stage=fragment -o $@ $< 13 | 14 | -------------------------------------------------------------------------------- /examples/drawidx/indexed.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/drawidx/indexed.spv -------------------------------------------------------------------------------- /examples/drawidx/indexed.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 Model; 5 | mat4 View; 6 | mat4 Proj; 7 | } Camera; 8 | 9 | layout(location = 0) in vector3 Pos; 10 | layout(location = 1) in vector3 Color; 11 | // layout(location = 2) in vector2 TexCoord; 12 | 13 | layout(location = 0) out vector3 FragColor; 14 | // layout(location = 1) out vector2 FragTexCoord; 15 | 16 | void main() { 17 | vector4 pos = vector4(Pos, 1.0); 18 | gl_Position = Camera.Proj * Camera.View * Camera.Model * pos; 19 | FragColor = Color; 20 | // FragTexCoord = TexCoord; 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/drawidx/vtxcolor.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vector3 FragColor; 4 | 5 | layout(location = 0) out vector4 OutColor; 6 | 7 | void main() { 8 | OutColor = vector4(FragColor, 1.0); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/drawidx/vtxcolor.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/drawidx/vtxcolor.spv -------------------------------------------------------------------------------- /examples/drawtri/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of HLSL files for compute 2 | 3 | all: trianglelit.spv vtxcolor.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | %.spv : %.vert 9 | glslc -fshader-stage=vertex -o $@ $< 10 | 11 | %.spv : %.frag 12 | glslc -fshader-stage=fragment -o $@ $< 13 | 14 | -------------------------------------------------------------------------------- /examples/drawtri/drawtri.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "runtime" 11 | "time" 12 | 13 | vk "github.com/goki/vulkan" 14 | 15 | "cogentcore.org/core/vgpu" 16 | "github.com/go-gl/glfw/v3.3/glfw" 17 | ) 18 | 19 | func init() { 20 | // must lock main thread for gpu! 21 | runtime.LockOSThread() 22 | } 23 | 24 | func main() { 25 | if vgpu.Init() != nil { 26 | return 27 | } 28 | 29 | glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI) 30 | window, err := glfw.CreateWindow(1024, 768, "Draw Triangle", nil, nil) 31 | vgpu.IfPanic(err) 32 | 33 | // note: for graphics, require these instance extensions before init gpu! 34 | winext := window.GetRequiredInstanceExtensions() 35 | gp := vgpu.NewGPU() 36 | gp.AddInstanceExt(winext...) 37 | vgpu.Debug = true 38 | gp.Config("drawtri") 39 | 40 | // gp.PropertiesString(true) // print 41 | 42 | surfPtr, err := window.CreateWindowSurface(gp.Instance, nil) 43 | if err != nil { 44 | log.Println(err) 45 | return 46 | } 47 | sf := vgpu.NewSurface(gp, vk.SurfaceFromPointer(surfPtr)) 48 | 49 | fmt.Printf("format: %s\n", sf.Format.String()) 50 | 51 | sy := gp.NewGraphicsSystem("drawtri", &sf.Device) 52 | pl := sy.NewPipeline("drawtri") 53 | sy.ConfigRender(&sf.Format, vgpu.UndefinedType) 54 | sf.SetRender(&sy.Render) 55 | 56 | pl.AddShaderFile("trianglelit", vgpu.VertexShader, "trianglelit.spv") 57 | pl.AddShaderFile("vtxcolor", vgpu.FragmentShader, "vtxcolor.spv") 58 | 59 | sy.Config() 60 | 61 | destroy := func() { 62 | vk.DeviceWaitIdle(sf.Device.Device) 63 | sy.Destroy() 64 | sf.Destroy() 65 | gp.Destroy() 66 | window.Destroy() 67 | vgpu.Terminate() 68 | } 69 | 70 | frameCount := 0 71 | stTime := time.Now() 72 | 73 | renderFrame := func() { 74 | // fmt.Printf("frame: %d\n", frameCount) 75 | // rt := time.Now() 76 | idx, ok := sf.AcquireNextImage() 77 | if !ok { 78 | return 79 | } 80 | // fmt.Printf("\nacq: %v\n", time.Now().Sub(rt)) 81 | descIndex := 0 // if running multiple frames in parallel, need diff sets 82 | cmd := sy.CmdPool.Buff 83 | sy.ResetBeginRenderPass(cmd, sf.Frames[idx], descIndex) 84 | // fmt.Printf("rp: %v\n", time.Now().Sub(rt)) 85 | pl.BindPipeline(cmd) 86 | pl.Draw(cmd, 3, 1, 0, 0) 87 | sy.EndRenderPass(cmd) 88 | sf.SubmitRender(cmd) // this is where it waits for the 16 msec 89 | // fmt.Printf("submit %v\n", time.Now().Sub(rt)) 90 | sf.PresentImage(idx) 91 | // fmt.Printf("present %v\n\n", time.Now().Sub(rt)) 92 | frameCount++ 93 | eTime := time.Now() 94 | dur := float64(eTime.Sub(stTime)) / float64(time.Second) 95 | if dur > 10 { 96 | fps := float64(frameCount) / dur 97 | fmt.Printf("fps: %.0f\n", fps) 98 | frameCount = 0 99 | stTime = eTime 100 | } 101 | } 102 | 103 | exitC := make(chan struct{}, 2) 104 | 105 | fpsDelay := time.Second / 600 106 | fpsTicker := time.NewTicker(fpsDelay) 107 | for { 108 | select { 109 | case <-exitC: 110 | fpsTicker.Stop() 111 | destroy() 112 | return 113 | case <-fpsTicker.C: 114 | if window.ShouldClose() { 115 | exitC <- struct{}{} 116 | continue 117 | } 118 | glfw.PollEvents() 119 | renderFrame() 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/drawtri/trianglelit.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/drawtri/trianglelit.spv -------------------------------------------------------------------------------- /examples/drawtri/trianglelit.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) out vector3 fragColor; 4 | 5 | // note: original VulkanTutorial uses CW instead of CCW winding order! 6 | vector2 positions[3] = vector2[]( 7 | vector2(-0.5, 0.5), 8 | vector2(0.5, 0.5), 9 | vector2(0.0, -0.5) 10 | ); 11 | 12 | vector3 colors[3] = vector3[]( 13 | vector3(1.0, 0.0, 0.0), 14 | vector3(0.0, 1.0, 0.0), 15 | vector3(0.0, 0.0, 1.0) 16 | ); 17 | 18 | void main() { 19 | gl_Position = vector4(positions[gl_VertexIndex], 0.0, 1.0); 20 | fragColor = colors[gl_VertexIndex]; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /examples/drawtri/vtxcolor.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vector3 fragColor; 4 | 5 | layout(location = 0) out vector4 outColor; 6 | 7 | void main() { 8 | outColor = vector4(fragColor, 1.0); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/drawtri/vtxcolor.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/drawtri/vtxcolor.spv -------------------------------------------------------------------------------- /examples/exptest/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dxc compiling of HLSL files for compute 2 | 3 | all: gpu_exptest.spv 4 | 5 | %.spv : %.hlsl 6 | dxc -spirv -O3 -Ges -T cs_6_2 -E main -Fo $@ $< 7 | 8 | -------------------------------------------------------------------------------- /examples/exptest/README.md: -------------------------------------------------------------------------------- 1 | This example tests for numerical differences between the GPU and CPU, running the same code on the same numbers on both sides and comparing the results. 2 | 3 | In axon, we have divergences from GABA and NMDA conductances, and these appear to be inevitable, given the outcome of these tests. 4 | 5 | Here's a summary of the results: 6 | 7 | ``` 8 | // Out[idx.x] = FastExp(In[idx.x]); // 0 diffs 9 | float vbio = In[idx.x]; 10 | float eval = 0.1 * ((vbio + 90.0) + 10.0); 11 | // Out[idx.x] = (vbio + 90.0) / (1.0 + FastExp(eval)); // lots of diffs 12 | // Out[idx.x] = (vbio + 90.0) / (1.0 + exp(eval)); // worse 13 | // Out[idx.x] = eval; // 0 diff 14 | // Out[idx.x] = 1.0 / eval; // a few 2.98e-8 diffs already! 15 | Out[idx.x] = 1.0 / FastExp(eval); // lots more diffs, e-08, -09 16 | ``` 17 | 18 | Interestingly, FastExp itself does not have any diffs but even simple 1.0 / division introduces issues. 19 | 20 | -------------------------------------------------------------------------------- /examples/exptest/exptest.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | "unsafe" 11 | 12 | "cogentcore.org/core/math32" 13 | "cogentcore.org/core/vgpu" 14 | ) 15 | 16 | func init() { 17 | // must lock main thread for gpu! 18 | runtime.LockOSThread() 19 | } 20 | 21 | func main() { 22 | if vgpu.InitNoDisplay() != nil { 23 | return 24 | } 25 | 26 | gp := vgpu.NewComputeGPU() 27 | vgpu.Debug = true 28 | gp.Config("exptest") 29 | fmt.Printf("Running on GPU: %s\n", gp.DeviceName) 30 | 31 | fmt.Printf("Max StructuredBuffer Size: %X\n", gp.GPUProperties.Limits.MaxStorageBufferRange) 32 | 33 | // gp.PropertiesString(true) // print 34 | 35 | sy := gp.NewComputeSystem("exptest") 36 | pl := sy.NewPipeline("exptest") 37 | pl.AddShaderFile("gpu_exptest", vgpu.ComputeShader, "gpu_exptest.spv") 38 | 39 | vars := sy.Vars() 40 | set := vars.AddSet() 41 | 42 | n := 64 43 | 44 | threads := 64 45 | nInt := math32.IntMultiple(float32(n), float32(threads)) 46 | n = int(nInt) // enforce optimal n's -- otherwise requires range checking 47 | nGps := n / threads // dispatch n 48 | fmt.Printf("n: %d\n", n) 49 | 50 | inv := set.Add("In", vgpu.Float32, n, vgpu.Storage, vgpu.ComputeShader) 51 | outv := set.Add("Out", vgpu.Float32, n, vgpu.Storage, vgpu.ComputeShader) 52 | _ = outv 53 | 54 | set.ConfigValues(1) // one val per var 55 | sy.Config() // configures vars, allocates vals, configs pipelines.. 56 | 57 | ivals := make([]float32, n) 58 | cpuValues := make([]float32, n) 59 | 60 | // st := float32(-89) 61 | // st := float32(3) 62 | st := float32(-70) 63 | inc := float32(1.0e-01) 64 | cur := st 65 | for i := 0; i < n; i++ { 66 | ivals[i] = cur 67 | // cpuValues[i] = math32.FastExp(ivals[i]) // 0 diffs 68 | vbio := ivals[i] 69 | eval := 0.1 * ((vbio + 90.0) + 10.0) 70 | // cpuValues[i] = (vbio + 90.0) / (1.0 + math32.FastExp(eval)) // lots of diffs 71 | // cpuValues[i] = eval // 0 diff 72 | cpuValues[i] = float32(1.0) / eval // no diff from casting 73 | // cpuValues[i] = 1.0 / math32.FastExp(eval) 74 | cur += inc 75 | } 76 | 77 | ivl, _ := inv.Values.ValueByIndexTry(0) 78 | ivl.CopyFromBytes(unsafe.Pointer(&(ivals[0]))) 79 | sy.Mem.SyncToGPU() 80 | 81 | vars.BindDynValuesAllIndex(0) 82 | 83 | cmd := sy.ComputeCmdBuff() 84 | 85 | sy.ComputeResetBindVars(cmd, 0) 86 | pl.ComputeDispatch(cmd, nGps, 1, 1) 87 | sy.ComputeCmdEnd(cmd) 88 | sy.ComputeSubmitWait(cmd) 89 | 90 | sy.Mem.SyncValueIndexFromGPU(0, "Out", 0) 91 | _, ovl, _ := vars.ValueByIndexTry(0, "Out", 0) 92 | 93 | odat := ovl.Floats32() 94 | for i := 0; i < n; i++ { 95 | diff := odat[i] - cpuValues[i] 96 | fmt.Printf("In: %d\tival: %g\tcpu: %g\tgpu: %g\tdiff: %g\n", i, ivals[i], cpuValues[i], odat[i], diff) 97 | } 98 | fmt.Printf("\n") 99 | 100 | sy.Destroy() 101 | gp.Destroy() 102 | vgpu.Terminate() 103 | } 104 | -------------------------------------------------------------------------------- /examples/exptest/gpu_exptest.hlsl: -------------------------------------------------------------------------------- 1 | // HLSL compute example 2 | 3 | [[vk::binding(0, 0)]] RWStructuredBuffer In; 4 | [[vk::binding(1, 0)]] RWStructuredBuffer Out; 5 | 6 | // FastExp is a quartic spline approximation to the Exp function, by N.N. Schraudolph 7 | // It does not have any of the sanity checking of a standard method -- returns 8 | // nonsense when arg is out of range. Runs in 2.23ns vs. 6.3ns for 64bit which is faster 9 | // than math32.Exp actually. 10 | float FastExp(float x) { 11 | if (x <= -88.02969) { // this doesn't add anything and -exp is main use-case anyway 12 | return 0; 13 | } 14 | int i = int(12102203*x) + 127*(1<<23); 15 | int m = i >> 7 & 0xFFFF; // copy mantissa 16 | i += (((((((((((3537 * m) >> 16) + 13668) * m) >> 18) + 15817) * m) >> 14) - 80470) * m) >> 11); 17 | return asfloat(uint(i)); 18 | } 19 | 20 | 21 | [numthreads(64, 1, 1)] 22 | void main(uint3 idx : SV_DispatchThreadID) { 23 | // Out[idx.x] = FastExp(In[idx.x]); // 0 diffs 24 | float vbio = In[idx.x]; 25 | float eval = 0.1 * ((vbio + 90.0) + 10.0); 26 | // Out[idx.x] = (vbio + 90.0) / (1.0 + FastExp(eval)); // lots of diffs 27 | // Out[idx.x] = (vbio + 90.0) / (1.0 + exp(eval)); // worse 28 | // Out[idx.x] = eval; // 0 diff 29 | Out[idx.x] = float(1.0) / eval; // a few 2.98e-8 diffs already! no diff from casting 30 | // Out[idx.x] = 1.0 / FastExp(eval); // lots more diffs, e-08, -09 31 | } 32 | 33 | -------------------------------------------------------------------------------- /examples/exptest/gpu_exptest.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/exptest/gpu_exptest.spv -------------------------------------------------------------------------------- /examples/images/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/ground.png -------------------------------------------------------------------------------- /examples/images/sound1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/sound1.png -------------------------------------------------------------------------------- /examples/images/teximg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/teximg.jpg -------------------------------------------------------------------------------- /examples/images/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/text.png -------------------------------------------------------------------------------- /examples/images/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/up.png -------------------------------------------------------------------------------- /examples/images/wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/wood.png -------------------------------------------------------------------------------- /examples/images/world1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/images/world1.png -------------------------------------------------------------------------------- /examples/memtest/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dxc compiling of HLSL files for compute 2 | 3 | all: gpu_memtest.spv 4 | 5 | %.spv : %.hlsl 6 | dxc -spirv -O3 -Ges -T cs_6_2 -E main -Fo $@ $< 7 | 8 | -------------------------------------------------------------------------------- /examples/memtest/gpu_memtest.hlsl: -------------------------------------------------------------------------------- 1 | // HLSL memory test example 2 | 3 | [[vk::binding(0, 0)]] RWStructuredBuffer Ba; 4 | [[vk::binding(1, 0)]] RWStructuredBuffer Bb; 5 | 6 | [numthreads(64, 1, 1)] 7 | void main(uint3 idx : SV_DispatchThreadID) { 8 | Ba[idx.x] = idx.x; 9 | Bb[idx.x] = idx.x; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /examples/memtest/gpu_memtest.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/memtest/gpu_memtest.spv -------------------------------------------------------------------------------- /examples/memtest/memtest.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | 11 | "cogentcore.org/core/math32" 12 | "cogentcore.org/core/vgpu" 13 | ) 14 | 15 | func init() { 16 | // must lock main thread for gpu! 17 | runtime.LockOSThread() 18 | } 19 | 20 | func main() { 21 | if vgpu.InitNoDisplay() != nil { 22 | return 23 | } 24 | 25 | gp := vgpu.NewComputeGPU() 26 | vgpu.Debug = true 27 | gp.Config("memtest") 28 | fmt.Printf("Running on GPU: %s\n", gp.DeviceName) 29 | 30 | // gp.PropertiesString(true) // print 31 | 32 | sy := gp.NewComputeSystem("memtest") 33 | sy.StaticVars = true // not working yet 34 | pl := sy.NewPipeline("memtest") 35 | pl.AddShaderFile("gpu_memtest", vgpu.ComputeShader, "gpu_memtest.spv") 36 | 37 | vars := sy.Vars() 38 | set := vars.AddSet() 39 | 40 | n := 64 41 | 42 | threads := 64 43 | nInt := math32.IntMultiple(float32(n), float32(threads)) 44 | n = int(nInt) // enforce optimal n's -- otherwise requires range checking 45 | nGps := n / threads // dispatch n 46 | 47 | maxBuff := (gp.GPUProperties.Limits.MaxStorageBufferRange - 16) / 4 48 | mem2g := ((1 << 31) - 1) / 4 49 | mem1g := ((1 << 30) - 1) / 4 50 | 51 | fmt.Printf("Sizes: Max StructuredBuffer: %X 2 GiB: %X 1 GiB: %X\n", maxBuff, mem2g, mem1g) 52 | 53 | ban := maxBuff // this causes the error: writes to a instead of b 54 | // ban := mem2g // works with this 55 | bbn := maxBuff 56 | 57 | bav := set.Add("Ba", vgpu.Uint32, int(ban), vgpu.Storage, vgpu.ComputeShader) 58 | bbv := set.Add("Bb", vgpu.Uint32, int(bbn), vgpu.Storage, vgpu.ComputeShader) 59 | 60 | _, _ = bav, bbv 61 | 62 | set.ConfigValues(1) // one val per var 63 | sy.Config() // configures vars, allocates vals, configs pipelines.. 64 | 65 | // bahost := make([]float32, bav) 66 | // bbhost := make([]float32, bbv) 67 | 68 | // vars.BindDynValuesAllIndex(0) 69 | 70 | cmd := sy.ComputeCmdBuff() 71 | sy.ComputeResetBindVars(cmd, 0) 72 | pl.ComputeDispatch(cmd, nGps, 1, 1) 73 | sy.ComputeCmdEnd(cmd) 74 | sy.ComputeSubmitWait(cmd) 75 | 76 | sy.Mem.SyncValueIndexFromGPU(0, "Ba", 0) 77 | _, bavl, _ := vars.ValueByIndexTry(0, "Ba", 0) 78 | sy.Mem.SyncValueIndexFromGPU(0, "Bb", 0) 79 | _, bbvl, _ := vars.ValueByIndexTry(0, "Bb", 0) 80 | 81 | bas := bavl.UInts32() 82 | bbs := bbvl.UInts32() 83 | for i := 0; i < n; i++ { 84 | fmt.Printf("%d\ta: %X\tb: %X\n", i, bas[i], bbs[i]) 85 | } 86 | fmt.Printf("\n") 87 | 88 | sy.Destroy() 89 | gp.Destroy() 90 | vgpu.Terminate() 91 | } 92 | -------------------------------------------------------------------------------- /examples/renderframe/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of HLSL files for compute 2 | 3 | all: indexed.spv vtxcolor.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | %.spv : %.vert 9 | glslc -fshader-stage=vertex -o $@ $< 10 | 11 | %.spv : %.frag 12 | glslc -fshader-stage=fragment -o $@ $< 13 | 14 | -------------------------------------------------------------------------------- /examples/renderframe/gtigen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "goki generate ./..."; DO NOT EDIT. 2 | 3 | package main 4 | 5 | import ( 6 | "goki.dev/gti" 7 | "goki.dev/ordmap" 8 | ) 9 | 10 | var _ = gti.AddType(>i.Type{ 11 | Name: "main.CamView", 12 | ShortName: "main.CamView", 13 | IDName: "cam-view", 14 | Doc: "", 15 | Directives: gti.Directives{}, 16 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 17 | {"Model", >i.Field{Name: "Model", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 18 | {"View", >i.Field{Name: "View", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 19 | {"Prjn", >i.Field{Name: "Prjn", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 20 | }), 21 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 22 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 23 | }) 24 | -------------------------------------------------------------------------------- /examples/renderframe/indexed.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/renderframe/indexed.spv -------------------------------------------------------------------------------- /examples/renderframe/indexed.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 Model; 5 | mat4 View; 6 | mat4 Proj; 7 | } Camera; 8 | 9 | layout(location = 0) in vector3 Pos; 10 | layout(location = 1) in vector3 Color; 11 | // layout(location = 2) in vector2 TexCoord; 12 | 13 | layout(location = 0) out vector3 FragColor; 14 | // layout(location = 1) out vector2 FragTexCoord; 15 | 16 | void main() { 17 | vector4 pos = vector4(Pos, 1.0); 18 | gl_Position = Camera.Proj * Camera.View * Camera.Model * pos; 19 | FragColor = Color; 20 | // FragTexCoord = TexCoord; 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/renderframe/vtxcolor.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vector3 FragColor; 4 | 5 | layout(location = 0) out vector4 OutColor; 6 | 7 | void main() { 8 | OutColor = vector4(FragColor, 1.0); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/renderframe/vtxcolor.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/renderframe/vtxcolor.spv -------------------------------------------------------------------------------- /examples/texture/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of gsl HLSL files for compute 2 | 3 | all: texture_vert.spv texture_frag.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | %.spv : %.vert 9 | glslc -fshader-stage=vertex -o $@ $< 10 | 11 | %.spv : %.frag 12 | glslc -fshader-stage=fragment -o $@ $< 13 | 14 | -------------------------------------------------------------------------------- /examples/texture/gtigen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "goki generate ./..."; DO NOT EDIT. 2 | 3 | package main 4 | 5 | import ( 6 | "goki.dev/gti" 7 | "goki.dev/ordmap" 8 | ) 9 | 10 | var _ = gti.AddType(>i.Type{ 11 | Name: "main.CamView", 12 | ShortName: "main.CamView", 13 | IDName: "cam-view", 14 | Doc: "", 15 | Directives: gti.Directives{}, 16 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 17 | {"Model", >i.Field{Name: "Model", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 18 | {"View", >i.Field{Name: "View", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 19 | {"Prjn", >i.Field{Name: "Prjn", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 20 | }), 21 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 22 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 23 | }) 24 | -------------------------------------------------------------------------------- /examples/texture/texture_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | layout(push_constant) uniform TexIndexUni { 5 | int TexIndex; 6 | }; 7 | 8 | layout(set = 1, binding = 0) uniform sampler2DArray TexSampler[]; 9 | 10 | layout(location = 0) in vector3 FragColor; 11 | layout(location = 1) in vector2 FragTexCoord; 12 | 13 | layout(location = 0) out vector4 OutColor; 14 | 15 | void main() { 16 | OutColor = texture(TexSampler[TexIndex], vector3(FragTexCoord, 0)); 17 | // OutColor = vector4(FragColor, 1.0); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /examples/texture/texture_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/texture/texture_frag.spv -------------------------------------------------------------------------------- /examples/texture/texture_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/examples/texture/texture_vert.spv -------------------------------------------------------------------------------- /examples/texture/texture_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(set = 0, binding = 0) uniform UniformBufferObject { 4 | mat4 Model; 5 | mat4 View; 6 | mat4 Proj; 7 | } Camera; 8 | 9 | layout(location = 0) in vector3 Pos; 10 | layout(location = 1) in vector3 Color; 11 | layout(location = 2) in vector2 TexCoord; 12 | 13 | layout(location = 0) out vector3 FragColor; 14 | layout(location = 1) out vector2 FragTexCoord; 15 | 16 | void main() { 17 | vector4 pos = vector4(Pos, 1.0); 18 | gl_Position = Camera.Proj * Camera.View * Camera.Model * pos; 19 | FragColor = Color; 20 | FragTexCoord = TexCoord; 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/vdraw/vdraw.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "image" 10 | "image/color" 11 | "image/draw" 12 | _ "image/jpeg" 13 | _ "image/png" 14 | "log" 15 | "math/rand" 16 | "os" 17 | "path/filepath" 18 | "runtime" 19 | "time" 20 | 21 | vk "github.com/goki/vulkan" 22 | 23 | "cogentcore.org/core/vgpu" 24 | "cogentcore.org/core/vgpu/vdraw" 25 | "github.com/go-gl/glfw/v3.3/glfw" 26 | ) 27 | 28 | func init() { 29 | // must lock main thread for gpu! 30 | runtime.LockOSThread() 31 | } 32 | 33 | func OpenImage(fname string) image.Image { 34 | file, err := os.Open(fname) 35 | defer file.Close() 36 | if err != nil { 37 | fmt.Printf("image: %s\n", err) 38 | } 39 | gimg, _, err := image.Decode(file) 40 | if err != nil { 41 | fmt.Println(err) 42 | } 43 | return gimg 44 | } 45 | 46 | func main() { 47 | if vgpu.Init() != nil { 48 | return 49 | } 50 | 51 | glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI) 52 | window, err := glfw.CreateWindow(1024, 768, "vDraw Test", nil, nil) 53 | vgpu.IfPanic(err) 54 | 55 | // note: for graphics, require these instance extensions before init gpu! 56 | winext := window.GetRequiredInstanceExtensions() 57 | gp := vgpu.NewGPU() 58 | gp.AddInstanceExt(winext...) 59 | vgpu.Debug = true 60 | gp.Config("vDraw test") 61 | 62 | // gp.PropertiesString(true) // print 63 | 64 | surfPtr, err := window.CreateWindowSurface(gp.Instance, nil) 65 | if err != nil { 66 | log.Println(err) 67 | return 68 | } 69 | sf := vgpu.NewSurface(gp, vk.SurfaceFromPointer(surfPtr)) 70 | 71 | fmt.Printf("format: %s\n", sf.Format.String()) 72 | 73 | drw := &vdraw.Drawer{} 74 | drw.YIsDown = true 75 | drw.ConfigSurface(sf, 16) // requires 2 NDesc 76 | 77 | drw.SetMaxTextures(32) // test resizing 78 | 79 | destroy := func() { 80 | vk.DeviceWaitIdle(sf.Device.Device) 81 | drw.Destroy() 82 | sf.Destroy() 83 | gp.Destroy() 84 | window.Destroy() 85 | vgpu.Terminate() 86 | } 87 | 88 | imgFiles := []string{"ground.png", "wood.png", "teximg.jpg"} 89 | imgs := make([]image.Image, len(imgFiles)) 90 | 91 | stoff := 15 // causes images to wrap around sets, so this tests that.. 92 | 93 | for i, fnm := range imgFiles { 94 | pnm := filepath.Join("../images", fnm) 95 | imgs[i] = OpenImage(pnm) 96 | drw.SetGoImage(i+stoff, 0, imgs[i], vgpu.NoFlipY) 97 | } 98 | 99 | // icons loaded into a texture array 100 | iconFiles := []string{"sound1.png", "text.png", "up.png", "world1.png"} 101 | iconImgs := make([]image.Image, len(iconFiles)) 102 | iconIndex := 0 103 | iconFmt := vgpu.NewImageFormat(20, 22, len(iconFiles)) 104 | drw.ConfigImage(iconIndex, iconFmt) 105 | for i, fnm := range iconFiles { 106 | pnm := filepath.Join("../images", fnm) 107 | iconImgs[i] = OpenImage(pnm) 108 | drw.SetGoImage(iconIndex, i, iconImgs[i], vgpu.NoFlipY) 109 | } 110 | 111 | drw.SyncImages() 112 | 113 | rendImgs := func(idx int) { 114 | fmt.Println(drw.DestSize()) 115 | descIndex := 0 116 | if idx+stoff >= vgpu.MaxTexturesPerSet { 117 | descIndex = 1 118 | } 119 | drw.StartDraw(descIndex) // specifically starting with correct descIndex is key.. 120 | drw.Scale(idx+stoff, 0, sf.Format.Bounds(), image.ZR, vdraw.Src, vgpu.NoFlipY, 0) 121 | for i := range imgFiles { 122 | // dp := image.Point{rand.Intn(500), rand.Intn(500)} 123 | dp := image.Point{i * 50, i * 50} 124 | drw.Copy(i+stoff, 0, dp, image.ZR, vdraw.Src, vgpu.NoFlipY) 125 | } 126 | for i := range iconFiles { 127 | dp := image.Point{rand.Intn(500), rand.Intn(500)} 128 | drw.Copy(iconIndex, i, dp, image.ZR, vdraw.Over, vgpu.NoFlipY) 129 | } 130 | drw.EndDraw() 131 | } 132 | 133 | _ = rendImgs 134 | 135 | red := color.RGBA{255, 0, 0, 255} 136 | green := color.RGBA{0, 255, 0, 255} 137 | blue := color.RGBA{0, 0, 255, 255} 138 | 139 | colors := []color.Color{color.White, color.Black, red, green, blue} 140 | 141 | fillRnd := func() { 142 | nclr := len(colors) 143 | drw.StartFill() 144 | for i := 0; i < 5; i++ { 145 | sp := image.Point{rand.Intn(500), rand.Intn(500)} 146 | sz := image.Point{rand.Intn(500), rand.Intn(500)} 147 | drw.FillRect(colors[i%nclr], image.Rectangle{Min: sp, Max: sp.Add(sz)}, draw.Src) 148 | } 149 | drw.EndFill() 150 | } 151 | 152 | frameCount := 0 153 | stTime := time.Now() 154 | 155 | renderFrame := func() { 156 | fcr := frameCount % 4 157 | _ = fcr 158 | switch { 159 | case fcr < 3: 160 | rendImgs(fcr) 161 | default: 162 | fillRnd() 163 | } 164 | frameCount++ 165 | 166 | eTime := time.Now() 167 | dur := float64(eTime.Sub(stTime)) / float64(time.Second) 168 | if dur > 100 { 169 | fps := float64(frameCount) / dur 170 | fmt.Printf("fps: %.0f\n", fps) 171 | frameCount = 0 172 | stTime = eTime 173 | } 174 | } 175 | 176 | glfw.PollEvents() 177 | renderFrame() 178 | glfw.PollEvents() 179 | 180 | exitC := make(chan struct{}, 2) 181 | 182 | fpsDelay := time.Second / 1 183 | fpsTicker := time.NewTicker(fpsDelay) 184 | for { 185 | select { 186 | case <-exitC: 187 | fpsTicker.Stop() 188 | destroy() 189 | return 190 | case <-fpsTicker.C: 191 | if window.ShouldClose() { 192 | exitC <- struct{}{} 193 | continue 194 | } 195 | glfw.PollEvents() 196 | renderFrame() 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /glfw.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !offscreen && ((darwin && !ios) || windows || (linux && !android) || dragonfly || openbsd) 6 | 7 | package vgpu 8 | 9 | import ( 10 | "cogentcore.org/core/base/errors" 11 | "github.com/go-gl/glfw/v3.3/glfw" 12 | vk "github.com/goki/vulkan" 13 | ) 14 | 15 | // note: this file contains the glfw dependencies, for desktop platform builds 16 | // other platforms (mobile, web) need to provide their own Init() and Terminate() 17 | // methods. 18 | 19 | // Init initializes vulkan system for Display-enabled use, using glfw. 20 | // Must call before doing any vgpu stuff. 21 | // Calls glfw.Init and sets the Vulkan instance proc addr and calls Init. 22 | // IMPORTANT: must be called on the main initial thread! 23 | func Init() error { 24 | err := glfw.Init() 25 | if err != nil { 26 | return errors.Log(err) 27 | } 28 | vk.SetGetInstanceProcAddr(glfw.GetVulkanGetInstanceProcAddress()) 29 | return errors.Log(vk.Init()) 30 | } 31 | 32 | // Terminate shuts down the vulkan system -- call as last thing before quitting. 33 | // IMPORTANT: must be called on the main initial thread! 34 | func Terminate() { 35 | glfw.Terminate() 36 | } 37 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module goki.dev/vgpu/v2 2 | 3 | go 1.22 4 | 5 | toolchain go1.22.5 6 | 7 | require ( 8 | cogentcore.org/core v0.3.3-0.20240831051617-88df75477149 9 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a 10 | github.com/goki/vulkan v1.0.8 11 | github.com/stretchr/testify v1.9.0 12 | goki.dev/gti v0.1.32 13 | goki.dev/ordmap v0.5.10 14 | golang.org/x/tools v0.23.0 15 | ) 16 | 17 | require ( 18 | github.com/Bios-Marcel/wastebasket v0.0.4-0.20240213135800-f26f1ae0a7c4 // indirect 19 | github.com/Masterminds/vcs v1.13.3 // indirect 20 | github.com/anthonynsimon/bild v0.13.0 // indirect 21 | github.com/aymerick/douceur v0.2.0 // indirect 22 | github.com/chewxy/math32 v1.10.1 // indirect 23 | github.com/cogentcore/webgpu v0.0.0-20240812054109-ca2e8adebe15 // indirect 24 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 25 | github.com/fatih/camelcase v1.0.0 // indirect 26 | github.com/fsnotify/fsnotify v1.7.0 // indirect 27 | github.com/goki/freetype v1.0.5 // indirect 28 | github.com/gorilla/css v1.0.1 // indirect 29 | github.com/h2non/filetype v1.1.3 // indirect 30 | github.com/hack-pad/go-indexeddb v0.3.2 // indirect 31 | github.com/hack-pad/hackpadfs v0.2.1 // indirect 32 | github.com/hack-pad/safejs v0.1.1 // indirect 33 | github.com/iancoleman/strcase v0.3.0 // indirect 34 | github.com/jinzhu/copier v0.4.0 // indirect 35 | github.com/kr/text v0.2.0 // indirect 36 | github.com/mitchellh/go-homedir v1.1.0 // indirect 37 | github.com/pelletier/go-toml/v2 v2.1.2-0.20240227203013-2b69615b5d55 // indirect 38 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 39 | github.com/rogpeppe/go-internal v1.12.0 // indirect 40 | goki.dev/enums v0.9.56 // indirect 41 | goki.dev/glop v0.1.9 // indirect 42 | goki.dev/laser v0.1.34 // indirect 43 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect 44 | golang.org/x/image v0.18.0 // indirect 45 | golang.org/x/mod v0.19.0 // indirect 46 | golang.org/x/net v0.27.0 // indirect 47 | golang.org/x/sync v0.7.0 // indirect 48 | golang.org/x/sys v0.22.0 // indirect 49 | golang.org/x/text v0.16.0 // indirect 50 | gopkg.in/yaml.v3 v3.0.1 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /gosl/alignsl/README.md: -------------------------------------------------------------------------------- 1 | # AlignSL 2 | 3 | alignsl performs 16-byte alignment and total size modulus checking of struct types to ensure HLSL (and GSL) compatibility. 4 | 5 | Checks that `struct` sizes are an even multiple of 16 bytes (e.g., 4 float32's), fields are 32 or 64 bit types: [U]Int32, Float32, [U]Int64, Float64, and that fields that are other struct types are aligned at even 16 byte multiples. 6 | 7 | It is called with a [golang.org/x/tools/go/packages](https://pkg.go.dev/golang.org/x/tools/go/packages) `Package` that provides the `types.Sizes` and `Types.Scope()` to get the types. 8 | 9 | The `CheckPackage` method checks all types in a `Package`, and returns an error if there are any violations -- this error string contains a full user-friendly warning message that can be printed. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /gosl/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // copied from go src/cmd/gofmt/doc.go: 6 | 7 | // Copyright 2009 The Go Authors. All rights reserved. 8 | // Use of this source code is governed by a BSD-style 9 | // license that can be found in the LICENSE file. 10 | 11 | /* 12 | gosl translates Go source code into HLSL compatible shader code. 13 | use //gosl:start and //gosl:end to 14 | bracket code that should be copied into shaders/.hlsl 15 | use //gosl main: instead of start for shader code that is 16 | commented out in the .go file, which will be copied into the filename 17 | and uncommented. 18 | 19 | pass filenames or directory names for files to process. 20 | 21 | Usage: 22 | 23 | gosl [flags] [path ...] 24 | 25 | The flags are: 26 | 27 | -out string 28 | output directory for shader code, relative to where gosl is invoked (default "shaders") 29 | */ 30 | package main 31 | -------------------------------------------------------------------------------- /gosl/examples/basic/README.md: -------------------------------------------------------------------------------- 1 | # basic 2 | 3 | This example just does some basic calculations on data structures and reports the time difference between the CPU and GPU. Getting about a 20x speedup on a Macbook Pro M3 Max. 4 | 5 | # Building 6 | 7 | There is a `//go:generate` comment directive in `main.go` that calls `gosl` on the relevant files, so you can do `go generate` followed by `go build` to run it. There is also a `Makefile` with the same `gosl` command, so `make` can be used instead of go generate. 8 | 9 | The generated files go into the `shaders/` subdirectory. 10 | 11 | 12 | -------------------------------------------------------------------------------- /gosl/examples/basic/compute.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | //gosl:hlsl basic 10 | // #include "fastexp.hlsl" 11 | //gosl:end basic 12 | 13 | //gosl:start basic 14 | 15 | // DataStruct has the test data 16 | type DataStruct struct { 17 | 18 | // raw value 19 | Raw float32 20 | 21 | // integrated value 22 | Integ float32 23 | 24 | // exp of integ 25 | Exp float32 26 | 27 | // must pad to multiple of 4 floats for arrays 28 | Pad2 float32 29 | } 30 | 31 | // ParamStruct has the test params 32 | type ParamStruct struct { 33 | 34 | // rate constant in msec 35 | Tau float32 36 | 37 | // 1/Tau 38 | Dt float32 39 | 40 | pad, pad1 float32 41 | } 42 | 43 | // IntegFromRaw computes integrated value from current raw value 44 | func (ps *ParamStruct) IntegFromRaw(ds *DataStruct) { 45 | ds.Integ += ps.Dt * (ds.Raw - ds.Integ) 46 | ds.Exp = math32.FastExp(-ds.Integ) 47 | } 48 | 49 | //gosl:end basic 50 | 51 | // note: only core compute code needs to be in shader -- all init is done CPU-side 52 | 53 | func (ps *ParamStruct) Defaults() { 54 | ps.Tau = 5 55 | ps.Update() 56 | } 57 | 58 | func (ps *ParamStruct) Update() { 59 | ps.Dt = 1.0 / ps.Tau 60 | } 61 | 62 | //gosl:hlsl basic 63 | /* 64 | // // note: double-commented lines required here -- binding is var, set 65 | [[vk::binding(0, 0)]] RWStructuredBuffer Params; 66 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 67 | 68 | [numthreads(64, 1, 1)] 69 | 70 | void main(uint3 idx : SV_DispatchThreadID) { 71 | Params[0].IntegFromRaw(Data[idx.x]); 72 | } 73 | */ 74 | //gosl:end basic 75 | -------------------------------------------------------------------------------- /gosl/examples/basic/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This example just does some basic calculations on data structures and 6 | // reports the time difference between the CPU and GPU. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "math/rand" 12 | "runtime" 13 | "unsafe" 14 | 15 | "cogentcore.org/core/base/timer" 16 | "cogentcore.org/core/math32" 17 | "cogentcore.org/core/vgpu" 18 | ) 19 | 20 | // note: standard one to use is plain "gosl" which should be go install'd 21 | 22 | //go:generate ../../gosl cogentcore.org/core/math32/fastexp.go compute.go 23 | 24 | func init() { 25 | // must lock main thread for gpu! 26 | runtime.LockOSThread() 27 | } 28 | 29 | func main() { 30 | if vgpu.InitNoDisplay() != nil { 31 | return 32 | } 33 | 34 | gp := vgpu.NewComputeGPU() 35 | // vgpu.Debug = true 36 | gp.Config("basic") 37 | 38 | // gp.PropsString(true) // print 39 | 40 | n := 100000000 // get 80x with 100m, 50x with 10m 41 | threads := 64 42 | nInt := int(math32.IntMultiple(float32(n), float32(threads))) 43 | n = nInt // enforce optimal n's -- otherwise requires range checking 44 | nGps := nInt / threads // dispatch n 45 | 46 | pars := &ParamStruct{} 47 | pars.Defaults() 48 | 49 | data := make([]DataStruct, n) 50 | for i := range data { 51 | d := &data[i] 52 | d.Raw = rand.Float32() 53 | d.Integ = 0 54 | } 55 | 56 | cpuTmr := timer.Time{} 57 | cpuTmr.Start() 58 | for i := range data { 59 | d := &data[i] 60 | pars.IntegFromRaw(d) 61 | } 62 | cpuTmr.Stop() 63 | 64 | sy := gp.NewComputeSystem("basic") 65 | pl := sy.NewPipeline("basic") 66 | pl.AddShaderFile("basic", vgpu.ComputeShader, "shaders/basic.spv") 67 | 68 | vars := sy.Vars() 69 | setp := vars.AddSet() 70 | setd := vars.AddSet() 71 | 72 | parsv := setp.AddStruct("Params", int(unsafe.Sizeof(ParamStruct{})), 1, vgpu.Storage, vgpu.ComputeShader) 73 | datav := setd.AddStruct("Data", int(unsafe.Sizeof(DataStruct{})), n, vgpu.Storage, vgpu.ComputeShader) 74 | 75 | setp.ConfigValues(1) // one val per var 76 | setd.ConfigValues(1) // one val per var 77 | sy.Config() // configures vars, allocates vals, configs pipelines.. 78 | 79 | gpuFullTmr := timer.Time{} 80 | gpuFullTmr.Start() 81 | 82 | // this copy is pretty fast -- most of time is below 83 | pvl, _ := parsv.Values.ValueByIndexTry(0) 84 | pvl.CopyFromBytes(unsafe.Pointer(pars)) 85 | dvl, _ := datav.Values.ValueByIndexTry(0) 86 | dvl.CopyFromBytes(unsafe.Pointer(&data[0])) 87 | 88 | // gpuFullTmr := timer.Time{} 89 | // gpuFullTmr.Start() 90 | 91 | sy.Mem.SyncToGPU() 92 | 93 | vars.BindDynamicValueIndex(0, "Params", 0) 94 | vars.BindDynamicValueIndex(1, "Data", 0) 95 | 96 | cmd := sy.ComputeCmdBuff() 97 | sy.CmdResetBindVars(cmd, 0) 98 | 99 | // gpuFullTmr := timer.Time{} 100 | // gpuFullTmr.Start() 101 | 102 | gpuTmr := timer.Time{} 103 | gpuTmr.Start() 104 | 105 | pl.ComputeDispatch(cmd, nGps, 1, 1) 106 | sy.ComputeCmdEnd(cmd) 107 | sy.ComputeSubmitWait(cmd) 108 | 109 | gpuTmr.Stop() 110 | 111 | sy.Mem.SyncValueIndexFromGPU(1, "Data", 0) // this is about same as SyncToGPU 112 | dvl.CopyToBytes(unsafe.Pointer(&data[0])) 113 | 114 | gpuFullTmr.Stop() 115 | 116 | mx := min(n, 5) 117 | for i := 0; i < mx; i++ { 118 | d := &data[i] 119 | fmt.Printf("%d\tRaw: %g\tInteg: %g\tExp: %g\n", i, d.Raw, d.Integ, d.Exp) 120 | } 121 | fmt.Printf("\n") 122 | 123 | cpu := cpuTmr.Total 124 | gpu := gpuTmr.Total 125 | fmt.Printf("N: %d\t CPU: %v\t GPU: %v\t Full: %v\t CPU/GPU: %6.4g\n", n, cpu, gpu, gpuFullTmr.Total, float64(cpu)/float64(gpu)) 126 | 127 | sy.Destroy() 128 | gp.Destroy() 129 | vgpu.Terminate() 130 | } 131 | -------------------------------------------------------------------------------- /gosl/examples/basic/shaders/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dxc compiling of HLSL files for compute 2 | 3 | all: basic.spv 4 | 5 | %.spv : %.hlsl 6 | dxc -spirv -O3 -T cs_6_0 -E main -Fo $@ $< 7 | 8 | -------------------------------------------------------------------------------- /gosl/examples/basic/shaders/basic_nouse.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // DataStruct has the test data 4 | struct DataStruct { 5 | float Raw; 6 | float Integ; 7 | float Pad1; 8 | float Pad2; 9 | }; 10 | 11 | // ParamStruct has the test params 12 | struct ParamStruct { 13 | float Tau; 14 | float Dt; 15 | 16 | // IntegFmRaw computes integrated value from current raw value 17 | // void IntegFmRaw(inout DataStruct ds) { 18 | // ds.Integ = ds.Integ + this.Dt * (ds.Raw - ds.Integ); 19 | // } 20 | }; 21 | 22 | layout (local_size_x = 256) in; 23 | 24 | layout(set = 0, binding = 0) uniform Params { 25 | ParamStruct params; 26 | }; 27 | 28 | layout(set = 1, binding = 0) buffer Data { 29 | DataStruct data[]; 30 | }; 31 | 32 | void main() { 33 | uint idx = gl_GlobalInvocationID.x; 34 | data[idx].Integ = data[idx].Integ + params.Dt * (data[idx].Raw - data[idx].Integ); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /gosl/examples/rand/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ../../gosl rand.go rand.hlsl 3 | 4 | -------------------------------------------------------------------------------- /gosl/examples/rand/README.md: -------------------------------------------------------------------------------- 1 | # rand 2 | 3 | This example tests the `slrand` random number generation functions. The `go test` in `slrand` itself tests the Go version of the code against known values, and this example tests the GPU HLSL versions against the Go versions. 4 | 5 | The output shows the first 5 sets of random numbers, with the CPU on the first line and the GPU on the second line. If there are any `*` on any of the lines, then there is a difference between the two, and an error message will be reported at the bottom. 6 | 7 | The total time to generate 10 million random numbers is shown at the end, comparing time on the CPU vs. GPU. On a Mac Book Pro M3 Max laptop, the CPU took roughly _140 times_ longer to generate the random numbers than the GPU. 8 | 9 | # Building 10 | 11 | There is a `//go:generate` comment directive in `main.go` that calls `gosl` on the relevant files, so you can do `go generate` followed by `go build` to run it. There is also a `Makefile` with the same `gosl` command, so `make` can be used instead of go generate. 12 | 13 | The generated files go into the `shaders/` subdirectory. 14 | 15 | Ignore the type alignment checking errors about Uint2 and Vector2 not being an even multiple of 16 bytes -- we have put in the necessary padding. 16 | 17 | -------------------------------------------------------------------------------- /gosl/examples/rand/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | "unsafe" 11 | 12 | "log/slog" 13 | 14 | "cogentcore.org/core/base/timer" 15 | "cogentcore.org/core/math32" 16 | "cogentcore.org/core/vgpu" 17 | "cogentcore.org/core/vgpu/gosl/sltype" 18 | ) 19 | 20 | // note: standard one to use is plain "gosl" which should be go install'd 21 | 22 | //go:generate ../../gosl rand.go rand.hlsl 23 | 24 | func init() { 25 | // must lock main thread for gpu! 26 | runtime.LockOSThread() 27 | } 28 | 29 | func main() { 30 | if vgpu.InitNoDisplay() != nil { 31 | return 32 | } 33 | 34 | gp := vgpu.NewComputeGPU() 35 | // vgpu.Debug = true 36 | gp.Config("slrand") 37 | 38 | // gp.PropsString(true) // print 39 | 40 | // n := 10 41 | n := 10000000 42 | threads := 64 43 | nInt := int(math32.IntMultiple(float32(n), float32(threads))) 44 | n = nInt // enforce optimal n's -- otherwise requires range checking 45 | nGps := nInt / threads // dispatch n 46 | 47 | dataC := make([]Rnds, n) 48 | dataG := make([]Rnds, n) 49 | 50 | cpuTmr := timer.Time{} 51 | cpuTmr.Start() 52 | 53 | seed := sltype.Uint2{0, 0} 54 | 55 | for i := range dataC { 56 | d := &dataC[i] 57 | d.RndGen(seed, uint32(i)) 58 | } 59 | cpuTmr.Stop() 60 | 61 | sy := gp.NewComputeSystem("slrand") 62 | pl := sy.NewPipeline("slrand") 63 | pl.AddShaderFile("slrand", vgpu.ComputeShader, "shaders/rand.spv") 64 | 65 | vars := sy.Vars() 66 | setc := vars.AddSet() 67 | setd := vars.AddSet() 68 | 69 | ctrv := setc.AddStruct("Counter", int(unsafe.Sizeof(seed)), 1, vgpu.Storage, vgpu.ComputeShader) 70 | datav := setd.AddStruct("Data", int(unsafe.Sizeof(Rnds{})), n, vgpu.Storage, vgpu.ComputeShader) 71 | 72 | setc.ConfigValues(1) // one val per var 73 | setd.ConfigValues(1) // one val per var 74 | sy.Config() // configures vars, allocates vals, configs pipelines.. 75 | 76 | gpuFullTmr := timer.Time{} 77 | gpuFullTmr.Start() 78 | 79 | // this copy is pretty fast -- most of time is below 80 | cvl, _ := ctrv.Values.ValueByIndexTry(0) 81 | cvl.CopyFromBytes(unsafe.Pointer(&seed)) 82 | dvl, _ := datav.Values.ValueByIndexTry(0) 83 | dvl.CopyFromBytes(unsafe.Pointer(&dataG[0])) 84 | 85 | // gpuFullTmr := timer.Time{} 86 | // gpuFullTmr.Start() 87 | 88 | sy.Mem.SyncToGPU() 89 | 90 | vars.BindDynamicValueIndex(0, "Counter", 0) 91 | vars.BindDynamicValueIndex(1, "Data", 0) 92 | 93 | cmd := sy.ComputeCmdBuff() 94 | sy.CmdResetBindVars(cmd, 0) 95 | 96 | // gpuFullTmr := timer.Time{} 97 | // gpuFullTmr.Start() 98 | 99 | gpuTmr := timer.Time{} 100 | gpuTmr.Start() 101 | 102 | pl.ComputeDispatch(cmd, nGps, 1, 1) 103 | sy.ComputeCmdEnd(cmd) 104 | sy.ComputeSubmitWait(cmd) 105 | 106 | gpuTmr.Stop() 107 | 108 | sy.Mem.SyncValueIndexFromGPU(1, "Data", 0) // this is about same as SyncToGPU 109 | dvl.CopyToBytes(unsafe.Pointer(&dataG[0])) 110 | 111 | gpuFullTmr.Stop() 112 | 113 | anyDiffEx := false 114 | anyDiffTol := false 115 | mx := min(n, 5) 116 | fmt.Printf("Index\tDif(Ex,Tol)\t CPU \t then GPU\n") 117 | for i := 0; i < n; i++ { 118 | dc := &dataC[i] 119 | dg := &dataG[i] 120 | smEx, smTol := dc.IsSame(dg) 121 | if !smEx { 122 | anyDiffEx = true 123 | } 124 | if !smTol { 125 | anyDiffTol = true 126 | } 127 | if i > mx { 128 | continue 129 | } 130 | exS := " " 131 | if !smEx { 132 | exS = "*" 133 | } 134 | tolS := " " 135 | if !smTol { 136 | tolS = "*" 137 | } 138 | fmt.Printf("%d\t%s %s\t%s\n\t\t%s\n", i, exS, tolS, dc.String(), dg.String()) 139 | } 140 | fmt.Printf("\n") 141 | 142 | if anyDiffEx { 143 | slog.Error("Differences between CPU and GPU detected at Exact level (excludes Gauss)") 144 | } 145 | if anyDiffTol { 146 | slog.Error("Differences between CPU and GPU detected at Tolerance level", "tolerance", Tol) 147 | } 148 | 149 | cpu := cpuTmr.Total 150 | gpu := gpuTmr.Total 151 | fmt.Printf("N: %d\t CPU: %v\t GPU: %v\t Full: %v\t CPU/GPU: %6.4g\n", n, cpu, gpu, gpuFullTmr.Total, float64(cpu)/float64(gpu)) 152 | 153 | sy.Destroy() 154 | gp.Destroy() 155 | vgpu.Terminate() 156 | } 157 | -------------------------------------------------------------------------------- /gosl/examples/rand/rand.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "cogentcore.org/core/math32" 7 | "cogentcore.org/core/vgpu/gosl/slrand" 8 | "cogentcore.org/core/vgpu/gosl/sltype" 9 | ) 10 | 11 | //gosl:hlsl rand 12 | // #include "slrand.hlsl" 13 | //gosl:end rand 14 | 15 | //gosl:start rand 16 | 17 | type Rnds struct { 18 | Uints sltype.Uint2 19 | pad, pad1 int32 20 | Floats sltype.Float2 21 | pad2, pad3 int32 22 | Floats11 sltype.Float2 23 | pad4, pad5 int32 24 | Gauss sltype.Float2 25 | pad6, pad7 int32 26 | } 27 | 28 | // RndGen calls random function calls to test generator. 29 | // Note that the counter to the outer-most computation function 30 | // is passed by *value*, so the same counter goes to each element 31 | // as it is computed, but within this scope, counter is passed by 32 | // reference (as a pointer) so subsequent calls get a new counter value. 33 | // The counter should be incremented by the number of random calls 34 | // outside of the overall update function. 35 | func (r *Rnds) RndGen(counter sltype.Uint2, idx uint32) { 36 | r.Uints = slrand.Uint2(&counter, idx) 37 | r.Floats = slrand.Float2(&counter, idx) 38 | r.Floats11 = slrand.Float112(&counter, idx) 39 | r.Gauss = slrand.NormFloat2(&counter, idx) 40 | } 41 | 42 | //gosl:end rand 43 | 44 | const Tol = 1.0e-4 // fails at lower tol eventually -- -6 works for many 45 | 46 | func FloatSame(f1, f2 float32) (exact, tol bool) { 47 | exact = f1 == f2 48 | tol = math32.Abs(f1-f2) < Tol 49 | return 50 | } 51 | 52 | func Float2Same(f1, f2 sltype.Float2) (exact, tol bool) { 53 | e1, t1 := FloatSame(f1.X, f2.X) 54 | e2, t2 := FloatSame(f1.Y, f2.Y) 55 | exact = e1 && e2 56 | tol = t1 && t2 57 | return 58 | } 59 | 60 | // IsSame compares values at two levels: exact and with Tol 61 | func (r *Rnds) IsSame(o *Rnds) (exact, tol bool) { 62 | e1 := r.Uints == o.Uints 63 | e2, t2 := Float2Same(r.Floats, o.Floats) 64 | e3, t3 := Float2Same(r.Floats11, o.Floats11) 65 | _, t4 := Float2Same(r.Gauss, o.Gauss) 66 | exact = e1 && e2 && e3 // skip e4 -- know it isn't 67 | tol = t2 && t3 && t4 68 | return 69 | } 70 | 71 | func (r *Rnds) String() string { 72 | return fmt.Sprintf("U: %x\t%x\tF: %g\t%g\tF11: %g\t%g\tG: %g\t%g", r.Uints.X, r.Uints.Y, r.Floats.X, r.Floats.Y, r.Floats11.X, r.Floats11.Y, r.Gauss.X, r.Gauss.Y) 73 | } 74 | -------------------------------------------------------------------------------- /gosl/examples/rand/rand.hlsl: -------------------------------------------------------------------------------- 1 | 2 | // binding is var, set 3 | [[vk::binding(0, 0)]] RWStructuredBuffer Counter; 4 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 5 | 6 | [numthreads(64, 1, 1)] 7 | 8 | void main(uint3 idx : SV_DispatchThreadID) { 9 | Data[idx.x].RndGen(Counter[0], idx.x); 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /gosl/examples/rand/shaders/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dxc compiling of HLSL files for compute 2 | 3 | all: rand.spv 4 | 5 | %.spv : %.hlsl 6 | dxc -spirv -O3 -T cs_6_0 -E main -Fo $@ $< 7 | 8 | -------------------------------------------------------------------------------- /gosl/files.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "io/fs" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | 16 | "golang.org/x/tools/go/packages" 17 | ) 18 | 19 | // LoadedPackageNames are single prefix names of packages that were 20 | // loaded in the list of files to process 21 | var LoadedPackageNames = map[string]bool{} 22 | 23 | func IsGoFile(f fs.DirEntry) bool { 24 | name := f.Name() 25 | return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && !f.IsDir() 26 | } 27 | 28 | func IsHLSLFile(f fs.DirEntry) bool { 29 | name := f.Name() 30 | return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".hlsl") && !f.IsDir() 31 | } 32 | 33 | func IsSPVFile(f fs.DirEntry) bool { 34 | name := f.Name() 35 | return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".spv") && !f.IsDir() 36 | } 37 | 38 | func AddFile(fn string, fls []string, procd map[string]bool) []string { 39 | if _, has := procd[fn]; has { 40 | return fls 41 | } 42 | fls = append(fls, fn) 43 | procd[fn] = true 44 | dir, _ := filepath.Split(fn) 45 | if dir != "" { 46 | dir = dir[:len(dir)-1] 47 | pd, sd := filepath.Split(dir) 48 | if pd != "" { 49 | dir = sd 50 | } 51 | if !(dir == "math32") { 52 | if _, has := LoadedPackageNames[dir]; !has { 53 | LoadedPackageNames[dir] = true 54 | // fmt.Printf("package: %s\n", dir) 55 | } 56 | } 57 | } 58 | return fls 59 | } 60 | 61 | // FilesFromPaths processes all paths and returns a full unique list of files 62 | // for subsequent processing. 63 | func FilesFromPaths(paths []string) []string { 64 | fls := make([]string, 0, len(paths)) 65 | procd := make(map[string]bool) 66 | for _, path := range paths { 67 | switch info, err := os.Stat(path); { 68 | case err != nil: 69 | var pkgs []*packages.Package 70 | dir, fl := filepath.Split(path) 71 | if dir != "" && fl != "" && strings.HasSuffix(fl, ".go") { 72 | pkgs, err = packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedFiles}, dir) 73 | } else { 74 | fl = "" 75 | pkgs, err = packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedFiles}, path) 76 | } 77 | if err != nil { 78 | fmt.Println(err) 79 | continue 80 | } 81 | pkg := pkgs[0] 82 | gofls := pkg.GoFiles 83 | if len(gofls) == 0 { 84 | fmt.Printf("WARNING: no go files found in path: %s\n", path) 85 | } 86 | if fl != "" { 87 | for _, gf := range gofls { 88 | if strings.HasSuffix(gf, fl) { 89 | fls = AddFile(gf, fls, procd) 90 | // fmt.Printf("added file: %s from package: %s\n", gf, path) 91 | break 92 | } 93 | } 94 | } else { 95 | for _, gf := range gofls { 96 | fls = AddFile(gf, fls, procd) 97 | // fmt.Printf("added file: %s from package: %s\n", gf, path) 98 | } 99 | } 100 | case !info.IsDir(): 101 | path := path 102 | fls = AddFile(path, fls, procd) 103 | default: 104 | // Directories are walked, ignoring non-Go, non-HLSL files. 105 | err := filepath.WalkDir(path, func(path string, f fs.DirEntry, err error) error { 106 | if err != nil || !(IsGoFile(f) || IsHLSLFile(f)) { 107 | return err 108 | } 109 | _, err = f.Info() 110 | if err != nil { 111 | return nil 112 | } 113 | fls = AddFile(path, fls, procd) 114 | return nil 115 | }) 116 | if err != nil { 117 | log.Println(err) 118 | } 119 | } 120 | } 121 | return fls 122 | } 123 | 124 | func CopyFile(src, dst string) error { 125 | in, err := os.Open(src) 126 | if err != nil { 127 | return err 128 | } 129 | defer in.Close() 130 | out, err := os.Create(dst) 131 | if err != nil { 132 | return err 133 | } 134 | defer out.Close() 135 | _, err = io.Copy(out, in) 136 | return err 137 | } 138 | 139 | func CopySlrand() error { 140 | hdr := "slrand.hlsl" 141 | tofn := filepath.Join(*outDir, hdr) 142 | 143 | pnm := "cogentcore.org/core/vgpu/gosl/slrand" 144 | 145 | pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedFiles}, pnm) 146 | if err != nil { 147 | fmt.Println(err) 148 | return err 149 | } 150 | if len(pkgs) != 1 { 151 | err = fmt.Errorf("%s package not found", pnm) 152 | fmt.Println(err) 153 | return err 154 | } 155 | pkg := pkgs[0] 156 | var fn string 157 | if len(pkg.GoFiles) > 0 { 158 | fn = pkg.GoFiles[0] 159 | } else if len(pkg.OtherFiles) > 0 { 160 | fn = pkg.GoFiles[0] 161 | } else { 162 | err = fmt.Errorf("No files found in package: %s", pnm) 163 | fmt.Println(err) 164 | return err 165 | } 166 | dir, _ := filepath.Split(fn) 167 | // dir = filepath.Join(dir, "slrand") 168 | fmfn := filepath.Join(dir, hdr) 169 | CopyFile(fmfn, tofn) 170 | return nil 171 | } 172 | 173 | // RemoveGenFiles removes .go, .hlsl, .spv files in shader generated dir 174 | func RemoveGenFiles(dir string) { 175 | err := filepath.WalkDir(dir, func(path string, f fs.DirEntry, err error) error { 176 | if err != nil { 177 | return err 178 | } 179 | if IsGoFile(f) || IsHLSLFile(f) || IsSPVFile(f) { 180 | os.Remove(path) 181 | } 182 | return nil 183 | }) 184 | if err != nil { 185 | log.Println(err) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /gosl/gosl.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // copied and heavily edited from go src/cmd/gofmt/gofmt.go: 6 | 7 | // Copyright 2009 The Go Authors. All rights reserved. 8 | // Use of this source code is governed by a BSD-style 9 | // license that can be found in the LICENSE file. 10 | 11 | package main 12 | 13 | import ( 14 | "flag" 15 | "fmt" 16 | "os" 17 | "strings" 18 | 19 | "cogentcore.org/core/vgpu/gosl/slprint" 20 | ) 21 | 22 | // flags 23 | var ( 24 | outDir = flag.String("out", "shaders", "output directory for shader code, relative to where gosl is invoked; must not be an empty string") 25 | excludeFunctions = flag.String("exclude", "Update,Defaults", "comma-separated list of names of functions to exclude from exporting to HLSL") 26 | keepTmp = flag.Bool("keep", false, "keep temporary converted versions of the source files, for debugging") 27 | debug = flag.Bool("debug", false, "enable debugging messages while running") 28 | excludeFunctionMap = map[string]bool{} 29 | ) 30 | 31 | // Keep these in sync with go/format/format.go. 32 | const ( 33 | tabWidth = 8 34 | printerMode = slprint.UseSpaces | slprint.TabIndent | printerNormalizeNumbers 35 | 36 | // printerNormalizeNumbers means to canonicalize number literal prefixes 37 | // and exponents while printing. See https://golang.org/doc/go1.13#gosl. 38 | // 39 | // This value is defined in go/printer specifically for go/format and cmd/gosl. 40 | printerNormalizeNumbers = 1 << 30 41 | ) 42 | 43 | func usage() { 44 | fmt.Fprintf(os.Stderr, "usage: gosl [flags] [path ...]\n") 45 | flag.PrintDefaults() 46 | } 47 | 48 | func main() { 49 | flag.Usage = usage 50 | flag.Parse() 51 | goslMain() 52 | } 53 | 54 | func GoslArgs() { 55 | exs := *excludeFunctions 56 | ex := strings.Split(exs, ",") 57 | for _, fn := range ex { 58 | excludeFunctionMap[fn] = true 59 | } 60 | } 61 | 62 | func goslMain() { 63 | if *outDir == "" { 64 | fmt.Println("Must have an output directory (default shaders), specified in -out arg") 65 | os.Exit(1) 66 | return 67 | } 68 | 69 | if gomod := os.Getenv("GO111MODULE"); gomod == "off" { 70 | fmt.Println("gosl only works in go modules mode, but GO111MODULE=off") 71 | os.Exit(1) 72 | return 73 | } 74 | 75 | os.MkdirAll(*outDir, 0755) 76 | RemoveGenFiles(*outDir) 77 | 78 | args := flag.Args() 79 | if len(args) == 0 { 80 | fmt.Printf("at least one file name must be passed\n") 81 | return 82 | } 83 | 84 | GoslArgs() 85 | ProcessFiles(args) 86 | } 87 | -------------------------------------------------------------------------------- /gosl/gosl_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "flag" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "testing" 14 | 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | var update = flag.Bool("update", false, "update .golden files") 19 | 20 | func runTest(t *testing.T, in, out string) { 21 | // process flags 22 | _, err := os.Lstat(in) 23 | if err != nil { 24 | t.Error(err) 25 | return 26 | } 27 | 28 | sls, err := ProcessFiles([]string{in}) 29 | if err != nil { 30 | t.Error(err) 31 | return 32 | } 33 | 34 | expected, err := os.ReadFile(out) 35 | if err != nil { 36 | t.Error(err) 37 | return 38 | } 39 | 40 | var got []byte 41 | for _, b := range sls { 42 | got = b 43 | break 44 | } 45 | 46 | if !bytes.Equal(got, expected) { 47 | if *update { 48 | if in != out { 49 | if err := os.WriteFile(out, got, 0666); err != nil { 50 | t.Error(err) 51 | } 52 | return 53 | } 54 | // in == out: don't accidentally destroy input 55 | t.Errorf("WARNING: -update did not rewrite input file %s", in) 56 | } 57 | 58 | assert.Equal(t, expected, got) 59 | if err := os.WriteFile(in+".gosl", got, 0666); err != nil { 60 | t.Error(err) 61 | } 62 | } 63 | } 64 | 65 | // TestRewrite processes testdata/*.input files and compares them to the 66 | // corresponding testdata/*.golden files. The gosl flags used to process 67 | // a file must be provided via a comment of the form 68 | // 69 | // //gosl flags 70 | // 71 | // in the processed file within the first 20 lines, if any. 72 | func TestRewrite(t *testing.T) { 73 | if gomod := os.Getenv("GO111MODULE"); gomod == "off" { 74 | t.Error("gosl only works in go modules mode, but GO111MODULE=off") 75 | return 76 | } 77 | 78 | // determine input files 79 | match, err := filepath.Glob("testdata/*.go") 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | 84 | if *outDir != "" { 85 | os.MkdirAll(*outDir, 0755) 86 | } 87 | 88 | for _, in := range match { 89 | name := filepath.Base(in) 90 | t.Run(name, func(t *testing.T) { 91 | out := in // for files where input and output are identical 92 | if strings.HasSuffix(in, ".go") { 93 | out = in[:len(in)-len(".go")] + ".golden" 94 | } 95 | runTest(t, in, out) 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /gosl/shaders/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dxc compiling of HLSL files for compute 2 | 3 | all: basic.spv 4 | 5 | %.spv : %.hlsl 6 | dxc -spirv -O3 -T cs_6_0 -E main -Fo $@ $< 7 | 8 | -------------------------------------------------------------------------------- /gosl/shaders/basic.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef __BASIC_HLSL__ 2 | #define __BASIC_HLSL__ 3 | 4 | 5 | 6 | // note: here is the hlsl version, only included in hlsl 7 | 8 | // MyTrickyFun this is the GPU version of the tricky function 9 | float MyTrickyFun(float x) { 10 | return 16; // ok actually not tricky here, but whatever 11 | } 12 | 13 | 14 | // FastExp is a quartic spline approximation to the Exp function, by N.N. Schraudolph 15 | // It does not have any of the sanity checking of a standard method -- returns 16 | // nonsense when arg is out of range. Runs in 2.23ns vs. 6.3ns for 64bit which is faster 17 | // than exp actually. 18 | float FastExp(float x) { 19 | if (x <= -88.76731) { // this doesn't add anything and -exp is main use-case anyway 20 | return 0; 21 | } 22 | int i = int(12102203*x) + 127*(1<<23); 23 | int m = i >> 7 & 0xFFFF; // copy mantissa 24 | i += (((((((((((3537 * m) >> 16) + 13668) * m) >> 18) + 15817) * m) >> 14) - 80470) * m) >> 11); 25 | return asfloat(uint(i)); 26 | } 27 | 28 | // NeuronFlags are bit-flags encoding relevant binary state for neurons 29 | typedef int NeuronFlags; 30 | 31 | // The neuron flags 32 | 33 | // NeuronOff flag indicates that this neuron has been turned off (i.e., lesioned) 34 | static const NeuronFlags NeuronOff = 1; 35 | 36 | // NeuronHasExt means the neuron has external input in its Ext field 37 | static const NeuronFlags NeuronHasExt = 1 << 2; 38 | 39 | // NeuronHasTarg means the neuron has external target input in its Target field 40 | static const NeuronFlags NeuronHasTarg = 1 << 3; 41 | 42 | // NeuronHasCmpr means the neuron has external comparison input in its Target field -- used for computing 43 | // comparison statistics but does not drive neural activity ever 44 | static const NeuronFlags NeuronHasCmpr = 1 << 4; 45 | 46 | // Modes are evaluation modes (Training, Testing, etc) 47 | typedef int Modes; 48 | 49 | // The evaluation modes 50 | 51 | static const Modes NoEvalMode = 0; 52 | 53 | // AllModes indicates that the log should occur over all modes present in other items. 54 | static const Modes AllModes = 1; 55 | 56 | // Train is this a training mode for the env 57 | static const Modes Train = 2; 58 | 59 | // Test is this a test mode for the env 60 | static const Modes Test = 3; 61 | 62 | // DataStruct has the test data 63 | struct DataStruct { 64 | 65 | // raw value 66 | float Raw; 67 | 68 | // integrated value 69 | float Integ; 70 | 71 | // exp of integ 72 | float Exp; 73 | 74 | // must pad to multiple of 4 floats for arrays 75 | float Pad2; 76 | }; 77 | 78 | // ParamStruct has the test params 79 | struct ParamStruct { 80 | 81 | // rate constant in msec 82 | float Tau; 83 | 84 | // 1/Tau 85 | float Dt; 86 | int Option; // note: standard bool doesn't work 87 | 88 | float pad; // comment this out to trigger alignment warning 89 | void IntegFromRaw(inout DataStruct ds, inout float modArg) { 90 | // note: the following are just to test basic control structures 91 | float newVal = this.Dt*(ds.Raw-ds.Integ) + modArg; 92 | if (newVal < -10 || this.Option==1) { 93 | newVal = -10; 94 | } 95 | ds.Integ += newVal; 96 | ds.Exp = exp(-ds.Integ); 97 | } 98 | 99 | void AnotherMeth(inout DataStruct ds) { 100 | for (int i = 0; i < 10; i++) { 101 | ds.Integ *= 0.99; 102 | } 103 | NeuronFlags flag; 104 | flag &=~NeuronHasExt; // clear flag -- op doesn't exist in C 105 | 106 | Modes mode = Test; 107 | switch (mode) { 108 | case 3: 109 | // fallthrough 110 | 111 | case 2:{ 112 | float ab = float(.5); 113 | ds.Exp *= ab; 114 | break; } 115 | default:{ 116 | float ab = float(1); 117 | ds.Exp *= ab; 118 | break; } 119 | } 120 | } 121 | 122 | }; 123 | 124 | 125 | [[vk::binding(0, 0)]] StructuredBuffer Params; 126 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 127 | [numthreads(1, 1, 1)] 128 | void main(uint3 idx : SV_DispatchThreadID) { 129 | Params[0].IntegFromRaw(Data[idx.x], Data[idx.x].Pad2); 130 | } 131 | #endif // __BASIC_HLSL__ 132 | -------------------------------------------------------------------------------- /gosl/shaders/basic.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/gosl/shaders/basic.spv -------------------------------------------------------------------------------- /gosl/slbool/README.md: -------------------------------------------------------------------------------- 1 | # slbool 2 | 3 | `slbool` defines a HLSL and Go friendly `int32` Bool type. The standard HLSL bool type causes obscure errors, and the int32 obeys the 4 byte basic alignment requirements. 4 | 5 | `gosl` automatically converts this Go code into appropriate HLSL code. 6 | 7 | 8 | -------------------------------------------------------------------------------- /gosl/slbool/slbool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | package slbool defines a HLSL friendly int32 Bool type. 7 | The standard HLSL bool type causes obscure errors, 8 | and the int32 obeys the 4 byte basic alignment requirements. 9 | 10 | gosl automatically converts this Go code into appropriate HLSL code. 11 | */ 12 | package slbool 13 | 14 | // Bool is an HLSL friendly int32 Bool type. 15 | type Bool int32 16 | 17 | const ( 18 | // False is the [Bool] false value 19 | False Bool = 0 20 | // True is the [Bool] true value 21 | True Bool = 1 22 | ) 23 | 24 | // Bool returns the Bool as a standard Go bool 25 | func (b Bool) Bool() bool { 26 | return b == True 27 | } 28 | 29 | // IsTrue returns whether the bool is true 30 | func (b Bool) IsTrue() bool { 31 | return b == True 32 | } 33 | 34 | // IsFalse returns whether the bool is false 35 | func (b Bool) IsFalse() bool { 36 | return b == False 37 | } 38 | 39 | // SetBool sets the Bool from a standard Go bool 40 | func (b *Bool) SetBool(bb bool) { 41 | *b = FromBool(bb) 42 | } 43 | 44 | // String returns the bool as a string ("true"/"false") 45 | func (b Bool) String() string { 46 | if b.IsTrue() { 47 | return "true" 48 | } 49 | return "false" 50 | } 51 | 52 | // FromString sets the bool from the given string 53 | func (b *Bool) FromString(s string) { 54 | if s == "true" || s == "True" { 55 | b.SetBool(true) 56 | } else { 57 | b.SetBool(false) 58 | } 59 | 60 | } 61 | 62 | // MarshalText implements the [encoding/text.Marshaler] interface 63 | func (b Bool) MarshalText() ([]byte, error) { return []byte(b.String()), nil } 64 | 65 | // UnmarshalText implements the [encoding/text.Unmarshaler] interface 66 | func (b *Bool) UnmarshalText(s []byte) error { b.FromString(string(s)); return nil } 67 | 68 | // IsTrue returns whether the given bool is true 69 | func IsTrue(b Bool) bool { 70 | return b == True 71 | } 72 | 73 | // IsFalse returns whether the given bool is false 74 | func IsFalse(b Bool) bool { 75 | return b == False 76 | } 77 | 78 | // FromBool returns the given Go bool as a [Bool] 79 | func FromBool(b bool) Bool { 80 | if b { 81 | return True 82 | } 83 | return False 84 | } 85 | -------------------------------------------------------------------------------- /gosl/slbool/slboolcore/slboolcore.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package slboolcore 6 | 7 | import ( 8 | "cogentcore.org/core/core" 9 | "cogentcore.org/core/vgpu/gosl/slbool" 10 | ) 11 | 12 | func init() { 13 | core.AddValueType[slbool.Bool, core.Switch]() 14 | } 15 | -------------------------------------------------------------------------------- /gosl/slrand/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of HLSL files for compute 2 | 3 | all: slrand.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | 9 | -------------------------------------------------------------------------------- /gosl/slrand/README.md: -------------------------------------------------------------------------------- 1 | # slrand 2 | 3 | This package contains HLSL header files and matching Go code for various random number generation (RNG) functions. The `gosl` tool will automatically copy the `slrand.hlsl` self-contained file into the destination `shaders` directory if the Go code contains `slrand.` prefix. Here's how you include: 4 | 5 | ```Go 6 | //gosl: hlsl mycode 7 | // #include "slrand.hlsl" 8 | //gosl: end mycode 9 | ``` 10 | 11 | `slrand` uses the [Philox2x32](https://github.com/DEShawResearch/random123) algorithm which is also available on CUDA on their [cuRNG](https://docs.nvidia.com/cuda/curand/host-api-overview.html) and in [Tensorflow](https://www.tensorflow.org/guide/random_numbers#general). A recent [evaluation](https://www.mdpi.com/2079-3197/9/12/142#B69-computation-09-00142) showed it to be the fastest GPU RNG, which also passes the standards for statistical quality (e.g., BigCrush). It is a counter based RNG, [CBRNG](https://en.wikipedia.org/wiki/Counter-based_random_number_generator_(CBRNG), where the random number is a direct function of the input state, with no other internal state. For a useful discussion of other alternatives, see [reddit cpp thread](https://www.reddit.com/r/cpp/comments/u3cnkk/old_rand_method_faster_than_new_alternatives/). The code is based on the D.E. Shaw [github](https://github.com/DEShawResearch/random123/blob/main/include/Random123/philox.h) implementation. 12 | 13 | The key advantage of this algorithm is its *stateless* nature, where the result is a deterministic but highly nonlinear function of its two inputs: 14 | ``` 15 | uint2 res = Philox2x32(inout uint2 counter, uint key); 16 | ``` 17 | where the HLSL `uint2` type is 2 `uint32` 32-bit unsigned integers. For GPU usage, the `key` is always set to the unique element being processed (e.g., the index of the data structure being updated), ensuring that different numbers are generated for each such element, and the `counter` should be configured as a shared global value that is incremented after every RNG call. For example, if 4 RNG calls happen within a given set of GPU code, each thread starts with the same starting `counter` value, which is passed around as a local `uint2` variable and incremented locally for each RNG. Then, after all threads have been performed, the shared starting `counter` is incremented using `CounterAdd` by 4. 18 | 19 | The `Float` and `Uint32` etc wrapper functions around Philox2x32 will automatically increment the counter var passed to it, using the `CounterIncr()` method that manages the two 32 bit numbers as if they are a full 64 bit uint. 20 | 21 | The `slrand.Counter` struct provides a 16-byte aligned type for storing and incrementing the global counter. The `Seed` method initializes the starting counter value by setting the Hi uint32 value to given seed, which thus provides a random sequence length of over 4 billion numbers within the Lo uint32 counter -- use more widely spaced seed values for longer unique sequences. 22 | 23 | `gosl` will automatically translate the Go versions of the `slrand` package functions into their HLSL equivalents. 24 | 25 | See the [axon](https://github.com/emer/gosl/v2/tree/main/examples/axon) and [rand](https://github.com/emer/gosl/v2/tree/main/examples/rand) examples for how to use in combined Go / GPU code. In the axon example, the `slrand.Counter` is added to the `Time` context struct, and incremented after each cycle based on the number of random numbers generated for a single pass through the code, as determined by the parameter settings. The index of each neuron being processed is used as the `key`, which is consistent in CPU and GPU versions. Within each cycle, a *local* arg variable is incremented on each GPU processor as the computation unfolds, passed by reference after the top-level, so it updates as each RNG call is made within each pass. 26 | 27 | Critically, these examples show that the CPU and GPU code produce identical random number sequences, which is otherwise quite difficult to achieve without this specific form of RNG. 28 | 29 | # Implementational details 30 | 31 | Unfortunately, vulkan `glslang` does not support 64 bit integers, even though the shader language model has somehow been updated to support them: https://github.com/KhronosGroup/glslang/issues/2965 -- https://github.com/microsoft/DirectXShaderCompiler/issues/2067. This would also greatly speed up the impl: https://github.com/microsoft/DirectXShaderCompiler/issues/2821. 32 | 33 | The result is that we have to use the slower version of the MulHiLo algorithm using only 32 bit uints. 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /gosl/slrand/slrand_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package slrand 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "testing" 11 | 12 | "cogentcore.org/core/vgpu/gosl/sltype" 13 | ) 14 | 15 | // Known Answer Test for values from the DEShawREsearch reference impl 16 | func TestKAT(t *testing.T) { 17 | kats := []struct { 18 | ctr sltype.Uint2 19 | key uint32 20 | res sltype.Uint2 21 | }{{sltype.Uint2{0, 0}, 0, sltype.Uint2{0xff1dae59, 0x6cd10df2}}, 22 | {sltype.Uint2{0xffffffff, 0xffffffff}, 0xffffffff, sltype.Uint2{0x2c3f628b, 0xab4fd7ad}}, 23 | {sltype.Uint2{0x243f6a88, 0x85a308d3}, 0x13198a2e, sltype.Uint2{0xdd7ce038, 0xf62a4c12}}} 24 | 25 | for _, tv := range kats { 26 | r := Philox2x32(tv.ctr, tv.key) 27 | if r != tv.res { 28 | fmt.Printf("ctr: %v key: %d != result: %v -- got: %v\n", tv.ctr, tv.key, tv.res, r) 29 | } 30 | } 31 | } 32 | 33 | // Float01 Known Answer Test for float conversion values from the DEShawREsearch reference impl 34 | func TestFloat01KAT(t *testing.T) { 35 | minint := math.MinInt32 36 | kats := []struct { 37 | base uint32 38 | add uint32 39 | res float32 40 | }{{0, 0, 1.16415321826934814453e-10}, 41 | {uint32(minint), 0, 0.5}, 42 | {math.MaxInt32, 0, 0.5}, 43 | {math.MaxUint32, 0, 0.99999994}, 44 | } 45 | 46 | for _, tv := range kats { 47 | r := Uint32ToFloat(tv.base + tv.add) 48 | if r != tv.res { 49 | fmt.Printf("base: %x add: %x != result: %g -- got: %g\n", tv.base, tv.add, tv.res, r) 50 | } 51 | } 52 | } 53 | 54 | // Float11 Known Answer Test for float conversion values from the DEShawREsearch reference impl 55 | func TestFloat11KAT(t *testing.T) { 56 | minint := math.MinInt32 57 | kats := []struct { 58 | base uint32 59 | add uint32 60 | res float32 61 | }{{0, 0, 2.32830643653869628906e-10}, 62 | {uint32(minint), 0, -1.0}, 63 | {math.MaxInt32, 0, 1.0}, 64 | {math.MaxUint32, 0, -2.32830643653869628906e-10}, 65 | } 66 | 67 | for _, tv := range kats { 68 | r := Uint32ToFloat11(tv.base + tv.add) 69 | if r != tv.res { 70 | fmt.Printf("base: %x add: %x != result: %g -- got: %g\n", tv.base, tv.add, tv.res, r) 71 | } 72 | } 73 | } 74 | 75 | func TestRand(t *testing.T) { 76 | var counter sltype.Uint2 77 | for i := 0; i < 10; i++ { 78 | fmt.Printf("%g\t%g\t%g\n", Float(&counter, 0), Float11(&counter, 1), NormFloat(&counter, 2)) 79 | } 80 | } 81 | 82 | func TestCounter(t *testing.T) { 83 | counter := sltype.Uint2{X: 0xfffffffe, Y: 0} 84 | ctr := counter 85 | CounterAdd(&ctr, 4) 86 | if ctr.X != 2 && ctr.Y != 1 { 87 | t.Errorf("Should be 2, 1: %v\n", ctr) 88 | } 89 | ctr = counter 90 | CounterAdd(&ctr, 1) 91 | if ctr.X != 0xffffffff && ctr.Y == 0 { 92 | t.Errorf("Should be 0, 0xfffffffe: %v\n", ctr) 93 | } 94 | ctr = counter 95 | CounterAdd(&ctr, 2) 96 | if ctr.X != 0 && ctr.Y == 1 { 97 | t.Errorf("Should be 0, 1: %v\n", ctr) 98 | } 99 | ctr = counter 100 | CounterIncr(&ctr) 101 | CounterIncr(&ctr) 102 | if ctr.X != 0 && ctr.Y != 1 { 103 | t.Errorf("Should be 0, 1: %v\n", ctr) 104 | } 105 | } 106 | 107 | func TestIntn(t *testing.T) { 108 | var counter sltype.Uint2 109 | n := uint32(20) 110 | for i := 0; i < 1000; i++ { 111 | r := Uintn(&counter, 0, n) 112 | if r >= n { 113 | t.Errorf("r >= n: %d\n", r) 114 | } 115 | // fmt.Printf("%d\t%d\n", i, r) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /gosl/sltype/README.md: -------------------------------------------------------------------------------- 1 | # sltype 2 | 3 | Package `sltype` provides type definitions for standard HLSL types, using aliases to the equivalent [math32](https://cogentcore.org/core/math32) types where possible, but including other type names not defined there. 4 | 5 | These types will be converted to their equivalent HLSL types automatically by gosl, as will the corresponding `math32` type names. 6 | 7 | -------------------------------------------------------------------------------- /gosl/sltype/float.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sltype 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // Float is identical to a float32 10 | type Float = float32 11 | 12 | // Float2 is a length 2 vector of float32 13 | type Float2 = math32.Vector2 14 | 15 | // Float3 is a length 3 vector of float32 16 | type Float3 = math32.Vector3 17 | 18 | // Float4 is a length 4 vector of float32 19 | type Float4 = math32.Vector4 20 | -------------------------------------------------------------------------------- /gosl/sltype/int.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sltype 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // Int is identical to an int32 10 | type Int = int32 11 | 12 | // Int2 is a length 2 vector of int32 13 | type Int2 = math32.Vector2i 14 | 15 | // Int3 is a length 3 vector of int32 16 | type Int3 = math32.Vector3i 17 | 18 | // Int4 is a length 4 vector of int32 19 | type Int4 struct { 20 | X int32 21 | Y int32 22 | Z int32 23 | W int32 24 | } 25 | 26 | //////////////////////////////////////// 27 | // Unsigned 28 | 29 | // Uint is identical to a uint32 30 | type Uint = uint32 31 | 32 | // Uint2 is a length 2 vector of uint32 33 | type Uint2 struct { 34 | X uint32 35 | Y uint32 36 | } 37 | 38 | // Uint3 is a length 3 vector of uint32 39 | type Uint3 struct { 40 | X uint32 41 | Y uint32 42 | Z uint32 43 | } 44 | 45 | // Uint4 is a length 4 vector of uint32 46 | type Uint4 struct { 47 | X uint32 48 | Y uint32 49 | Z uint32 50 | W uint32 51 | } 52 | 53 | func (u *Uint4) SetFrom2(u2 Uint2) { 54 | u.X = u2.X 55 | u.Y = u2.Y 56 | u.Z = 0 57 | u.W = 1 58 | } 59 | -------------------------------------------------------------------------------- /gosl/testdata/basic.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "math" 5 | 6 | "cogentcore.org/core/math32" 7 | "cogentcore.org/core/vgpu/gosl/slbool" 8 | ) 9 | 10 | //gosl:endhlsl basic 11 | 12 | // note: this code is included in the go pre-processing output but 13 | // then removed from the final hlsl output. 14 | // Use when you need different versions of the same function for CPU vs. GPU 15 | 16 | // MyTrickyFun this is the CPU version of the tricky function 17 | func MyTrickyFun(x float32) float32 { 18 | return 10 // ok actually not tricky here, but whatever 19 | } 20 | 21 | //gosl:end basic 22 | 23 | //gosl:hlsl basic 24 | 25 | // // note: here is the hlsl version, only included in hlsl 26 | 27 | // // MyTrickyFun this is the GPU version of the tricky function 28 | // float MyTrickyFun(float x) { 29 | // return 16; // ok actually not tricky here, but whatever 30 | // } 31 | 32 | //gosl:end basic 33 | 34 | //gosl:start basic 35 | 36 | // FastExp is a quartic spline approximation to the Exp function, by N.N. Schraudolph 37 | // It does not have any of the sanity checking of a standard method -- returns 38 | // nonsense when arg is out of range. Runs in 2.23ns vs. 6.3ns for 64bit which is faster 39 | // than math32.Exp actually. 40 | func FastExp(x float32) float32 { 41 | if x <= -88.76731 { // this doesn't add anything and -exp is main use-case anyway 42 | return 0 43 | } 44 | i := int32(12102203*x) + 127*(1<<23) 45 | m := i >> 7 & 0xFFFF // copy mantissa 46 | i += (((((((((((3537 * m) >> 16) + 13668) * m) >> 18) + 15817) * m) >> 14) - 80470) * m) >> 11) 47 | return math.Float32frombits(uint32(i)) 48 | } 49 | 50 | // NeuronFlags are bit-flags encoding relevant binary state for neurons 51 | type NeuronFlags int32 52 | 53 | // The neuron flags 54 | const ( 55 | // NeuronOff flag indicates that this neuron has been turned off (i.e., lesioned) 56 | NeuronOff NeuronFlags = 1 57 | 58 | // NeuronHasExt means the neuron has external input in its Ext field 59 | NeuronHasExt NeuronFlags = 1 << 2 60 | 61 | // NeuronHasTarg means the neuron has external target input in its Target field 62 | NeuronHasTarg NeuronFlags = 1 << 3 63 | 64 | // NeuronHasCmpr means the neuron has external comparison input in its Target field -- used for computing 65 | // comparison statistics but does not drive neural activity ever 66 | NeuronHasCmpr NeuronFlags = 1 << 4 67 | ) 68 | 69 | // Modes are evaluation modes (Training, Testing, etc) 70 | type Modes int32 71 | 72 | // The evaluation modes 73 | const ( 74 | NoEvalMode Modes = iota 75 | 76 | // AllModes indicates that the log should occur over all modes present in other items. 77 | AllModes 78 | 79 | // Train is this a training mode for the env 80 | Train 81 | 82 | // Test is this a test mode for the env 83 | Test 84 | ) 85 | 86 | // DataStruct has the test data 87 | type DataStruct struct { 88 | 89 | // raw value 90 | Raw float32 91 | 92 | // integrated value 93 | Integ float32 94 | 95 | // exp of integ 96 | Exp float32 97 | 98 | // must pad to multiple of 4 floats for arrays 99 | Pad2 float32 100 | } 101 | 102 | // ParamStruct has the test params 103 | type ParamStruct struct { 104 | 105 | // rate constant in msec 106 | Tau float32 107 | 108 | // 1/Tau 109 | Dt float32 110 | Option slbool.Bool // note: standard bool doesn't work 111 | 112 | pad float32 // comment this out to trigger alignment warning 113 | } 114 | 115 | func (ps *ParamStruct) IntegFromRaw(ds *DataStruct, modArg *float32) { 116 | // note: the following are just to test basic control structures 117 | newVal := ps.Dt*(ds.Raw-ds.Integ) + *modArg 118 | if newVal < -10 || ps.Option.IsTrue() { 119 | newVal = -10 120 | } 121 | ds.Integ += newVal 122 | ds.Exp = math32.Exp(-ds.Integ) 123 | } 124 | 125 | // AnotherMeth does more computation 126 | func (ps *ParamStruct) AnotherMeth(ds *DataStruct) { 127 | for i := 0; i < 10; i++ { 128 | ds.Integ *= 0.99 129 | } 130 | var flag NeuronFlags 131 | flag &^= NeuronHasExt // clear flag -- op doesn't exist in C 132 | 133 | mode := Test 134 | switch mode { 135 | case Test: 136 | fallthrough 137 | case Train: 138 | ab := float32(.5) 139 | ds.Exp *= ab 140 | default: 141 | ab := float32(1) 142 | ds.Exp *= ab 143 | } 144 | } 145 | 146 | //gosl:end basic 147 | 148 | // note: only core compute code needs to be in shader -- all init is done CPU-side 149 | 150 | func (ps *ParamStruct) Defaults() { 151 | ps.Tau = 5 152 | ps.Update() 153 | } 154 | 155 | func (ps *ParamStruct) Update() { 156 | ps.Dt = 1.0 / ps.Tau 157 | } 158 | 159 | //gosl:hlsl basic 160 | /* 161 | [[vk::binding(0, 0)]] StructuredBuffer Params; 162 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 163 | [numthreads(1, 1, 1)] 164 | void main(uint3 idx : SV_DispatchThreadID) { 165 | Params[0].IntegFromRaw(Data[idx.x], Data[idx.x].Pad2); 166 | } 167 | */ 168 | //gosl:end basic 169 | -------------------------------------------------------------------------------- /gosl/testdata/basic.go.gosl: -------------------------------------------------------------------------------- 1 | 2 | //gosl nohlsl: basic 3 | 4 | // note: this code is included in the go pre-processing output but 5 | // then removed from the final hlsl output. 6 | // Use when you need different versions of the same function for CPU vs. GPU 7 | 8 | // MyTrickyFun this is the CPU version of the tricky function 9 | float MyTrickyFun(float x) { 10 | return 10; // ok actually not tricky here, but whatever 11 | } 12 | 13 | //gosl end: basic 14 | //gosl hlsl: basic 15 | 16 | // // note: here is the hlsl version, only included in hlsl 17 | 18 | // // MyTrickyFun this is the GPU version of the tricky function 19 | // float MyTrickyFun(float x) { 20 | // return 16; // ok actually not tricky here, but whatever 21 | // } 22 | 23 | //gosl end: basic 24 | 25 | // FastExp is a quartic spline approximation to the Exp function, by N.N. Schraudolph 26 | // It does not have any of the sanity checking of a standard method -- returns 27 | // nonsense when arg is out of range. Runs in 2.23ns vs. 6.3ns for 64bit which is faster 28 | // than exp actually. 29 | float FastExp(float x) { 30 | if (x <= -88.76731) { // this doesn't add anything and -exp is main use-case anyway 31 | return 0; 32 | } 33 | int i = int(12102203*x) + 127*(1<<23); 34 | int m = i >> 7 & 0xFFFF; // copy mantissa 35 | i += (((((((((((3537 * m) >> 16) + 13668) * m) >> 18) + 15817) * m) >> 14) - 80470) * m) >> 11); 36 | return asfloat(uint(i)); 37 | } 38 | 39 | // NeuronFlags are bit-flags encoding relevant binary state for neurons 40 | typedef int NeuronFlags; 41 | 42 | // The neuron flags 43 | 44 | // NeuronOff flag indicates that this neuron has been turned off (i.e., lesioned) 45 | static const NeuronFlags NeuronOff = 1; 46 | 47 | // NeuronHasExt means the neuron has external input in its Ext field 48 | static const NeuronFlags NeuronHasExt = 1 << 2; 49 | 50 | // NeuronHasTarg means the neuron has external target input in its Target field 51 | static const NeuronFlags NeuronHasTarg = 1 << 3; 52 | 53 | // NeuronHasCmpr means the neuron has external comparison input in its Target field -- used for computing 54 | // comparison statistics but does not drive neural activity ever 55 | static const NeuronFlags NeuronHasCmpr = 1 << 4; 56 | 57 | // Modes are evaluation modes (Training, Testing, etc) 58 | typedef int Modes; 59 | 60 | // The evaluation modes 61 | 62 | static const Modes NoEvalMode = 0; 63 | 64 | // AllModes indicates that the log should occur over all modes present in other items. 65 | static const Modes AllModes = 1; 66 | 67 | // Train is this a training mode for the env 68 | static const Modes Train = 2; 69 | 70 | // Test is this a test mode for the env 71 | static const Modes Test = 3; 72 | 73 | // DataStruct has the test data 74 | struct DataStruct { 75 | 76 | // raw value 77 | float Raw; 78 | 79 | // integrated value 80 | float Integ; 81 | 82 | // exp of integ 83 | float Exp; 84 | 85 | // must pad to multiple of 4 floats for arrays 86 | float Pad2; 87 | }; 88 | 89 | // ParamStruct has the test params 90 | struct ParamStruct { 91 | 92 | // rate constant in msec 93 | float Tau; 94 | 95 | // 1/Tau 96 | float Dt; 97 | int Option; // note: standard bool doesn't work 98 | 99 | float pad; // comment this out to trigger alignment warning 100 | void IntegFromRaw(inout DataStruct ds, inout float modArg) { 101 | // note: the following are just to test basic control structures 102 | float newVal = this.Dt*(ds.Raw-ds.Integ) + modArg; 103 | if (newVal < -10 || this.Option==1) { 104 | newVal = -10; 105 | } 106 | ds.Integ += newVal; 107 | ds.Exp = exp(-ds.Integ); 108 | } 109 | 110 | void AnotherMeth(inout DataStruct ds) { 111 | for (int i = 0; i < 10; i++) { 112 | ds.Integ *= 0.99; 113 | } 114 | NeuronFlags flag; 115 | flag &=~NeuronHasExt; // clear flag -- op doesn't exist in C 116 | 117 | Modes mode = Test; 118 | switch (mode) { 119 | case 3: 120 | // fallthrough 121 | 122 | case 2:{ 123 | float ab = float(.5); 124 | ds.Exp *= ab; 125 | break; } 126 | default:{ 127 | float ab = float(1); 128 | ds.Exp *= ab; 129 | break; } 130 | } 131 | } 132 | 133 | }; 134 | 135 | 136 | //gosl hlsl: basic 137 | /* 138 | [[vk::binding(0, 0)]] StructuredBuffer Params; 139 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 140 | [numthreads(1, 1, 1)] 141 | void main(uint3 idx : SV_DispatchThreadID) { 142 | Params[0].IntegFromRaw(Data[idx.x], Data[idx.x].Pad2); 143 | } 144 | */ 145 | //gosl end: basic 146 | -------------------------------------------------------------------------------- /gosl/testdata/basic.golden: -------------------------------------------------------------------------------- 1 | 2 | 3 | // note: here is the hlsl version, only included in hlsl 4 | 5 | // MyTrickyFun this is the GPU version of the tricky function 6 | float MyTrickyFun(float x) { 7 | return 16; // ok actually not tricky here, but whatever 8 | } 9 | 10 | 11 | // FastExp is a quartic spline approximation to the Exp function, by N.N. Schraudolph 12 | // It does not have any of the sanity checking of a standard method -- returns 13 | // nonsense when arg is out of range. Runs in 2.23ns vs. 6.3ns for 64bit which is faster 14 | // than exp actually. 15 | float FastExp(float x) { 16 | if (x <= -88.76731) { // this doesn't add anything and -exp is main use-case anyway 17 | return 0; 18 | } 19 | int i = int(12102203*x) + 127*(1<<23); 20 | int m = i >> 7 & 0xFFFF; // copy mantissa 21 | i += (((((((((((3537 * m) >> 16) + 13668) * m) >> 18) + 15817) * m) >> 14) - 80470) * m) >> 11); 22 | return asfloat(uint(i)); 23 | } 24 | 25 | // NeuronFlags are bit-flags encoding relevant binary state for neurons 26 | typedef int NeuronFlags; 27 | 28 | // The neuron flags 29 | 30 | // NeuronOff flag indicates that this neuron has been turned off (i.e., lesioned) 31 | static const NeuronFlags NeuronOff = 1; 32 | 33 | // NeuronHasExt means the neuron has external input in its Ext field 34 | static const NeuronFlags NeuronHasExt = 1 << 2; 35 | 36 | // NeuronHasTarg means the neuron has external target input in its Target field 37 | static const NeuronFlags NeuronHasTarg = 1 << 3; 38 | 39 | // NeuronHasCmpr means the neuron has external comparison input in its Target field -- used for computing 40 | // comparison statistics but does not drive neural activity ever 41 | static const NeuronFlags NeuronHasCmpr = 1 << 4; 42 | 43 | // Modes are evaluation modes (Training, Testing, etc) 44 | typedef int Modes; 45 | 46 | // The evaluation modes 47 | 48 | static const Modes NoEvalMode = 0; 49 | 50 | // AllModes indicates that the log should occur over all modes present in other items. 51 | static const Modes AllModes = 1; 52 | 53 | // Train is this a training mode for the env 54 | static const Modes Train = 2; 55 | 56 | // Test is this a test mode for the env 57 | static const Modes Test = 3; 58 | 59 | // DataStruct has the test data 60 | struct DataStruct { 61 | 62 | // raw value 63 | float Raw; 64 | 65 | // integrated value 66 | float Integ; 67 | 68 | // exp of integ 69 | float Exp; 70 | 71 | // must pad to multiple of 4 floats for arrays 72 | float Pad2; 73 | }; 74 | 75 | // ParamStruct has the test params 76 | struct ParamStruct { 77 | 78 | // rate constant in msec 79 | float Tau; 80 | 81 | // 1/Tau 82 | float Dt; 83 | int Option; // note: standard bool doesn't work 84 | 85 | float pad; // comment this out to trigger alignment warning 86 | void IntegFromRaw(inout DataStruct ds, inout float modArg) { 87 | // note: the following are just to test basic control structures 88 | float newVal = this.Dt*(ds.Raw-ds.Integ) + modArg; 89 | if (newVal < -10 || this.Option==1) { 90 | newVal = -10; 91 | } 92 | ds.Integ += newVal; 93 | ds.Exp = exp(-ds.Integ); 94 | } 95 | 96 | void AnotherMeth(inout DataStruct ds) { 97 | for (int i = 0; i < 10; i++) { 98 | ds.Integ *= 0.99; 99 | } 100 | NeuronFlags flag; 101 | flag &=~NeuronHasExt; // clear flag -- op doesn't exist in C 102 | 103 | Modes mode = Test; 104 | switch (mode) { 105 | case 3: 106 | // fallthrough 107 | 108 | case 2:{ 109 | float ab = float(.5); 110 | ds.Exp *= ab; 111 | break; } 112 | default:{ 113 | float ab = float(1); 114 | ds.Exp *= ab; 115 | break; } 116 | } 117 | } 118 | 119 | }; 120 | 121 | 122 | [[vk::binding(0, 0)]] StructuredBuffer Params; 123 | [[vk::binding(0, 1)]] RWStructuredBuffer Data; 124 | [numthreads(1, 1, 1)] 125 | void main(uint3 idx : SV_DispatchThreadID) { 126 | Params[0].IntegFromRaw(Data[idx.x], Data[idx.x].Pad2); 127 | } 128 | -------------------------------------------------------------------------------- /gosl/threading/threading.go: -------------------------------------------------------------------------------- 1 | package threading 2 | 3 | import ( 4 | "math" 5 | "sync" 6 | ) 7 | 8 | // Maps the given function across the [0, total) range of items, using 9 | // nThreads goroutines. 10 | func ParallelRun(fun func(st, ed int), total int, nThreads int) { 11 | itemsPerThr := int(math.Ceil(float64(total) / float64(nThreads))) 12 | waitGroup := sync.WaitGroup{} 13 | for start := 0; start < total; start += itemsPerThr { 14 | start := start // be extra sure with closure 15 | end := min(start+itemsPerThr, total) 16 | waitGroup.Add(1) // todo: move out of loop 17 | go func() { 18 | fun(start, end) 19 | waitGroup.Done() 20 | }() 21 | } 22 | waitGroup.Wait() 23 | } 24 | -------------------------------------------------------------------------------- /gpu_android.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build android 6 | 7 | package vgpu 8 | 9 | import vk "github.com/goki/vulkan" 10 | 11 | // Set this variable to true prior to initializing the GPU 12 | // when using the Swiftshader software emulator (used 13 | // by the android studio emulator on macos) or other such 14 | // emulators that fail due to the need for the standard 15 | // DeviceFeaturesNeeded. 16 | var AndroidSoftwareEmulator = false 17 | 18 | func PlatformDefaults(gp *GPU) { 19 | if AndroidSoftwareEmulator { 20 | gp.DeviceFeaturesNeeded = &vk.PhysicalDeviceVulkan12Features{ 21 | SType: vk.StructureTypePhysicalDeviceVulkan12Features, 22 | } 23 | } else { 24 | gp.DeviceFeaturesNeeded = &vk.PhysicalDeviceVulkan12Features{ 25 | SType: vk.StructureTypePhysicalDeviceVulkan12Features, 26 | DescriptorBindingVariableDescriptorCount: vk.True, 27 | DescriptorBindingPartiallyBound: vk.True, 28 | RuntimeDescriptorArray: vk.True, 29 | DescriptorIndexing: vk.True, // might not be needed? not for phong or vdraw 30 | DescriptorBindingSampledImageUpdateAfterBind: vk.True, // might not be needed? not for phong or vdraw 31 | } 32 | } 33 | } 34 | 35 | func Terminate() { 36 | } 37 | -------------------------------------------------------------------------------- /gpu_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin 6 | 7 | package vgpu 8 | 9 | import ( 10 | "unsafe" 11 | 12 | vk "github.com/goki/vulkan" 13 | ) 14 | 15 | func PlatformDefaults(gp *GPU) { 16 | gp.DeviceExts = append(gp.DeviceExts, []string{"VK_KHR_portability_subset"}...) 17 | gp.DeviceExts = append(gp.DeviceExts) 18 | gp.InstanceExts = append(gp.InstanceExts, vk.KhrGetPhysicalDeviceProperties2ExtensionName) 19 | gp.InstanceExts = append(gp.InstanceExts, vk.KhrPortabilityEnumerationExtensionName) 20 | 21 | portFeatures := unsafe.Pointer(&vk.PhysicalDevicePortabilitySubsetFeatures{ 22 | SType: vk.StructureTypePhysicalDevicePortabilitySubsetFeatures, 23 | ConstantAlphaColorBlendFactors: vk.True, 24 | Events: vk.True, 25 | ImageViewFormatReinterpretation: vk.True, 26 | ImageViewFormatSwizzle: vk.True, 27 | ImageView2DOn3DImage: vk.False, 28 | MultisampleArrayImage: vk.True, 29 | MutableComparisonSamplers: vk.True, 30 | PointPolygons: vk.False, 31 | SamplerMipLodBias: vk.False, 32 | SeparateStencilMaskRef: vk.True, 33 | ShaderSampleRateInterpolationFunctions: vk.True, 34 | TessellationIsolines: vk.False, 35 | TessellationPointMode: vk.False, 36 | TriangleFans: vk.False, 37 | VertexAttributeAccessBeyondStride: vk.True, 38 | }) 39 | 40 | gp.DeviceFeaturesNeeded = &vk.PhysicalDeviceVulkan12Features{ 41 | SType: vk.StructureTypePhysicalDeviceVulkan12Features, 42 | DescriptorBindingVariableDescriptorCount: vk.True, 43 | DescriptorBindingPartiallyBound: vk.True, 44 | RuntimeDescriptorArray: vk.True, 45 | DescriptorIndexing: vk.True, // might not be needed? not for phong or vdraw 46 | DescriptorBindingSampledImageUpdateAfterBind: vk.True, // might not be needed? not for phong or vdraw 47 | PNext: portFeatures, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /gpu_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build (linux && !android) || dragonfly || openbsd 6 | 7 | package vgpu 8 | 9 | import vk "github.com/goki/vulkan" 10 | 11 | func PlatformDefaults(gp *GPU) { 12 | gp.DeviceFeaturesNeeded = &vk.PhysicalDeviceVulkan12Features{ 13 | SType: vk.StructureTypePhysicalDeviceVulkan12Features, 14 | DescriptorBindingVariableDescriptorCount: vk.True, 15 | DescriptorBindingPartiallyBound: vk.True, 16 | RuntimeDescriptorArray: vk.True, 17 | DescriptorIndexing: vk.True, // might not be needed? not for phong or vdraw 18 | DescriptorBindingSampledImageUpdateAfterBind: vk.True, // might not be needed? not for phong or vdraw 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gpu_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build windows 6 | 7 | package vgpu 8 | 9 | import vk "github.com/goki/vulkan" 10 | 11 | func PlatformDefaults(gp *GPU) { 12 | gp.DeviceFeaturesNeeded = &vk.PhysicalDeviceVulkan12Features{ 13 | SType: vk.StructureTypePhysicalDeviceVulkan12Features, 14 | DescriptorBindingVariableDescriptorCount: vk.True, 15 | DescriptorBindingPartiallyBound: vk.True, 16 | RuntimeDescriptorArray: vk.True, 17 | DescriptorIndexing: vk.True, // might not be needed? not for phong or vdraw 18 | DescriptorBindingSampledImageUpdateAfterBind: vk.True, // might not be needed? not for phong or vdraw 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /memory_32.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | //go:build 386 || arm 9 | // +build 386 arm 10 | 11 | package vgpu 12 | 13 | // ByteCopyMemoryLimit represents the total number of bytes 14 | // that can be copied from a Vulkan Memory Buffer to a byte slice. 15 | const ByteCopyMemoryLimit int = 0x7FFFFFF 16 | -------------------------------------------------------------------------------- /memory_64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | //go:build !386 && !arm 9 | // +build !386,!arm 10 | 11 | package vgpu 12 | 13 | // ByteCopyMemoryLimit represents the total number of bytes 14 | // that can be copied from a Vulkan Memory Buffer to a byte slice. 15 | const ByteCopyMemoryLimit int = 0x7fffffffffff 16 | -------------------------------------------------------------------------------- /roles.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vgpu 6 | 7 | import ( 8 | vk "github.com/goki/vulkan" 9 | ) 10 | 11 | // VarRoles are the functional roles of variables, corresponding 12 | // to Vertex input vectors and all the different "uniform" types 13 | // as enumerated in vk.DescriptorType. This does NOT map directly 14 | // to DescriptorType because we combine vertex and uniform data 15 | // and require a different ordering. 16 | type VarRoles int32 //enums:enum 17 | 18 | const ( 19 | UndefVarRole VarRoles = iota 20 | Vertex // vertex shader input data: mesh geometry points, normals, etc. These are automatically located in a separate Set, VertexSet (-2), and managed separately. 21 | Index // for indexed access to Vertex data, also located in VertexSet (-2) -- only one such Var per VarSet should be present -- will automatically be used if a dynamically bound val is set 22 | Push // for push constants, which have a minimum of 128 bytes and are stored directly in the command buffer -- they do not require any host-device synchronization or buffering, and are fully dynamic. They are ideal for transformation matricies or indexes for accessing data. They are stored in a special PushSet (-1) and managed separately. 23 | Uniform // read-only general purpose data, uses UniformBufferDynamic with offset specified at binding time, not during initial configuration -- compared to Storage, Uniform items can be put in local cache for each shader and thus can be much faster to access -- use for a smaller number of parameters such as transformation matricies 24 | Storage // read-write general purpose data, in StorageBufferDynamic (offset set at binding) -- this is a larger but slower pool of memory, with more flexible alignment constraints, used primarily for compute data 25 | UniformTexel // read-only image-formatted data, which cannot be accessed via ImageView or Sampler -- only for rare cases where optimized image format (e.g., rgb values of specific bit count) is useful. No Dynamic mode is available, so this can only be used for a fixed Value. 26 | StorageTexel // read-write image-formatted data, which cannot be accessed via ImageView or Sampler -- only for rare cases where optimized image format (e.g., rgb values of specific bit count) is useful. No Dynamic mode is available, so this can only be used for a fixed Value. 27 | StorageImage // read-write access through an ImageView (but not a Sampler) of an Image 28 | TextureRole // a Texture is a CombinedImageSampler in Vulkan terminology -- a combination of a Sampler and a specific Image, which appears as a single entity in the shader. 29 | ) 30 | 31 | // IsDynamic returns true if role has dynamic offset binding 32 | func (vr VarRoles) IsDynamic() bool { 33 | return vr == Uniform || vr == Storage 34 | } 35 | 36 | // BuffType returns type of memory buffer for this role 37 | func (vr VarRoles) BuffType() BuffTypes { 38 | return RoleBuffers[vr] 39 | } 40 | 41 | // VkDescriptor returns the vk.DescriptorType 42 | func (vr VarRoles) VkDescriptor() vk.DescriptorType { 43 | return RoleDescriptors[vr] 44 | } 45 | 46 | // VkDescriptorStatic returns the vk.DescriptorType for 47 | // static variable binding type 48 | func (vr VarRoles) VkDescriptorStatic() vk.DescriptorType { 49 | return StaticRoleDescriptors[vr] 50 | } 51 | 52 | var RoleDescriptors = map[VarRoles]vk.DescriptorType{ 53 | Uniform: vk.DescriptorTypeUniformBufferDynamic, 54 | Storage: vk.DescriptorTypeStorageBufferDynamic, 55 | UniformTexel: vk.DescriptorTypeUniformTexelBuffer, 56 | StorageTexel: vk.DescriptorTypeStorageTexelBuffer, 57 | StorageImage: vk.DescriptorTypeStorageImage, 58 | TextureRole: vk.DescriptorTypeCombinedImageSampler, 59 | } 60 | 61 | // For static variable binding 62 | var StaticRoleDescriptors = map[VarRoles]vk.DescriptorType{ 63 | Uniform: vk.DescriptorTypeUniformBuffer, 64 | Storage: vk.DescriptorTypeStorageBuffer, 65 | UniformTexel: vk.DescriptorTypeUniformTexelBuffer, 66 | StorageTexel: vk.DescriptorTypeStorageTexelBuffer, 67 | StorageImage: vk.DescriptorTypeStorageImage, 68 | TextureRole: vk.DescriptorTypeCombinedImageSampler, 69 | } 70 | 71 | // RoleBuffers maps VarRoles onto type of memory buffer 72 | var RoleBuffers = map[VarRoles]BuffTypes{ 73 | UndefVarRole: StorageBuff, 74 | Vertex: VtxIndexBuff, 75 | Index: VtxIndexBuff, 76 | Uniform: UniformBuff, 77 | Storage: StorageBuff, 78 | UniformTexel: UniformBuff, 79 | StorageTexel: StorageBuff, 80 | StorageImage: StorageBuff, 81 | TextureRole: TextureBuff, 82 | } 83 | -------------------------------------------------------------------------------- /shader.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | package vgpu 9 | 10 | import ( 11 | "log" 12 | "os" 13 | "unsafe" 14 | 15 | vk "github.com/goki/vulkan" 16 | ) 17 | 18 | // Shader manages a single Shader program 19 | type Shader struct { 20 | Name string 21 | Type ShaderTypes 22 | VkModule vk.ShaderModule 23 | } 24 | 25 | // Init initializes the shader 26 | func (sh *Shader) Init(name string, typ ShaderTypes) { 27 | sh.Name = name 28 | sh.Type = typ 29 | } 30 | 31 | // OpenFile loads given SPIR-V ".spv" code from file for the Shader. 32 | func (sh *Shader) OpenFile(dev vk.Device, fname string) error { 33 | b, err := os.ReadFile(fname) 34 | if err != nil { 35 | log.Printf("vgpu.Shader OpenFile: %s\n", err) 36 | return err 37 | } 38 | return sh.OpenCode(dev, b) 39 | } 40 | 41 | // OpenCode loads given SPIR-V ".spv" code for the Shader. 42 | func (sh *Shader) OpenCode(dev vk.Device, code []byte) error { 43 | uicode := SliceUint32(code) 44 | var module vk.ShaderModule 45 | ret := vk.CreateShaderModule(dev, &vk.ShaderModuleCreateInfo{ 46 | SType: vk.StructureTypeShaderModuleCreateInfo, 47 | CodeSize: uint64(len(code)), 48 | PCode: uicode, 49 | }, nil, &module) 50 | if IsError(ret) { 51 | return NewError(ret) 52 | } 53 | sh.VkModule = module 54 | return nil 55 | } 56 | 57 | // Free deletes the shader module, which can be done after the pipeline 58 | // is created. 59 | func (sh *Shader) Free(dev vk.Device) { 60 | if sh.VkModule == vk.NullShaderModule { 61 | return 62 | } 63 | vk.DestroyShaderModule(dev, sh.VkModule, nil) 64 | sh.VkModule = vk.NullShaderModule 65 | } 66 | 67 | // todo: use 1.17 unsafe.Slice function 68 | // https://stackoverflow.com/questions/11924196/convert-between-slices-of-different-types 69 | 70 | func SliceUint32(data []byte) []uint32 { 71 | return (*[ByteCopyMemoryLimit / 4]uint32)(unsafe.Pointer(unsafe.Pointer(&(data[0]))))[:len(data)/4] 72 | } 73 | 74 | // ShaderTypes is a list of GPU shader types 75 | type ShaderTypes int32 76 | 77 | const ( 78 | VertexShader ShaderTypes = iota 79 | TessCtrlShader 80 | TessEvalShader 81 | GeometryShader 82 | FragmentShader 83 | ComputeShader 84 | AllShaders 85 | ) 86 | 87 | var ShaderStageFlags = map[ShaderTypes]vk.ShaderStageFlagBits{ 88 | VertexShader: vk.ShaderStageVertexBit, 89 | TessCtrlShader: vk.ShaderStageTessellationControlBit, 90 | TessEvalShader: vk.ShaderStageTessellationEvaluationBit, 91 | GeometryShader: vk.ShaderStageGeometryBit, 92 | FragmentShader: vk.ShaderStageFragmentBit, 93 | ComputeShader: vk.ShaderStageComputeBit, 94 | AllShaders: vk.ShaderStageAll, 95 | } 96 | 97 | var ShaderPipelineFlags = map[ShaderTypes]vk.PipelineStageFlagBits{ 98 | VertexShader: vk.PipelineStageVertexShaderBit, 99 | TessCtrlShader: vk.PipelineStageTessellationControlShaderBit, 100 | TessEvalShader: vk.PipelineStageTessellationEvaluationShaderBit, 101 | GeometryShader: vk.PipelineStageGeometryShaderBit, 102 | FragmentShader: vk.PipelineStageFragmentShaderBit, 103 | ComputeShader: vk.PipelineStageComputeShaderBit, 104 | } 105 | -------------------------------------------------------------------------------- /strings.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | package vgpu 9 | 10 | import ( 11 | "strings" 12 | "unicode" 13 | ) 14 | 15 | func CheckExisting(actual, required []string) (existing []string, missing int) { 16 | existing = make([]string, 0, len(required)) 17 | for j := range required { 18 | req := SafeString(required[j]) 19 | for i := range actual { 20 | if SafeString(actual[i]) == req { 21 | existing = append(existing, req) 22 | } 23 | } 24 | } 25 | missing = len(required) - len(existing) 26 | return existing, missing 27 | } 28 | 29 | var end = "\x00" 30 | var endChar byte = '\x00' 31 | 32 | func SafeString(s string) string { 33 | if len(s) == 0 { 34 | return end 35 | } 36 | if s[len(s)-1] != endChar { 37 | return s + end 38 | } 39 | return s 40 | } 41 | 42 | func SafeStrings(list []string) []string { 43 | for i := range list { 44 | list[i] = SafeString(list[i]) 45 | } 46 | return list 47 | } 48 | 49 | func CleanString(s string) string { 50 | s = SafeString(s) 51 | ss := "" 52 | lastSpace := false 53 | for _, r := range s { 54 | if unicode.IsLetter(r) || unicode.IsNumber(r) { 55 | ss += string(r) 56 | lastSpace = false 57 | } else { 58 | if !lastSpace { 59 | ss += " " 60 | lastSpace = true 61 | } 62 | } 63 | } 64 | if lastSpace { 65 | return ss[:len(ss)-1] 66 | } 67 | return ss 68 | } 69 | 70 | func HasAllStrings(s string, strs []string) bool { 71 | for _, ss := range strs { 72 | if !strings.Contains(s, ss) { 73 | return false 74 | } 75 | } 76 | return true 77 | } 78 | -------------------------------------------------------------------------------- /szalloc/gtigen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "goki generate ./..."; DO NOT EDIT. 2 | 3 | package szalloc 4 | 5 | import ( 6 | "goki.dev/gti" 7 | "goki.dev/ordmap" 8 | ) 9 | 10 | var _ = gti.AddType(>i.Type{ 11 | Name: "goki.dev/vgpu/v2/szalloc.Idxs", 12 | ShortName: "szalloc.Idxs", 13 | IDName: "idxs", 14 | Doc: "Idxs contains the indexes where a given item image size is allocated\nthere is one of these per each ItemSizes", 15 | Directives: gti.Directives{}, 16 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 17 | {"PctSize", >i.Field{Name: "PctSize", Type: "mat32.Vec2", Doc: "percent size of this image relative to max size allocated", Directives: gti.Directives{}}}, 18 | {"GpIdx", >i.Field{Name: "GpIdx", Type: "int", Doc: "group index", Directives: gti.Directives{}}}, 19 | {"ItemIdx", >i.Field{Name: "ItemIdx", Type: "int", Doc: "item index within group (e.g., Layer)", Directives: gti.Directives{}}}, 20 | }), 21 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 22 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 23 | }) 24 | 25 | var _ = gti.AddType(>i.Type{ 26 | Name: "goki.dev/vgpu/v2/szalloc.SzAlloc", 27 | ShortName: "szalloc.SzAlloc", 28 | IDName: "sz-alloc", 29 | Doc: "SzAlloc manages allocation of sizes to a spec'd maximum number\nof groups. Used for allocating texture images to image arrays\nunder the severe constraints of only 16 images.\nOnly a maximum of MaxItemsPerGp items can be allocated per grouping.", 30 | Directives: gti.Directives{}, 31 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 32 | {"On", >i.Field{Name: "On", Type: "bool", Doc: "true if configured and ready to use", Directives: gti.Directives{}}}, 33 | {"MaxGps", >i.Field{Name: "MaxGps", Type: "image.Point", Doc: "maximum number of groups in X and Y dimensions", Directives: gti.Directives{}}}, 34 | {"MaxNGps", >i.Field{Name: "MaxNGps", Type: "int", Doc: "maximum number of groups = X * Y", Directives: gti.Directives{}}}, 35 | {"MaxItemsPerGp", >i.Field{Name: "MaxItemsPerGp", Type: "int", Doc: "maximum number of items per group -- constraint is enforced in addition to MaxGps", Directives: gti.Directives{}}}, 36 | {"ItemSizes", >i.Field{Name: "ItemSizes", Type: "[]image.Point", Doc: "original list of item sizes to be allocated", Directives: gti.Directives{}}}, 37 | {"UniqSizes", >i.Field{Name: "UniqSizes", Type: "[]image.Point", Doc: "list of all unique sizes -- operate on this for grouping", Directives: gti.Directives{}}}, 38 | {"UniqSzMap", >i.Field{Name: "UniqSzMap", Type: "map[image.Point]int", Doc: "map of all unique sizes, with group index as value", Directives: gti.Directives{}}}, 39 | {"UniqSzItems", >i.Field{Name: "UniqSzItems", Type: "[]int", Doc: "indexes into UniqSizes slice, ordered by ItemSizes indexes", Directives: gti.Directives{}}}, 40 | {"GpSizes", >i.Field{Name: "GpSizes", Type: "[]image.Point", Doc: "list of allocated group sizes", Directives: gti.Directives{}}}, 41 | {"GpAllocs", >i.Field{Name: "GpAllocs", Type: "[][]int", Doc: "allocation of image indexes by group -- first index is group, second is list of items for that group", Directives: gti.Directives{}}}, 42 | {"ItemIdxs", >i.Field{Name: "ItemIdxs", Type: "[]*Idxs", Doc: "allocation image value indexes to image indexes", Directives: gti.Directives{}}}, 43 | {"XSizes", >i.Field{Name: "XSizes", Type: "[]int", Doc: "sorted list of all unique sizes", Directives: gti.Directives{}}}, 44 | {"YSizes", >i.Field{Name: "YSizes", Type: "[]int", Doc: "sorted list of all unique sizes", Directives: gti.Directives{}}}, 45 | {"GpNs", >i.Field{Name: "GpNs", Type: "image.Point", Doc: "number of items in each dimension group (X, Y)", Directives: gti.Directives{}}}, 46 | {"XGpIdxs", >i.Field{Name: "XGpIdxs", Type: "[]int", Doc: "list of x group indexes", Directives: gti.Directives{}}}, 47 | {"YGpIdxs", >i.Field{Name: "YGpIdxs", Type: "[]int", Doc: "list of y group indexes", Directives: gti.Directives{}}}, 48 | }), 49 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 50 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 51 | }) 52 | -------------------------------------------------------------------------------- /szalloc/idxs.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package szalloc 6 | 7 | import ( 8 | "image" 9 | 10 | "cogentcore.org/core/math32" 11 | ) 12 | 13 | // Indexes contains the indexes where a given item image size is allocated 14 | // there is one of these per each ItemSizes 15 | type Indexes struct { 16 | 17 | // percent size of this image relative to max size allocated 18 | PctSize math32.Vector2 19 | 20 | // group index 21 | GpIndex int 22 | 23 | // item index within group (e.g., Layer) 24 | ItemIndex int 25 | } 26 | 27 | func NewIndexes(gpi, itmi int, sz, mxsz image.Point) *Indexes { 28 | ii := &Indexes{} 29 | ii.Set(gpi, itmi, sz, mxsz) 30 | return ii 31 | } 32 | 33 | func (ii *Indexes) Set(gpi, itmi int, sz, mxsz image.Point) { 34 | ii.GpIndex = gpi 35 | ii.ItemIndex = itmi 36 | ii.PctSize.X = float32(sz.X) / float32(mxsz.X) 37 | ii.PctSize.Y = float32(sz.Y) / float32(mxsz.Y) 38 | } 39 | -------------------------------------------------------------------------------- /szalloc/szalloc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package szalloc 6 | 7 | import ( 8 | "image" 9 | "math/rand" 10 | "testing" 11 | 12 | "cogentcore.org/core/math32" 13 | ) 14 | 15 | func TestRandSzAlloc(t *testing.T) { 16 | var sa SzAlloc 17 | nsz := 300 18 | szs := make([]image.Point, nsz) 19 | for i := range szs { 20 | szs[i] = image.Point{X: rand.Intn(1024), Y: rand.Intn(1024)} 21 | } 22 | sa.SetSizes(image.Point{4, 4}, 20, szs) 23 | sa.Alloc() 24 | if len(sa.GpAllocs) != 16 { 25 | t.Error("failed, N gpallocs != 16\n") 26 | } 27 | // sa.PrintGps() 28 | } 29 | 30 | func TestUniqSzAlloc(t *testing.T) { 31 | var sa SzAlloc 32 | nsz := 20 33 | szs := make([]image.Point, nsz) 34 | for i := range szs { 35 | if i%2 == 0 { 36 | szs[i] = image.Point{X: 9, Y: 9} 37 | } else { 38 | szs[i] = image.Point{X: rand.Intn(1024), Y: rand.Intn(1024)} 39 | } 40 | } 41 | sa.SetSizes(image.Point{4, 4}, 20, szs) 42 | sa.Alloc() 43 | if len(sa.GpAllocs) != 11 { 44 | t.Error("failed, N gpallocs != 11\n") 45 | } 46 | // sa.PrintGps() 47 | } 48 | 49 | func TestPctWin(t *testing.T) { 50 | pct := float32(.7) 51 | for u := float32(0); u < 3; u += .1 { 52 | pu := math32.Mod(u*pct, pct) 53 | _ = pu 54 | // fmt.Printf("u: %g pu: %g\n", u, pu) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /szalloc/uniqints.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package szalloc 6 | 7 | import ( 8 | "image" 9 | "sort" 10 | 11 | "cogentcore.org/core/math32" 12 | ) 13 | 14 | // UniqSortedInts returns the ints in sorted order with only unique vals 15 | func UniqSortedInts(vals []int) []int { 16 | sort.Ints(vals) 17 | sz := len(vals) 18 | lst := vals[0] 19 | uvals := make([]int, 0, sz) 20 | uvals = append(uvals, vals[0]) 21 | for i := 1; i < sz; i++ { 22 | v := vals[i] 23 | if v != lst { 24 | uvals = append(uvals, v) 25 | } 26 | } 27 | return uvals 28 | } 29 | 30 | // SizeGroups returns evenly spaced size groups of max N -- could be less 31 | func SizeGroups(sizes []int, maxN int) []int { 32 | ns := len(sizes) 33 | mxgp := min(ns, maxN) 34 | nper := float32(ns) / float32(mxgp) 35 | 36 | idxs := make([]int, mxgp) 37 | for i := 0; i < mxgp; i++ { 38 | cut := int(math32.Round(float32(i+1) * nper)) 39 | if cut >= ns { 40 | cut = ns - 1 41 | } 42 | idxs[i] = cut 43 | } 44 | idxs[mxgp-1] = ns - 1 45 | return idxs 46 | } 47 | 48 | // PointsClone returns clone of []image.Point list 49 | func PointsClone(pts []image.Point) []image.Point { 50 | np := len(pts) 51 | cpts := make([]image.Point, np) 52 | copy(cpts, pts) 53 | return cpts 54 | } 55 | -------------------------------------------------------------------------------- /texture.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vgpu 6 | 7 | import ( 8 | vk "github.com/goki/vulkan" 9 | ) 10 | 11 | // Texture supplies an Image and a Sampler 12 | type Texture struct { 13 | Image 14 | 15 | // sampler for image 16 | Sampler 17 | } 18 | 19 | func (tx *Texture) Defaults() { 20 | tx.Image.Format.Defaults() 21 | tx.Sampler.Defaults() 22 | } 23 | 24 | func (tx *Texture) Destroy() { 25 | tx.Sampler.Destroy(tx.Image.Dev) 26 | tx.Image.Destroy() 27 | } 28 | 29 | // AllocTexture allocates texture device image, stdview, and sampler 30 | func (tx *Texture) AllocTexture() { 31 | tx.AllocImage() 32 | if tx.Sampler.VkSampler == vk.NullSampler { 33 | tx.Sampler.Config(tx.GPU, tx.Dev) 34 | } 35 | tx.ConfigStdView() 36 | } 37 | 38 | /////////////////////////////////////////////////// 39 | 40 | // Sampler represents a vulkan image sampler 41 | type Sampler struct { 42 | Name string 43 | 44 | // for U (horizontal) axis -- what to do when going off the edge 45 | UMode SamplerModes 46 | 47 | // for V (vertical) axis -- what to do when going off the edge 48 | VMode SamplerModes 49 | 50 | // for W (horizontal) axis -- what to do when going off the edge 51 | WMode SamplerModes 52 | 53 | // border color for Clamp modes 54 | Border BorderColors 55 | 56 | // the vulkan sampler 57 | VkSampler vk.Sampler 58 | } 59 | 60 | func (sm *Sampler) Defaults() { 61 | sm.UMode = Repeat 62 | sm.VMode = Repeat 63 | sm.WMode = Repeat 64 | sm.Border = BorderTrans 65 | } 66 | 67 | // Config configures sampler on device 68 | func (sm *Sampler) Config(gp *GPU, dev vk.Device) { 69 | sm.Destroy(dev) 70 | var samp vk.Sampler 71 | ret := vk.CreateSampler(dev, &vk.SamplerCreateInfo{ 72 | SType: vk.StructureTypeSamplerCreateInfo, 73 | MagFilter: vk.FilterLinear, 74 | MinFilter: vk.FilterLinear, 75 | AddressModeU: sm.UMode.VkMode(), 76 | AddressModeV: sm.VMode.VkMode(), 77 | AddressModeW: sm.WMode.VkMode(), 78 | AnisotropyEnable: vk.True, 79 | MaxAnisotropy: gp.GPUProperties.Limits.MaxSamplerAnisotropy, 80 | BorderColor: sm.Border.VkColor(), 81 | UnnormalizedCoordinates: vk.False, 82 | CompareEnable: vk.False, 83 | MipmapMode: vk.SamplerMipmapModeLinear, 84 | }, nil, &samp) 85 | IfPanic(NewError(ret)) 86 | sm.VkSampler = samp 87 | } 88 | 89 | func (sm *Sampler) Destroy(dev vk.Device) { 90 | if sm.VkSampler != vk.NullSampler { 91 | vk.DestroySampler(dev, sm.VkSampler, nil) 92 | sm.VkSampler = vk.NullSampler 93 | } 94 | } 95 | 96 | // Texture image sampler modes 97 | type SamplerModes int32 //enums:enum 98 | 99 | const ( 100 | // Repeat the texture when going beyond the image dimensions. 101 | Repeat SamplerModes = iota 102 | 103 | // Like repeat, but inverts the coordinates to mirror the image when going beyond the dimensions. 104 | MirroredRepeat 105 | 106 | // Take the color of the edge closest to the coordinate beyond the image dimensions. 107 | ClampToEdge 108 | 109 | // Return a solid color when sampling beyond the dimensions of the image. 110 | ClampToBorder 111 | 112 | // Like clamp to edge, but instead uses the edge opposite to the closest edge. 113 | MirrorClampToEdge 114 | ) 115 | 116 | func (sm SamplerModes) VkMode() vk.SamplerAddressMode { 117 | return VulkanSamplerModes[sm] 118 | } 119 | 120 | var VulkanSamplerModes = map[SamplerModes]vk.SamplerAddressMode{ 121 | Repeat: vk.SamplerAddressModeRepeat, 122 | MirroredRepeat: vk.SamplerAddressModeMirroredRepeat, 123 | ClampToEdge: vk.SamplerAddressModeClampToEdge, 124 | ClampToBorder: vk.SamplerAddressModeClampToBorder, 125 | MirrorClampToEdge: vk.SamplerAddressModeMirrorClampToEdge, 126 | } 127 | 128 | ////////////////////////////////////////////////////// 129 | 130 | // Texture image sampler modes 131 | type BorderColors int32 //enums:enum -trim-prefix Border 132 | 133 | const ( 134 | // Repeat the texture when going beyond the image dimensions. 135 | BorderTrans BorderColors = iota 136 | BorderBlack 137 | BorderWhite 138 | ) 139 | 140 | func (bc BorderColors) VkColor() vk.BorderColor { 141 | return VulkanBorderColors[bc] 142 | } 143 | 144 | var VulkanBorderColors = map[BorderColors]vk.BorderColor{ 145 | BorderTrans: vk.BorderColorIntTransparentBlack, 146 | BorderBlack: vk.BorderColorIntOpaqueBlack, 147 | BorderWhite: vk.BorderColorIntOpaqueWhite, 148 | } 149 | -------------------------------------------------------------------------------- /vdraw/README.md: -------------------------------------------------------------------------------- 1 | # vDraw: vGPU version of Go image/draw compositing functionality 2 | 3 | This package uses [Alpha Compositing](https://en.wikipedia.org/wiki/Alpha_compositing) to render rectangular regions onto a render target, using vGPU, consistent with the [image/draw](https://pkg.go.dev/image/draw) package in Go. Although "draw" is not a great name for this functionality, it is hard to come up with a better one that isn't crazy long, so we're adopting it -- at least it is familiar. 4 | 5 | The Cogent Core GUI, and probably other 2D-based GUI frameworks, uses a strategy of rendering to various rectangular sub-regions (in Cogent Core these are `core.Viewport` objects) that are updated separately, and then the final result can be composited together into a single overall image that can be pasted onto the final window surface that the user sees. Furthermore, in Gi3D, the 3D Scene is rendered to a framebuffer, which is likewise composited into the final surface window. 6 | 7 | This package supports these rectangular image region composition operations, via a simple render pipeline that just renders a rectangular shape with a texture. There is also a simple fill pipeline that renders a single color into a rectangle. 8 | 9 | The max number of images that can be pre-loaded and used per set, per render pass is only 16 -- see MaxImages. Therefore, we use 4 sets of 16. The last set is configured for 3D images with 128 layers to be used as sprites. 10 | 11 | The fill color is uploaded as a push constant and thus is not subject to any limitations. 12 | 13 | # Implementation: Var map 14 | 15 | ``` 16 | Set: -2 17 | Role: Vertex 18 | Var: 0: Pos Float32Vector2[4] (size: 8) Values: 1 19 | Role: Index 20 | Var: 1: Index Uint16[6] (size: 2) Values: 1 21 | Set: -1 22 | Role: Push 23 | Var: 0: Mtxs Struct (size: 128) Values: 0 24 | Set: 0 25 | Role: TextureRole 26 | Var: 0: Tex ImageRGBA32 (size: 4) Values: 1 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /vdraw/fill.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vdraw 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "image/draw" 11 | "unsafe" 12 | 13 | "cogentcore.org/core/math32" 14 | "cogentcore.org/core/vgpu" 15 | ) 16 | 17 | // FillRect fills given color to render target, to given region. 18 | // op is the drawing operation: Src = copy source directly (blit), 19 | // Over = alpha blend with existing 20 | func (dw *Drawer) FillRect(clr color.Color, reg image.Rectangle, op draw.Op) error { 21 | return dw.Fill(clr, math32.Identity3(), reg, op) 22 | } 23 | 24 | // Fill fills given color to to render target. 25 | // src2dst is the transform mapping source to destination 26 | // coordinates (translation, scaling), 27 | // reg is the region to fill 28 | // op is the drawing operation: Src = copy source directly (blit), 29 | // Over = alpha blend with existing 30 | func (dw *Drawer) Fill(clr color.Color, src2dst math32.Matrix3, reg image.Rectangle, op draw.Op) error { 31 | sy := &dw.Sys 32 | cmd := sy.CmdPool.Buff 33 | vars := sy.Vars() 34 | 35 | dsz := dw.DestSize() 36 | tmat := dw.ConfigMtxs(src2dst, dsz, reg, op, false) 37 | clr4 := math32.NewVector4Color(clr) 38 | clr4.ToSlice(tmat.UVP[:], 12) // last column holds color 39 | 40 | matv, _ := vars.VarByNameTry(vgpu.PushSet, "Mtxs") 41 | fpl := sy.PipelineMap["fill"] 42 | fpl.Push(cmd, matv, unsafe.Pointer(tmat)) 43 | fpl.BindDrawVertex(cmd, 0) 44 | 45 | return nil 46 | } 47 | 48 | // StartFill starts color fill drawing rendering process on render target. 49 | // It returns false if rendering can not proceed. 50 | func (dw *Drawer) StartFill() bool { 51 | sy := &dw.Sys 52 | fpl := sy.PipelineMap["fill"] 53 | cmd := sy.CmdPool.Buff 54 | if dw.Surf != nil { 55 | idx, ok := dw.Surf.AcquireNextImage() 56 | if !ok { 57 | return false 58 | } 59 | dw.Impl.SurfIndex = idx 60 | sy.ResetBeginRenderPassNoClear(cmd, dw.Surf.Frames[dw.Impl.SurfIndex], 0) 61 | } else { 62 | sy.ResetBeginRenderPassNoClear(cmd, dw.Frame.Frames[0], 0) 63 | } 64 | fpl.BindPipeline(cmd) 65 | return true 66 | } 67 | 68 | // EndFill ends color filling rendering process on render target 69 | func (dw *Drawer) EndFill() { 70 | sy := &dw.Sys 71 | cmd := sy.CmdPool.Buff 72 | sy.EndRenderPass(cmd) 73 | if dw.Surf != nil { 74 | dw.Surf.SubmitRender(cmd) // this is where it waits for the 16 msec 75 | dw.Surf.PresentImage(dw.Impl.SurfIndex) 76 | } else { 77 | dw.Frame.SubmitRender(cmd) 78 | dw.Frame.WaitForRender() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /vdraw/gtigen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "goki generate ./..."; DO NOT EDIT. 2 | 3 | package vdraw 4 | 5 | import ( 6 | "goki.dev/gti" 7 | "goki.dev/ordmap" 8 | ) 9 | 10 | var _ = gti.AddType(>i.Type{ 11 | Name: "goki.dev/vgpu/v2/vdraw.Mtxs", 12 | ShortName: "vdraw.Mtxs", 13 | IDName: "mtxs", 14 | Doc: "Mtxs are the projection matricies", 15 | Directives: gti.Directives{}, 16 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 17 | {"MVP", >i.Field{Name: "MVP", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 18 | {"UVP", >i.Field{Name: "UVP", Type: "mat32.Mat4", Doc: "", Directives: gti.Directives{}}}, 19 | }), 20 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 21 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 22 | }) 23 | 24 | var _ = gti.AddType(>i.Type{ 25 | Name: "goki.dev/vgpu/v2/vdraw.DrawerImpl", 26 | ShortName: "vdraw.DrawerImpl", 27 | IDName: "drawer-impl", 28 | Doc: "DrawerImpl contains implementation state -- ignore..", 29 | Directives: gti.Directives{}, 30 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 31 | {"SurfIdx", >i.Field{Name: "SurfIdx", Type: "uint32", Doc: "surface index for current render process", Directives: gti.Directives{}}}, 32 | {"MaxTextures", >i.Field{Name: "MaxTextures", Type: "int", Doc: "maximum number of images per pass -- set by user at config", Directives: gti.Directives{}}}, 33 | {"FlipY", >i.Field{Name: "FlipY", Type: "bool", Doc: "whether to render image with flipped Y", Directives: gti.Directives{}}}, 34 | {"LastOp", >i.Field{Name: "LastOp", Type: "draw.Op", Doc: "last draw operation used -- used for switching pipeline", Directives: gti.Directives{}}}, 35 | }), 36 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 37 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 38 | }) 39 | 40 | var _ = gti.AddType(>i.Type{ 41 | Name: "goki.dev/vgpu/v2/vdraw.Drawer", 42 | ShortName: "vdraw.Drawer", 43 | IDName: "drawer", 44 | Doc: "Drawer is the vDraw implementation, which draws Textures\nor Fills solid colors to a render target (Surface, RenderFrame).\nImage and color palette must be set prior to a given render pass.\nMultiple fill operations can be performed in one pass, but only\none Image can be used at a time.", 45 | Directives: gti.Directives{}, 46 | Fields: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{ 47 | {"Sys", >i.Field{Name: "Sys", Type: "vgpu.System", Doc: "drawing system", Directives: gti.Directives{}}}, 48 | {"Surf", >i.Field{Name: "Surf", Type: "*vgpu.Surface", Doc: "surface if render target", Directives: gti.Directives{}}}, 49 | {"Frame", >i.Field{Name: "Frame", Type: "*vgpu.RenderFrame", Doc: "render frame if render target", Directives: gti.Directives{}}}, 50 | {"YIsDown", >i.Field{Name: "YIsDown", Type: "bool", Doc: "render so the Y axis points down, with 0,0 at the upper left, which is the Vulkan standard. default is Y is up, with 0,0 at bottom left, which is OpenGL default. this must be set prior to configuring, the surface, as it determines the rendering parameters.", Directives: gti.Directives{}}}, 51 | {"Impl", >i.Field{Name: "Impl", Type: "DrawerImpl", Doc: "implementation state -- ignore", Directives: gti.Directives{}}}, 52 | {"UpdtMu", >i.Field{Name: "UpdtMu", Type: "sync.Mutex", Doc: "mutex on updating", Directives: gti.Directives{}}}, 53 | }), 54 | Embeds: ordmap.Make([]ordmap.KeyVal[string, *gti.Field]{}), 55 | Methods: ordmap.Make([]ordmap.KeyVal[string, *gti.Method]{}), 56 | }) 57 | -------------------------------------------------------------------------------- /vdraw/shaders/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of GSL, HLSL files for compute 2 | 3 | all: draw_vert.spv draw_frag.spv draw_depma_frag.spv fill_vert.spv fill_frag.spv 4 | 5 | %.spv : %.hlsl 6 | glslc -fshader-stage=compute -o $@ $< 7 | 8 | %.spv : %.vert 9 | glslc -fshader-stage=vertex -o $@ $< 10 | 11 | %.spv : %.frag 12 | glslc -fshader-stage=fragment -o $@ $< 13 | 14 | -------------------------------------------------------------------------------- /vdraw/shaders/draw_depma_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | // must use mat4 -- mat3 alignment issues are horrible. 5 | // each mat4 = 64 bytes, so full 128 byte total, but only using mat3. 6 | // pack the tex, layer indexes into [3][0-1] of mvp, 7 | // and the fill color into [3][0-3] of uvp 8 | layout(push_constant) uniform Mtxs { 9 | mat4 mvp; 10 | mat4 uvp; 11 | }; 12 | 13 | layout(set = 0, binding = 0) uniform sampler2DArray Tex[]; 14 | 15 | layout(location = 0) in vector2 uv; 16 | layout(location = 0) out vector4 outputColor; 17 | 18 | void main() { 19 | int idx = int(mvp[3][0]); 20 | int layer = int(mvp[3][1]); 21 | outputColor = texture(Tex[idx], vector3(uv,layer)); 22 | // de-pre-multiplied-alpha version: undo 23 | if (outputColor.a > 0) { 24 | outputColor.r = outputColor.r / outputColor.a; 25 | outputColor.g = outputColor.g / outputColor.a; 26 | outputColor.b = outputColor.b / outputColor.a; 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /vdraw/shaders/draw_depma_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vdraw/shaders/draw_depma_frag.spv -------------------------------------------------------------------------------- /vdraw/shaders/draw_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | // must use mat4 -- mat3 alignment issues are horrible. 5 | // each mat4 = 64 bytes, so full 128 byte total, but only using mat3. 6 | // pack the tex, layer indexes into [3][0-1] of mvp, 7 | // and the fill color into [3][0-3] of uvp 8 | layout(push_constant) uniform Mtxs { 9 | mat4 mvp; 10 | mat4 uvp; 11 | }; 12 | 13 | layout(set = 0, binding = 0) uniform sampler2DArray Tex[]; 14 | 15 | layout(location = 0) in vector2 uv; 16 | layout(location = 0) out vector4 outputColor; 17 | 18 | void main() { 19 | int idx = int(mvp[3][0]); 20 | int layer = int(mvp[3][1]); 21 | outputColor = texture(Tex[idx], vector3(uv,layer)); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /vdraw/shaders/draw_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vdraw/shaders/draw_frag.spv -------------------------------------------------------------------------------- /vdraw/shaders/draw_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vdraw/shaders/draw_vert.spv -------------------------------------------------------------------------------- /vdraw/shaders/draw_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must use mat4 -- mat3 alignment issues are horrible. 4 | // each mat4 = 64 bytes, so full 128 byte total, but only using mat3. 5 | // pack the tex, layer indexes into [3][0-1] of mvp, 6 | // and the fill color into [3][0-3] of uvp 7 | layout(push_constant) uniform Mtxs { 8 | mat4 mvp; 9 | mat4 uvp; 10 | }; 11 | 12 | layout(location = 0) in vector2 pos; 13 | layout(location = 0) out vector2 uv; 14 | 15 | void main() { 16 | vector3 p = vector3(pos, 1); 17 | gl_Position = vector4(mat3(mvp) * p, 1); 18 | uv = (mat3(uvp) * p).xy; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /vdraw/shaders/fill_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must use mat4 -- mat3 alignment issues are horrible. 4 | // each mat4 = 64 bytes, so full 128 byte total, but only using mat3. 5 | // pack the tex, layer indexes into [3][0-1] of mvp, 6 | // and the fill color into [3][0-3] of uvp 7 | layout(push_constant) uniform Mtxs { 8 | mat4 mvp; 9 | mat4 uvp; 10 | }; 11 | 12 | layout(location = 0) out vector4 outputColor; 13 | 14 | void main() { 15 | vector4 color = uvp[3]; 16 | outputColor = color; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /vdraw/shaders/fill_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vdraw/shaders/fill_frag.spv -------------------------------------------------------------------------------- /vdraw/shaders/fill_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vdraw/shaders/fill_vert.spv -------------------------------------------------------------------------------- /vdraw/shaders/fill_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must use mat4 -- mat3 alignment issues are horrible. 4 | // each mat4 = 64 bytes, so full 128 byte total, but only using mat3. 5 | // pack the tex, layer indexes into [3][0-1] of mvp, 6 | // and the fill color into [3][0-3] of uvp 7 | layout(push_constant) uniform Mtxs { 8 | mat4 mvp; 9 | mat4 uvp; 10 | }; 11 | 12 | layout(location = 0) in vector2 pos; 13 | 14 | void main() { 15 | vector3 p = vector3(pos, 1); 16 | gl_Position = vector4(mat3(mvp) * p, 1); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /vdraw/vdraw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vdraw 6 | 7 | //go:generate core generate 8 | 9 | import ( 10 | "image" 11 | "sync" 12 | 13 | "cogentcore.org/core/vgpu" 14 | ) 15 | 16 | // Drawer is the vDraw implementation, which draws Textures 17 | // or Fills solid colors to a render target (Surface, RenderFrame). 18 | // Image and color palette must be set prior to a given render pass. 19 | // Multiple fill operations can be performed in one pass, but only 20 | // one Image can be used at a time. 21 | type Drawer struct { 22 | 23 | // drawing system 24 | Sys vgpu.System 25 | 26 | // surface if render target 27 | Surf *vgpu.Surface 28 | 29 | // render frame if render target 30 | Frame *vgpu.RenderFrame 31 | 32 | // render so the Y axis points down, with 0,0 at the upper left, which is the Vulkan standard. default is Y is up, with 0,0 at bottom left, which is OpenGL default. this must be set prior to configuring, the surface, as it determines the rendering parameters. 33 | YIsDown bool 34 | 35 | // implementation state -- ignore 36 | Impl DrawerImpl 37 | 38 | // mutex on updating 39 | UpdateMu sync.Mutex `display:"-" copier:"-" json:"-" xml:"-"` 40 | } 41 | 42 | // ConfigSurface configures the Drawer to use given surface as a render target. 43 | // maxTextures is maximum number of images that can be used per pass. 44 | // If maxTextures > vgpu.MaxTexturesPerSet (16) then multiple descriptor sets 45 | // are used to hold more textures. 46 | func (dw *Drawer) ConfigSurface(sf *vgpu.Surface, maxTextures int) { 47 | dw.Impl.MaxTextures = maxTextures 48 | dw.Surf = sf 49 | dw.Sys.InitGraphics(sf.GPU, "vdraw.Drawer", &sf.Device) 50 | dw.Sys.ConfigRender(&dw.Surf.Format, vgpu.UndefinedType) 51 | sf.SetRender(&dw.Sys.Render) 52 | 53 | dw.ConfigSys() 54 | } 55 | 56 | // ConfigFrame configures the Drawer to use a RenderFrame as a render target, 57 | // of given size. Use dw.Frame.SetSize to resize later. 58 | // Frame is owned and managed by the Drawer. 59 | // Uses given Device -- if nil, one is made. 60 | // If maxTextures > vgpu.MaxTexturesPerSet (16) then multiple descriptor sets 61 | // are used to hold more textures. 62 | func (dw *Drawer) ConfigFrame(dev *vgpu.Device, size image.Point, maxTextures int) { 63 | dw.Impl.MaxTextures = maxTextures 64 | dw.Frame = vgpu.NewRenderFrame(dw.Sys.GPU, dev, size) 65 | dw.Sys.InitGraphics(dw.Sys.GPU, "vdraw.Drawer", &dw.Frame.Device) 66 | dw.Sys.ConfigRenderNonSurface(&dw.Frame.Format, vgpu.UndefinedType) 67 | dw.Frame.SetRender(&dw.Sys.Render) 68 | 69 | dw.ConfigSys() 70 | } 71 | 72 | // SetMaxTextures updates the max number of textures for drawing 73 | // Must call this prior to doing any allocation of images. 74 | func (dw *Drawer) SetMaxTextures(maxTextures int) { 75 | sy := &dw.Sys 76 | vars := sy.Vars() 77 | txset := vars.SetMap[0] 78 | txset.ConfigValues(maxTextures) 79 | dw.Impl.MaxTextures = maxTextures 80 | vars.NDescs = vgpu.NDescForTextures(dw.Impl.MaxTextures) 81 | vars.Config() // update after config changes 82 | } 83 | 84 | // MaxTextures returns the max number of textures for drawing, which 85 | // is [Drawer.Impl.MaxTextures]. 86 | func (dw *Drawer) MaxTextures() int { 87 | return dw.Impl.MaxTextures 88 | } 89 | 90 | func (dw *Drawer) Destroy() { 91 | dw.Sys.Destroy() 92 | if dw.Frame != nil { 93 | dw.Frame.Destroy() 94 | dw.Frame = nil 95 | } 96 | } 97 | 98 | // DestSize returns the size of the render destination 99 | func (dw *Drawer) DestSize() image.Point { 100 | if dw.Surf != nil { 101 | return dw.Surf.Format.Size 102 | } else { 103 | return dw.Frame.Format.Size 104 | } 105 | } 106 | 107 | // DestBounds returns the bounds of the render destination 108 | func (dw *Drawer) DestBounds() image.Rectangle { 109 | if dw.Surf != nil { 110 | return dw.Surf.Format.Bounds() 111 | } else { 112 | return dw.Frame.Format.Bounds() 113 | } 114 | } 115 | 116 | func (dw *Drawer) Surface() any { 117 | return dw.Surf 118 | } 119 | -------------------------------------------------------------------------------- /vgpu_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This is initially adapted from https://github.com/vulkan-go/asche 6 | // Copyright © 2017 Maxim Kupriianov , under the MIT License 7 | 8 | package vgpu 9 | -------------------------------------------------------------------------------- /vkinit/vkinit.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build (linux && cgo) || (darwin && cgo) || (freebsd && cgo) 6 | 7 | /* 8 | package vkinit handles loading and initialization of Vulkan without 9 | any dependency upon glfw 10 | */ 11 | package vkinit 12 | 13 | // #cgo LDFLAGS: -ldl 14 | // #include 15 | // #include 16 | import "C" 17 | import ( 18 | "fmt" 19 | "unsafe" 20 | 21 | vk "github.com/goki/vulkan" 22 | ) 23 | 24 | // IsLoaded is set to true when library is loaded 25 | var IsLoaded = false 26 | 27 | // LoadVulkan loads and initializes the vulkan library, using default lib names 28 | // for each different platform. 29 | func LoadVulkan() error { 30 | if IsLoaded { 31 | return nil 32 | } 33 | clibnm := C.CString(DlName) 34 | defer C.free(unsafe.Pointer(clibnm)) 35 | handle := C.dlopen(clibnm, C.RTLD_LAZY) 36 | if handle == nil { 37 | return fmt.Errorf("Vulkan library named: %s not found!\n", DlName) 38 | } 39 | cpAddr := C.CString("vkGetInstanceProcAddr") 40 | defer C.free(unsafe.Pointer(cpAddr)) 41 | pAddr := C.dlsym(handle, cpAddr) 42 | if pAddr == nil { 43 | return fmt.Errorf("Vulkan instance proc addr not found!\n") 44 | } 45 | vk.SetGetInstanceProcAddr(pAddr) 46 | IsLoaded = true 47 | return vk.Init() 48 | } 49 | -------------------------------------------------------------------------------- /vkinit/vkinit_bsd.go: -------------------------------------------------------------------------------- 1 | //go:build freebsd || netbsd || openbsd 2 | 3 | package vkinit 4 | 5 | var DlName = "libvulkan.so" 6 | -------------------------------------------------------------------------------- /vkinit/vkinit_darwin.go: -------------------------------------------------------------------------------- 1 | package vkinit 2 | 3 | var DlName = "libvulkan.1.dylib" 4 | -------------------------------------------------------------------------------- /vkinit/vkinit_lin.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package vkinit 4 | 5 | var DlName = "libvulkan.so.1" 6 | -------------------------------------------------------------------------------- /vkinit/vkinit_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vkinit 6 | 7 | import ( 8 | "fmt" 9 | "syscall" 10 | "unsafe" 11 | 12 | vk "github.com/goki/vulkan" 13 | ) 14 | 15 | var DlName = "vulkan-1.dll" 16 | 17 | func LoadVulkan() error { 18 | handle, err := syscall.LoadLibrary(DlName) 19 | if err != nil { 20 | return fmt.Errorf("Vulkan library named: %s not found!\n", DlName) 21 | } 22 | pAddr, err := syscall.GetProcAddress(handle, "vkGetInstanceProcAddr") 23 | if err != nil { 24 | return fmt.Errorf("Vulkan instance proc addr not found!\n") 25 | } 26 | vk.SetGetInstanceProcAddr(unsafe.Pointer(pAddr)) 27 | return vk.Init() 28 | } 29 | -------------------------------------------------------------------------------- /vphong/README.md: -------------------------------------------------------------------------------- 1 | # vPhong is a Blinn-Phong rendering system in vGPU 2 | 3 | Blinn-Phong is a standard lighting model that used to be built into OpenGL, and is widely used in 3D graphics: [wikipedia Blinn-Phong](https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model), [learnopengl.com](https://learnopengl.com/Lighting/Basic-Lighting). 4 | 5 | 6 | Supports 4 different types of lights, with a max of 8 instances of each light type: 7 | 8 | * Ambient: light emitted from everywhere -- provides a background of diffuse light bouncing around in all directions. 9 | 10 | * Directional: light directed along a given vector (specified as a position of a light shining toward the origin), with no attenuation. This is like the sun. 11 | 12 | * Point: an omnidirectional light with a position and associated decay factors, which divide the light intensity as a function of linear and quadratic distance. The quadratic factor dominates at longer distances.light radiating out in all directdions from a specific point, 13 | 14 | * Spot: a light with a position and direction and associated decay factors and angles, which divide the light intensity as a function of linear and quadratic distance. The quadratic factor dominates at longer distances. 15 | 16 | Meshes are indexed triangles. 17 | 18 | There are 3 rendering pipelines: 19 | * Texture: color comes from texture image 20 | * OneColor: a single color for the entire mesh. 21 | * PerVertex: color is provided per vertex by the mesh. 22 | 23 | The color model has the following factors: 24 | * `Color` = main color of surface arising from one of the 3 sources listed above, used for both ambient and diffuse color in standard Phong model -- alpha component determines transparency -- note that transparent objects require more complex rendering, to sort objects as a function of depth. 25 | * `Emissive` = color that surface emits independent of any lighting -- i.e., glow -- can be used for marking lights with an object. 26 | * `Shiny` = specular shininess factor -- how focally the surface shines back directional light -- this is an exponential factor, with 0 = very broad diffuse reflection, and higher values (typically max of 128) having a smaller more focal specular reflection. Default is 30. 27 | * `Reflect` = reflectiveness of the surface in the region where specular reflected light is emitted -- 1 for full shiny white reflection (specular) color, 0 = no shine reflection. The specular color is always set to white, which will reflect the light color accurately. 28 | * `Bright` = overall multiplier on final computed color value -- can be used to tune the overall brightness of various surfaces relative to each other for a given set of lighting parameters. 29 | 30 | # Known Issues 31 | 32 | There must be at least one texture image, otherwise the Mac VKMolten system triggers this error. The system automatically adds a dummy image to deal with this. 33 | 34 | ``` 35 | [mvk-error] VK_ERROR_INVALID_SHADER_NV: Unable to convert SPIR-V to MSL: 36 | MSL conversion error: Unsized array of images is not supported in MSL. 37 | ``` 38 | 39 | # Layout of Vars 40 | 41 | ``` 42 | Set: -2 43 | Role: Vertex 44 | Var: 0: Pos Float32Vector3 (size: 12) Values: 6 45 | Var: 1: Norm Float32Vector3 (size: 12) Values: 6 46 | Var: 2: Tex Float32Vector2 (size: 8) Values: 6 47 | Var: 3: Color Float32Vector4 (size: 16) Values: 6 48 | Role: Index 49 | Var: 4: Index Uint32 (size: 4) Values: 6 50 | Set: -1 51 | Role: Push 52 | Var: 0: PushU Struct (size: 128) Values: 1 53 | Set: 0 54 | Role: Uniform 55 | Var: 0: Mtxs Struct (size: 128) Values: 1 56 | Set: 1 57 | Role: Uniform 58 | Var: 0: NLights Struct (size: 16) Values: 1 59 | Set: 2 60 | Role: Uniform 61 | Var: 0: AmbLights Struct[8] (size: 16) Values: 1 62 | Var: 1: DirLights Struct[8] (size: 32) Values: 1 63 | Var: 2: PointLights Struct[8] (size: 48) Values: 1 64 | Var: 3: SpotLights Struct[8] (size: 64) Values: 1 65 | Set: 3 66 | Role: TextureRole 67 | Var: 0: Tex ImageRGBA32 (size: 4) Values: 3 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /vphong/camera.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // CameraViewMat returns the camera view matrix, based position 10 | // of camera facing at target position, with given up vector. 11 | func CameraViewMat(pos, target, up math32.Vector3) *math32.Matrix4 { 12 | var lookq math32.Quat 13 | lookq.SetFromRotationMatrix(math32.NewLookAt(pos, target, up)) 14 | scale := math32.Vec3(1, 1, 1) 15 | var cview math32.Matrix4 16 | cview.SetTransform(pos, lookq, scale) 17 | view, _ := cview.Inverse() 18 | return view 19 | } 20 | -------------------------------------------------------------------------------- /vphong/color.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | import ( 8 | "fmt" 9 | "image/color" 10 | "log" 11 | 12 | "cogentcore.org/core/math32" 13 | ) 14 | 15 | // Colors are the material colors with padding for direct uploading to shader 16 | type Colors struct { 17 | 18 | // main color of surface, used for both ambient and diffuse color in standard Phong model -- alpha component determines transparency -- note that transparent objects require more complex rendering 19 | Color math32.Vector4 20 | 21 | // X = shininess spread factor, Y = shine reflection factor, Z = brightness factor: shiny = specular shininess factor -- how focally the surface shines back directional light -- this is an exponential factor, with 0 = very broad diffuse reflection, and higher values (typically max of 128) having a smaller more focal specular reflection. Shine reflect = 1 for full shine white reflection (specular) color, 0 = no shine reflection. bright = overall multiplier on final computed color value -- can be used to tune the overall brightness of various surfaces relative to each other for a given set of lighting parameters. W is used for Tex idx. 22 | ShinyBright math32.Vector4 23 | 24 | // color that surface emits independent of any lighting -- i.e., glow -- can be used for marking lights with an object 25 | Emissive math32.Vector4 26 | } 27 | 28 | // NewGoColor sets the colors from standard Go colors 29 | func NewColors(clr, emis color.Color, shiny, reflect, bright float32) *Colors { 30 | cl := &Colors{} 31 | cl.SetColors(clr, emis, shiny, reflect, bright) 32 | return cl 33 | } 34 | 35 | // SetColors sets the colors from standard Go colors 36 | func (cl *Colors) SetColors(clr, emis color.Color, shiny, reflect, bright float32) { 37 | cl.Color = math32.NewVector4Color(clr).SRGBToLinear() 38 | cl.Emissive = math32.NewVector4Color(emis).SRGBToLinear() 39 | cl.ShinyBright.X = shiny 40 | cl.ShinyBright.Y = reflect 41 | cl.ShinyBright.Z = bright 42 | } 43 | 44 | // AddColor adds to list of colors, which can be use for a materials library 45 | func (ph *Phong) AddColor(name string, clr *Colors) { 46 | ph.Colors.Add(name, clr) 47 | } 48 | 49 | // UseColorIndex selects color by index for current render step 50 | func (ph *Phong) UseColorIndex(idx int) error { 51 | if err := ph.Colors.IndexIsValid(idx); err != nil { 52 | log.Println(err) 53 | return err 54 | } 55 | clr := ph.Colors.ValueByIndex(idx) 56 | ph.Cur.Color = *clr 57 | return nil 58 | } 59 | 60 | // UseColorName selects color by name for current render step 61 | func (ph *Phong) UseColorName(name string) error { 62 | idx, ok := ph.Colors.IndexByKeyTry(name) 63 | if !ok { 64 | err := fmt.Errorf("vphong:UseColorName -- name not found: %s", name) 65 | log.Println(err) 66 | return err 67 | } 68 | clr := ph.Colors.ValueByIndex(idx) 69 | ph.Cur.Color = *clr 70 | return nil 71 | } 72 | 73 | // UseColors sets the color values for current render step 74 | func (ph *Phong) UseColor(clr, emis color.Color, shiny, reflect, bright float32) { 75 | ph.Cur.Color.SetColors(clr, emis, shiny, reflect, bright) 76 | } 77 | 78 | // RenderOnecolor renders current settings to onecolor pipeline 79 | func (ph *Phong) RenderOnecolor() { 80 | sy := &ph.Sys 81 | cmd := sy.CmdPool.Buff 82 | pl := sy.PipelineMap["onecolor"] 83 | push := ph.Cur.NewPush() 84 | ph.Push(pl, push) 85 | pl.BindDrawVertex(cmd, ph.Cur.DescIndex) 86 | } 87 | 88 | // RenderVtxColor renders current settings to vertexcolor pipeline 89 | func (ph *Phong) RenderVtxColor() { 90 | sy := &ph.Sys 91 | cmd := sy.CmdPool.Buff 92 | pl := sy.PipelineMap["pervertex"] 93 | push := ph.Cur.NewPush() 94 | ph.Push(pl, push) 95 | pl.BindDrawVertex(cmd, ph.Cur.DescIndex) 96 | } 97 | -------------------------------------------------------------------------------- /vphong/enumgen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "core generate"; DO NOT EDIT. 2 | 3 | package vphong 4 | 5 | import ( 6 | "cogentcore.org/core/enums" 7 | ) 8 | 9 | var _SetsValues = []Sets{0, 1, 2, 3} 10 | 11 | // SetsN is the highest valid value for type Sets, plus one. 12 | const SetsN Sets = 4 13 | 14 | var _SetsValueMap = map[string]Sets{`MtxsSet`: 0, `NLightSet`: 1, `LightSet`: 2, `TexSet`: 3} 15 | 16 | var _SetsDescMap = map[Sets]string{0: ``, 1: ``, 2: ``, 3: ``} 17 | 18 | var _SetsMap = map[Sets]string{0: `MtxsSet`, 1: `NLightSet`, 2: `LightSet`, 3: `TexSet`} 19 | 20 | // String returns the string representation of this Sets value. 21 | func (i Sets) String() string { return enums.String(i, _SetsMap) } 22 | 23 | // SetString sets the Sets value from its string representation, 24 | // and returns an error if the string is invalid. 25 | func (i *Sets) SetString(s string) error { return enums.SetString(i, s, _SetsValueMap, "Sets") } 26 | 27 | // Int64 returns the Sets value as an int64. 28 | func (i Sets) Int64() int64 { return int64(i) } 29 | 30 | // SetInt64 sets the Sets value from an int64. 31 | func (i *Sets) SetInt64(in int64) { *i = Sets(in) } 32 | 33 | // Desc returns the description of the Sets value. 34 | func (i Sets) Desc() string { return enums.Desc(i, _SetsDescMap) } 35 | 36 | // SetsValues returns all possible values for the type Sets. 37 | func SetsValues() []Sets { return _SetsValues } 38 | 39 | // Values returns all possible values for the type Sets. 40 | func (i Sets) Values() []enums.Enum { return enums.Values(_SetsValues) } 41 | 42 | // MarshalText implements the [encoding.TextMarshaler] interface. 43 | func (i Sets) MarshalText() ([]byte, error) { return []byte(i.String()), nil } 44 | 45 | // UnmarshalText implements the [encoding.TextUnmarshaler] interface. 46 | func (i *Sets) UnmarshalText(text []byte) error { return enums.UnmarshalText(i, text, "Sets") } 47 | -------------------------------------------------------------------------------- /vphong/mtxs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | import ( 8 | "unsafe" 9 | 10 | "cogentcore.org/core/math32" 11 | ) 12 | 13 | // Mtxs contains the camera view and projection matricies, for uniform uploading 14 | type Mtxs struct { 15 | 16 | // View Matrix: transforms world into camera-centered, 3D coordinates 17 | View math32.Matrix4 18 | 19 | // Projection Matrix: transforms camera coords into 2D render coordinates 20 | Projection math32.Matrix4 21 | } 22 | 23 | // SetViewProjection sets the camera view and projection matrixes, and updates 24 | // uniform data, so they are ready to use. 25 | func (ph *Phong) SetViewProjection(view, projection *math32.Matrix4) { 26 | ph.Cur.VPMtx.View = *view 27 | ph.Cur.VPMtx.Projection = *projection 28 | vars := ph.Sys.Vars() 29 | _, mtx, _ := vars.ValueByIndexTry(int(MtxsSet), "Mtxs", 0) 30 | mtx.CopyFromBytes(unsafe.Pointer(&ph.Cur.VPMtx)) 31 | ph.Sys.Mem.SyncToGPU() 32 | vars.BindDynamicValueIndex(int(MtxsSet), "Mtxs", 0) 33 | } 34 | 35 | // SetModelMtx sets the model pose matrix -- must be set per render step 36 | // (otherwise last one will be used) 37 | func (ph *Phong) SetModelMtx(model *math32.Matrix4) { 38 | ph.Cur.ModelMtx = *model 39 | } 40 | -------------------------------------------------------------------------------- /vphong/sets.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | // Sets are variable set numbers - must coordinate with System sets! 8 | type Sets int32 //enums:enum 9 | 10 | const ( 11 | MtxsSet Sets = iota 12 | NLightSet 13 | LightSet 14 | TexSet 15 | ) 16 | -------------------------------------------------------------------------------- /vphong/shaders/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for glslc compiling of GSL, HLSL files for compute 2 | 3 | all: texture_vert.spv texture_frag.spv onecolor_vert.spv onecolor_frag.spv pervertex_vert.spv pervertex_frag.spv 4 | 5 | texture_frag.spv : phong_frag.frag 6 | 7 | onecolor_frag.spv : phong_frag.frag 8 | 9 | pervertex_frag.spv : phong_frag.frag 10 | 11 | %.spv : %.hlsl 12 | glslc -fshader-stage=compute --target-env=vulkan1.2 -O -o $@ $< 13 | 14 | %.spv : %.vert 15 | glslc -fshader-stage=vertex --target-env=vulkan1.2 -O -o $@ $< 16 | 17 | %.spv : %.frag 18 | glslc -fshader-stage=fragment --target-env=vulkan1.2 -O -o $@ $< 19 | 20 | rebuild: 21 | rm *.spv 22 | 23 | -------------------------------------------------------------------------------- /vphong/shaders/onecolor_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | // must be <= 128 bytes -- contains all per-object data 5 | layout(push_constant) uniform PushU { 6 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 7 | vec4 Color; // 16 8 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 9 | vec4 Emissive; // 16 rgb, a = TexPct.Y 10 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 11 | }; 12 | 13 | layout(set = 0, binding = 0) uniform MtxsU { 14 | mat4 ViewMtx; 15 | mat4 PrjnMtx; 16 | }; 17 | 18 | layout(location = 0) in vec4 Pos; 19 | layout(location = 1) in vec3 Norm; 20 | layout(location = 2) in vec3 CamDir; 21 | // layout(location = 3) in vec2 TexCoord; 22 | 23 | layout(location = 0) out vec4 outColor; 24 | 25 | #include "phong_frag.frag" 26 | 27 | void main() { 28 | float opacity = Color.a; 29 | vec3 clr = Color.rgb; 30 | 31 | // Calculates the Ambient+Diffuse and Specular colors for this fragment using the Phong model. 32 | float Shiny = ShinyBright.x; 33 | float Reflect = ShinyBright.y; 34 | float Bright = ShinyBright.z; 35 | vec3 Specular = vec3(1,1,1); 36 | PhongModel(Pos, Norm, CamDir, clr, clr, Specular, Shiny, Reflect, Bright, opacity, outColor); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /vphong/shaders/onecolor_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/onecolor_frag.spv -------------------------------------------------------------------------------- /vphong/shaders/onecolor_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/onecolor_vert.spv -------------------------------------------------------------------------------- /vphong/shaders/onecolor_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must be <= 128 bytes -- contains all per-object data 4 | layout(push_constant) uniform PushU { 5 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 6 | vec4 Color; // 16 7 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 8 | vec4 Emissive; // 16 rgb, a = TexPct.Y 9 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 10 | }; 11 | 12 | layout(set = 0, binding = 0) uniform MtxsU { 13 | mat4 ViewMtx; 14 | mat4 PrjnMtx; 15 | }; 16 | 17 | layout(location = 0) in vec3 VtxPos; 18 | layout(location = 1) in vec3 VtxNorm; 19 | // layout(location = 2) in vec2 VtxTex; 20 | // layout(location = 3) in vec4 VtxColor; 21 | 22 | layout(location = 0) out vec4 Pos; 23 | layout(location = 1) out vec3 Norm; 24 | layout(location = 2) out vec3 CamDir; 25 | // layout(location = 3) out vec2 TexCoord; 26 | 27 | void main() { 28 | vec4 vPos = vec4(VtxPos, 1.0); 29 | mat4 MVMtx = ViewMtx * ModelMtx; 30 | Pos = MVMtx * vPos; 31 | mat3 NormMtx = transpose(inverse(mat3(MVMtx))); 32 | Norm = normalize(NormMtx * VtxNorm).xyz; 33 | CamDir = normalize(-Pos.xyz); 34 | // TexCoord = VtxTex; 35 | gl_Position = PrjnMtx * MVMtx * vPos; 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /vphong/shaders/pervertex_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | // must be <= 128 bytes -- contains all per-object data 5 | layout(push_constant) uniform PushU { 6 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 7 | vec4 Color; // 16 8 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 9 | vec4 Emissive; // 16 rgb, a = TexPct.Y 10 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 11 | }; 12 | 13 | layout(set = 0, binding = 0) uniform MtxsU { 14 | mat4 ViewMtx; 15 | mat4 PrjnMtx; 16 | }; 17 | 18 | layout(location = 0) in vec4 Pos; 19 | layout(location = 1) in vec3 Norm; 20 | layout(location = 2) in vec3 CamDir; 21 | // layout(location = 3) in vec2 TexCoord; 22 | layout(location = 3) in vec4 VtxColor; 23 | 24 | layout(location = 0) out vec4 outColor; 25 | 26 | #include "phong_frag.frag" 27 | 28 | void main() { 29 | float opacity = VtxColor.a; 30 | vec3 clr = SRGBToLinear(VtxColor.rgb); // we need to undo gamma on incoming colors 31 | // vec3 clr = VtxColor.rgb; 32 | 33 | // Calculates the Ambient+Diffuse and Specular colors for this fragment using the Phong model. 34 | float Shiny = ShinyBright.x; 35 | float Reflect = 0; // ShinyBright.y; 36 | float Bright = ShinyBright.z; 37 | vec3 Specular = vec3(1,1,1); 38 | PhongModel(Pos, Norm, CamDir, clr, clr, Specular, Shiny, Reflect, Bright, opacity, outColor); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /vphong/shaders/pervertex_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/pervertex_frag.spv -------------------------------------------------------------------------------- /vphong/shaders/pervertex_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/pervertex_vert.spv -------------------------------------------------------------------------------- /vphong/shaders/pervertex_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must be <= 128 bytes -- contains all per-object data 4 | layout(push_constant) uniform PushU { 5 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 6 | vec4 Color; // 16 7 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 8 | vec4 Emissive; // 16 rgb, a = TexPct.Y 9 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 10 | }; 11 | 12 | layout(set = 0, binding = 0) uniform MtxsU { 13 | mat4 ViewMtx; 14 | mat4 PrjnMtx; 15 | }; 16 | 17 | layout(location = 0) in vec3 VtxPos; 18 | layout(location = 1) in vec3 VtxNorm; 19 | // layout(location = 2) in vec2 VtxTex; 20 | layout(location = 3) in vec4 VtxColor; 21 | 22 | layout(location = 0) out vec4 Pos; 23 | layout(location = 1) out vec3 Norm; 24 | layout(location = 2) out vec3 CamDir; 25 | // layout(location = 3) out vec2 TexCoord; 26 | layout(location = 3) out vec4 VColor; 27 | 28 | void main() { 29 | vec4 vPos = vec4(VtxPos, 1.0); 30 | mat4 MVMtx = ViewMtx * ModelMtx; 31 | Pos = MVMtx * vPos; 32 | mat3 NormMtx = transpose(inverse(mat3(MVMtx))); 33 | Norm = normalize(NormMtx * VtxNorm).xyz; 34 | CamDir = normalize(-Pos.xyz); 35 | // TexCoord = VtxTex; 36 | VColor = VtxColor; 37 | gl_Position = PrjnMtx * MVMtx * vPos; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /vphong/shaders/texture_frag.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_nonuniform_qualifier : require 3 | 4 | // must be <= 128 bytes -- contains all per-object data 5 | layout(push_constant) uniform PushU { 6 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 7 | vec4 Color; // 16 8 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 9 | vec4 Emissive; // 16 rgb, a = TexPct.Y 10 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 11 | }; 12 | 13 | layout(set = 0, binding = 0) uniform MtxsU { 14 | mat4 ViewMtx; 15 | mat4 PrjnMtx; 16 | }; 17 | 18 | layout(set = 3, binding = 0) uniform sampler2DArray TexSampler[]; 19 | 20 | layout(location = 0) in vec4 Pos; 21 | layout(location = 1) in vec3 Norm; 22 | layout(location = 2) in vec3 CamDir; 23 | layout(location = 3) in vec2 TexCoord; 24 | 25 | layout(location = 0) out vec4 outColor; 26 | 27 | #include "phong_frag.frag" 28 | 29 | void TexWin(vec2 pct, out vec2 tc) { 30 | tc = mod(pct * (TexCoord * TexRepeatOff.xy + TexRepeatOff.zw), pct); 31 | } 32 | 33 | void main() { 34 | int TexIndexP = int(ShinyBright.w); 35 | int TexIndex = TexIndexP / 1024; 36 | int TexLay = TexIndexP % 1024; 37 | vec2 TexPct = vec2(ModelMtx[3][3], Emissive.a); 38 | vec2 tc; 39 | TexWin(TexPct, tc); 40 | vec4 TColor = texture(TexSampler[TexIndex], vec3(tc, TexLay)); 41 | float opacity = TColor.a; 42 | vec3 clr = TColor.rgb; 43 | 44 | // Calculates the Ambient+Diffuse and Specular colors for this fragment using the Phong model. 45 | float Shiny = ShinyBright.x; 46 | float Reflect = ShinyBright.y; 47 | float Bright = ShinyBright.z; 48 | vec3 Specular = vec3(1,1,1); 49 | PhongModel(Pos, Norm, CamDir, clr, clr, Specular, Shiny, Reflect, Bright, opacity, outColor); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /vphong/shaders/texture_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/texture_frag.spv -------------------------------------------------------------------------------- /vphong/shaders/texture_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goki/vgpu/d85ed6ea6c44fb08f979bffc8573ee8574fe8985/vphong/shaders/texture_vert.spv -------------------------------------------------------------------------------- /vphong/shaders/texture_vert.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // must be <= 128 bytes -- contains all per-object data 4 | layout(push_constant) uniform PushU { 5 | mat4 ModelMtx; // 64 bytes, [3][3] = TexPct.X 6 | vec4 Color; // 16 7 | vec4 ShinyBright; // 16 x = Shiny, y = Reflect, z = Bright, w = TexIndex 8 | vec4 Emissive; // 16 rgb, a = TexPct.Y 9 | vec4 TexRepeatOff; // 16 xy = Repeat, zw = Offset 10 | }; 11 | 12 | layout(set = 0, binding = 0) uniform MtxsU { 13 | mat4 ViewMtx; 14 | mat4 PrjnMtx; 15 | }; 16 | 17 | layout(location = 0) in vec3 VtxPos; 18 | layout(location = 1) in vec3 VtxNorm; 19 | layout(location = 2) in vec2 VtxTex; 20 | // layout(location = 3) in vec4 VtxColor; 21 | 22 | layout(location = 0) out vec4 Pos; 23 | layout(location = 1) out vec3 Norm; 24 | layout(location = 2) out vec3 CamDir; 25 | layout(location = 3) out vec2 TexCoord; 26 | 27 | void main() { 28 | vec4 vPos = vec4(VtxPos, 1.0); 29 | vec4 vNorm = vec4(VtxNorm, 1.0); 30 | mat4 MMtx = ModelMtx; 31 | MMtx[3][3] = 1; 32 | mat4 MVMtx = ViewMtx * MMtx; 33 | Pos = MVMtx * vPos; 34 | mat3 NormMtx = transpose(inverse(mat3(MVMtx))); 35 | Norm = normalize(NormMtx * VtxNorm).xyz; 36 | CamDir = normalize(-Pos.xyz); 37 | TexCoord = VtxTex; 38 | gl_Position = PrjnMtx * MVMtx * vPos; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /vphong/system.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | import ( 8 | "embed" 9 | "unsafe" 10 | 11 | "cogentcore.org/core/math32" 12 | "cogentcore.org/core/vgpu" 13 | vk "github.com/goki/vulkan" 14 | ) 15 | 16 | //go:embed shaders/*.spv 17 | var content embed.FS 18 | 19 | // CurRender holds info about the current render as updated by 20 | // Use* methods -- determines which pipeline is used. 21 | // Default is single color. 22 | type CurRender struct { 23 | 24 | // index of descriptor collection to use -- for threaded / parallel rendering -- see vgup.Vars NDescs for more info 25 | DescIndex int 26 | 27 | // a texture was selected -- if true, overrides other options 28 | UseTexture bool 29 | 30 | // a per-vertex color was selected 31 | UseVtxColor bool 32 | 33 | // current model pose matrix 34 | ModelMtx math32.Matrix4 35 | 36 | // camera view and projection matrixes 37 | VPMtx Mtxs 38 | 39 | // current color surface properties 40 | Color Colors 41 | 42 | // texture parameters -- repeat, offset 43 | TexPars TexPars 44 | 45 | // index of currently selected texture 46 | TexIndex int 47 | } 48 | 49 | // PushU is the push constants structure, holding everything that 50 | // updates per object -- avoids any limitations on capacity. 51 | type PushU struct { 52 | 53 | // Model Matrix: poses object in world coordinates 54 | ModelMtx math32.Matrix4 55 | 56 | // surface colors 57 | Color Colors 58 | 59 | // texture parameters 60 | Tex TexPars 61 | } 62 | 63 | // NewPush generates a new Push object based on current render settings 64 | // unsafe.Pointer does not work having this be inside the CurRender obj itself 65 | // so we create one afresh. 66 | func (cr *CurRender) NewPush() *PushU { 67 | pu := &PushU{} 68 | pu.ModelMtx = cr.ModelMtx 69 | pu.Color = cr.Color 70 | // tex set specifically in tex 71 | return pu 72 | } 73 | 74 | // ConfigPipeline configures graphics settings on the pipeline 75 | func (ph *Phong) ConfigPipeline(pl *vgpu.Pipeline) { 76 | pl.SetGraphicsDefaults() 77 | if ph.Wireframe { 78 | pl.SetRasterization(vk.PolygonModeLine, vk.CullModeNone, vk.FrontFaceCounterClockwise, 1.0) 79 | } else { 80 | pl.SetRasterization(vk.PolygonModeFill, vk.CullModeNone, vk.FrontFaceCounterClockwise, 1.0) 81 | } 82 | } 83 | 84 | // ConfigSys configures the vDraw System and pipelines. 85 | func (ph *Phong) ConfigSys() { 86 | tpl := ph.Sys.NewPipeline("texture") 87 | ph.ConfigPipeline(tpl) 88 | opl := ph.Sys.NewPipeline("onecolor") 89 | ph.ConfigPipeline(opl) 90 | vpl := ph.Sys.NewPipeline("pervertex") 91 | ph.ConfigPipeline(vpl) 92 | 93 | tpl.AddShaderEmbed("texture_vert", vgpu.VertexShader, content, "shaders/texture_vert.spv") 94 | tpl.AddShaderEmbed("texture_frag", vgpu.FragmentShader, content, "shaders/texture_frag.spv") 95 | 96 | opl.AddShaderEmbed("onecolor_vert", vgpu.VertexShader, content, "shaders/onecolor_vert.spv") 97 | opl.AddShaderEmbed("onecolor_frag", vgpu.FragmentShader, content, "shaders/onecolor_frag.spv") 98 | 99 | vpl.AddShaderEmbed("pervertex_vert", vgpu.VertexShader, content, "shaders/pervertex_vert.spv") 100 | vpl.AddShaderEmbed("pervertex_frag", vgpu.FragmentShader, content, "shaders/pervertex_frag.spv") 101 | 102 | vars := ph.Sys.Vars() 103 | vars.NDescs = 1 // > 1 causes mysterious failures.. 104 | pcset := vars.AddPushSet() // TexPush 105 | vset := vars.AddVertexSet() 106 | mtxset := vars.AddSet() // set = 0 107 | nliteset := vars.AddSet() // set = 1 108 | liteset := vars.AddSet() // set = 2 109 | txset := vars.AddSet() // set = 3 110 | 111 | vector4sz := vgpu.Float32Vector4.Bytes() 112 | 113 | vset.Add("Pos", vgpu.Float32Vector3, 0, vgpu.Vertex, vgpu.VertexShader) 114 | vset.Add("Norm", vgpu.Float32Vector3, 0, vgpu.Vertex, vgpu.VertexShader) 115 | vset.Add("Tex", vgpu.Float32Vector2, 0, vgpu.Vertex, vgpu.VertexShader) 116 | vset.Add("Color", vgpu.Float32Vector4, 0, vgpu.Vertex, vgpu.VertexShader) 117 | vset.Add("Index", vgpu.Uint32, 0, vgpu.Index, vgpu.VertexShader) 118 | 119 | pcset.AddStruct("PushU", int(unsafe.Sizeof(PushU{})), 1, vgpu.Push, vgpu.VertexShader, vgpu.FragmentShader) 120 | 121 | mtxset.AddStruct("Mtxs", vgpu.Float32Matrix4.Bytes()*2, 1, vgpu.Uniform, vgpu.VertexShader, vgpu.FragmentShader) 122 | 123 | nliteset.AddStruct("NLights", 4*4, 1, vgpu.Uniform, vgpu.FragmentShader) 124 | liteset.AddStruct("AmbLights", vector4sz*1, MaxLights, vgpu.Uniform, vgpu.FragmentShader) 125 | liteset.AddStruct("DirLights", vector4sz*2, MaxLights, vgpu.Uniform, vgpu.FragmentShader) 126 | liteset.AddStruct("PointLights", vector4sz*3, MaxLights, vgpu.Uniform, vgpu.FragmentShader) 127 | liteset.AddStruct("SpotLights", vector4sz*4, MaxLights, vgpu.Uniform, vgpu.FragmentShader) 128 | 129 | txset.Add("Tex", vgpu.ImageRGBA32, 1, vgpu.TextureRole, vgpu.FragmentShader) 130 | // tximgv.TextureOwns = true 131 | 132 | pcset.ConfigValues(1) 133 | mtxset.ConfigValues(1) 134 | nliteset.ConfigValues(1) 135 | liteset.ConfigValues(1) 136 | } 137 | 138 | // Push pushes given push constant data 139 | func (ph *Phong) Push(pl *vgpu.Pipeline, push *PushU) { 140 | sy := &ph.Sys 141 | cmd := sy.CmdPool.Buff 142 | vars := sy.Vars() 143 | pvar, _ := vars.VarByNameTry(int(vgpu.PushSet), "PushU") 144 | pl.Push(cmd, pvar, unsafe.Pointer(push)) 145 | } 146 | -------------------------------------------------------------------------------- /vphong/vphong.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vphong 6 | 7 | //go:generate core generate 8 | 9 | import ( 10 | "sync" 11 | 12 | "cogentcore.org/core/base/ordmap" 13 | "cogentcore.org/core/vgpu" 14 | ) 15 | 16 | // MaxLights is upper limit on number of any given type of light 17 | const MaxLights = 8 18 | 19 | // Phong implements standard Blinn-Phong rendering pipelines in a vgpu System. 20 | // Must Add all Lights, Meshes, Colors, Textures first, and call 21 | // Config() to configure everything prior to first RenderStart. 22 | // 23 | // Meshes are configured initially with numbers of points, then 24 | // after Config(), points are set by calling MeshFloatsBy* and 25 | // assigning values. 26 | // 27 | // If any changes are made to numbers or sizes of anything, 28 | // you must call Config() again. 29 | // 30 | // Changes to data only can be synced by calling Sync() 31 | // 32 | // Rendering starts with RenderStart, followed by Use* calls 33 | // to specify the parameters for each item, and then a Draw call 34 | // to add the rendering command, followed by RenderEnd. 35 | type Phong struct { 36 | 37 | // number of each type of light 38 | NLights NLights 39 | 40 | // ambient lights 41 | Ambient [MaxLights]AmbientLight 42 | 43 | // directional lights 44 | Dir [MaxLights]DirLight 45 | 46 | // point lights 47 | Point [MaxLights]PointLight 48 | 49 | // spot lights 50 | Spot [MaxLights]SpotLight 51 | 52 | // render using wireframe instead of filled polygons -- this must be set prior to configuring the Phong rendering system 53 | Wireframe bool `default:"false"` 54 | 55 | // state for current rendering 56 | Cur CurRender 57 | 58 | // meshes -- holds all the mesh data -- must be configured prior to rendering 59 | Meshes ordmap.Map[string, *Mesh] 60 | 61 | // textures -- must be configured prior to rendering -- a maximum of 16 textures is supported for full cross-platform portability 62 | Textures ordmap.Map[string, *Texture] 63 | 64 | // colors, optionally available for looking up by name -- not used directly in rendering 65 | Colors ordmap.Map[string, *Colors] 66 | 67 | // rendering system 68 | Sys vgpu.System 69 | 70 | // mutex on updating 71 | UpdateMu sync.Mutex `display:"-" copier:"-" json:"-" xml:"-"` 72 | } 73 | 74 | func (ph *Phong) Destroy() { 75 | ph.Sys.Destroy() 76 | } 77 | 78 | // Config configures everything after everything has been Added 79 | func (ph *Phong) Config() { 80 | ph.ConfigMeshesTextures() 81 | ph.UpdateMu.Lock() 82 | ph.Sys.Config() 83 | 84 | ph.ConfigLights() 85 | ph.AllocTextures() 86 | ph.Sys.Mem.SyncToGPU() 87 | ph.UpdateMu.Unlock() 88 | } 89 | 90 | // ConfigMeshesTextures configures the Meshes and Textures based 91 | // on everything added in the Phong config, prior to Sys.Config() 92 | // which does host allocation. 93 | func (ph *Phong) ConfigMeshesTextures() { 94 | ph.UpdateMu.Lock() 95 | ph.Sys.Mem.Free() 96 | ph.ConfigMeshes() 97 | ph.ConfigTextures() 98 | ph.UpdateMu.Unlock() 99 | } 100 | 101 | // Sync synchronizes any changes in val data up to GPU device memory. 102 | // any changes in numbers or sizes of any element requires a Config call. 103 | func (ph *Phong) Sync() { 104 | ph.UpdateMu.Lock() 105 | ph.Sys.Mem.SyncToGPU() 106 | ph.UpdateMu.Unlock() 107 | } 108 | 109 | /////////////////////////////////////////////////// 110 | // Rendering 111 | 112 | // Render does one step of rendering given current Use* settings 113 | func (ph *Phong) Render() { 114 | ph.UpdateMu.Lock() 115 | sy := &ph.Sys 116 | cmd := sy.CmdPool.Buff 117 | sy.CmdBindVars(cmd, 0) // updates all dynamics 118 | switch { 119 | case ph.Cur.UseTexture: 120 | ph.RenderTexture() 121 | case ph.Cur.UseVtxColor: 122 | ph.RenderVtxColor() 123 | default: 124 | ph.RenderOnecolor() 125 | } 126 | ph.UpdateMu.Unlock() 127 | } 128 | -------------------------------------------------------------------------------- /vshape/README.md: -------------------------------------------------------------------------------- 1 | # vshape: Compositional 3D shape library 2 | 3 | `vshape` provides a library of 3D shapes, built from indexed triangle meshes, which can be added together in `ShapeGroup` lists. Each `Shape` can report the number of points and indexes based on configured parameters, and keeps track of its offset within an overall `math32.ArrayF32` allocated based on total numbers. In this way, separate Allocate then Configure phases are supported, as required by the vgpu Memory allocation system. 4 | 5 | It only has a dependency on the [math32](https://cogentcore.org/core/math32) package and could be used for anything. 6 | 7 | Basic building blocks (e.g., Plane, SphereSector) have standalone methods, in addition to Shape elements. 8 | 9 | Here are the shapes: 10 | 11 | * Plane 12 | * Box 13 | * Sphere (including various partial segments thereof) 14 | * Cylinder, Cone (including different size top and bottom radii, e.g., Cones) 15 | * Capsule: a cylinder with half-sphere caps -- good for simple body segments 16 | 17 | 18 | -------------------------------------------------------------------------------- /vshape/box.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // Box is a rectangular-shaped solid (cuboid) 10 | type Box struct { 11 | ShapeBase 12 | 13 | // size along each dimension 14 | Size math32.Vector3 15 | 16 | // number of segments to divide each plane into (enforced to be at least 1) -- may potentially increase rendering quality to have > 1 17 | Segs math32.Vector3i 18 | } 19 | 20 | // NewBox returns a Box shape with given size 21 | func NewBox(width, height, depth float32) *Box { 22 | bx := &Box{} 23 | bx.Defaults() 24 | bx.Size.Set(width, height, depth) 25 | return bx 26 | } 27 | 28 | func (bx *Box) Defaults() { 29 | bx.Size.Set(1, 1, 1) 30 | bx.Segs.Set(1, 1, 1) 31 | } 32 | 33 | func (bx *Box) N() (numVertex, nIndex int) { 34 | numVertex, nIndex = BoxN(bx.Segs) 35 | return 36 | } 37 | 38 | // SetBox sets points in given allocated arrays 39 | func (bx *Box) Set(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32) { 40 | hSz := SetBox(vertexArray, normArray, textureArray, indexArray, bx.VtxOff, bx.IndexOff, bx.Size, bx.Segs, bx.Pos) 41 | 42 | mn := bx.Pos.Sub(hSz) 43 | mx := bx.Pos.Add(hSz) 44 | bx.CBBox.Set(&mn, &mx) 45 | } 46 | 47 | // PlaneN returns the N's for a single plane's worth of 48 | // vertex and index data with given number of segments. 49 | // Note: In *vertex* units, not float units (i.e., x3 to get 50 | // actual float offset in Vtx array). 51 | func BoxN(segs math32.Vector3i) (numVertex, nIndex int) { 52 | nv, ni := PlaneN(int(segs.X), int(segs.Y)) 53 | numVertex += 2 * nv 54 | nIndex += 2 * ni 55 | nv, ni = PlaneN(int(segs.X), int(segs.Z)) 56 | numVertex += 2 * nv 57 | nIndex += 2 * ni 58 | nv, ni = PlaneN(int(segs.Z), int(segs.Y)) 59 | numVertex += 2 * nv 60 | nIndex += 2 * ni 61 | return 62 | } 63 | 64 | // SetBox sets box vertex, norm, tex, index data at 65 | // given starting *vertex* index (i.e., multiply this *3 to get 66 | // actual float offset in Vtx array), and starting Index index. 67 | // for given 3D size, and given number of segments per side. 68 | // finely subdividing a plane allows for higher-quality lighting 69 | // and texture rendering (minimum of 1 will be enforced). 70 | // pos is a 3D position offset. returns 3D size of plane. 71 | func SetBox(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32, vtxOff, idxOff int, size math32.Vector3, segs math32.Vector3i, pos math32.Vector3) math32.Vector3 { 72 | hSz := size.DivScalar(2) 73 | 74 | numVertex, nIndex := PlaneN(int(segs.X), int(segs.Y)) 75 | 76 | voff := vtxOff 77 | ioff := idxOff 78 | 79 | // start with neg z as typically back 80 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.X, math32.Y, -1, -1, size.X, size.Y, -hSz.X, -hSz.Y, -hSz.Z, int(segs.X), int(segs.Y), pos) // nz 81 | voff += numVertex 82 | ioff += nIndex 83 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.X, math32.Z, 1, -1, size.X, size.Z, -hSz.X, -hSz.Z, -hSz.Y, int(segs.X), int(segs.Z), pos) // ny 84 | voff += numVertex 85 | ioff += nIndex 86 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.Z, math32.Y, -1, -1, size.Z, size.Y, -hSz.Z, -hSz.Y, hSz.X, int(segs.Z), int(segs.Y), pos) // px 87 | voff += numVertex 88 | ioff += nIndex 89 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.Z, math32.Y, 1, -1, size.Z, size.Y, -hSz.Z, -hSz.Y, -hSz.X, int(segs.Z), int(segs.Y), pos) // nx 90 | voff += numVertex 91 | ioff += nIndex 92 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.X, math32.Z, 1, 1, size.X, size.Z, -hSz.X, -hSz.Z, hSz.Y, int(segs.X), int(segs.Z), pos) // py 93 | voff += numVertex 94 | ioff += nIndex 95 | SetPlane(vertexArray, normArray, textureArray, indexArray, voff, ioff, math32.X, math32.Y, 1, -1, size.X, size.Y, -hSz.X, -hSz.Y, hSz.Z, int(segs.X), int(segs.Y), pos) // pz 96 | return hSz 97 | } 98 | -------------------------------------------------------------------------------- /vshape/capsule.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // Capsule is a generalized capsule shape: a cylinder with hemisphere end caps. 10 | // Supports different radii on each end. 11 | // Height is along the Y axis -- total height is Height + TopRad + BotRad. 12 | type Capsule struct { 13 | ShapeBase 14 | 15 | // height of the cylinder portion 16 | Height float32 17 | 18 | // radius of the top -- set to 0 to omit top cap 19 | TopRad float32 20 | 21 | // radius of the bottom -- set to 0 to omit bottom cap 22 | BotRad float32 23 | 24 | // number of radial segments (32 is a reasonable default for full circle) 25 | RadialSegs int `min:"1"` 26 | 27 | // number of height segments 28 | HeightSegs int 29 | 30 | // number of segments in the hemisphere cap ends (16 is a reasonable default) 31 | CapSegs int 32 | 33 | // starting angle in degrees, relative to -1,0,0 left side starting point 34 | AngStart float32 `min:"0" max:"360" step:"5"` 35 | 36 | // total angle to generate in degrees (max 360) 37 | AngLen float32 `min:"0" max:"360" step:"5"` 38 | } 39 | 40 | // NewCapsule returns a Capsule shape with given radius, height, 41 | // number of radial segments, number of height segments, 42 | // and presence of a top and/or bottom cap. 43 | // Height is along the Y axis. 44 | func NewCapsule(height, radius float32, segs, heightSegs int) *Capsule { 45 | cp := &Capsule{} 46 | cp.Defaults() 47 | 48 | cp.Height = height 49 | cp.TopRad = radius 50 | cp.BotRad = radius 51 | cp.RadialSegs = segs 52 | cp.CapSegs = segs 53 | cp.HeightSegs = heightSegs 54 | return cp 55 | } 56 | 57 | func (cp *Capsule) Defaults() { 58 | cp.Height = 1 59 | cp.TopRad = 0.5 60 | cp.BotRad = 0.5 61 | cp.RadialSegs = 32 62 | cp.HeightSegs = 32 63 | cp.CapSegs = 32 64 | cp.AngStart = 0 65 | cp.AngLen = 360 66 | } 67 | 68 | func (cp *Capsule) N() (numVertex, nIndex int) { 69 | numVertex, nIndex = CylinderSectorN(cp.RadialSegs, cp.HeightSegs, false, false) 70 | if cp.BotRad > 0 { 71 | nv, ni := SphereSectorN(cp.RadialSegs, cp.CapSegs, 90, 90) 72 | numVertex += nv 73 | nIndex += ni 74 | } 75 | if cp.TopRad > 0 { 76 | nv, ni := SphereSectorN(cp.RadialSegs, cp.CapSegs, 0, 90) 77 | numVertex += nv 78 | nIndex += ni 79 | } 80 | return 81 | } 82 | 83 | // SetCapsuleSector sets points in given allocated arrays 84 | func (cp *Capsule) Set(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32) { 85 | voff := cp.VtxOff 86 | ioff := cp.IndexOff 87 | cp.CBBox = SetCylinderSector(vertexArray, normArray, textureArray, indexArray, voff, ioff, cp.Height, cp.TopRad, cp.BotRad, cp.RadialSegs, cp.HeightSegs, cp.AngStart, cp.AngLen, false, false, cp.Pos) 88 | nv, ni := CylinderSectorN(cp.RadialSegs, cp.HeightSegs, false, false) 89 | voff += nv 90 | ioff += ni 91 | 92 | if cp.BotRad > 0 { 93 | ps := cp.Pos 94 | ps.Y -= cp.Height / 2 95 | cbb := SetSphereSector(vertexArray, normArray, textureArray, indexArray, voff, ioff, cp.BotRad, cp.RadialSegs, cp.CapSegs, cp.AngStart, cp.AngLen, 90, 90, ps) 96 | cp.CBBox.ExpandByBox(cbb) 97 | nv, ni = SphereSectorN(cp.RadialSegs, cp.CapSegs, 90, 90) 98 | voff += nv 99 | ioff += ni 100 | } 101 | if cp.TopRad > 0 { 102 | ps := cp.Pos 103 | ps.Y += cp.Height / 2 104 | cbb := SetSphereSector(vertexArray, normArray, textureArray, indexArray, voff, ioff, cp.TopRad, cp.RadialSegs, cp.CapSegs, cp.AngStart, cp.AngLen, 0, 90, ps) 105 | cp.CBBox.ExpandByBox(cbb) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /vshape/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | vshape provides a library of 3D shapes, built from indexed triangle meshes, which can be added together in `ShapeGroup` lists. Each `Shape` can report the number of points and indexes based on configured parameters, and keeps track of its offset within an overall `math32.ArrayF32` allocated based on total numbers. In this way, separate Allocate then Configure phases are supported, as required by the vgpu Memory allocation system. 7 | 8 | Basic building blocks (e.g., Plane, SphereSector) have standalone methods, in addition to Shape elements. 9 | */ 10 | package vshape 11 | -------------------------------------------------------------------------------- /vshape/group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // ShapeGroup is a group of shapes -- returns summary data for shape elements 10 | type ShapeGroup struct { 11 | ShapeBase 12 | 13 | // list of shapes in group 14 | Shapes []Shape 15 | } 16 | 17 | // N returns number of vertex, index points in this shape element. 18 | func (sb *ShapeGroup) N() (numVertex, nIndex int) { 19 | numVertex = 0 20 | nIndex = 0 21 | for _, sh := range sb.Shapes { 22 | nv, ni := sh.N() 23 | numVertex += nv 24 | nIndex += ni 25 | } 26 | return 27 | } 28 | 29 | // Set sets points in given allocated arrays, also updates offsets 30 | func (sb *ShapeGroup) Set(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32) { 31 | vo := sb.VtxOff 32 | io := sb.IndexOff 33 | sb.CBBox.SetEmpty() 34 | for _, sh := range sb.Shapes { 35 | sh.SetOffs(vo, io) 36 | sh.Set(vertexArray, normArray, textureArray, indexArray) 37 | sb.CBBox.ExpandByBox(sh.BBox()) 38 | nv, ni := sh.N() 39 | vo += nv 40 | io += ni 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vshape/shape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import "cogentcore.org/core/math32" 8 | 9 | // Shape is an interface for all shape-constructing elements 10 | type Shape interface { 11 | // N returns number of vertex, index points in this shape element 12 | N() (numVertex, nIndex int) 13 | 14 | // Offs returns starting offset for vertices, indexes in full shape array, 15 | // in terms of points, not floats 16 | Offs() (vtxOff, idxOff int) 17 | 18 | // SetOffs sets starting offset for vertices, indexes in full shape array, 19 | // in terms of points, not floats 20 | SetOffs(vtxOff, idxOff int) 21 | 22 | // Set sets points in given allocated arrays 23 | Set(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32) 24 | 25 | // BBox returns the bounding box for the shape, typically centered around 0 26 | // This is only valid after Set has been called. 27 | BBox() math32.Box3 28 | } 29 | 30 | // ShapeBase is the base shape element 31 | type ShapeBase struct { 32 | 33 | // vertex offset, in points 34 | VtxOff int 35 | 36 | // index offset, in points 37 | IndexOff int 38 | 39 | // cubic bounding box in local coords 40 | CBBox math32.Box3 41 | 42 | // all shapes take a 3D position offset to enable composition 43 | Pos math32.Vector3 44 | } 45 | 46 | // Offs returns starting offset for vertices, indexes in full shape array, 47 | // in terms of points, not floats 48 | func (sb *ShapeBase) Offs() (vtxOff, idxOff int) { 49 | vtxOff, idxOff = sb.VtxOff, sb.IndexOff 50 | return 51 | } 52 | 53 | // SetOffs sets starting offsets for vertices, indexes in full shape array 54 | func (sb *ShapeBase) SetOffs(vtxOff, idxOff int) { 55 | sb.VtxOff, sb.IndexOff = vtxOff, idxOff 56 | } 57 | 58 | // BBox returns the bounding box for the shape, typically centered around 0 59 | // This is only valid after Set has been called. 60 | func (sb *ShapeBase) BBox() math32.Box3 { 61 | return sb.CBBox 62 | } 63 | 64 | // SetColor sets color for given range of vertex indexes 65 | func SetColor(colorArray math32.ArrayF32, vtxOff int, numVertex int, clr math32.Vector4) { 66 | cidx := vtxOff * 4 67 | for vi := 0; vi < numVertex; vi++ { 68 | clr.ToSlice(colorArray, cidx+vi*4) 69 | } 70 | } 71 | 72 | // BBoxFromVtxs returns the bounding box updated from the range of vertex points 73 | func BBoxFromVtxs(vertexArray math32.ArrayF32, vtxOff int, numVertex int) math32.Box3 { 74 | bb := math32.B3Empty() 75 | vidx := vtxOff * 3 76 | var vtx math32.Vector3 77 | for vi := 0; vi < numVertex; vi++ { 78 | vtx.FromSlice(vertexArray, vidx+vi*3) 79 | bb.ExpandByPoint(vtx) 80 | } 81 | return bb 82 | } 83 | -------------------------------------------------------------------------------- /vshape/torus.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import ( 8 | "math" 9 | 10 | "cogentcore.org/core/math32" 11 | ) 12 | 13 | // Torus is a torus mesh, defined by the radius of the solid tube and the 14 | // larger radius of the ring. 15 | type Torus struct { 16 | ShapeBase 17 | 18 | // larger radius of the torus ring 19 | Radius float32 20 | 21 | // radius of the solid tube 22 | TubeRadius float32 23 | 24 | // number of segments around the radius of the torus (32 is reasonable default for full circle) 25 | RadialSegs int `min:"1"` 26 | 27 | // number of segments for the tube itself (32 is reasonable default for full height) 28 | TubeSegs int `min:"1"` 29 | 30 | // starting radial angle in degrees relative to 1,0,0 starting point 31 | AngStart float32 `min:"0" max:"360" step:"5"` 32 | 33 | // total radial angle to generate in degrees (max = 360) 34 | AngLen float32 `min:"0" max:"360" step:"5"` 35 | } 36 | 37 | // NewTorus returns a Torus mesh with the specified outer ring radius, 38 | // solid tube radius, and number of segments (resolution). 39 | func NewTorus(radius, tubeRadius float32, segs int) *Torus { 40 | tr := &Torus{} 41 | tr.Defaults() 42 | tr.Radius = radius 43 | tr.TubeRadius = tubeRadius 44 | tr.RadialSegs = segs 45 | tr.TubeSegs = segs 46 | return tr 47 | } 48 | 49 | func (tr *Torus) Defaults() { 50 | tr.Radius = 1 51 | tr.TubeRadius = .1 52 | tr.RadialSegs = 32 53 | tr.TubeSegs = 32 54 | tr.AngStart = 0 55 | tr.AngLen = 360 56 | } 57 | 58 | func (tr *Torus) N() (numVertex, nIndex int) { 59 | numVertex, nIndex = TorusSectorN(tr.RadialSegs, tr.TubeSegs) 60 | return 61 | } 62 | 63 | // Set sets points for torus in given allocated arrays 64 | func (tr *Torus) Set(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32) { 65 | tr.CBBox = SetTorusSector(vertexArray, normArray, textureArray, indexArray, tr.VtxOff, tr.IndexOff, tr.Radius, tr.TubeRadius, tr.RadialSegs, tr.TubeSegs, tr.AngStart, tr.AngLen, tr.Pos) 66 | } 67 | 68 | // TorusSectorN returns N's for a torus geometry with 69 | // number of radial segments, number of tubular segments, 70 | // radial sector start angle and length in degrees (0 - 360) 71 | func TorusSectorN(radialSegs, tubeSegs int) (numVertex, nIndex int) { 72 | numVertex = (radialSegs + 1) * (tubeSegs + 1) 73 | nIndex = radialSegs * tubeSegs * 6 74 | return 75 | } 76 | 77 | // SetTorusSector sets torus sector vertex, norm, tex, index data 78 | // at given starting *vertex* index (i.e., multiply this *3 to get 79 | // actual float offset in Vtx array), and starting Index index, 80 | // with the specified revolution radius, tube radius, 81 | // number of radial segments, number of tubular segments, 82 | // radial sector start angle and length in degrees (0 - 360) 83 | // pos is an arbitrary offset (for composing shapes), 84 | // returns bounding box. 85 | func SetTorusSector(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32, vtxOff, idxOff int, radius, tubeRadius float32, radialSegs, tubeSegs int, angStart, angLen float32, pos math32.Vector3) math32.Box3 { 86 | angStRad := math32.DegToRad(angStart) 87 | angLenRad := math32.DegToRad(angLen) 88 | 89 | idx := 0 90 | vidx := vtxOff * 3 91 | tidx := vtxOff * 2 92 | 93 | bb := math32.Box3{} 94 | bb.SetEmpty() 95 | 96 | var center math32.Vector3 97 | for j := 0; j <= radialSegs; j++ { 98 | for i := 0; i <= tubeSegs; i++ { 99 | u := angStRad + float32(i)/float32(tubeSegs)*angLenRad 100 | v := float32(j) / float32(radialSegs) * math.Pi * 2 101 | 102 | center.X = radius * math32.Cos(u) 103 | center.Y = radius * math32.Sin(u) 104 | 105 | var pt math32.Vector3 106 | pt.X = (radius + tubeRadius*math32.Cos(v)) * math32.Cos(u) 107 | pt.Y = (radius + tubeRadius*math32.Cos(v)) * math32.Sin(u) 108 | pt.Z = tubeRadius * math32.Sin(v) 109 | pt.SetAdd(pos) 110 | vertexArray.SetVector3(vidx+idx*3, pt) 111 | textureArray.Set(tidx+idx*2, float32(i)/float32(tubeSegs), float32(j)/float32(radialSegs)) 112 | normArray.SetVector3(vidx+idx*3, pt.Sub(center).Normal()) 113 | bb.ExpandByPoint(pt) 114 | idx++ 115 | } 116 | } 117 | 118 | vOff := uint32(vtxOff) 119 | ii := idxOff 120 | for j := 1; j <= radialSegs; j++ { 121 | for i := 1; i <= tubeSegs; i++ { 122 | a := (tubeSegs+1)*j + i - 1 123 | b := (tubeSegs+1)*(j-1) + i - 1 124 | c := (tubeSegs+1)*(j-1) + i 125 | d := (tubeSegs+1)*j + i 126 | indexArray.Set(ii, vOff+uint32(a), vOff+uint32(b), vOff+uint32(d), vOff+uint32(b), vOff+uint32(c), vOff+uint32(d)) 127 | ii += 6 128 | } 129 | } 130 | return bb 131 | } 132 | -------------------------------------------------------------------------------- /vshape/triangle.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Cogent Core. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vshape 6 | 7 | import ( 8 | "cogentcore.org/core/math32" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////// 12 | // Triangle 13 | 14 | // TriangleN returns 3, 3 15 | func TriangleN() (numVertex, nIndex int) { 16 | return 3, 3 17 | } 18 | 19 | // SetTriangle sets one triangle of vertex data indexes, and optionally 20 | // texUV coords, at given starting *vertex* index (i.e., multiply this *3 21 | // to get actual float offset in Vtx array), and starting Index index. 22 | // Norm is auto-computed, and bounds expanded. 23 | // pos is a 3D position offset. returns 3D size of plane. 24 | // returns bounding box. 25 | func SetTriangle(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32, vtxOff, idxOff int, a, b, c math32.Vector3, texs []math32.Vector2, pos math32.Vector3) math32.Box3 { 26 | hasTex := texs != nil 27 | vidx := vtxOff * 3 28 | tidx := vtxOff * 2 29 | 30 | norm := math32.Normal(a, b, c) 31 | 32 | a.Add(pos).ToSlice(vertexArray, vidx) 33 | norm.ToSlice(normArray, vidx) 34 | b.Add(pos).ToSlice(vertexArray, vidx+3) 35 | norm.ToSlice(normArray, vidx+3) 36 | c.Add(pos).ToSlice(vertexArray, vidx+6) 37 | norm.ToSlice(normArray, vidx+6) 38 | if hasTex { 39 | texs[0].ToSlice(textureArray, tidx) 40 | texs[1].ToSlice(textureArray, tidx+2) 41 | texs[2].ToSlice(textureArray, tidx+4) 42 | } 43 | 44 | indexArray.Set(idxOff, uint32(vtxOff), uint32(vtxOff+1), uint32(vtxOff+2)) 45 | 46 | bb := math32.B3Empty() 47 | bb.ExpandByPoints([]math32.Vector3{a, b, c}) 48 | return bb 49 | } 50 | 51 | //////////////////////////////////////////////////////////////// 52 | // Quad 53 | 54 | // QuadN returns 4, 6 55 | func QuadN() (numVertex, nIndex int) { 56 | return 4, 6 57 | } 58 | 59 | // SetQuad sets quad vertex data (optionally texUV, color, and indexes) 60 | // at given starting *vertex* index (i.e., multiply this *3 to get actual float 61 | // offset in Vtx array), and starting Index index. 62 | // Norm is auto-computed, and bbox expanded by points. 63 | // pos is a 3D position offset. returns 3D size of plane. 64 | // returns bounding box. 65 | func SetQuad(vertexArray, normArray, textureArray math32.ArrayF32, indexArray math32.ArrayU32, vtxOff, idxOff int, vtxs []math32.Vector3, texs []math32.Vector2, pos math32.Vector3) math32.Box3 { 66 | hasTex := texs != nil 67 | vidx := vtxOff * 3 68 | tidx := vtxOff * 2 69 | 70 | norm := math32.Normal(vtxs[0], vtxs[1], vtxs[2]) 71 | 72 | for vi := range vtxs { 73 | vtxs[vi].Add(pos).ToSlice(vertexArray, vidx) 74 | norm.ToSlice(normArray, vidx) 75 | vidx += 3 76 | if hasTex { 77 | texs[vi].ToSlice(textureArray, tidx) 78 | tidx += 2 79 | } 80 | } 81 | 82 | indexArray.Set(idxOff, uint32(vtxOff), uint32(vtxOff+1), uint32(vtxOff+2), 83 | uint32(vtxOff), uint32(vtxOff+2), uint32(vtxOff+3)) 84 | 85 | bb := math32.B3Empty() 86 | bb.ExpandByPoints(vtxs) 87 | return bb 88 | } 89 | --------------------------------------------------------------------------------